JS:一切皆对象
原型
-
对象拥有
__proto__,指向其构造函数的prototype,即构造函数的原型,在浏览器中,以[Prototype]]表示 -
构造函数拥有
prototype,当new时,会将构造函数的prototype属性与其实例的__proto__属性值拷贝(如果该属性是对象,则拷贝引用地址) -
构造函数的终点是
Function,Function是构造函数,所以也拥有prototype属性 -
JS一切皆对象,函数也是对象,作为函数和对象,所以函数同时拥有了
__proto__和prototype属性 -
JS一切皆对象,原型也是对象,作为对象,所以原型也拥有
__proto__,这个__proto__指向构造这个对象的构造函数的原型,即构造函数的prototype属性,即对象.__proto__===构造函数.prototype -
一切对象都是由构造函数构造的,包括函数自己,所以,函数本质上是Function的实例
注意:这里的对象,指的是对象,而非基本数据类型,如number、string等
// Function自己构造了自己 console.log( Function.__proto__===Function.prototype ); // Object对象由Function构造,但同时Object对象在语言层面的构造函数的原型是Object.prototype,也就是说Object对象由Function和Object共同构造 console.log( Object instanceof Object ); console.log( Object.__proto__===Function.prototype ); // Function与Object互相继承,鸡生蛋、蛋生鸡 console.log( Function instanceof Object ); console.log( Object instanceof Function ); // Function与Object作为函数对象,构造函数的原型相同 console.log( Function.__proto__ === Object.__proto__ ); // 原型链的顶端是Object构造函数的prototype console.log( Function.prototype.__proto__===Object.prototype ); // Object.prototype是特例,是原型,同是是对象,但这个原型没有原型,即null console.log( Object.prototype.__proto__===null ); // {}不是函数生成的对象,所以不是由Function函数构造 console.log( {} instanceof Function ); // {}是字面对象,由Object函数构造 console.log( {} instanceof Object ); // Object.toString指向Function.prototype.toString console.log(Object.toString===Function.prototype.toString); // Object.prototype.toString指向Function原型的原型的toString,也就是Object.prototype.toString,也就是原型终点的toString console.log(Object.prototype.toString===Function.prototype.__proto__.toString); function myFunction1(a, b){ return a + b; } // 函数继承自Function,说明函数也是由Function构造的 console.log( myFunction1 instanceof Function ); // 函数也可直接用new Function 声明函数 const myFunction2 = new Function( 'a', 'b', 'return a + b' ); console.log( myFunction2( 1, 2 ) ); // 3 -
由上述得知,对象拥有
__proto__属性,Function.prototype作为原型,原型也是对象,也拥有__proto__属性,这个__proto__属性指向该原型的构造函数的原型,也就是原型的终点Object.prototype,即Function.prototype.__proto__===Object.prototype
原型链
- 如果当前对象没有某方法,会沿着原型链去找,也就是通过该对象的
__proto__去它的构造函数的prototype属性中去找,找到则调用,未找到,继续沿着构造函数.prototype.__proto__去找,即原型链去找,即查找的方向是对象.__proto__.__proto__.....去找,直至原型链终点Object.prototype
toString.call
好了,在以上表述明确的基础上,可以理解为什么必须用Object.prototype.toString.call,而不是直接用Object.toString.call去获取对象类型的字符串表示了
Object.toString
Object对象是全局对象,在浏览器里,Object来自于windowObject对象自己没有toString方法,所以会从原型链上找,也就是通过Object.__proto__上去它的构造函数的prototype中去找,而Object.__proto__===Function.prototype,Function.prototype中有toString方法,找到了,就不再沿原型链继续找了,所以Object.toString本质上调用的是Function.prototype.toString
Object.prototype.toString
- 此方法是原型终点的方法,
Function.prototype上重写的toString方法不同 - 所以
Object.prototype.toString!==Function.prototype.toString,也就是Object.prototype.toString!==Object.toString
call
原型与原型链讲完了,现在只剩call了
-
Object.prototype.toString.call
查看浏览器,
Object.prototype中只有toString方法,没有call方法,而且toString方法是一个函数,那么Object.prototype.toString.call是怎么的呢? -
看了前面的原型与原型链,可明白,任何对象都是由构造函数构造的,构造函数也是对象,而构造函数的终点是
Function,也就是说,任何函数都有__proto__的属性,该属性来自于Function.prototype -
toString是一个函数,最终也是由构造函数构造的,也就是说,toString.__proto__===Function.prototype -
再重复一遍,
toString中的call方法,来自于Function.prototype,也就是说,任何函数对象,通过原型链,都能调用到Function.prototype中的任何方法 -
所以,本质上,
Object.prototype.toString.call调用的不是Object.prototype.call,而是调用Function.prototype.call这个方法 -
同理:
Object.toString.call === Function.prototype.call -
同理:
Object.call === Function.prototype.call
散会