JS继承
//构造函数
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name)
}
}
Person.prototype.age = 10
1.原型链继承
function Per(){
this.name = ‘jack‘
}
Per.prototype = new Person()
var per1 = new Per()
console.log(per1.age,per1.name) //10 jack
//10 per1.age->per1.__proto__ === Per.prototype -> Per.prototype = new Person()->实例new Person()的构造函数是Person -> per1.__proto__ 从 Person.prototype中找age = 10
console.log(per1 instanceof Per,per1 instanceof Person,per1 instanceof Object) //true true true
//instanceof的判断逻辑是per的__proto__一层一层往上找
var per2 = new Per()
per2.__proto__.__proto__.age = 20
console.log(per1.age) //20
让新实例的原型等于父类的实例。
缺点:1、新实例无法向父类构造函数传参。2、继承单一。3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
2.借用构造函数继承
function Con(name){
Person.call(this,name)
}
let con1 = new Con(‘jack‘)
console.log(con1.name,con1.age) // jack undefined
console.log(con1 instanceof Con,con1 instanceof Person)//true false
用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
缺点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。2、无法实现构造函数的复用。3、每个新实例都有父类构造函数的副本,臃肿。
call(),apply(),bind()()用法
Fun().call(this,‘param1‘,‘param2‘)
Fun().apply(this,[‘param1‘,‘param2‘])
Fun().bind(this,‘param1‘,‘param2‘)()
//相同点第一个参数都是this的指向对象(一个是函数运行的作用域),上面继承:是把Person()中的this指向Con()中的this所在的作用域;后面是参数入参方式允许是各种类型,包括函数 、 object 等等
3.组合继承
function Con(name){
Person.call(this,name)
}
Con.prototype = new Person()//替换Con的prototype为Person的实例
let con1 = new Con(‘Jack‘)
console.log(con1.name, con1.age)//Jack 10
console.log(con1 instanceof Con, con1 instanceof Person)//true true
//con1.age->con1.__proto__(Con.prototype中找)->Con.prototype = new Person(),Person实例中没有->去Person实例的__proto__找(Person.prototype中找)->10
结合了两种模式的优点,传参和复用
缺点:调用了两次父类构造函数(一次引用Person,一次引用Person实例),Con.prototype被替换
4.原型式继承
function content(obj){
function F(){}
F.prototype = obj
return new F()
}
let per = new Person(‘jack‘),
con1 = content(per)
console.log(con1.name,con1.age,con1) //jack 10
console.log(con1 instanceof Person) //true
//函数content返回一个函数F的实例,函数F原型链继承Person
用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
缺点:1、所有实例都会继承原型上的属性。2、无法实现复用。(新实例属性都是后面添加的)3、无法向父类构造函数传参
5.寄生式继承
function content(obj) {
function F() {}
F.prototype = obj
return new F()
}
let per = new Person()
function subobject(obj) {
let sub = content(obj)
sub.name = ‘jack‘
return sub
}
let con1 = subobject(per)
console.log(con1.name, con1.age, con1) //jack 10
console.log(con1 instanceof Person) //true
给原型式继承外面套了个壳子
缺点:没用到原型,无法复用。
6.寄生组合式继承(常用)
function content(obj) {
function F() {}
F.prototype = obj
return new F()
}
let per = content(Person.prototype) //F的实例继承了Person的原型
function Con(name) {
Person.call(this,name)
}
Con.prototype = per //Con的原型为F的实例
per.constructor = Con //per的constructor指向构造函数F(),改为指向Con()
let con1 = new Con(‘jack‘)
console.log(con1.name, con1.age, con1) //jack 10
console.log(con1 instanceof Person) //true
修复组合继承的问题
7.ES6 Class 继承
class Person { // 定义了一个名字为Person的类
constructor(x, y) { // constructor是一个构造方法,用来接收参数
this.x = x;
this.y = y;
this.z = 1;
this.a = 3;
}
toString() {
return "toString";
}
static staticFun() {
return "static";
}
}
class Son extends Person {
constructor(x, y, color) {
super(x, y); //子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。
this.z = 2;
this.a = 4;
super.a = 5;
this.color = color;
console.log(super.a) //undefined
console.log(this.a); // 5
}
// toString() { // 这是一个类的方法,注意前面没有function
// return this.color + ‘ ‘ + super.toString(); // 调用父类的toString()
// }
}
let son1 = new Son(1, 1, "red");
console.log(son1.toString(),Son.staticFun())//toString,static
console.log(son1 instanceof Son, son1 instanceof Person) //true true
参考:(侵立删)
1、js继承的6种方式
2、js 总结ES6中Class以及继承