AI智能
改变未来

一篇文章图文并茂地带你轻松学完 JavaScript 继承


JavaScript 继承

在阅读本文章之前,已经默认你了解了基础的

JavaScript

语法知识,基础的

ES6

语法知识 。

继承种类

简单的继承种类可以分为

  1. 构造函数继承
  2. 原型链继承
  3. class继承
  4. 寄生继承

其中

class

继承是

ES6

后提供的一种语法糖,方便其他面向对象语言的程序员更好的接受

JavaScript

中的继承,本质上还是原型链继承。

1. 构造函数继承

function Person() {this.name = \"name\";this.eat = function() {console.log(\"eat\");}}function Student() {Person.call(this); // 继承this.age = 20;}const student = new Student();console.log(student);

2. 原型链继承

原型与原型链前置相关内容可以参考 点这里

function Person() {this.name = \"name\";}function Student() {this.age = 20;}Student.prototype = new Person();Student.prototype.constructor = Student;	// 指利用 Student 构造函数 进行实例初始化const stu = new Student();console.log(stu.name); // \"name\"console.log(stu);

利用在原型和原型链所学知识

Student

实例对象的

__proto__

将会指向

Person

实例,从而实现继承的效果

stu

:

3. class继承

constructor

是构造函数,可以结合原型链中的

constructor

属性看

class People {constructor() {this.name = \"name\";}}class Student extends People {constructor() {super()this.age = 20;}}console.log(new Student())

可以发现,其实就是基于原型链继承,只不过

constructor

是

class Student

4. 寄生继承

在

JavaScript

设计模式中,有

工厂模式

,具体可以上网查询

工厂模式

意味着只要传入适当的参数 (加工),就会给予一个实例,就像工厂制造东西一样。

而寄生继承,用的就是工厂模式的思想

function People() {}People.prototype.eat = function() {console.log(\"eat\");}function createInstance() {const obj = Object.create(People.prototype)Object.assign(obj, ...arguments);return obj;}const stu1 = createInstance({ age: 20 });console.log(stu1);const stu2 = createInstance({ age: 30 });console.log(stu2);

下面是

stu1

的打印结果

继承优化

1. 构造函数继承

利用

Student

构造出来的实例,属性和方法是不共享的

function People(name) {this.name = name;this.eat = function () {console.log(\"eat\");};}function Student(name) {People.call(this, name);this.age = 20;}const stu1 = new Student(\"huro\");const stu2 = new Student(\"lero\");console.log(stu1.name === stu2.name);	// falseconsole.log(stu1.eat === stu2.eat);		// false

对于方法来说我们希望是共享的,否则实际上浪费了很多内存。

2. 组合继承

基于原型的方法是实例共享的,我们将方法放入原型,而属性放在构造函数内,这样就叫做组合继承,组合继承可以解决浪费多余内存的问题。

function People(name) {this.name = name;}People.prototype.sayName = function() {console.log(this.name);}function Student() {People.call(this);this.age = \"20\";}Sad8tudent.prototype = new People();Student.prototype.constructor = Student;const stu1 = new Student();const stu2 = new Student();console.log(stu1.sayName === stu2.sayName);

然而,还是有个缺点,我们打印

stu1

在

__proto__

中 有个

name

属性,这个属性其实我们是不需要的,我们希望每个实例能够独享属性,这个

name

属性的存在不但加大了内存开销,还导致当

delete stu1.name

的时候,出现还能使用

stu1.name

的情况,这是我们不想要的

3. 组合寄生继承

顾名思义,组合寄生继承就是结合组合继承和寄生继承

function People(name) {this.name = name;}People.prototype.sayName = function() {console.log(this.name);}function Student() {People.call(this);this.age = \"20\";}Student.prototype = Object.create(People.prototype); // 实际上只变化这一行Student.prototype.constructor = Student;const stu1 = new Student();const stu2 = new Student();console.log(stu1.sayName === stu2.sayName);

通过这种方式创造的继承,弥补了组合继承的不足,节省了内存,并且使得实例共享方法独享属性。

那么

ES6

语法提供的

class

是否也有这种 \”聪明\” 的设计呢?如果有的话,我们直接利用

class

就可以了

  1. class

    继承

class People {constructor() {this.name = \"name\";}eat() {console.log(\"eat\");}}class Student extends People {constructor() {super()this.age = 20;}}const stu1 = new Student();const stu2 = new Student();console.log(stu1.eat === stu2.eat); // true

extends

继承的是原型链的方法

super

继承的是独享的属性和方法

可以发现其实是和组合寄生继承类似的

哦哦,那肯定啊,不然

ES6

不被喷死啊。

继承优势 (选择)

用

ES6

的

class

语法有什么优势呢?

  1. 最大的优势是在于可以继承原生构造函数
  2. 原生构造函数

  3. Boolean
  4. Number
  5. String
  6. Array
  7. Date
  8. Function
  9. RegExp
  10. Error
  11. Object

在

ES5

语法中,你无法原生构造函数的属性,你可能会尝试这样写

const MyArray() {Array.apply(this, arguments);}MyArray.prototype = Object.create(Array.prototype);MyArray.prototype.constructor = MyArray;

当用这种方式继承的时候,你会发现他与

Array

这个类的行为完全不一致

const names = new MyArray();names[0] = \"huro\";console.log(names.length); // 0

原生构造函数无法绑定

this

而

class继承

可以

class MyArray extends Array {}const names = new MyArray();names[0] = \"huro\";console.log(names.length); // 1
  1. 是否一定具有
    __proto__

在原型和原型链章节中,我们说到实例的

__proto__

指向构造函数的

prototype

实际上并不是所有浏览器都是支持

__proto__

的,而

class

作为构造函数的语法糖,一定有这两个属性。

  1. 更严格的控制
function People(name) {this.name = name;this.eat = function () {console.log(\"eat\");};}function Student(name) {People.call(this, name);this.age = 20;}const stu1 = Student(\"huro\"); // new?console.log(stu1);

利用构造函数实例化对象的时候,如果忘传了

new

会怎么样,这个时候显然也成立,因为会被当做一个函数看待,由于是全局调用,因此

this

在浏览器环境下就是

window

这样相当于给

window

赋值了

name

和

eat

这个时候解释器也不会报错,因为没有任何方法可以区分一个函数是否是构造函数,因此可能出现意想不到的错误。

而用

class

方式继承,好处就是如果不写

new

直接报错。

class MyArray extends Array {}const names = MyArray(); // class constructor MyArray cannot be invoked without \"new\"

除此之外,在继承的构造函数里,如果没写

super

关键字或

super

不在构造函数顶部也会报错

class MyArray extends Array {constructor(){// Must call super constructor in derived class before accessing \'this\' or returning from derived constructor}}

总结

更严格的语法检查,更多的优化,使得

class继承

应该是目前来看最为优质的继承方式。 为了能看懂他人的代码,以及更好的兼容性,其他的继承方式也要有所了解。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 一篇文章图文并茂地带你轻松学完 JavaScript 继承