原型对象prototype
的主要作用是解决实例对象之间数据共享,节省内存空间。
我们以一段代码开始我们的原型之旅。
// 构造函数
function Foo() {}
// 实例对象
const foo = new Foo();
// 普通函数
function demo() {}
console.dir(Foo);
console.dir(foo);
console.dir(demo);
复制代码
显式原型和隐式原型
从控制台的打印结果可以看出,构造函数Foo
(函数对象)和普通函数demo
都有一个prototype
属性和__proto__
属性。实例对象foo
有一个__proto__
属性。我们称prototype
为显式原型,__proto__
为隐式原型。
所有函数都是Function
的实例
console.log(demo instanceof Function) // true
console.log(Foo instanceof Function) // true
复制代码
我们经常说实例对象上会有__proto__
属性,只要有__proto__
就可以说明他一定是个实例对象,那么构造函数为什么也会有__proto__
属性呢?这就要归功于Function
了,所有函数都是通过new Function()
生成的,也就是所有函数都是Function
的实例,所以函数也有__proto__
属性。
从打印的结果上看,函数和构造函数的结构是一模一样的,也就是说构造函数也是函数。
显式原型和隐式原型的关系
函数的prototype
属性是在函数定义的时候自动添加的。默认值是一个空的Object
实例对象。创建的大概过程Fn.prototype = {}
实例对象的__proto__
属性是在对象创建的时候自动添加的,默认值是构造函数的prototype
属性值。创建的大概过程this.__proto__ = Fn.prototype
//验证
console.log(foo.__proto__ === Foo.prototype);//true
复制代码
所以可以得出结论:实例对象的隐式原型的值等于其构造函数的显示原型的值,它们俩指向同一块内存区域。
原型链
// 构造函数
function Foo(w) {
this.w = w;
}
Foo.prototype.a = 1;
Object.prototype.b = 2;
// 实例对象
const foo = new Foo(1);
console.log(foo.w); // 1
console.log(foo.a); // 1
console.log(foo.b); // 2
console.log(foo.c); // undefined
复制代码
foo
上并没有a
和b
,那么为什么可以访问到呢? 这就要归功于原型链的机制了。
内存图
原型链的查找机制
当访问一个对象的属性的时,首先会在自身属性中去查找,找到就返回;如果没有,就再沿着__proto__
这条链向上查找,找到就返回,如果最终没有找到,返回undefined。Object
原型对象就是原型链的尽头。
原型链是沿着隐式原型__proto__
查找的。
那么这条链是如何产生的呢?从上文我们知道对象的隐式原型的值和它的构造函数显式原型的值是相等的。而且构造函数的显式原型对象默认是一个Object
实例对象, 也就是说prototype
是Object
的实例对象,那么prototype
的隐式原型__proto__
就指向Object
的显式原型prototype
。
在本例中也就是
console.log(Foo.prototype.__proto__ === Object.prototype);//true
复制代码
既然构造函数的显式原型对象默认是一个Object
实例对象,那么Object.prototype
它是不是Object
的实例对象呢?答案是否定的。
console.log(Object.prototype instanceof Object); //false
复制代码
我们要修改一下前面的结论: 函数的显式原型prototype
指向的对象,默认是空Object
的实例对象(但是Object不满足
)。
console.log(Object.prototype.__proto__); // null
复制代码
那么Object.prototype
是谁的实例呢?让我们来看看Object.prototype
的隐式原型__proto__
,不幸的是它的隐式原型为null
,也就是说Object
原型对象就是原型链的尽头。
// foo.__proto__是 Foo.prototype
// foo.__proto__.__proto__ 是 Foo.prototype.__proto__是Object.prototype
// foo.__proto__.__proto__.__proto__ 是Object.prototype.__proto__是 undefined/null
foo.__proto__.__proto__.__proto__ = null
复制代码
一个比较复杂的原型链
instanceof
的实现机制
表达式: A instanceof B
如果B函数的显式原型对象prototype
在A对象的原型链__proto__
上,返回true
,否则返回false
。
简单实现,只能判断实例对象哦
function myInstanceof(left, right){
// 获取实例对象的隐式原型
let proto = Object.getPrototypeOf(left);
// 获取函数显式原型对象
const prototype = right.prototype;
while(true){
if(!proto) return false;
if(proto === prototype) return true;
// 沿着原型链查找
proto = Object.getPrototypeOf(proto);
}
}
复制代码