一、typeof
|
|
使用typeof检测数据类型,首先返回的都是一个字符串,其次字符串中包含了对应的数据类型,例如:”number”、”string”、”boolean”、”undefined”、”function”、”object”
typeof (引用类型) 除了函数, 都是 ‘object’,比如 typeof /123/
typeof null 为’object’
typeof undefined 为 ‘undefined’,通常, 如果使用两等号, null == undefined 为真.
转换为数字的常见用法 “10”-0或+”10”, 如果没有转换成功,返回NaN,由于NaN 的一个特性: NaN != NaN,故判断转换成功与否的常见做法: (这也是我参见 jQuery的源码发现的,jQuery源码读100遍都不为过)
12("10x" - 0) == ("10x" - 0);// 结果为假!
面试题:
|
|
typeof的局限性:不能具体的细分是数组还是正则,还是对象中其他的值,因为使用typeof检测数据类型,对于对象数据类型中的所有的值,最后返回的结果都是”object”。
应用一:添加默认值
|
|
应用二:回调函数调用
|
|
二、instanceof
用于判断一个变量是否某个对象的实例,或用于判断一个变量是否某个对象的实例;
|
|
只要在当前实例的原型链上,用instanceof检测出来的结果都是true,所以在类的原型继承中,最后检测出来的结果未必是正确的,例如:
|
|
三、constructor
用于判断一个变量的原型,constructor 属性返回对创建此对象的数组函数的引用
Javascript中对象的prototype属性的解释是:返回对象类型原型的引用
constructor即构造函数,作用和instanceof非常的相似。
|
|
constructor可以处理基本数据类型的检测
|
|
constructor检测Object和instanceof不一样,一般情况下是检测不了的
|
|
constructor的局限性:我们可以把类的原型进行重写,在重写的过程中,很有可能把之前的constructor给覆盖了,这样检测出来的结果就是不准确的。
|
|
对于特殊的数据类型null和undefined,它们的所属类型是Null和Undefined,但是浏览器把这两个类保护起来了,不允许在外面访问使用。
|
|
四、Object.prototype.toStrong.call()
toString的理解:
咋一看应该是转换为字符串,但是某些toString方法不仅仅是转换为字符串。对于Number、String、Boolean、Array、RegExp、Date、Function原型上的toString方法都是把当前的数据类转换为字符串的类型(它们的作用仅仅是用来转换为字符串的)
Object.prototype.toString并不是用来转换为字符串的。
12({name:"iceman"}).toString() // --> "[object Object]"Math.toString() // --> "[object Math]"
Number.prototype.toString是转换字符串的
|
|
Object.prototype.toStrong.call()是检测数据类型最准确最常用的方式,起原理为:
- 先获取Object原型上的toString方法,让方法执行,并且改变方法中的this关键字的指向;
- Object.prototype.toString 它的作用是返回当前方法的执行主体(方法中this)所属类的详细信息;
|
|
检测其他类型:
|
|
实际使用:
|
|
检测原型继承的情况:
|
|
|
|
使用jQuery中的方法$.type()
现在看看jQuery是怎么做的
|
|
|
|
上面的打印结果与
|
|
不谋而合!
这是jQuery.type 的核心方法
|
|
注意,为什么把 null 或者 undefined 单独讨论呢,因为 在一些版本浏览器中
123 > console.log(core_toString.call(null));> console.log(core_toString.call(undefined));>
>
这是会报错的!
如果是对象类型,另:由于 在一些低版本的浏览器中,typeof /123/ 会返回的是 “function” 而不是 “object”,所以这里要判断是否是函数,要明白 这里的 typeof obj === function
不是为了函数讨论的,因为函数本身就可以通过typeof 来得到类型.
|
|
就直接返回class2type 中键值对的结果,,如果不是,那么一定就是基本类型, 通过 typeof 就可以啦.
|
|
但是 jQuery.type 有一个很大的缺陷
这是一个自定义类型
|
|
// 注意,这里会打印 [object Object],通过上面的方法,无法得到精确的自定义类型
这也是 它的一个大缺陷了!
下面,我们通过构造函数的方式来获取精确类型
通过构造函数来获取类型
在理解这个方法之前,需要理解两个点
prorotype 原型属性
我们知道,任何对象或者函数都直接或者间接的继承自Object 或者 Function, (其实最终Function 是继承自 Object 的,这属于原型链的知识了,见下图)。那么,任何一个对象都具有原型对象 proto (这个对象只在chrome 和 firefox 暴露,但是在其他浏览器中也是存在的),这个原型对象就是这个对象的构造函数的原型属性(这里可能有点绕,直接上图).
由于 任何函数都具有 原型属性prototype,并且这个原型属性具有一个默认属性 constructor,它是这个函数的引用,看下面的代码
|
|
发现,这两个东西其实一个东西
但是,在某些情况下,需要这么写
|
|
这么做,就会覆盖原本的 protype 方法,那么construcor 就不存在了,这是,必须要显示的申明这个对象,
construction: Person, 这句话非常重要,作用是修正this指向
|
|
在jQuery的中,就是这么做的,
|
|
关于 jQuery对象封装的方式 也是非常值得研究
注意,这里已经不是熟悉 [object Object],而是 已经重写了.
也就是,如果调用一个函数的toString() 方法.那么就会打印这个函数的函数体.
如何通过构造函数来获得变量的类型?
判断是否是基本类型
|
|
如果是对象或者函数类型
|
|
现在要做的事 : 如何将Person 提取出来呢?
毋庸置疑,字符串切割那一套肯定可以办到,但是太 low 啦!
这里,我使用正则将Person提取出来
|
|
|
|
其实,除了上面的正则,每个函数还有一个name属性**,返回函数名,但是ie8 是不支持的.
因此上面的代码可以写为:
|
|
但是上面的代码太丑啦,将其简化
简化
|
|
还是比较麻烦,继续简化
|
|
好了,已经全部弄完了,写个代码测试一下:
|
|
1.有时会看到Object.prototype.toString.call()
2.toString()是一个怎样的方法,他定义在哪里?
3.call.apply.bind可以吗?
4.为神马要去call呢?用 Object.prototype.toString.call(obj) 而不用 obj.toString() 呢?
|
|
|
|
就是怕你重写了toString,所以才要用object 最原始的他toString,所以才去call。
5.Object.prototype.toString方法的原理是什么?
参考链接:http://www.jb51.net/article/79941.htm
在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法.
12var arr = [];console.log(Object.prototype.toString.call(arr)) //“[object Array]”
本文要讲的就是,toString方法是如何做到这一点的,原理是什么.
ECMAScript 3
在ES3中,Object.prototype.toString方法的规范如下:
115.2.4.2 Object.prototype.toString()
在toString方法被调用时,会执行下面的操作步骤:
\1. 获取this对象的[[Class]]属性的值.
\2. 计算出三个字符串”[object “, 第一步的操作结果Result(1), 以及 “]”连接后的新字符串.
\3. 返回第二步的操作结果Result(2).
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的
[[Class]]一个字符串值,表明了该对象的类型.
然后给了一段解释:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值(查看 15.2.4.2).
也就是说,把Object.prototype.toString方法返回的字符串,去掉前面固定的”[object “和后面固定的”]”,就是内部属性[[class]]的值,也就达到了判断对象类型的目的.jQuery中的工具方法$.type(),就是干这个的.
在ES3中,规范文档并没有总结出[[class]]内部属性一共有几种,不过我们可以自己统计一下,原生对象的[[class]]内部属性的值一共有10种.分别是:”Array”, “Boolean”, “Date”, “Error”, “Function”, “Math”, “Number”, “Object”, “RegExp”, “String”.
ECMAScript 5
在ES5.1中,除了规范写的更详细一些以外,Object.prototype.toString方法和[[class]]内部属性的定义上也有一些变化,Object.prototype.toString方法的规范如下:
15.2.4.2 Object.prototype.toString ( )
在toString方法被调用时,会执行下面的操作步骤:
如果this的值为undefined,则返回”[object Undefined]”.
如果this的值为null,则返回”[object Null]”.
让O成为调用ToObject(this)的结果.
让class成为O的内部属性[[Class]]的值.
返回三个字符串”[object “, class, 以及 “]”连接后的新字符串.
可以看出,比ES3多了1,2,3步.第1,2步属于新规则,比较特殊,因为”Undefined”和”Null”并不属于[[class]]属性的值,需要注意的是,这里和严格模式无关(大部分函数在严格模式下,this的值才会保持undefined或null,非严格模式下会自动成为全局对象).第3步并不算是新规则,因为在ES3的引擎中,也都会在这一步将三种原始值类型转换成对应的包装对象,只是规范中没写出来.ES5中,[[Class]]属性的解释更加详细:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是除了”Arguments”, “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”, “String”之外的的任何字符串.[[Class]]内部属性是引擎内部用来判断一个对象属于哪种类型的值的.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值(查看 15.2.4.2).
和ES3对比一下,第一个差别就是[[class]]内部属性的值多了两种,成了12种,一种是arguments对象的[[class]]成了”Arguments”,而不是以前的”Object”,还有就是多个了全局对象JSON,它的[[class]]值为”JSON”.第二个差别就是,宿主对象的[[class]]内部属性的值,不能和这12种值冲突,不过在支持ES3的浏览器中,貌似也没有发现哪些宿主对象故意使用那10个值.
ECMAScript 6
ES6目前还只是工作草案,但能够肯定的是,[[class]]内部属性没有了,取而代之的是另外一个内部属性[[NativeBrand]].[[NativeBrand]]属性是这么定义的:
内部属性属性值描述
[[NativeBrand]]枚举NativeBrand的一个成员.该属性的值对应一个标志值(tag value),可以用来区分原生对象的类型.
[[NativeBrand]]属性的解释:
[[NativeBrand]]内部属性用来识别某个原生对象是否为符合本规范的某一种特定类型的对象.[[NativeBrand]]内部属性的值为下面这些枚举类型的值中的一个:NativeFunction, NativeArray, StringWrapper, BooleanWrapper, NumberWrapper, NativeMath, NativeDate, NativeRegExp, NativeError, NativeJSON, NativeArguments, NativePrivateName.[[NativeBrand]]内部属性仅用来区分区分特定类型的ECMAScript原生对象.只有在表10中明确指出的对象类型才有[[NativeBrand]]内部属性.
表10 — [[NativeBrand]]内部属性的值
属性值对应类型
NativeFunctionFunction objects
NativeArrayArray objects
StringWrapperString objects
BooleanWrapperBoolean objects
NumberWrapperNumber objects
NativeMathThe Math object
NativeDateDate objects
NativeRegExpRegExp objects
NativeErrorError objects
NativeJSONThe JSON object
NativeArgumentsArguments objects
NativePrivateNamePrivate Name objects
可见,和[[class]]不同的是,并不是每个对象都拥有[[NativeBrand]].同时,Object.prototype.toString方法的规范也改成了下面这样:
15.2.4.2 Object.prototype.toString ( )
在toString方法被调用时,会执行下面的操作步骤:
如果this的值为undefined,则返回”[object Undefined]”.
如果this的值为null,则返回”[object Null]”.
让O成为调用ToObject(this)的结果.
如果O有[[NativeBrand]]内部属性,让tag成为表29中对应的值.
否则
让hasTag成为调用O的[[HasProperty]]内部方法后的结果,参数为@@toStringTag.
如果hasTag为false,则让tag为”Object”.
否则,
让tag成为调用O的[[Get]]内部方法后的结果,参数为@@toStringTag.
如果tag是一个abrupt completion,则让tag成为NormalCompletion(“???”).
让tag成为tag.[[value]].
如果Type(tag)不是字符串,则让tag成为”???”.
如果tag的值为”Arguments”, “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”,或
者”String”中的任一个,则让tag成为字符串”~”和tag当前的值连接后的结果.
返回三个字符串”[object “, tag, and “]”连接后的新字符串.
表29 — [[NativeBrand]] 标志值
[[NativeBrand]]值标志值
NativeFunction”Function”
NativeArray”Array”
StringWrapper”String”
BooleanWrapper”Boolean”
NumberWrapper”Number”
NativeMath”Math”
NativeDate”Date”
NativeRegExp”RegExp”
NativeError”Error”
NativeJSON”JSON”
NativeArguments”Arguments”
可以看到,在规范上有了很大的变化,不过对于普通用户来说,貌似感觉不到.
也许你发现了,ES6里的新类型Map,Set等,都没有在表29中.它们在执行toString方法的时候返回的是什么?
``
console.log(Object.prototype.toString.call(Map())) //"[object Map]"
console.log(Object.prototype.toString.call(Set())) //"[object Set]"
其中的字符串”Map”是怎么来的呢:
15.14.5.13 Map.prototype.@@toStringTag
@@toStringTag 属性的初始值为字符串”Map”.
由于ES6的规范还在制定中,各种相关规定都有可能改变,所以如果想了解更多细节.看看下面这两个链接,现在只需要知道的是:[[class]]没了,使用了更复杂的机制.
以上所述是JavaScript中Object.prototype.toString方法的原理