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
尊重原创,转载请注明
Thanks
http://segmentfault.com/q/1010000000669230
http://es5.github.io/#x15.2.4.2
- 以太坊·将数据写入到区块链中
- 如何使用Faster R-CNN来计算对象个数
- hyperledger v1.0.5 区块链运维入门
- 在TensorBoard中使用t-SNE实现TensorFlow自动编码器的可视化嵌入
- 以太坊智能合约开发入门
- CatBoost:一个自动处理分类(CAT)数据的机器学习库
- Python机器学习的练习八:异常检测和推荐系统
- Blade 模板中有关 section 的那些事
- 分布式计划任务设计与实现
- 怎样在Python的深度学习库Keras中使用度量
- 网络设备配置管理与版本控制
- 使用Python对Instagram进行数据分析
- 解决多标签分类问题(包括案例研究)
- Docker Compose + GPU + TensorFlow = Heart
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 这 6 点知识让我对 JavaScript 的对象有了更进一步的了解
- Linux进程间通信(中)之信号、信号量实践
- Linux进程间通信(下)之共享内存实践
- 手把手教你快速使用Vmware虚拟机安装Linux操作系统实验环境
- 天啊!鹅厂都开始做开发板了?网红腾讯物联网开发板终极开箱评测,让我们一睹为快!
- 我用 React 和 Vue 构建了同款应用,来看看哪里不一样(2020 版)
- 大点干!早点散----------深入剖析Redis集群原理与实验
- 微信群总是有人发广告?看我用Python写一个自动化机器人消灭他!
- 苏宁基于 ClickHouse 的大数据全链路监控实践
- CORS Cross Origin Resource Sharing
- 从0开始做播放器-第二季-第2章-Android NDK 工程的建立和 JNI 的基本用法
- 记一次线上问题排查-maven父子结构依赖所遇到的坑
- 『技术随手学』解决 pip conda install 网络故障中断
- boost asio
- 7.SwrContext音频重采样使用