JavaScript高级程序设计读书分享之8章——8.3继承

文章详细阐述了JavaScript中的原型链概念,包括每个构造函数的prototype、__proto__指针、默认继承自Object,以及instanceof和isPrototypeOf()方法。讨论了原型链中的问题,如引用值共享,并介绍了解决策略,如盗用构造函数、组合继承、原型式继承。此外,提到了Object.create()方法在原型式继承中的作用,以及寄生式组合继承以优化组合继承的效率。
摘要由CSDN通过智能技术生成

JavaScript高级程序设计(第4版)读书分享笔记记录

适用于刚入门前端的同志

 原型链

ECMA-262 原型链 定义为 ECMAScript 的主要继承方式。

原型链词义:每个构造函数都有一个原型对象(prototype),原型对象有一个属性(constructor)指回构造函数,而实例有一个内部指针(__proto__)指向原型对象如果原型是另一个类型的实例呢(我们让原型对象等于另一个类型的实例)?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想。

原型对象,构造函数,实例的名词解释,在上一章有提到

例子:

function SuperType() { 
 this.property = true; 
}

SuperType.prototype.getSuperValue = function() { 
 return this.property; 
}; 
function SubType() { 
 this.subproperty = false; 
} 
// 继承 SuperType 
SubType.prototype = new SuperType(); 
SubType.prototype.getSubValue = function () { 
    return this.subproperty; 
}; 
let instance = new SubType();

如此层层递进,就构成了实例与原型的链条,形成了原型链

 

默认原型

        实际上,原型链中还有一环。默认情况下,所有引用类型都继承自 Object ,这也是通过原型链实现的。任何函数的默认原型都是一个 Object 的实例,这意味着这个实例有一个内部指针指向
Object.prototype
补充了Object的原型链关系图:

 原型与继承关系

原型与实例的关系可以通过两种方式来确定:
        第一种方式是使用 instanceof 操作符,如果一个实例的原型链中出现过相应的构造函数,则 instanceof 返回 true
还是以上面代码为例子:
        
console.log(instance instanceof Object); // true 
console.log(instance instanceof SuperType); // true 
console.log(instance instanceof SubType); // true
确定这种关系的第二种方式是使用 isPrototypeOf()方法,只要原型链中包含这个原型,这个方法就返回 true
console.log(Object.prototype.isPrototypeOf(instance)); // true 
console.log(SuperType.prototype.isPrototypeOf(instance)); // true 
console.log(SubType.prototype.isPrototypeOf(instance)); // true

原型链的问题

        主要问题出现在原型中包含引用值的时候。前面在谈到原型的问题时也提到过,原型中包含的引用值会在所有实例间共享,这也是为什么属性常会 在构造函数中定义而不会定义在原型上的原因
例子:
function SuperType() { 
 this.colors = ["red", "blue", "green"]; 
} 
function SubType() {} 
// 继承 SuperType 
SubType.prototype = new SuperType(); 
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,black"

盗用构造函数

为了解决原型包含引用值导致的继承问题

基本思路很简单:在子类 构造函数中调用父类构造函数。可以使用 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"

组合继承

组合继承 (有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来
基本的思路:是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性

组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式
例子:
function SuperType(name){ 
    
//实例属性
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
//原型链属性,方法
SuperType.prototype.sayName = function() { 
 console.log(this.name); 
}; 
function SubType(name, age){ 
 // 继承属性
 SuperType.call(this, name); 
 this.age = age; 
} 
// 继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.sayAge = function() { 
 console.log(this.age); 
}; 
let instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
console.log(instance1.colors); // "red,blue,green,black" 
instance1.sayName(); // "Nicholas"; 
instance1.sayAge(); // 29 


let instance2 = new SubType("Greg", 27); 
console.log(instance2.colors); // "red,blue,green" 
instance2.sayName(); // "Greg"; 
instance2.sayAge(); // 27

原型式继承

原型继承例子:

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"

        这里使用到的object()方法,是会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。

        ECMAScript 5 通过增加 Object.create() 方法将原型式继承的概念规范化了。这个方法接收两个 参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的 object() 方法效果相同。
        原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住, 属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

寄生式组合继承

组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是
创建子类原型时调用,另一次是在子类构造函数中调用,所以诞生了寄生式组合继承
例子:
function inheritPrototype(subType, superType) { 
 let prototype = object(superType.prototype); // 创建对象
 prototype.constructor = subType; // 增强对象 
 subType.prototype = prototype; // 赋值对象
}

function SuperType(name) { 
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
SuperType.prototype.sayName = function() { 
 console.log(this.name); 
}; 
function SubType(name, age) { 
 SuperType.call(this, name); 
  this.age = age; 
} 

inheritPrototype(SubType, SuperType); 

SubType.prototype.sayAge = function() { 
 console.log(this.age); 
}; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值