今天学习 vue 看 node_modules 源码,看到 shallow-clone、以及 clone-deep 时,isPlainObject() 方法源码中使用 Object.prototype.toString.call(o) === '[object Object]' 判断 o 是否是对象。所以后面将自己的理解与总结记录下来。
广为人知判断 js 数据类型的方法
js基本类型:null undefined string number boolean symbol(ES6新增)
js内置引用类型:Object Array Function Date RegExp Error ...
typeof 操作符判断基本类型
console.log(typeof 'a') // string
console.log(typeof 1) // number
console.log(typeof true) // boolean
console.log(typeof undefined) // undefined
console.log(typeof symbol('1')) // symbol
// typeof null 是 object, js 遗留的 bug
console.log(typeof null) // object
// 大多引用型对象返回 object
console.log(typeof {}) // object
console.log(typeof []) // object
// 例外
console.log(typeof function a() {}) // function
instanceof 操作符判断引用类型
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
// 对象
console.log({} instanceof Object) // true
// 数组
console.log([] instanceof Array) // true
// 日期对象
console.log(new Date() instanceof Date) // true
// 函数
console.log(function a() {} instanceof Function) // true
// 正则
console.log(/a+/ instanceof RegExp) // true
// 所有引用类型对象的 __proto__ 都指向 Object.prototype
console.log([] instanceof Object) // true
console.log(function a() {} instanceof Object) // true
...
Object.prototype.toString.call/apply() 判断任何数据类型
不存在浏览器兼容性问题
判断基本类型
console.log(Object.prototype.toString.call('a')) // [object String]
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call(false)) // [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(Symbol(1))) // [object Symbol]
判断引用类型
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(new Date())) // [object Date]
console.log(Object.prototype.toString.call(function a(){})) // [object Function]
console.log(Object.prototype.toString.call(/a+/)) // [object RegExp]
Object.prototype.toString.call/apply() 原理
首先,说一下 Object 和 Object.prototype
Object 是创建 js 对象的构造函数,我们可以用 new 操作符创建一个对象
var person = new Object({name: 'Tom'}); // {name: 'Tom'}
// 等同于
var person = {name: 'Tom'}; // {name: 'Tom'}
接下来看一下另一种创建 person 对象的方法
// 声明一个 Person 构造函数
function Person(name) {
this.name = name;
// 我们重写一下 toString() 方法
this.toString = function() {
console.log('Name:', this.name);
}
}
var person = new Person('Tom'); // {name: 'Tom'}
person.toString(); // Name: Tom
Object.prototype.toString.call(person) 输出 [object Object],说明 Object.prototype.toString.call(person) 不会调用 Person 的 toString()方法
Object.prototype.toString.call(person); // [object Object]
Object.prototype 属性表示 Object 的原型对象,在 javascript 中几乎所有的对象都是 Object 的实例
控制台打印一下 Object.prototype,我们可以看到是一个对象,有很多内置的方法,我们现在只讨论 toString() 方法
constructor : ƒ Object()
hasOwnProperty : ƒ hasOwnProperty()
...
toString : ƒ toString()
...
接下来,说一下 toString() 方法
在js中,每个对象都有 toString() 方法,返回对象的字符串形式,但是不同对象 toString() 的结果又有差异,这是因为 js 不同构造函数实现 toString() 的方法不同
console.log('a'.toString()); // "a"
console.log(Symbol(1).toString()) // "Symbol(1)"
// 按照字符串的 toString() 方法,结果应该是 "[1,2,3]",事实却不是,所以证明两个方法实现不同
console.log([1,2,3].toString()) // "1,2,3"
// Object 构造函数实现的 toString() 方法
console.log({a:1}.toString()) // "[object Object]"
现在模拟一下 Object 构造函数实现 toString() 方法
function MyObject(o) {
this.toString = function() {
// 此处只是模拟生成 [object Object] 的格式
return `[object ${o.constructor.name}]`;
}
}
var person = new MyObject({name: 'Tom'});
person.toString(); // "[object Object]"
var arr = new MyObject([1,2,3]);
person.toString(); // "[object Array]"
结论
Object.prototype.toString.call/apply() 其实是利用 Object.prototype.toString() 方法可以返回 "[obejct 对象构造函数名]" 的原理来确定数据类型,并且 js 中所有对象都是Object 的实例,可以使用 call/apply 调用 Object.prototype 原型对象的 toString() 方法。
至于 Object.prototype.toString() 内部实现原理,等到有机会再去研究。