原型链
再上一篇有简单讲过原型:JavaScript(5)--- 面向对象 + 原型
讲原型链知识之前,先说几个重要的结论。
1、原型链就是 对象的__proto__所连接的链状结构
2、prototype 属性是函数独有的
3、__proto__ 属性是对象独有的,实例原型(Object.prototype)也是对象,所以也会有__proto__属性
下面我们一步一步来讲解原型链
一、prototype属性
1、构造函数创建对象
我们先使用构造函数创建一个对象:
function Person() {
}
var p = new Person();
p.name = 'xiaoxiao';
console.log(p.name); //xiaoxiao
2、 prototype属性
概念
它是 函数独有的属性,它从一个函数指向另一个对象,代表这个对象是这个函数的原型对象,这个对象也是当前函数所创建的实例的原型对象。
prototype设计之初就是为了实现继承,让构造函数创建的所有实例,都能够共享这个原型属性和方法。
有了prototype我们不需要为每一个实例创建重复的属性方法,而是将属性方法创建在构造函数的原型对象上(prototype)。那些不需要共享的才创建在构造函数中。
示例
function Person(){
}
Person.prototype.age=18; //原型属性
var p1 = new Person();
var p2 = new Person();
console.log(p1.age);//18
console.log(p2.age);//18
思考
这个函数的 prototype 属性到底指向的是什么呢?
其实,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型 。
思考
那么什么是原型呢?
每一个 JavaScript 对象 (null 除外 ) 在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型 ” 继承 ” 属性。
这里p1和p2就是实例对象,而Person.prototype就是它们的原型对象。p1和p2可以去继承原型对象的方法和属性。
让我们用一张图表示构造函数和实例原型之间的关系:
二、__proto__属性
概念
__proto__属性是对象(包括函数,函数也是对象)独有的。__proto__属性是从一个对象指向另一个对象,该属性指向的就是该对象的原型对象(也可以理解为父对象)。
示例
function Person(){
}
var p = new Person();
console.log(p.__proto__ === Person.prototype);//true
更新下关系图
__proto__通常称为隐式原型,prototype为显示原型,那我们可以说一个对象的隐式原型指向了该对象的构造函数的显示原型。那么我们在显示原型上定义的属性方法,
通过隐式原型传递给了构造函数的实例。这样一来实例就能很容易的访问到构造函数原型上的方法和属性了。
三、constructor属性
概念
constructor是对象才有的属性,它是从一个对象指向一个函数的。指向的函数就是该对象的构造函数。
注意了 实例原型也是对象,所以也会有constructor属性,我们来验证一下
function Person(){
}
console.log(Person === Person.prototype.constructor);//true
console.log(person.constructor); // ƒ Person(){}
再更新下关系图
总结 通过上面的演示说明,我们可以得出
function Person(){
}
var person = new Person();
console.log(person.__proto__ === Person.prototype);//true
console.log(Person === Person.prototype.constructor);//true
了解了构造函数、实例原型、和实例之间的关系,接下来我们讲讲实例和原型的关系
四、实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
示例
function Person(){
}
var person = new Person();
Person.prototype.name='张三';
person.name='李四';
console.log(person.name);//李四
delete person.name;
console.log(person.name);//张三
在这个例子中,我们设置了 person 的 name 属性,所以我们可以读取到为 ’李四’ ,当我们删除了 person 的 name 属性时,读取 person.name ,从 person 中找不到
就会从 person 的原型中查找,name为 ’张三’ 。但是万一Person.prototype原型中还没有找到呢?那会到原型的原型去查找。也就是Object.prototype
示例
function Person(){
}
var person = new Person();
Person.prototype.name='李四';
person.name='张三';
console.log(person.name);//张三
delete person.name;
console.log(person.name);//李四
Object.prototype.name='obj';
delete Person.prototype.name;
console.log(person.name);//obj
所以原型对象是通过 Object 构造函数生成的,结合之前所讲 , 实例的 _proto_ 指向构造函数的 prototype, 所
再更新下关系图
五、原型链
那 Object.prototypey也是对象,那它也应该会有原型。只不过有是有,只是为null
, 所以查到 Object.prototype 就可以停止查找了 。
所以最后一张关系图就是
总结
图中由相互关联的原型组成的链状结构就是原型链,也就是红色的这条线
。
参考
1、JavaScript深入之从原型到原型链 非常感谢
2、JS中的原型与原型链
3、JavaScript原型&原型链
4、JS原型链简单图解
5、一道美团外卖面试题
别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。
攻我盾者,乃我内心之矛(3)。