专栏首页偏前端工程师的驿站JS魔法堂:函数重载 之 获取变量的数据类型

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*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                             

  尊重原创,转载请注明

Thanks                              

http://segmentfault.com/q/1010000000669230

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JS魔法堂:剖析源码理解Promises/A规范

    一、前言                                 Promises/A是由CommonJS组织制定的异步模式编程规范,有不少库已根据该规...

    ^_^肥仔John
  • Design Pattern: Not Just Mixin Pattern

    Brief                                 从Mix-In模式到Mixin模式,中文常用翻译为“混入/织入模式”。单纯从名字上看...

    ^_^肥仔John
  • 意译:自调用函数表达式

    一、写在前面   本文将一如既往地遵循从自身理解出发,而非100%按原文逐句翻译的方式进行“伪翻译”,若有谬误请各位指正,谢谢!! 二、介绍   IIFE(th...

    ^_^肥仔John
  • JQuery中常用方法备忘

    本文转载自博客园,原文地址 http://www.cnblogs.com/xzf158/archive/2008/10/14/logan.html

    乔达摩@嘿
  • 开启数据科学之旅

    不是很久以前,商人们往往找占星家来预测下明年是否能挣钱,虽然这毫无根据,并且结果也不确定,但如果听专家的建议来为自己的商业行为作出决定,与此有什么本质却别?现在...

    老齐
  • 微软数据科学负责人:数据不是现实,“数据驱动”会导致很多问题

    编者按:数据已经成为我们个人生活、业务和工作中越来越重要的一个部分。那些需要花时间解决棘手问题的人将依靠数据来帮助他们理解这个世界,并有所创新。本文作者 Bil...

    企鹅号小编
  • 中国人民大学文继荣:大数据的经验主义解释

    image.png 大数据似乎在一夜之间迅速走红,它势不可挡地冲击着金融、零售等各个行业。云计算将如何改变计算的世界?未来将有怎样的应用前景?如何解决“...

    腾讯研究院
  • ​画解算法:100. 相同的树

    https://leetcode-cn.com/problems/same-tree/

    灵魂画师牧码
  • 如何用Python读取开放数据?

    当你开始接触丰富多彩的开放数据集时,CSV、JSON和XML等格式名词就会奔涌而来。如何用Python高效地读取它们,为后续的整理和分析做准备呢?本文为你一步步...

    企鹅号小编
  • 独家 | 机器学习先驱Michael Jordan清华演讲汉化视频+PPT:计算思维,推断思维与数据科学

    大数据文摘

扫码关注云+社区

领取腾讯云代金券