zoukankan      html  css  js  c++  java
  • JS魔法堂:函数重载 之 获取变量的数据类型

    Brief                              

      有时我们需要根据入参的数据类型来决定调用哪个函数实现,就是说所谓的函数重载(function overloading)。因为JS没有内置函数重载的特性,正好给机会我们思考和实现一套这样的机制。

    使用方式:

    function foo(){
      return dispatch(this, arguments)
    }
    foo["object,number"] = function(o, n){console.log(o.toString() + ":" + n)}
    foo["string"] = function(s){console.log(s)}
    foo["array"] = function(a){console.log(a[0])}

    机制实现:

    ;(function(e/*xports*/){
      e.dispatch = function(thisValue, args){
        var rSignature = getSignature(args)
        for (var p in args.callee){
          if (rSignature.test(p)){
            return args.callee[p].apply(thisValue, args)
          }
        }
      }
      function getSignature(args){
        var arg, types = []
        for (var i = 0, len = args.length; i < len; ++i){
          arg = args[i]
          types.push(type(arg))
        }
        var rTypes = "^\s*" + types.join("s*,\s*") + "s*$"
        return RegExp(rTypes)
      }
      function type(val){
        // TODO
      }
    }(window))

     那现在问题就落在type函数的实现上了!

     关于获取变量的数据类型有typeof、Object.prototype.toString.call和obj.constructor.name三种方式,下面我们一起来了解一下!

    typeof Operator                        

      语法: typeof val 

      内部逻辑:

    function typeof(val){
      var ret = Type(val)
      if (ret === "Reference" 
          && IsUnresolvableReference(val)) return "undefined"
      ret = GetValue(val)
      ret = Type(ret)
      return ret
    }

      Type(val)抽象操作的逻辑:

      Undefined -> "undefined"

      Null -> "object"

      Boolean -> "boolean"

      Number -> "number"

      String -> "string"

      Objecjt,若对象为native object并且没有[[Call]]内置方法,则返回"object"

          若对象为native object或host object且有[[Call]]内置方法,则返回"function"

          若对象为host object并且没有[[Call]]内置方法,则返回除"undefined"、"boolean"、"number"和"string"外的数据类型字符串。

      native object,就是Math、{foo:1}、[]、new Object()和RegExp等JS规范中定义的对象,其中Math、RegExp等程序运行时立即被初始化的对象被称为built-in object。

      host object,就是宿主环境提供的对象,如浏览器的window和nodejs的global。

      从上述Type(val)抽象操作的逻辑得知:

        1. typeof能清晰区分Boolean、Number、String和Function的数据类型;

        2. 对于未声明和变量值为Undefined的变量无法区分,但对未声明的变量执行typeof操作不会报异常;

        3. typeof对Null、数组和对象是无能的。

      针对2、3点我们可以求助于 Object.prototype.toString.call(val) 。

    Object.prototype.toString.call(val)              

      Object.prototype.toString.call(val)或({}).toString.call(val)均是获取val的内置属性[[Class]]属性值,并经过加工后返回。

      内部逻辑:

    function Object.prototype.toString(){
      if (this === undefined) return "[object Undefined]"
      if (this === null) return "[object Null]"
      var o = ToObject(this)
      var clazz = o.[[Class]]
      return "[object " + clazz + "]"
    }

      注意:1. 由于内部硬编码null返回"[object Null]",因此虽然null本应不属于Object类型,但JS中我们依然将其当作Object来使用(历史+避免破坏已有库的兼容性,导致后来无法修正该错误了);

               2. 即使入参为primitive value,但内部还是会对其进行装箱操作(通过ToObject抽象操作)。

      那现在我们就需要了解一下[[Class]]内部属性了。

      内部属性[[Class]]

      在构造对象时会根据对象的类型设置[[Class]]的值,而其值类型为字符串。对于native object而言,其值范围是:Arguments
    Array、Boolean、Date、Error、Function、JSON、Math、Number、Object、RegExp、String。对于host object而言,则用HTMLElement、HTMLDocument等了。

          注意:[[Class]]是用于内部区分不同类型的对象。也就是仅支持JS语言规范和宿主环境提供的对象类型而已,而自定义的对象类型是无法存储在[[Class]]中。

    function Foo(){}
    var foo = new Foo()
    console.log(({}).toString.call(foo)) // 显示[object Object]

      于是我们需要求助于constructor.name属性了。

    obj.constructor.name                    

    function Foo(){}
    var foo = new Foo()
    console.log(foo.constructor.name) // 显示Foo

    那如果采用匿名函数表达式的方式定义构造函数呢?只能说直接没辙,要不在构造函数上添加个函数属性来保存(如Foo.className="Foo"),要不自己构建一个类工厂搞定。

    Implementaion of type function              

      综上所述得到如下实现:

    /*
     * 获取对象的数据类型
     * @method type
     * @param {Any} object - 获取数据类型的对象
     * @param {Function} [getClass] - 用户自定义获取数据类型的方法
     * @returns {String} 数据类型名称
     */
    function type(o/*bject*/, g/*etClass*/){
      var t = typeof o
      if ("object" !== t) return t.replace(/^[a-z]/, function(l){return l.toUpperCase()})
    
      var rType = /s*[s*objects*([0-9a-z]+)s*]s*/i
      t = ({}).toString.call(o)
      t = t.match(rType)[1]
      if ("Object" !== t) return t
    
      t = o.constructor.name
      if (!t && arguments.callee(g) === "Function"){
        t = g(o)
      }
      t = t || "Object"
      return t
    }

    Consolusion                             

      尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/5156912.html^_^肥子John

    Thanks                              

    http://segmentfault.com/q/1010000000669230

    http://es5.github.io/#x15.2.4.2

  • 相关阅读:
    hasCode in Java
    如何区分同一Class的不同实例对象
    如何构建XML文件
    Spring <context:property-placeholder/>的作用
    关于List的几个方法
    Java 中那些不常用的关键字
    设计模式
    Java源代码阅读-Object.toString()
    修复启动项
    centos关闭防火前
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/5156912.html
Copyright © 2011-2022 走看看