彻底弄清楚JavaScript的原型机制
本文主要阐述 JavaScript 的原型机制,剖析清楚 Function 和 Object 的之间的关系
JS 函数声明方式
方式1:使用 function
加函数名来声明
1 | function Person() { |
方式2:使用 Function 构造函数声明
1 | var person = new Function(undefined, 'console.log("zzg")'); |
JS 函数调用方式
普通函数调用
1 | Person(); // zzg |
使用构造函数调用
1 | new Person(); // zzg |
JavaScript 的原型
1 | function Person(name) { |
每一个函数都有一个 prototype
属性,指向他的原型对象
,原型对象
里面有一个 constructor
属性,指向函数本身。
1 | console.log(Person.prototype) // // {constructor: ƒ} |
当使用一个函数当做构造函数来调用后,其实例对象有一个内部对象 __proto__
指向其原型对象
1 | // 输出 p1 对象的原型对象 |
此时我们可以画出以下的原理图:
由于 p1
对象的原型对象 Person.prototype
也是对象,那么他谁构造的实例呢?他的构造函数又是谁?
1 | // 输出 Person.prototype 的原型对象 |
从上面可以知道,函数的原型对象 Person.prototype
是由 Object
构造出来的
1 | console.log(Person.prototype.__proto__ === Object.prototype); // true |
那么 Object
的原型对象 Object.prototype
又是谁呢?
1 | console.log(Object.prototype.__proto__); // null |
于是我可以得到以下的关系:
1 | console.log( p1.__proto__ === Person.prototype); // true |
此时我们可以画出一下的原理图:
此时的链条就是原型链
Function 和 Object 的关系
上面的方式二中 Function
可以使用 new Function
调用,那说明 Function
也可以当做一个构造函数来使用,Object
也是如此。
1 | console.log(Function); // ƒ Function() { [native code] } |
可以发现他们两个是 JS 内置的原生的函数,既然是函数按照上面的规则是不是也得有原型对象呢?
1 | // Function 的原型对象 Function.prototype 是一个函数 |
上面知道 Object
的原型对象Object.prototype
,是对象,他的构造函数又是谁?
1 | console.log(Object.prototype.constructor); // ƒ Object() { [native code] } |
从上面可以知道 Object
的原型对象 Object.prototype
的构造函数是 ƒ Object() { [native code] }
,那么这个内置的 Object 构造函数又是谁构造的呢?
1 | console.log(Object.prototype.constructor.constructor); // ƒ Function() { [native code] } |
至此我们总结一下:
内置的 Function
函数是 Object
的构造函数,那么 Object
函数是 Function
函数的一个实例,我们判断一下
1 | console.log(Object instanceof Function); // true |
既然 Object
是 Function
的实例,那么他内部的 __proto__
属性指向谁呢?
1 | console.log(Object.__proto__); // ƒ () { [native code] } |
根据上面的规则,那么 Function
原型对象肯定和 Object
这个实例肯定也是有关联的
1 | console.log(Function.prototype === Object.__proto__); // true |
至此我们弄明白了 Object
和 Function
的关系,以及他们内部是如何关联的,但是还剩下一个点,Function
这个内置是由谁构造的呢?
1 | // 谁是 Function 的构造器 |
从上面的代码可以知道,Function
自己创造了自己,Function
的原型对象 Function.prototype
的构造函数指向自身
至此我们弄明白了 Function
是由谁创造的,还剩下 Function
的原型对象又是谁创造的呢?
1 | // 发现他是一个内置的函数 |
由此我们可以分析出来:Function.prototype
指向了 Object.prototype
,所有我们可以在函数上使用toString
这些方法
所以基于 instaceof 的规则下面的判断是成立的,因为 Function
的 __proto__
指向了 Function.prototype
,而 Function.prototype
又是指向Object.prototype
,就像 p1
的 __proto__
属性指向 Person.prototype
。
1 | console.log(Function instanceof Object); // true |
此时我们可以画出以下的原理图:
Person 和 Function 的关系
其实从函数的声明 方式2
我们就猜测的到,Person 函数肯定是由 Function
创造的,我们不妨验证一下,Person 的 __proto__
属性指向谁
1 | console.log(Person.__proto__ === Function.prototype); // true |
此时我们可以画出以下的原理图:
总结
上面的分析非常的饶人,最后总结一下:
- 从构造函数的角度上来说,
Object
继承了Function
(Object
由Function
构造) - 从原型的角度上来说
Function
继承了Object
(Function
的原型对象指向了Function
的原型对象)