typeof
typeof 操作符返回一個字符串,表示未經(jīng)計算的操作數(shù)的類型。
語法
typeof 運算符后接操作數(shù):
typeof operand
typeof(operand)
參數(shù)
operand 一個表示對象或原始值的表達式,其類型將被返回。
描述
下面總結(jié)了 typeof 可能的返回值。有關類型和原始值的更多信息,可查看 JavaScript 數(shù)據(jù)結(jié)構(gòu) 頁面。
類型 | 結(jié)果 |
Undefined | “undefined” |
Null | “object” (見下文) |
Boolean | “boolean” |
Number | “number” |
BigInt (ECMAScript 2020 新增) | “bigint” |
String | “string” |
Symbol (ECMAScript 2015 新增) | “symbol” |
宿主對象(由 JS 環(huán)境提供) | 取決于具體實現(xiàn) |
Function 對象 (按照 ECMA-262 規(guī)范實現(xiàn) [[Call]]) | “function” |
其他任何對象 | “object” |
原始值
除對象類型(object)以外的其它任何類型定義的不可變的值(值本身無法被改變)。例如(與 C 語言不同),JavaScript 中字符串是不可變的(譯注:如,JavaScript 中對字符串的操作一定返回了一個新字符串,原始字符串并沒有被改變)。我們稱這些類型的值為“原始值”。
特性
1、typeof 總是返回一個字符串。
2、typeof 能正確判斷原始值的類型,null 除外;引用類型數(shù)據(jù)能正確判斷 Function、函數(shù)的類型,其他的都會返回 ‘object’。
示例
// 數(shù)值console.log(typeof 37 === ‘number’) // trueconsole.log(typeof 3.14 === ‘number’) // trueconsole.log(typeof (42) === ‘number’) // trueconsole.log(typeof Math.LN2 === ‘number’) // trueconsole.log(typeof Infinity === ‘number’) // trueconsole.log(typeof NaN === ‘number’) // true 盡管它是 “Not-A-Number” (非數(shù)值) 的縮寫console.log(typeof Number(1) === ‘number’) // true Number 會嘗試把參數(shù)解析成數(shù)值console.log(typeof 42n === ‘bigint’) // true// 字符串console.log(typeof ” === ‘string’) // trueconsole.log(typeof ‘bla’ === ‘string’) // trueconsole.log(typeof `template literal` === ‘string’) // trueconsole.log(typeof ‘1’ === ‘string’) // true 注意內(nèi)容為數(shù)字的字符串仍是字符串console.log(typeof (typeof 1) === ‘string’) // true typeof 總是返回一個字符串console.log(typeof String(1) === ‘string’) // true String 將任意值轉(zhuǎn)換為字符串,比 toString 更安全// 布爾值console.log(typeof true === ‘boolean’) // trueconsole.log(typeof false === ‘boolean’) // trueconsole.log(typeof Boolean(1) === ‘boolean’) // Boolean() 會基于參數(shù)是真值還是虛值進行轉(zhuǎn)換console.log(typeof !!(1) === ‘boolean’) // true 兩次調(diào)用 ! (邏輯非) 操作符相當于 Boolean()// Symbolsconsole.log(typeof Symbol() === ‘symbol’) // trueconsole.log(typeof Symbol(‘foo’) === ‘symbol’) // trueconsole.log(typeof Symbol.iterator === ‘symbol’) // true// Undefinedconsole.log(typeof undefined === ‘undefined’) // trueconsole.log(typeof declaredButUndefinedVariable === ‘undefined’) // trueconsole.log(typeof undeclaredVariable === ‘undefined’) // true// 對象console.log(typeof { a: 1 } === ‘object’) // true// 使用 Array.isArray 或者 Object.prototype.toString.call// 區(qū)分數(shù)組和普通對象console.log(typeof [1, 2, 4] === ‘object’) // trueconsole.log(typeof new Date() === ‘object’) // trueconsole.log(typeof /regex/ === ‘object’) // true 歷史結(jié)果請參閱正則表達式部分// 使用 new 操作符// 除 Function 外的所有構(gòu)造函數(shù)的類型都是 ‘object’var func = new Function()console.log(typeof func) // 返回 ‘function’var A = function() {}var b = new A()console.log(typeof b) // 返回 ‘object’// 下面的例子令人迷惑,非常危險,沒有用處。避免使用它們。console.log(typeof new Boolean(true) === ‘object’) // trueconsole.log(typeof new Number(1) === ‘object’) // trueconsole.log(typeof new String(‘abc’) === ‘object’) // true// 函數(shù)console.log(typeof function () { } === ‘function’) // trueconsole.log(typeof class C { } === ‘function’) // trueconsole.log(typeof Math.sin === ‘function’) // true// Null// JavaScript 誕生以來便如此console.log(typeof null === ‘object’) // true
在 JavaScript 最初的實現(xiàn)中,JavaScript 中的值是由一個表示類型的標簽和實際數(shù)據(jù)值表示的。對象的類型標簽是 0。由于 null 代表的是空指針(大多數(shù)平臺下值為 0x00),因此,null 的類型標簽是 0,typeof null 也因此返回 “object”。(參考來源)
console.log(typeof 0); // numberconsole.log(typeof BigInt(Number.MAX_SAFE_INTEGER)); // bigintconsole.log(typeof ‘0’); // stringconsole.log(typeof true); // booleanconsole.log(typeof undefined); // undefinedconsole.log(typeof function () { }); // functionconsole.log(typeof Symbol); // functionconsole.log(typeof Symbol()); // symbolconsole.log(typeof Date); // functionconsole.log(typeof Date()); // stringconsole.log(typeof new Date); // objectconsole.log(typeof new Date()); // objectconsole.log(typeof RegExp); // functionconsole.log(typeof RegExp()); // objectconsole.log(typeof new RegExp); // objectconsole.log(typeof new RegExp()); // objectconsole.log(typeof []); // objectconsole.log(typeof {}); // objectconsole.log(typeof null); // object
如果我們想判斷一個對象的正確類型,可以考慮使用 instanceof,因為內(nèi)部機制是通過 判斷實例對象的 __proto__ 和生成該實例的構(gòu)造函數(shù)的 prototype 是不是引用的同一個地址(也就是原型鏈的方式)來判斷的。
錯誤
在 ECMAScript 2015 之前,typeof 總能保證對任何所給的操作數(shù)返回一個字符串。即便是沒有聲明的標識符,typeof 也能返回 ‘undefined’。使用 typeof 永遠不會拋出錯誤。
但在加入了塊級作用域的 let 和 const 之后,在其被聲明之前對塊中的 let 和 const 變量使用 typeof 會拋出一個 ReferenceError。塊作用域變量在塊的頭部處于“暫存死區(qū)”,直至其被初始化,在這期間,訪問變量將會引發(fā)錯誤。
typeof undeclaredVariable === ‘undefined’;typeof newLetVariable; // ReferenceErrortypeof newConstVariable; // ReferenceErrortypeof newClass; // ReferenceErrorlet newLetVariable;const newConstVariable = ‘hello’;class newClass{};
instanceof
instanceof 運算符用于檢測構(gòu)造函數(shù) prototype 屬性是否出現(xiàn)在某個實例對象的原型鏈上??梢杂脕砼袛喽紝儆?Object 類型和一些特殊情況的對象,如:數(shù)組和對象,但不能用于基礎數(shù)據(jù)類型。
語法
object instanceof constructor
參數(shù)
object 某個實例對象
constructor 某個構(gòu)造函數(shù)
描述
instanceof 運算符用來檢測 constructor.prototype 是否存在于參數(shù) object 的原型鏈上。
示例
B instanceof A:判斷 B 是否為 A 的實例,可以用于繼承關系中
function Car(make, model, year) { this.make = make; this.model = model; this.year = year;}const auto = new Car(‘Honda’, ‘Accord’, 1998);console.log(auto instanceof Car);// expected output: trueconsole.log(auto instanceof Object);// expected output: true// 定義構(gòu)造函數(shù)function C(){}function D(){}var o = new C();o instanceof C; // true,因為 Object.getPrototypeOf(o) === C.prototypeo instanceof D; // false,因為 D.prototype 不在 o 的原型鏈上o instanceof Object; // true,因為 Object.prototype.isPrototypeOf(o) 返回 trueC.prototype instanceof Object // true,同上C.prototype = {};var o2 = new C();o2 instanceof C; // trueo instanceof C; // false,C.prototype 指向了一個空對象,這個空對象不在 o 的原型鏈上.D.prototype = new C(); // 繼承var o3 = new D();o3 instanceof D; // trueo3 instanceof C; // true 因為 C.prototype 現(xiàn)在在 o3 的原型鏈上
需要注意的是,如果表達式 obj instanceof Foo 返回 true,則并不意味著該表達式會永遠返回 true,因為 Foo.prototype 屬性的值有可能會改變,改變之后的值很有可能不存在于 obj 的原型鏈上,這時原表達式的值就會成為 false。另外一種情況下,原表達式的值也會改變,就是改變對象 obj 的原型鏈的情況,雖然在目前的ES規(guī)范中,我們只能讀取對象的原型而不能改變它,但借助于非標準的 __proto__ 偽屬性,是可以實現(xiàn)的。比如執(zhí)行 obj.__proto__ = {} 之后,obj instanceof Foo 就會返回 false 了。
A 是 B 的父對象,c 是 B 的實例,c instanceof A 與 c instanceof B 結(jié)果均為 true。
function A() { }function B() { }B.prototype = new A()const c = new B()console.log(c instanceof B); // trueconsole.log(c instanceof A); // trueconsole.log(A instanceof B); // falseconsole.log(B instanceof A); // falseconsole.log(B.__proto__ === A.prototype); // falseconsole.log(B.prototype.__proto__ === A.prototype); // trueconsole.log(c instanceof Object); // true c 屬于console.log(B instanceof Object); // trueconsole.log(A instanceof Object); // true
演示 String 對象和 Date 對象都屬于 Object 類型和一些特殊情況
下面的代碼使用了 instanceof 來證明:String 和 Date 對象同時也屬于Object 類型(他們是由 Object 類派生出來的)。
但是,使用對象文字符號創(chuàng)建的對象在這里是一個例外:雖然原型未定義,但 instanceof Object 返回 true。
var simpleStr = “This is a simple string”;var myString = new String();var newStr = new String(“String created with constructor”);var myDate = new Date();var myObj = {};var myNonObj = Object.create(null);console.log(simpleStr instanceof String) // 返回 false, 非對象實例,因此返回 falseconsole.log(myString instanceof String) // 返回 trueconsole.log(newStr instanceof String) // 返回 trueconsole.log(myString instanceof Object) // 返回 trueconsole.log(myObj instanceof Object) // 返回 true, 盡管原型沒有定義console.log(({}) instanceof Object) // 返回 true, 同上console.log(myNonObj instanceof Object) // 返回 false, 一種創(chuàng)建非 Object 實例的對象的方法console.log(myString instanceof Date) // 返回 falseconsole.log(myDate instanceof Date) // 返回 trueconsole.log(myDate instanceof Object) // 返回 trueconsole.log(myDate instanceof String) // 返回 false
Object.prototype.toString.call(object)/Object.prototype.toString.apply(object)
toString.call() 或 toString.apply() 方法幾乎可以精準判斷各類數(shù)據(jù)的類型。
console.log(Object.prototype.toString.call(“kevin”)) // [object String]console.log(Object.prototype.toString.call(18)) // [object Number]console.log(Object.prototype.toString.call(true)) // [object Boolean]console.log(Object.prototype.toString.call(undefined)) // [object Undefined]console.log(Object.prototype.toString.call(null)) // [object Null]console.log(Object.prototype.toString.call(NaN)) // [object Number]console.log(Object.prototype.toString.call({ name: “kevin” })) // [object Object]console.log(Object.prototype.toString.call(function () { })) // [object Function]console.log(Object.prototype.toString.call([])) // [object Array]console.log(Object.prototype.toString.call(new Date)) // [object Date]console.log(Object.prototype.toString.call(/d/)) // [object RegExp]console.log(Object.prototype.toString.call(Math)) // [object Math]function Person() { }console.log(Object.prototype.toString.call(new Person)) // [object Object]var o = { [Symbol.toStringTag]: “A” }console.log(Object.prototype.toString.call(o)) // [object A]console.log(window.toString()) // “[object Window]”console.log(Object.prototype.toString.call(window)) // “[object Window]”console.log(Object.prototype.toString.call(Symbol())); // “[object Symbol]”// 封裝function getTypeof(data) { let dataType = Object.prototype.toString.call(data); return dataType.slice(8, -1)}console.log(getTypeof(18)) // Numberconsole.log(getTypeof(“kevin”)) // Stringconsole.log(getTypeof(new Date)) // Dateconsole.log(getTypeof([])) // Arrayconsole.log(getTypeof({})) // Objectfunction Person() { }console.log(getTypeof(new Person)) // Objectconsole.log(getTypeof(new Person())) // Object
語法
obj.toString()
返回值
一個表示該對象的字符串
描述
每個對象都有一個 toString() 方法,當該對象被表示為一個文本值時,或者一個對象以預期的字符串方式引用時自動調(diào)用。默認情況下,toString() 方法被每個 Object 對象繼承。如果此方法在自定義對象中未被覆蓋,toString() 返回 “[object type]”,其中 type 是對象的類型。以下代碼說明了這一點:
var o = new Object();console.log(o.toString()); // [object Object]
備注:如 ECMAScript 5 和隨后的 Errata 中所定義,從 JavaScript 1.8.5 開始,toString() 調(diào)用 null 返回[object Null],undefined 返回 [object Undefined]。請參閱下面的使用toString()檢測對象類型。
示例
覆蓋默認的 toString 方法
可以自定義一個方法,來取代默認的 toString() 方法。該 toString() 方法不能傳入?yún)?shù),并且必須返回一個字符串。自定義的 toString() 方法可以是任何我們需要的值,但如果它附帶有關對象的信息,它將變得非常有用。
以下代碼定義了 Dog 對象類型,并創(chuàng)建了一個 Dog 類型的 theDog 對象:
function Dog(name,breed,color,sex) { this.name = name; this.breed = breed; this.color = color; this.sex = sex;}var theDog = new Dog(“Gabby”, “Lab”, “chocolate”, “female”);// 如果當前的對象調(diào)用了 toString() 方法,它將會返回從 Object繼承而來的 toString() 方法的返回默認值:console.log(theDog.toString()); // 返回 [object Object]// 下面的代碼中定義了一個叫做 dogToString() 的方法來覆蓋默認的 toString() 方法。// 這個方法生成一個 “property = value;” 形式的字符串,該字符串包含了當前對象的 name、breed、color 和 sex 的值。Dog.prototype.toString = function dogToString() { var ret = “Dog ” + this.name + ” is a ” + this.sex + ” ” + this.color + ” ” + this.breed; return ret;}// 也可以這樣寫Dog.prototype.toString = function dogToString() { return `Dog ${this.name} is a ${this.sex} ${this.color} ${this.breed}`;}// 使用上述代碼,任何時候在字符串上下文中使用 theDog.toString() 時,// JavaScript 都會自動調(diào)用 dogToString() 方法(dogToString() 可以是一個匿名函數(shù)),并且返回以下字符串:// “Dog Gabby is a female chocolate Lab”console.log(theDog.toString()); // 返回 “Dog Gabby is a female chocolate Lab”// 也可以這樣寫Dog.prototype.toString = function dogToString() { return ‘[object Dog]’;}Dog.prototype[Symbol.toStringTag] = ‘Dog’// 也可以這樣寫theDog[Symbol.toStringTag] = ‘Dog’console.log(theDog.toString()); // 返回 [object Dog]console.log(Dog.prototype.toString()); // 返回 [object Dog]console.log(Dog.prototype.toString.call(theDog)); // 返回 [object Dog]console.log(Object.prototype.toString.call(theDog)); // 返回 [object Object]
使用 toString() 檢測對象
可以通過 toString() 來獲取每個對象的類型。為了每個對象都能通過 Object.prototype.toString() 來檢測,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式來調(diào)用,傳遞要檢查的對象作為第一個參數(shù),稱為 thisArg。
var toString = Object.prototype.toString;toString.call(new Date); // [object Date]toString.call(new String); // [object String]toString.call(Math); // [object Math]//Since JavaScript 1.8.5toString.call(undefined); // [object Undefined]toString.call(null); // [object Null]
檢測原理
Object.prototype.toString.call(obj) 類型檢測的原理是什么?首先我們來看一下 toString() 方法:
var num = 1console.log(num.toString()) // ‘1’var str = ‘kevin’console.log(str.toString()) // ‘kevin’var bool = falseconsole.log(bool.toString()) // ‘false’var arr = [1, 2, 3]console.log(arr.toString()) // ‘1,2,3’var obj = { name: ‘kevin’ }console.log(obj.toString()) // ‘[object Object]’var fn = function(){}console.log(fn.toString()) // ‘function(){}’console.log(JSON.toString()) // ‘[object JSON]’console.log(Atomics.toString()) // ‘[object Atomics]’console.log(null.toString()) // Cannot read property ‘toString’ of nullconsole.log(undefined.toString() // Cannot read property ‘toString’ of undefinedconsole.log(window.toString()) // ‘[object Window]’
從以上示例可以知道 toString 是將數(shù)據(jù)轉(zhuǎn)換為字符串(null 和 undefined 除外),并且各種類型的數(shù)據(jù)轉(zhuǎn)換為字符串的方式又不一樣。即若參數(shù)不為 null 或 undefined,則將參數(shù)轉(zhuǎn)為對象,再作判斷。對于原始類型,轉(zhuǎn)為對象的方法即裝箱。
轉(zhuǎn)為對象后,取得該對象的 [Symbol.toStringTag] 屬性值(可能會遍歷原型鏈)作為 tag,如無該屬性,或該屬性值不為字符串類型,則依下表取得 tag,然后返回 “[object ” + tag + “]” 形式的字符串。
新標準引入了 [Symbol.toStringTag] 屬性,是為了把此方法接口化,用于規(guī)范新引入的對象對此方法的調(diào)用。但對于“老舊”的對象,就只能直接輸出值,以保證兼容性。
// 1. 三個容器對象。這類對象用作命名空間,用于存儲同一類方法。JSON[Symbol.toStringTag]; // => “JSON”Math[Symbol.toStringTag]; // => “Math”Atomics[Symbol.toStringTag]; // => “Atomic”// 這三個對象的 toString() 都沒有重寫,直接調(diào)用 toString() 方法也可以得到相同的結(jié)果。JSON.toString(); // => “[object JSON]”Math.toString(); // => “[object Math]”Atomics.toString(); // => “[object Atomics]”// 2. 兩個新引入的類型 BigInt 和 Symbol。BigInt.prototype[Symbol.toStringTag]; // => “BigInt”Symbol.prototype[Symbol.toStringTag]; // => “Symbol”// 3. 四個集合(Collection)對象。Set.prototype[Symbol.toStringTag]; // => “Set”Map.prototype[Symbol.toStringTag]; // => “Map”WeakSet.prototype[Symbol.toStringTag]; // => “WeakSet”WeakMap.prototype[Symbol.toStringTag]; // => “WeakMap”// 4. 在不同的實現(xiàn)中,有些第三方對象也部署了此屬性。// 比如在瀏覽器中:Window.prototype[Symbol.toStringTag]; // => “Window”HTMLElement.prototype[Symbol.toStringTag]; // => “HTMLElement”Blob.prototype[Symbol.toStringTag]; // => “Blob”// 5. 模塊命名空間對象(Module Namespace Object)。// 新引入的模塊命名空間對象(Module Namespace Object)也是部署了此屬性的。import * as module from “./export.js”;module[Symbol.toStringTag]; // => “Moduel”// 6. 在 Node.js 中global[Symbol.toStringTag]; // => “global”
我們再來看一下 Object 以及其原型上的 toString 方法:
Object.toString(); // “function Object() { [native code] }”Object.prototype.toString(); // “[object Object]”var o = new Object();console.log(o.toString()); // 返回 [object Object]console.log(o.__proto__.toString()); // 返回 [object Object]console.log(o.__proto__.toString === Object.prototype.toString); // true
我們可以看出 Object 和它的原型鏈上各自有一個 toString 方法,Object 輸出的是其函數(shù)體 “function Object() { [native code] }”,而 Object 原型上輸出的是其類型 “[object Object]”。
數(shù)據(jù)類型 | 例子 | 輸出 |
字符串 | “foo”.toString() | “foo” |
數(shù)字 | 1.toString() | Uncaught SyntaxError: Invalid or unexpected token |
布爾值 | true.toString() | “true” |
undefined | undefined.toString() | Uncaught TypeError: Cannot read property ‘toString’ of undefined |
null | null.toString() | Uncaught TypeError: Cannot read property ‘toString’ of null |
String | String.toString() | “function String() {[native code]}” |
Number | Number.toString() | “function Number() {[native code]}” |
Boolean | Boolean.toString() | “function Boolean() {[native code]}” |
Array | Array.toString() | “function Array() {[native code]}” |
Function | Function.toString() | “function Function() {[native code]}” |
Date | Date.toString() | “function Date() {[native code]}” |
RegExp | RegExp.toString() | “function RegExp() {[native code]}” |
Error | Error.toString() | “function Error() {[native code]}” |
Promise | Promise.toString() | “function Promise() {[native code]}” |
Object | Object.toString() | “function Object() {[native code]}” |
Math | Math.toString() | “[object Math]” |
Window | Window.toString() | “function Window() { [native code] }” |
window | window.toString() | “[object Window]” |
數(shù)據(jù)類型調(diào)用 toString() 方法的返回值,由此我們看出不同的數(shù)據(jù)類型都有其自身toString()方法
// Boolean 類型,tag 為 “Boolean”console.log(Object.prototype.toString.call(true)); // => “[object Boolean]”// Number 類型,tag 為 “Number”console.log(Object.prototype.toString.call(1)); // => “[object Boolean]”// String 類型,tag 為 “String”console.log(Object.prototype.toString.call(“”)); // => “[object String]”// Array 類型,tag 為 “String”console.log(Object.prototype.toString.call([])); // => “[object Array]”// Arguments 類型,tag 為 “Arguments”console.log(Object.prototype.toString.call((function() { return arguments;})())); // => “[object Arguments]”// Function 類型, tag 為 “Function”console.log(Object.prototype.toString.call(function(){})); // => “[object Function]”// Error 類型(包含子類型),tag 為 “Error”console.log(Object.prototype.toString.call(new Error())); // => “[object Error]”// RegExp 類型,tag 為 “RegExp”console.log(Object.prototype.toString.call(/d+/)); // => “[object RegExp]”// Date 類型,tag 為 “Date”console.log(Object.prototype.toString.call(new Date())); // => “[object Date]”// 其他類型,tag 為 “Object”console.log(Object.prototype.toString.call(new class {})); // => “[object Object]”// window 全局對象console.log(Object.prototype.toString.call(window); // => “[object Window]”)
在 JavaScript 中,所有類都繼承于 Object,因此 toString 方法應該也被繼承了,但由上述可見事實并不像我們想的那樣,其實各數(shù)據(jù)類型使用 toString() 后的結(jié)果表現(xiàn)不一的原因在于:所有類在基礎 Object 的時候,改寫了 toString 方法。盡管如此,但 Object 原型上的方法是可以輸出數(shù)據(jù)類型的,因此我們想判斷數(shù)據(jù)類型時,也只能使用原型上的 toString 方法:Object.prototype.toString.call(object) 。
直接調(diào)用
toString(); // “[object Undefined]”(function(){ console.log(toString()); // [object Undefined]})();也就是說直接調(diào)用toString()方法,等價于Object.prototype.toString.call(); // “[object Undefined]”Object.prototype.toString.call(undefined); // “[object Undefined]”即:直接調(diào)用 toString() 方法這里不可以理解成為全局作用域調(diào)用 toString() 方法,即 window.toString()所以直接調(diào)用 toString() 應該就是變相的 undefined.toString() 方法(這里說的是相當于,實際 undefined 并沒有方法,調(diào)用會報錯)。
驗證
// 定義一個數(shù)組var arr = [1, 2, 3]// 數(shù)組原型上是否具有 toString() 方法console.log(Array.prototype.hasOwnProperty(‘toString’)) //true// 數(shù)組直接使用自身的 toString() 方法console.log(arr.toString()) // ‘1,2,3’// delete操作符刪除數(shù)組原型上的 toString()delete Array.prototype.toString// 刪除后,數(shù)組原型上是否還具有 toString() 方法console.log(Array.prototype.hasOwnProperty(‘toString’)) //false// 刪除后的數(shù)組再次使用 toString() 時,會向上層訪問這個方法,即 Object 的 toString()console.log(arr.toString()) // ‘[object Array]’
當我們把 Array 自身的 toString() 方法刪除之后,再次使用它時,由原型鏈它會向上查找這個方法,即 Object 的 toString(),也便將 Object 上的 toString() 方法作用在數(shù)組上,得出其數(shù)據(jù)類型 [object Array] 。
為什么需要call/apply
經(jīng)常有人用 toString.call/apply(類型) 去代替 Object.prototype.toString.call/apply(類型) 使用,其實這樣是不嚴謹?shù)?,容易導致一些問題,如下所示
function toString(){ console.log(“1”)}toString(); // 1toString.call({}); // 1toString.call([]); // 1
我們可以發(fā)現(xiàn),當我們自定義了 toString() 方法時,直接調(diào)用 toString() 方法,就不會再默認調(diào)用 Object 類的 toString() 方法,而是會使用我們自定義的方法,這樣可能得不到我們想要的結(jié)果,所以我們還是應當盡量使用 Object.prototype.toString.call/apply(類型)。
正因為 Object.prototype.toString() 本身允許被重寫,像 Array、Boolean、Number 的 toString 就被重寫過,所以需要調(diào)用 Object.prototype.toString.call(arg) 或 Object.prototype.toString.apply(arg) 或 Reflect.apply() 來判斷 arg 的類型,call 將 arg 的上下文指向 Object,所以 arg 執(zhí)行了 Object 的 toString() 方法。
至于 call,就是改變對象的 this 指向,當一個對象想調(diào)用另一個對象的方法,可以通過 call 或者 apply 改變其 this 指向,將其 this 指向擁有此方法的對象,就可以調(diào)用該方法了。
var x = { toString() { return “X”; },};x.toString(); // => “X”Object.prototype.toString.call(x); // => “[object Object]”Object.prototype.toString.apply(x); // => “[object Object]”Reflect.apply(Object.prototype.toString, x, []); // => “[object Object]”
判斷原生JSON對象
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON);console.log(isNativeJSON); // 輸出結(jié)果為 “[object JSON]” 說明JSON是原生的,否則不是;