js中的原型和继承
1.什么是原型?
- javascript中的对象有个内置属性
[[Prototype]]
,称为原型 [[Prototype]]
是对其他对象的引用- 每个函数都会创建一个
prototype
属性,这个属性是一个对象。分别称之为构建函数, 原型对象
[[Prototype]]原型
指向 一个prototype原型对象
function Car(name){this.name=name;this.type='手动挡';} //这个name直接挂载在Car
Car.prototype.name='车' //可以在Car的原型对象上挂载新的属性
Car.prototype.getName=function(){return this.name;} //挂载新的方法
Car.prototype.fuel='石油';
Car.prototype.getFuel=function(){return this.fuel;}
const car = new Car('小车车'); //car是Car的一个实例对象
const tesla = new Car('特斯拉');
tesla.type='自动挡';
console.log(car,tesla);//{小车车,手动挡} {特斯拉,自动挡}
console.dir(car); //car的原型指向Car.prototype
car.getName(); //getName方法在原型中,car有直接挂载name:小车车
car.getFuel(); //car没有直接挂载fuel,所以取原型中的fuel:石油
- 改变
Car.prototype
的属性时,car和tesla随之反应 - 改变直接挂载在
Car
上的属性时,car和tesla不受影响
2.什么是原型链?
- 创建卡丁车,使得卡丁车的原型是小车车
在原型链上,如果在对象上找不到需要的属性或者方法,引擎就会继续在[[Prototype]]指向的原型上查找。
3.原型链继承
- 在传统OOP中,继承的本质是复制,子类拷贝了父类的方法和属性。
- 根据原型的特性,js中继承的本质是链接。
- 探索差异
3.1 原始方式
Kart.prototype=car
让构造函数的prototype
指向另一个构造函数的实例。
数组
Array
, 日期Data
是常见的引用数据类型
引用数据类型在赋值时传递引用地址而不是具体值
function Car(){this.date=new Date();}
function Kart(){}
Kart.prototype=new Car(); // 原型对象指向Car的实例
const kart = new Kart();
const renault = new Kart();
kart.date.setFullYear(2000); //设置日期的年份
//观察renault.date是否随之变化。
缺点:当原型上的属性是引用数据类型时,所有实例都会共享这个属性。
3.2 经典继承
function Kart(){Car.call(this);}
在子类中调用父类的构造函数,实现上下文的绑定
缺点:丢失父类的原型
3.3 组合继承
function Kart(){Car.call(this);}Kart.prototype=new Car();
二者取其优,十分nice!
也有缺点:性能浪费!这里两次调用了Car的构造函数
在子类构造函数内部调用
Car.call(this)
在修改子类原型时调用new Car()
,将引入多余的属性或方法
产生不必要的内存消耗和性能损失
4. 原型式继承
由Douglas Crockford
在它的《JavaScript编程风格指南》
中提出。
- 原型链是实现对象之间继承关系的机制
- 原型式继承是基于原型链机制的继承方式
- 复制已有对象的属性和方法来创建新对象
经典函数: 原型链继承的核心代码
const object = function(o){
function F() {}
F.prototype = o
return new F()
}
//返回的实例对象的[[Prototype]]指向o,从而实现继承
4.1 原始
ES5的Object.create()将原型式继承规范化
缺点:共享引用类型的属性,同原始链继承(3.1)
4.2 寄生式组合继承
4.2.1 增强对象
function parasitize(o){
let clone = Object.create(o);
clone.run = function(){return '速度七十迈';}
return clone;
}
function Car(){this.date=new Date();}
const car = new Car();
const kart = parasitize(car);
const renault = parasitize(car);
4.2.2 改变原型的constructor
指向
function parasitize(Son,Father){
const prototype = Object.create(Father.prototype);//此prototype是副本
prototype.constructor = Son;
Son.prototype = prototype;
//两步下来,副本为Son所用
//无需返回,只需改变子类的原型对象
}
function Car(){this.date=new Date();}
function Kart(){Car.call(this)} //调用构造函数
parasitize(Kart,Car); //不用二次调用构造函数
const kart = new Kart();
const renault = new Kart();
原型链关系同 组合式继承(3.3)
寄生组合式继承是对组合式继承的优化改进。
通过使用
Object.create(Father.prototype)
避免调用父类构造函数,从而避免引入多余属性或方法