一、原型概念图
二、原型相关的几个属性
在介绍原型之前,我们先要了解几个属性:
1.1 显示原型属性 prototype
prototype
属性是一个显示原型属性,只有函数才拥有该属性。基本上所有函数都有这个属性,但是也有一个例外:
let fn = Function.prototype.bind() // 该函数没有 prototype 属性
当我们声明一个函数时,这个属性就被自动创建了,并且这个属性指向函数的原型对象:
function Foo() {}
console.log(Foo.prototype) // { constructor: f }
1.2 构造函数属性 constructor
原型对象的 constructor
属性是一个公共且不可枚举的属性,该属性指向构造函数:
Foo.prototype.construcotr === Foo // true
一旦我们改变了构造函数的显示原型指向,那新的原型对象就没有这个属性了(可以通过原型链取到 constructor
)。
function Foo() {}
Foo.prototype = { a: 1 }
console.log(Foo.prototype) // { a: 1 }
1.3 隐式原型属性 __proto__
__proto__
属性是一个隐式原型属性,指向创建该对象的构造函数的原型对象,每个对象都有该属性(除了 Object.create(null)
创建的对象外)。
其实这个属性指向 [[prototype]],但是 [[prototype]] 是内部属性,并不能访问到,所以在浏览器中通过 __proto__
来访问。
实例对象的 __proto__
产生过程:
在使用 new
操作符时,生成的实例对象则拥有了 __proto__
属性。所以我们可以认为,在 new
的过程中,新对象被添加了 __proto__
并且链接到构造函数的原型对象上。
三、原型链
在 JavaScript 中没有类的概念,为了实现类似继承的方式,通过 __proto__
将对象和原型对象联系起来组成原型链,可以让对象访问到不属于自己的属性。
// 在上图所示的概念图中的原型链示例
new Foo() --> Foo.prototype --> Object.prototype
四、Function.prototype 和 Object.prototype
从图中可以发现,所有对象都可以通过原型链最终找到 Object.prototype
,虽然 Object.prototype
也是一个对象,但是这个对象并不是由 new Object()
创建的,而是由引擎创建的。所以可以这样说,所以实例都是对象,但是对象不一定都是实例。
接下来我们来看 Function.prototype
这个特殊的对象,在浏览器中打印这个对象,会发现它其实是一个函数:
console.log(Function.prototype) // ƒ () { [native code] }
我们知道函数都是通过 new Function()
创建的,难道 Function.prototype
也是通过 new Function()
创建的吗?答案是否定的,这个函数也是由引擎创建的。
从图中可以发现,所有函数(对象)的 __proto__
都指向 Function.prototype
。 Function.prototype
也是由引擎创建的,所以我们又可以得出一个结论,不是所以的函数都是由 new Function()
创建的。
首先引擎创建了 Object.prototype
,然后创建了 Function.prototype
,并且通过 __proto__
将两者联系了起来。
有三个特殊的场景:
Function.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
五、总结
Function.prototype
和Object.prototype
是两个特殊的对象,它们由引擎来创建。- 函数的
prototype
属性指向函数的原型对象。 - 对象的
__proto__
属性指向创建该对象的构造函数的原型,__proto__
将对象和原型对象连接起来组成了原型链。
参考链接: