參考鏈接:https://www.talkingcoder.com/article/6333557442705696719
先看typeof
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <script type="text/javascript" src="" ></script> <title>Document</title> <style type="text/css"> </style> <script type="text/javascript"> var a; console.log("1:" + typeof a); var b = null; console.log("2:" + typeof b); var c = undefined; console.log("3:" + typeof c); var d = new Object; console.log("4:" + typeof d); var e = function() {}; console.log("5:" + typeof e); var f = {}; console.log("6:" + typeof f); var g = ''; console.log("7:" + typeof g); var h = []; console.log("8:" + typeof h); var i = true; console.log("9:" + typeof i); var j = 123; console.log("10:" + typeof j); var k = NaN; console.log("11:" + typeof k); var l = /^[-+]?\d+$/; console.log("12:" + typeof l); </script> </head> <body> </body> </html>
按照上面的打印結果,總結出下面要注意的幾點
-
typeof (引用類型) 除了函數, 都是 'object',比如 typeof /123/
-
typeof null 為'object'
-
typeof undefined 為 'undefined',通常, 如果使用兩等號, null == undefined 為真.
-
轉換為數字的常見用法 "10"-0或+"10", 如果沒有轉換成功,返回NaN,由於NaN 的一個特性: NaN != NaN,故判斷轉換成功與否的常見做法: (這也是我參見 jQuery的源碼發現的,jQuery源碼讀100遍都不為過)
("10x" - 0) == ("10x" - 0); // 結果為假!

再看看constructor 和 instanceof
instanceof 用於判斷一個變量是否某個對象的實例,或用於判斷一個變量是否某個對象的實例;
constructor 用於判斷一個變量的原型,constructor 屬性返回對創建此對象的數組函數的引用。
Javascript中對象的prototype屬性的解釋是:返回對象類型原型的引用。
<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title></title> <script type="text/javascript"> console.log("----------------Number---------------"); var A = 123; console.log(A instanceof Number); //false console.log(A.constructor == Number); //true console.log(A.constructor); console.log("----------------String---------------"); var B = "javascript"; console.log(B instanceof String); //false console.log(B.constructor == String); //true console.log(B.constructor); console.log("----------------Boolean---------------"); var C = true; console.log(C instanceof Boolean); //false console.log(C.constructor == Boolean); //true console.log(C.constructor); console.log("----------------null---------------"); var D = null; console.log(D instanceof Object); //false //console.log(D.constructor == null); //報錯 //console.log(D.constructor); //報錯 console.log("----------------undefined---------------"); var E = undefined; //console.log(E instanceof undefined); // //報錯 //console.log(E.constructor == undefined); //報錯 //console.log(E.constructor); //報錯 console.log("----------------function---------------"); var F = function() {}; console.log(F instanceof Function); console.log(F.constructor == Function); console.log(F.constructor); console.log("----------------new function---------------"); function SB() {}; var G = new SB(); console.log(G instanceof SB); console.log(G.constructor == SB); console.log(G.constructor); console.log("----------------new Object---------------"); var H = new Object; console.log(H instanceof Object); console.log(H.constructor == Object); console.log(H.constructor); console.log("-----------------Array--------------"); var I = []; console.log(I instanceof Array); console.log(I.constructor == Array); console.log(I.constructor); console.log("-----------------JSON--------------"); var J = { "sb": "javascript", "node": "very SB" }; console.log(J instanceof Object); console.log(J.constructor == Object); console.log(J.constructor); </script> </head> <body> </body> </html>

再看看{}.toString.call(obj)
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript"> console.log({}.toString.call(1)); console.log({}.toString.call("11")); console.log({}.toString.call(/123/)); console.log({}.toString.call({})); console.log({}.toString.call(function() {})); console.log({}.toString.call([])); console.log({}.toString.call(true)); console.log({}.toString.call(new Date())); console.log({}.toString.call(new Error())); console.log({}.toString.call(null)); console.log({}.toString.call(undefined)); console.log(String(null)); console.log(String(undefined)); </script> </head> <body> </body> </html>

