JS原型和原型链的理解

构造函数创建对象

我们首先使用构造函数来创建一个对象。

function A () {

};

let a = new A();
a.value = 'a';
console.log(a.value); //输出的结果为a.

prototype

每一个函数都会有一个prototype属性(只有函数才具有的属性),prototype属性指向的是调用构造函数创建的实例的原型。原型指的是每一个javascript对象在创建的时候(null除外)都会与之关联的另一个对象。而每一个对象都会从原型中继承该属性。

function A () {

};

A.prototype.value = 'A';
let a1 = new A();
let a2 = new A();
a1.value = 'a1';

console.log(a1);    //输出a1 --来自实例。
console.log(a2);    //输出A --来自原型。

delete a1.value;
console.log(a1);    //输出A --来自原型。

在以上例子中,需要访问a1中的value时,会在这该对象中搜索名为value的属性,因为value在该对象中的确存在,因此直接返回该值而不必再去搜索原型;同样的因为在a2中不存在value,因此会向上搜索原型,结果在原型中查找出了value属性。

当在对象中添加一个属性时,这个属性就会屏蔽在原型对象中保存的同名属性(a1中的value屏蔽了A中的value)。也就是说添加了这个属性阻止我们访问原型中的那个属性,但不会修改那个属性。不过使用delete操作符则可以完全删除实例的属性,从而重新访问到原型中的属性。

用一张图来表示构造函数和原型的关系:

_proto_

这是每一个对象都具有的属性(null除外),叫做_proto_,会指向该对象的原型。

function A () {

};

let a = new A();

console.log(a.__proto__ === A.prototype);  //true

由此我们能够更新一下关系图得到:

 constructor

既然构造函数和实例对象都能够指向原型,那么原型是否也包含一个属性来指向构造函数和实例对象的呢? 指向实例对象是没有的,因为一个构造函数可以生成多个实例对象。但是指向构造函数的却是有一个,这就要谈到今天要说的第三个属性constructor了。每一个原型都含有一个constructor属性用来指向关联的构造函数。

function A() {

};

console.log(A.prototype.constructor === A);    //true

所以再次更新关系图:

综上我们可以得出一个结论就是:

function A () {

};

let a = new A();

console.log(a.__proto__ === A.prototype);    //true
console.log(A.prototype.constructor === A);     //true

原型的由来

原型对象是通过Object构造函数生成的,结合之前所讲的_proto_指向构造函数的prototype。

function A () {

};

console.log(A.prototype.__proto__ === Object.prototype);    //true 

 由此我们可以得出关系图为:

 

 接下来我们再看看Object.prototpye的原型来自什么。

console.log(Object.prototype.__proto__);    //null

 引用阮一峰老师的《undefined和null的区别》

null 表示“没有对象”,即该处不应该有值。

所以 Object.prototype.__proto__ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。

所以查找属性的时候查到 Object.prototype 就可以停止查找了。

最后一张关系图也可以更新为:

总结:

A.__proto__ === Function.prototype 
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null 
A.protype__proto__ == Object.prototype
原文地址:https://www.cnblogs.com/pipim/p/13099418.html