共计 3019 个字符,预计需要花费 8 分钟才能阅读完成。
1. 背景介绍
继承,指一个对象间接应用另一对象的属性和办法。
JS 里罕用的有如下两种继承形式:
原型链继承(对象间的继承)
类式继承(构造函数间的继承)
2. 常识分析
JavaScript 语言的对象体系,不是基于“类”,而是基于构造函数(constructor)和原型(prototype)。
- 原型对象:只有创立一个新函数,就会依据特定的规定为该函数创立一个 prototype 属性指向其原型对象,默认状况下原型对象会主动取得一个 constructor 属性,该属性蕴含一个指向 prototype 属性所在函数的指针。
Function.prototype.constructor === Function //true
- 构造函数:自身是一个函数,出于创立特定类型新对象的目标而定义的,外部应用 this 变量,须要和 new 配合应用来创立实例,this 变量会绑定在实例对象上。
demo:
var f = function(){} | |
f.prototype.constructor === f //true |
1 原型链继承
function Box() {this.name = "lee";} function Desk() {this.age = 100;} | |
Desk.prototype.age = 200; function Table() {this.level = "aaaa";} | |
Desk.prototype = new Box(); //Desk 继承了 Box | |
Table.prototype = new Desk(); //Table 继承了 Desk 和 Box | |
var desk = new Desk(); | |
alert(desk.name); //lee | |
var table = new Table(); | |
alert(table.name); //lee // 如果要实例化 table,那么 Desk 实例中有 age=100,原型中减少雷同的属性 age=200,最初后果是多少呢?alert(table.age); //100,还是就近准则 // 毛病:无奈传参,援用共享,解决办法:对象假冒 |
2 经典继承: 借用构造函数或称为对象假冒继承
var desk = new Desk(200); | |
alert(desk.age); //200 | |
alert(desk.name); //lee,jack,hello | |
desk.name.push("AAA"); | |
alert(desk.name); //lee,jack,hello,AAA | |
var desk2 = new Desk(300); | |
alert(desk2.name); //lee,jack,hello | |
// 毛病:借用构造函数尽管解决了方才两种问题,但没有原型,复用则无从谈起。 |
3 组合继承:原型链 + 借用构造函数。
调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
// 组合式继承是 JavaScript 最罕用的继承模式;但,组合式继承也有一点小问题,就是超类型在应用过程中会被调用两次:一次是创立子类型的时候,另一次是在子类型构造函数的外部。寄生继承将解决这个问题 | |
function Box(age) {this.name = ['Lee', 'Jack', 'Hello'] | |
this.age = age; | |
} | |
Box.prototype.run = function () {return this.name + this.age;}; | |
function Desk(age) {Box.call(this, age); // 对象假冒,第二次调用 Box | |
} | |
Desk.prototype = new Box(); // 原型链继承 | |
var desk = new Desk(100); // 第一次调用 Box | |
alert(desk.run()); |
4 原型式继承:这种继承借助原型并基于已有的对象创立新对象,同时还不用因而创立自定义类型。
function obj(o) { // 传递一个字面量函数 | |
function F() {} // 创立一个构造函数 | |
F.prototype = o; // 吧字面量函数赋值给构造函数原型 | |
return new F(); // 最终返回出实例化的构造函数} | |
var box = { // 字面量对象 | |
name: "lee", | |
arr: ['gege', 'meimei', 'jiejie'] | |
} | |
var box1 = obj(box); | |
alert(box1.name); | |
box1.name = "jack"; | |
alert(box1.name); | |
alert(box1.arr); | |
box1.arr.push("父母"); | |
alert(box1.arr); | |
var box2 = obj(box); | |
alert(box2.name); | |
alert(box2.arr); // 援用类型共享了 |
5 寄生式继承:原型式 + 工厂模式,目标是封装创建对象过程
function obj(o) {function F() { } | |
F.prototype = o; | |
return new F();} | |
function create(box, desk) {var f = obj(box.prototype); | |
f.constructor = desk; | |
desk.prototype = f; | |
} | |
function Box(name) { | |
this.name = name; | |
this.arr = ['哥哥', '妹妹', '父母']; | |
} | |
Box.prototype.run = function () {return this.name;}; | |
function Desk(name, age) {Box.call(this, name); | |
this.age = age; | |
} | |
inPrototype(Box, Desk); // 通过这里实现继承 | |
var desk = new Desk('Lee', 100); | |
desk.arr.push('姐姐'); | |
alert(desk.arr); | |
alert(desk.run()); // 只共享了办法 | |
var desk2 = new Desk('Jack', 200); | |
alert(desk2.arr); // 援用问题解决 |
6.es6 继承
代码量少,易懂
//class 相当于 es5 中构造函数 | |
//class 中定义方法时,前后不能加 function,全副定义在 class 的 protopyte 属性中 | |
//class 中定义的所有办法是不可枚举的 | |
//class 中只能定义方法,不能定义对象,变量等 | |
//class 和办法内默认都是严格模式 | |
//es5 中 constructor 为隐式属性 | |
class People{constructor(name='wang',age='27'){ | |
this.name = name; | |
this.age = age; | |
} | |
eat(){console.log(`${this.name} ${this.age} eat food`) | |
} | |
} | |
// 继承父类 | |
class Woman extends People{constructor(name = 'ren',age = '27'){ | |
// 继承父类属性 | |
super(name, age); | |
} | |
eat(){ | |
// 继承父类办法 | |
super.eat()} | |
} | |
let wonmanObj=new Woman('xiaoxiami'); | |
wonmanObj.eat(); |
ES5 继承和 ES6 继承的区别
es5 继承首先是在子类中创立本人的 this 指向,最初将办法增加到 this 中
Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)
es6 继承是应用关键字先创立父类的实例对象 this,最初在子类 class 中批改 this
正文完
发表至: javascript
2020-12-22