JavaScript 原型链知识点复习
1. 学过之后过了一些时间再回来看发现原型链还是比较容易理解的,不想再详细的去重复细节了.
2. 简单复习重要的知识点:
【1】原型链的形成:首先我们知道 构造函数A 会有一个 prototype 指向原型对象,原型对象又有一个 constructor 指回 构造函数A,构造函数A 的实例对象有一个内部属性[[ prototype ]]指向这个构造函数的原型对象,但我们一般无法访问这个内部属性,而是通过浏览器暴露给我们的 __proto__ 去访问这个构造函数的原型对象. 
理解了这三者的关系,我们可以想到,如果 构造函数A 的 prototype 是 另一个 构造函数B 的实例,这样 构造函数A 的原型对象和实例就可以访问到 构造函数B 的原型对象,从而形成一个原型链.
【2】任何函数的原型对象都默认是一个 Object 的实例 ,这也是为什么我们的自定义类型可以调用 toString() 等 Object 上的方法的原因.
【3】原型与实例之间的关系可以通过 instanceof 或者 isPrototypeOf 确认
【4】可以通过原型链的自底向上的查找机制覆盖父方法
【5】构造函数的 prototype 如果以对象字面量创建,那么会重写原型链,慎用!
【6】原型链的问题:(1) 构造函数B 的实例属性 成为了 构造函数A 所有实例共享的属性,这不是我们所希望看到的.
(2) 构造函数A 在创建实例时无法给父构造函数传递参数.
因为原型链的问题,所以一般情况下我们不会单独使用原型链.
3. 解决原型链的问题:
【1】盗用构造函数:又称之为 经典继承、对象伪装【好烦,为什么这么多别名】,实际就是在子构造函数中使用apply、call在子构造函数作用域去调用父构造函数,这样每一个子构造函数实例都会有自己的一份属性和方法,不会再被所有实例所共享属性。
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { // 继承 SuperType SuperType.call(this); } let instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" let instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green"
同时还可以向父构造函数传参:
function SuperType(name){ this.name = name; } function SubType() { // 继承 SuperType 并传参 SuperType.call(this, "Nicholas"); // 实例属性 this.age = 29; } let instance = new SubType(); console.log(instance.name); // "Nicholas"; console.log(instance.age); // 29
单独使用盗用构造函数的问题和当初构造函数的问题一样,那就是没有办法使得所有实例对象共享属性和方法,因此它一般也不会单独使用.
【2】组合继承:又称作伪经典继承,将盗用构造函数和原型链方式结合起来,既可以使在原型对象上所有实例共享属性和方法,又可以使每个实例具备自身独有的实例属性和方法。
详细的示例可以参考 JavaScript 红宝书第四版P244,第四版话说真的比第三版读起来要舒服的多。
【3】原型式继承:不自定义类型也可以通过原型实现对象之间信息的共享,换个方式解释一下就是 不去创建一个构造函数,也能够通过使用 prototype 去让一个对象的属性和方法 被多个其他对象所共享.
function object(o) { function F() {} F.prototype = o; return new F(); }
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] };
let anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob");
let yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
ES5 Object.create() 方法实际上就是原型式继承的概念规范化,它接收两个参数,第二个参数可选,第一个参数就相当于是 要被共享的对象,即上例的 person,第二个参数作用是添加不希望被共享的属性和方法,其用法和 Object.defineProperties 相似,一个对象里面有多个属性,属性后跟冒号 + 这个属性的描述符对象【descriptor】
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); let yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person, { name: { value: "Greg" } }); console.log(anotherPerson.name); // "Greg"
【4】寄生式继承和寄生组合式继承单独开一个随笔去解释,这里到现在感觉太长了有点......

浙公网安备 33010602011771号