使用jQuery中的方法$.type()
現在看看jQuery是怎么做的
// 先申明一個對象,目的是用來做映射 var class2type = {}; // 申明一個core_toString() 的方法,得到最原始的toString() 方法,因為在很多對象中,toStrintg() 已經被重寫 var core_toString() = class2type.toString;
// 這里為 toStrintg() 后的結果和類型名做一個映射,申明一個core_toString() 后的結果,而值就是類型名 jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); });
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <script type="text/javascript" src="" ></script> <title>Document</title> <script type="text/javascript"> console.log($.type(1)); console.log($.type("11")); console.log($.type(/123/)); console.log($.type({})); console.log($.type(function() {})); console.log($.type([])); console.log($.type(true)); console.log($.type(new Date())); console.log($.type(new Error())); console.log($.type(null)); console.log($.type(undefined)); console.log(String(null)); console.log(String(undefined)); </script> </head> <body> </body> </html>

上面的打印結果與
class2type[ "[object " + name + "]" ] = name.toLowerCase();
不謀而合!
這是jQuery.type 的核心方法
type: function( obj ) { if ( obj == null ) { return String( obj ); } // Support: Safari <= 5.1 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ] || "object" : typeof obj; },
注意,為什么把 null 或者 undefined 單獨討論呢,因為 在一些版本瀏覽器中
console.log(core_toString.call(null)); console.log(core_toString.call(undefined));這是會報錯的!
如果是對象類型,另:由於 在一些低版本的瀏覽器中,typeof /123/ 會返回的是 "function" 而不是 "object",所以這里要判斷是否是函數,要明白 這里的 typeof obj === function 不是為了函數討論的,因為函數本身就可以通過typeof 來得到類型.
typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ]
就直接返回class2type 中鍵值對的結果,,如果不是,那么一定就是基本類型, 通過 typeof 就可以啦.
class2type[ core_toString.call(obj) ] || "object" : // 這是防止一些未知情況的,如果未取到,就返回object
但是 jQuery.type 有一個很大的缺陷
這是一個自定義類型
function Person(){ this.name = 'pawn'; } var p = Person.toString();
// 注意,這里會打印 [object Object],通過上面的方法,無法得到精確的自定義類型
這也是 它的一個大缺陷了!
下面,我們通過構造函數的方式來獲取精確類型
通過構造函數來獲取類型
在理解這個方法之前,需要理解兩個點
prorotype 原型屬性
我們知道,任何對象或者函數都直接或者間接的繼承自Object 或者 Function, (其實最終Function 是繼承自 Object 的,這屬於原型鏈的知識了,見下圖)。那么,任何一個對象都具有原型對象 __proto__ (這個對象只在chrome 和 firefox 暴露,但是在其他瀏覽器中也是存在的),這個原型對象就是這個對象的構造函數的原型屬性(這里可能有點繞,直接上圖).

由於 任何函數都具有 原型屬性prototype,並且這個原型屬性具有一個默認屬性 constructor,它是這個函數的引用,看下面的代碼
function Person(){ this.name = 'pawn'; } console.log(Person.prototype.constructor === Person); //true
發現,這兩個東西其實一個東西
但是,在某些情況下,需要這么寫
function Person(){ this.name = 'pawn'; } Person.protype = { XX: ... , xx: ... , ... }
這么做,就會覆蓋原本的 protype 方法,那么construcor 就不存在了,這是,必須要顯示的申明這個對象
Person.protype = { construction: Person, XX: ... , xx: ... , ... }
在jQuery的中,就是這么做的,
jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem;
關於 jQuery對象封裝的方式 也是非常值得研究

注意,這里已經不是熟悉 [object Object],而是 已經重寫了.
也就是,如果調用一個函數的toString() 方法.那么就會打印這個函數的函數體.

