在 JavaScript 中,所有类都继承于 Object,因此 toString 方法应该也被继承了,但由上述可见事实并不像我们想的那样,其实各数据类型使用 toString() 后的结果表现不一的原因在于:所有类在基础 Object 的时候,改写了 toString 方法。尽管如此,但 Object 原型上的方法是可以输出数据类型的,因此我们想判断数据类型时,也只能使用原型上的 toString 方法:Object.prototype.toString.call(object) 。
直接调用
toString(); // "[object Undefined]"
(function(){
console.log(toString()); // [object Undefined]
})();
也就是说直接调用toString()方法,等价于
Object.prototype.toString.call(); // "[object Undefined]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
即:直接调用 toString() 方法这里不可以理解成为全局作用域调用 toString() 方法,即 window.toString()
所以直接调用 toString() 应该就是变相的 undefined.toString() 方法(这里说的是相当于,实际 undefined 并没有方法,调用会报错)。
验证
// 定义一个数组
var arr = [1, 2, 3]
// 数组原型上是否具有 toString() 方法
console.log(Array.prototype.hasOwnProperty('toString')) //true
// 数组直接使用自身的 toString() 方法
console.log(arr.toString()) // '1,2,3'
// delete操作符删除数组原型上的 toString()
delete Array.prototype.toString
// 删除后,数组原型上是否还具有 toString() 方法
console.log(Array.prototype.hasOwnProperty('toString')) //false
// 删除后的数组再次使用 toString() 时,会向上层访问这个方法,即 Object 的 toString()
console.log(arr.toString()) // '[object Array]'
当我们把 Array 自身的 toString() 方法删除之后,再次使用它时,由原型链它会向上查找这个方法,即 Object 的 toString(),也便将 Object 上的 toString() 方法作用在数组上,得出其数据类型 [object Array] 。
为什么需要call/apply
经常有人用 toString.call/apply(类型) 去代替 Object.prototype.toString.call/apply(类型) 使用,其实这样是不严谨的,容易导致一些问题,如下所示
function toString(){
console.log("1")
}
toString(); // 1
toString.call({}); // 1
toString.call([]); // 1
我们可以发现,当我们自定义了 toString() 方法时,直接调用 toString() 方法,就不会再默认调用 Object 类的 toString() 方法,而是会使用我们自定义的方法,这样可能得不到我们想要的结果,所以我们还是应当尽量使用 Object.prototype.toString.call/apply(类型)。
正因为 Object.prototype.toString() 本身允许被重写,像 Array、Boolean、Number 的 toString 就被重写过,所以需要调用 Object.prototype.toString.call(arg) 或 Object.prototype.toString.apply(arg) 或 Reflect.apply() 来判断 arg 的类型,call 将 arg 的上下文指向 Object,所以 arg 执行了 Object 的 toString() 方法。
至于 call,就是改变对象的 this 指向,当一个对象想调用另一个对象的方法,可以通过 call 或者 apply 改变其 this 指向,将其 this 指向拥有此方法的对象,就可以调用该方法了。
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); // 输出结果为 "[object JSON]" 说明JSON是原生的,否则不是;