文章出處

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*object\s*([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


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()