如何通過構造函數來獲得變量的類型?
判斷是否是基本類型
var getType = function(obj){ if(obj == null){ return String(obj); } if(typeof obj === 'object' || typeof obj === 'fucntion'){ ... }else{ // 如果不是引用類型,那么就是基本類型 return typeof obj } }
如果是對象或者函數類型
function Person(){ this.name = 'pawn'; } var p = new Person(); console.log(p.constructor); //返回function Person(){...}
現在要做的事 : 如何將Person 提取出來呢?
毋庸置疑,字符串切割那一套肯定可以辦到,但是太 low 啦!
這里,我使用正則將Person提取出來
var regex = /function\s(.+?)\(/
function Person(){ this.name = 'pawn'; } var p = new Person(); var c = p.constructor var regex = /function\s(.+?)\(/; console.log('|' + regex.exec(c)[1] + '|');

其實,除了上面的正則,每個函數還有一個name屬性,返回函數名,但是ie8 是不支持的.
因此上面的代碼可以寫為:
var getType = function(obj){ if(obj == null){ return String(obj); } if(typeof obj === 'object' || typeof obj === 'function'){ var constructor = obj.constructor; if(constructor && constructor.name){ return constructor.name; } var regex = /function\s(.+?)\(/; return regex.exec(c)[1]; }else{ // 如果不是引用類型,那么就是基本;類型 return typeof obj; } };
但是上面的代碼太丑啦,將其簡化
簡化
var getType = function(obj){ if(obj == null){ return String(obj); } if(typeof obj === 'object' || typeof obj === 'function'){ return obj.constructor && obj.constructor.name.toLowerCase() || /function\s(.+?)\(/.exec(obj.constructor)[1].toLowerCase(); }else{ // 如果不是引用類型,那么就是基本類型 return typeof obj; } };
還是比較麻煩,繼續簡化
var getType = function(obj){ if(obj == null){ return String(obj); } return typeof obj === 'object' || typeof obj === 'function' ? obj.constructor && obj.constructor.name && obj.constructor.name.toLowerCase() || /function\s(.+?)\(/.exec(obj.constructor)[1].toLowerCase(): typeof obj; };
好了,已經全部弄完了,寫個代碼測試一下:
function Person(){ this.name = 'pawn'; } var p = new Person(); console.log(getType(p)); console.log(getType(1)); console.log(getType("a")); console.log(getType(false)); console.log(getType(/123/)); console.log(getType({})); console.log(getType(function(){})); console.log(getType(new Date())); console.log(getType(new Error())); console.log(getType( null)); console.log(getType( undefined));

1.有時會看到Object.prototype.toString.call()

2.toString()是一個怎樣的方法,他定義在哪里呢?

3.call.apply.bind可以嗎?

4.為神馬要去call呢?用 Object.prototype.toString.call(obj) 而不用 obj.toString() 呢?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> function A(){ this.say=function(){ console.log("我是1"); } } function B(){ this.say=function(){ console.log("我是2"); } } var a=new A(); var b=new B(); a.say.call(b); //我是1 </script> </head> <body> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> function A(){ this.name='SB'; this.say=function(){ console.log("我是1"); } } function B(){ A.call(this); //B繼承A,重寫say方法 this.say=function(){ console.log("我是2"); } } var a=new A(); var b=new B(); console.log(b.name); //SB b.say(); //我是2 a.say.call(b); //我是1 </script> </head> <body> </body> </html>
就是怕你重寫了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方法的原理,希望對大家有所幫助!
附加:
關於JS中原型鏈中的prototype與_proto_的個人理解與詳細總結
一直認為原型鏈太過復雜,尤其看過某圖后被繞暈了一整子,今天清理硬盤空間(渣電腦),偶然又看到這圖,勾起了點回憶,於是索性復習一下原型鏈相關的內容,表達能力欠缺邏輯混亂別見怪(為了防止新人__(此處指我)__被在此繞暈,圖片就放在末尾了。)
以下三點需要謹記
1.每個對象都具有一個名為__proto__的屬性;
2.每個構造函數(構造函數標准為大寫開頭,如Function(),Object()等等JS中自帶的構造函數,以及自己創建的)都具有一個名為prototype的方法(注意:既然是方法,那么就是一個對象(JS中函數同樣是對象),所以prototype同樣帶有__proto__屬性);
3.每個對象的__proto__屬性指向自身構造函數的prototype;
思路擴展如下
1 function Fun(){
2 }
3 // 我創造了一個函數Fn
4 // 這個函數由Function生成(Function作為構造函數)
5 var fn=new Fun()
6 // 我創建了一個函數fn
7 // 這個函數由Fn生成(Fn作為構造函數)
8
9
10 console.log(fn.__proto__===Fun.prototype) //true
11 // fn的__proto__指向其構造函數Fun的prototype
12 console.log(Fun.__proto__===Function.prototype) //true
13 // Fun的__proto__指向其構造函數Function的prototype
14 console.log(Function.__proto__===Function.prototype) //true
15 // Function的__proto__指向其構造函數Function的prototype
16 // 構造函數自身是一個函數,他是被自身構造的
17 console.log(Function.prototype.__proto__===Object.prototype) //true
18 // Function.prototype的__proto__指向其構造函數Object的prototype
19 // Function.prototype是一個對象,同樣是一個方法,方法是函數,所以它必須有自己的構造函數也就是Object
20 console.log(Fun.prototype.__proto__===Object.prototype) //true
21 // 與上條相同
22 // 此處可以知道一點,所有構造函數的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身)
23 console.log(Object.__proto__===Function.prototype) //true
24 // Object作為一個構造函數(是一個函數對象!!函數對象!!),所以他的__proto__指向Function.prototype
25 console.log(Object.prototype.__proto__===null) //true
26 // Object.prototype作為一切的源頭,他的__proto__是null
27
28 // 下面是一個新的,額外的例子
29
30 var obj={}
31 // 創建了一個obj
32 console.log(obj.__proto__===Object.prototype) //true
33 // obj作為一個直接以字面量創建的對象,所以obj__proto__直接指向了Object.prototype,而不需要經過Function了!!
34
35 // 下面是根據原型鏈延伸的內容
36 // 還有一個上文並未提到的constructor, constructor在原型鏈中,是作為對象prototypr的一個屬性存在的,它指向構造函數(由於主要講原型鏈,這個就沒在意、);
37
38 console.log(obj.__proto__.__proto__===null) //true
39 console.log(obj.__proto__.constructor===Object) //true
40 console.log(obj.__proto__.constructor.__proto__===Function.prototype) //true
41 console.log(obj.__proto__.constructor.__proto__.__proto__===Object.prototype) //true
42 console.log(obj.__proto__.constructor.__proto__.__proto__.__proto__===null) //true
43 console.log(obj.__proto__.constructor.__proto__.__proto__.constructor.__proto__===Function.prototype) //true
44
45
46 // 以上,有興趣的可以一一驗證 F12搞起.
為了方便記憶可以得出如下結論(如有錯誤歡迎斧正.....)
prototype是構造函數獨有的屬性;
對象的__prototype__屬性通常與其構造函數的prototype屬性相互對應;
所有構造函數的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身);
需要注意的指向是
Function的__proto__指向其構造函數Function的prototype;
Object作為一個構造函數(是一個函數對象!!函數對象!!),所以他的__proto__指向Function.prototype;
Function.prototype的__proto__指向其構造函數Object的prototype;
Object.prototype的__prototype__指向null(盡頭);
在文章結構順便附送上倆個與原型鏈相關的方法....歡迎使用
1.
括號內必須要加引號,並且直接寫入屬性名
某圖在這里 ps:本文是總結,加個人理解.....圖是好久前留存在電腦的....忘了是在哪看到的...


