原型是 JS 中继承的根底,JS 的继承次要依附原型链来实现
用图来示意:
原型链:让一个原型对象等于另一个类型的实例,层层递进,就形成了实例与原型的链条
继承
- 原型链继承: 子类的原型等于父类的实例
function Parent(){
this.color = 'blue'
// this.getParentValue = function(){
// return this.color
// }
}
// 将办法增加在原型中,防止每次 new,办法都被重复增加到 this 中,节约内存
Parent.prototype.getParentValue = function(){return this.color}
function Child(){this.childColor = 'yellow'}
Child.prototype = new Parent()
Child.prototype.getChildValue = function(){return this.childColor}
const exp = new Child()
console.log('pColor:',exp.getParentValue(),'color:',exp.getChildValue());
//pColor: blue color: yellow
长处:继承了构造函数及其原型的所有属性和办法。
毛病:1、在创立子类实例时,无奈向超类型的构造函数传参,继承繁多。
2、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例批改了原型援用类型的属性,另一个实例的原型属性也会被批改!)
function Parent() {
this.info = {
name:"lin",
age:42
}}
function Child(){
this.childinfo = {
name:"meng",
age:18
}
}
Child.prototype = new Parent()
const exp = new Child()
exp.info.name = "qqqq"
const exp1 = new Child()
console.log('info',exp1.info);//info {name: 'qqqq', age: 42}
- 构造函数继承: 在子类的外部调用父类,通过 call 扭转父类中 this 的指向
长处:能够在子类构造函数中,向超类型构造函数传递参数。
毛病:只继承了父类构造函数的属性,没有继承父类原型的属性。
function Parent(){
this.info = {
name:'lin',
age:42
}
}
function Child(){Parent.call(this)
}
const exp1 = new Child()
exp1.info.name = 'qqqqq'
console.log('exp1.info',exp1.info);//exp1.info {name: 'qqqqq', age: 42}
const exp2 = new Child()
console.log('exp2.info',exp2.info);//exp2.info {name: 'lin', age: 42}
- 组合继承:原型链 + 构造函数
应用原型链实现对原型属性和办法的继承,通过构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数的复用,又可能保障每个实例都有它本人的属性。
毛病:调用两次父类构造函数。
function Parent(name){
this.name = name;
this.colors = ['red','blue','green']
}
Parent.prototype.sayName = function(){console.log('Pname',this.name);
}
function Child(name,age){Parent.call(this,name)
this.age = age
}
Child.prototype = new Parent()
//Child.prototype.constructor = Child
Child.prototype.sayAge = function(){console.log('Cage:',this.age);
}
const exp1 = new Child('lin',18)
exp1.colors.push('black')
console.log('exp1.colors',exp1.colors);//exp1.colors (4) ['red', 'blue', 'green', 'black']
exp1.sayName() //Pname lin
exp1.sayAge()//Cage: 18
const exp2 = new Child('meng',20)
console.log('exp2.colors',exp2.colors); //exp2.colors (3) ['red', 'blue', 'green']
Q:为什么要设置 prototype.constructor?
A:建设原型链次要靠 proto 属性,因而 constructor 对原型链毫无影响。prototype.constructor 仅仅能够用于辨认对象是由哪个构造函数初始化的,仅此而已。
- 原型式继承:Object.create()
创立一个构造函数,构造函数的原型指向对象,而后调用 new 操作符创立实例,并返回这个实例,实质是一个浅拷贝,援用类型的数据共享在不同的实例之间
function newObj(o){const Object = function(){}
Object.prototype = o
return new Object()}
const obj = {
name:"lin",
age:18,
skills:['js','java'],
show:function(){return `${this.name},${this.age}`
}
}
const obj2 = newObj(obj)
obj2.skills.push('c')
console.log('obj2.skills',obj2.skills);//obj2.skills (3) ['js', 'java', 'c']
const obj3 = newObj(obj)//obj3.skills (3) ['js', 'java', 'c']
console.log('obj3.skills',obj3.skills);
- 寄生式继承:在原型式继承根底上进行封装,在对象上扩大新的办法,
function newerObj(o){const Obj = Object.create(o)
Obj.sayName = function(){return this.name}
return Obj
}
const obj = {
name:"lin",
age:18,
skills:['js','java'],
show:function(){return `${this.name},${this.age}`
}
}
const obj2 = newerObj(obj)
console.log('obj2',obj2.sayName());//obj2 lin
-
寄生组合式继承:借用构造函数继承属性,通过原型链继承办法
只调用了一次超类(父类)的结构, 并且防止了在子类 prototype 下面创立不必要,多余的属性function Inherit(child,parent){const protoT = Object.create(parent.prototype) protoT.constructor = child child.prototype = protoT } // 应用 function Parent(name){ this.name = name, this.colors = ['red','blue','green'] } Parent.prototype.sayName = function(){console.log('Pname',this.name); } function Child(name,age){Parent.call(this,name) this.age = age } Inherit(Child,Parent) Child.prototype.sayAge = function(){console.log('Cage',this.age); } const exp1 = new Child('lin',18) exp1.colors.push('black') console.log('exp1',exp1);//exp1 Child {name: 'lin', colors: Array(4), age: 18} const exp2 = new Child('meng',20) console.log('exp2',exp2);//exp2 Child {name: 'meng', colors: Array(3), age: 20}
开发人员普遍认为寄生组合式继承是援用类型最现实的继承范式。