原型链就是如下神图
通常继承与原型链是一并出现的
原型
原型链当然是原型组成的了。在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。与JavaScript 的 new 操作符一起使用。对原型对象的引用被复制到新实例的内部 [[Prototype]] 属性。
例如,当执行 var a1 = new A(); 时,JavaScript(在内存中创建对象之后,和在运行函数 A() 把 this 指向对象之前)设置 a1.[[Prototype]] = A.prototype;
然后当您访问实例的属性时,JavaScript 首先会检查它们是否直接存在于该对象上,如果不存在,则会 [[Prototype]] 中查找。这意味着你在 prototype 中定义的所有内容都可以由所有实例有效地共享,你甚至可以稍后更改部分 prototype,并在所有现有实例中显示更改(如果有必要的话)。
function Person(age) {
this.age = age
}
Person.prototype.name = 'kavin'
var person1 = new Person()
var person2 = new Person()
console.log(person1.name) //kavin
console.log(person2.name) //kavin
原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
proto
这是每个对象(除null外)都会有的属性,叫做proto,这个属性会指向该对象的原型。
Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
原型与实例
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。原型对象就是通过 Object 构造函数生成的,结合之前所讲,实例的 proto 指向构造函数的 prototype 。
原型链
简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》
图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
扩展原型链的方法
1.New-initialization
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = new foo;
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
优缺点:为使用此方法,这个问题中的函数必须要被初始化。 在这个初始化过程中,构造可以存储一个唯一的信息,并强制在每个对象中生成。但是,这个一次性生成的独特信息,可能会带来潜在的问题。另外,构造函数的初始化,可能会给生成对象带来并不想要的方法。 然而,如果你只在自己的代码中使用,你也清楚(或有通过注释等写明)各段代码在做什么,这些在大体上都根本不是问题(事实上,还常常是有益处的)。
2.Object.create
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
foo.prototype
);
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
foo.prototype,
{
bar_prop: {
value: "bar val"
}
}
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
优缺点:允许一次性地直接设置 proto 属性,以便浏览器能更好地优化对象。同时允许通过 Object.create(null) 来创建一个没有原型的对象。这个慢对象初始化在使用第二个参数的时候有可能成为一个性能黑洞,因为每个对象的描述符属性都有自己的描述对象。当以对象的格式处理成百上千的对象描述的时候,可能会造成严重的性能问题。
3.Object.setPrototypeOf
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = {
bar_prop: "bar val"
};
Object.setPrototypeOf(
proto, foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto;
proto=Object.setPrototypeOf(
{ bar_prop: "bar val" },
foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
优缺点:允许动态操作对象的原型,甚至能强制给通过 Object.create(null) 创建出来的没有原型的对象添加一个原型。这个方式表现并不好,应该被弃用。如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。
4.proto
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = {
bar_prop: "bar val",
__proto__: foo.prototype
};
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
var inst = {
__proto__: {
bar_prop: "bar val",
__proto__: {
foo_prop: "foo val",
__proto__: Object.prototype
}
}
};
console.log(inst.foo_prop);
console.log(inst.bar_prop)
优缺点:将 proto 设置为非对象的值会静默失败,并不会抛出错误。应该完全将其抛弃因为这个行为完全不具备性能可言。 如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。
上述内容摘自MDN和>https://www.cnblogs.com/loveyaxin/p/11151586.html