摘要:函数继承是在 JS 里比拟根底也是比拟重要的一部分,而且也是面试中经常要问到的。上面带你疾速理解 JS 中有哪几种是经常出现且必须把握的继承形式。把握上面的内容面试也差不多没问题啦~
本文分享自华为云社区《人类高质量 JS 函数继承》,作者:北极光之夜。
一. 前言:
函数继承是在 JS 里比拟根底也是比拟重要的一部分,而且也是面试中经常要问到的。上面带你疾速理解 JS 中有哪几种是经常出现且必须把握的继承形式。把握上面的内容面试也差不多没问题啦~
二. 原型链继承:
原型链继承的要点在于 父类的实例作为子类的原型。间接看上面这个例子:
// 父函数 Person
function Person(name, age) {
// 定义一些属性
this.name = name;
this.age = age;
this.nature = ["auroras", "wind", "moon"];
}
// 定义 Person 原型上的一个办法
Person.prototype.sayLove = function () {console.log(this.name + "like" + this.nature[0]);
};
// 子函数 Jack
function Jack() {}
// 父类的实例作为子类的原型(------------------- 实现外围 --------------------------)Jack.prototype = new Person();
当初咱们创立两个 Jack 的实例,测试看是否实现了继承 Person:
var jack1 = new Jack();
var jack2 = new Jack();
jack2.nature[0] = "sea";
jack1.sayLove();
jack2.sayLove();
console.log(jack1.nature);
console.log(jack2.nature);
看运行后果的确继承了,能执行 sayLove 办法。但有甚多毛病,创立 Jack 实例的时候传递不了参数 name 和 age,而且不同实例间 nature援用类型属性相互影响,一个扭转那都扭转:
三. 借用构造函数继承(对象假装):
外围在于“盗用构造函数”(constructor stealing)。在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简略对象,所以能够应用 apply()和 call()办法以新创建的对象为上下文执行构造函数。它能解决原型链继承中传参数和援用类型属性抵触。还是间接看例子:
// 父函数 Person
function Person(name, age) {
// 定义一些属性
this.name = name;
this.age = age;
this.nature = ["auroras", "wind", "moon"];
}
// 定义 Person 原型上的一个办法
Person.prototype.sayLove = function () {console.log(this.name + "like" + this.nature[0]);
};
// 子函数 Lucy
function Lucy(name, age) {
// 通过 call 把 this 指向 Lucy,相当于拷贝了一份父函数 Person 里的内容(--------- 实现外围 --------------)Person.call(this, name, age);
}
// 给子函数原型上也定义一个办法
Lucy.prototype.syaName = function () {console.log("My name is" + this.name);
};
当初咱们创立两个 Lucy 的实例,测试看是否实现了继承 Person:
var lucy1 = new Lucy("lucy1", "20");
var lucy2 = new Lucy("lucy2", "22");
lucy2.nature[0] = "sea";
console.log(lucy1.name);
console.log(lucy1.nature);
console.log(lucy2.nature);
lucy1.syaName();
lucy2.syaName();
lucy1.sayLove();
后果看能够继承了,能传参数,援用类型属性也不相互影响,然而毛病不言而喻,能够看到报错,无奈应用父类的原型上的办法 sayLove。
四. 组合继承:
组合继承就是联合了原型链继承和借用构造函数继承两者的外围实现的一种继承办法,既能传递参数,援用类型属性也互不影响,同时子类也能获取失去父类的办法。这也是目前比拟罕用的继承形式。间接看例子:
// 父函数 Person
function Person(name, age) {
// 定义一些属性
this.name = name;
this.age = age;
this.nature = ["auroras", "wind", "moon"];
}
// 定义 Person 原型上的一个办法
Person.prototype.sayLove = function () {console.log(this.name + "like" + this.nature[0]);
};
// 子函数 Lisa
function Lisa(name, age) {
// 通过 call 把 this 指向 Lisa,相当于拷贝了一份父函数 Person 里的内容(------ 实现外围 -----------)Person.call(this, name, age);
}
// 父类的实例作为子类的原型(-------------- 实现外围 -------------------)Lisa.prototype = new Person();
// 小知识点,这里是让 Lisa 的 constructor 从新指向 Lisa,不然因为 Lisa 的原型为 Person 实例,constructor 会指向 Person
Lisa.prototype.constructor = Lisa;
当初咱们创立两个 Lisa 的实例,测试看是否实现了继承 Person:
var lisa1 = new Lisa("lisa1", "20");
var lisa2 = new Lisa("lisa2", "21");
lisa2.nature[0] = "sea";
console.log(lisa1.name);
console.log(lisa1.nature);
console.log(lisa2.nature);
lisa1.sayLove();
lisa2.sayLove();
能够看到基本上实现了咱们继承的性能。也修补了原型链和借用构造函数继承的毛病。然而呢,它还是有一个小毛病,就是能够看到在代码正文实现外围那,两次都调用了 Person,那么 Lisa 原型上和实例上有了 两份雷同的属性,那就会多少有一些性能节约。
五. 寄生组合继承:
其实寄生组合继承和组合继承差不多的,就是多了一个解决组合继承上原型和实例产生两份雷同属性的毛病。解决外围是咱们既然只是想要子类原型赋值为父类原型,那没必要 new 一个父类实例。间接发明一个新对象,它值为父类的原型,再将它赋值给子类原型就行了。
其中用到 Object.create(proto,[propertiesObject])这个办法创立一个新对象。相当于新对象的__proto__为其参数 proto。当然 Object.create 可能低版本 ie 没有,所以上面也自定义封装了 Object.create 办法,当然只是简略封装。间接看例子:
// 父函数 Person
function Person(name, age) {
// 定义一些属性
this.name = name;
this.age = age;
this.nature = ["auroras", "wind", "moon"];
}
// 定义 Person 原型上的一个办法
Person.prototype.sayLove = function () {console.log(this.name + "like" + this.nature[0]);
};
// 子函数 Andy
function Andy(name, age) {Person.call(this, name, age);
}
// 如果没有 Object.create()办法,简略封装下
if (!Object.create) {Object.create = function (proto) {function Temp() {}
Temp.prototype = proto;
return new Temp();};
}
// 调用 Object.create 办法,新建一对像,其__proto__为 Person.prototype,并赋值给 Andy.prototype (------- 实现外围 ----------)
Andy.prototype = Object.create(Person.prototype);
// 批改 constructor 指向
Andy.prototype.constructor = Andy;
当初咱们创立两个 Andy 的实例,测试看是否实现了继承 Person:
console.log(Andy.prototype.__proto__ === Person.prototype);
var andy1 = new Andy("andy1", "20");
var andy2 = new Andy("andy2", "21");
andy2.nature[0] = "sea";
console.log(andy1.name);
console.log(andy1.nature);
console.log(andy2.nature);
andy1.sayLove();
andy2.sayLove();
完满运行:
六.class 继承:
ES6 出了 class 语法糖之后,就能够通过 class 定义类并实现类的继承。间接看例子:
// 定义一个父类 Animal
class Animal {
// 这里 constructor 指向类自身,跟 es5 行为一样的
constructor(name) {this.name = name;}
likeEat() {console.log(this.name + "like eat" + this.food);
}
}
// 定义一个子类 Dog,通过 extends 继承父类 Animal
class Dog extends Animal {constructor(name, food) {
// 通过 super(属性名)继承父类属性
super(name);
this.food = food;
}
likeEat() {
// 通过 super.+ 父类办法 实现继承父类办法
super.likeEat();}
}
new 一个 Dog 实例,测试看看,Dog 是否继承了 Animal:
var jinmao = new Dog("jinmao", "bone");
console.log(jinmao.name);
jinmao.likeEat();
能够看到完满实现了:
七. 总结:
下面就是这篇文章的全部内容啦,如果有谬误的中央恳请指出~
点击关注,第一工夫理解华为云陈腐技术~