一篇文章图文并茂地带你轻松学完 JavaScript 继承
2021-02-04 14:04
926 查看
JavaScript 继承
在阅读本文章之前,已经默认你了解了基础的
JavaScript语法知识,基础的
ES6语法知识 。
继承种类
简单的继承种类可以分为
- 构造函数继承
- 原型链继承
- class继承
- 寄生继承
其中
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); // false console.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"; } S ad8 tudent.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就可以了
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语法有什么优势呢?
- 最大的优势是在于可以继承原生构造函数
- Boolean
- Number
- String
- Array
- Date
- Function
- RegExp
- Error
- 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
- 是否一定具有
__proto__
在原型和原型链章节中,我们说到实例的
__proto__指向构造函数的
prototype
实际上并不是所有浏览器都是支持
__proto__的,而
class作为构造函数的语法糖,一定有这两个属性。
- 更严格的控制
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继承应该是目前来看最为优质的继承方式。 为了能看懂他人的代码,以及更好的兼容性,其他的继承方式也要有所了解。
相关文章推荐
- 一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(二)
- 一篇文章图文并茂地带你轻松学完 JavaScript 闭包
- 一篇清楚阐述 JAvaScript 传递数据 到 Flash 的文章
- 一篇JavaScript技术栈带你了解继承和原型链
- 推荐一篇文章,感觉学完Iterator模式后应当读一读
- 一篇关于虚继承和虚函数介绍的很好的文章
- 关于javascript语言的继承、面向对象问题的文章
- 用一篇文章总结Javascript
- (第2篇)一篇文章教你轻松安装hadoop
- (好文推荐)一篇文章看懂JavaScript作用域链
- 阿里巴巴技术文章分享 Javascript继承机制的实现
- jquery从头开始——一篇文章学完jQuery基础
- 一篇介绍javascript非常好的文章
- 轻松学习JavaScript十三:JavaScript基于面向对象之继承(包含面向对象继承机制)
- 超强大在线词频统计功能,从一篇文章到一个G的文本均可轻松统计
- 一篇文章教你轻松使用fastjson
- 一篇入门级的介绍javascript解释器实现的文章
- 一篇文章读懂HBase,从安装/架构/API全部剖析(图文并茂)--未完待续
- 一篇文章轻松搞定功能测试
- 学习javascript时从网上看到的一篇比较简单和基础的文章,特转载!