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(); 
}
这个 object()函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上,object()是对传入的对象执行了一次浅复制。
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】寄生式继承和寄生组合式继承单独开一个随笔去解释,这里到现在感觉太长了有点......

 

posted @ 2021-09-23 19:55  TwinkleG  Views(64)  Comments(0)    收藏  举报