基础学习 - 在JS 中的继承
栏目: JavaScript · 发布时间: 5年前
内容简介:最近在使用node-jsonwebtoken中发现了下面这个代码,感觉挺好看,于是就打算探索一些相关代码:
起因
最近在使用node-jsonwebtoken中发现了下面这个代码,感觉挺好看,于是就打算探索一些相关代码:
var JsonWebTokenError = function (message, error) { Error.call(this, message); if(Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } this.name = 'JsonWebTokenError'; this.message = message; if (error) this.inner = error; }; JsonWebTokenError.prototype = Object.create(Error.prototype); JsonWebTokenError.prototype.constructor = JsonWebTokenError; module.exports = JsonWebTokenError;
等会再来分析这个段代码.
找到MDN中关于继承部分(继承在原型链中是高级部分教程)如下:
在JavaScript中继承都是通过 原型链
来实现的。下面就来谈谈在JS 中继承
什么是继承?
继承是面向对象的软件的当中一个概念。在面向对象还有两个特征分别是多态、分装。继承是可以让自雷拥有父类的属性和方法或者重新定义、追加属性和方法等。
继承方法
原型链继承
//父类型 function Parent(name, age) { this.name = name, this.age = age, this.play = [1, 2, 3] this.setName = function () { } } Parent.prototype.setAge = function () { } //子类型 function Child(price) { this.price = price this.setScore = function () { } } Child.prototype = new Parent() // 子类型的原型为父类型的一个实例对象 var s1 = new Child(15000) var s2 = new Child(14000) console.log(s1,s2)
这种继承方式把 Child.prototype
设置直接设置为 Parent
对象, 子类就可以通过原型链 访问父级所有的属性和方法 了。后续若要增加新的属性,必须在 Child.prototype = new Parent()
后面,否则则会被覆盖.
在上面示例中需要说明的事,在使用父类属性的时候会遵守JS的数据类型变化规则, 原始值(Primitive values) 不可突变(not mutation), 对象则会进行突变的。这个JS 存储类型决定。原始值每次返回一定是一个新的值,而对象则代表是内存中一个区域地址,地址不变,具体在代码中表现是即使内部数据不同,但是他们依旧相等。这个则设计JS中深浅拷贝问题,后续再涉及。
使用子类构造函数来继承
function Parent(name, age) { this.name = name, this.age = age, this.setName = function () {} } Parent.prototype.setAge = function () {} function Child(name, age, price) { Parent(this, name, age) // 相当于: this.Parent(name, age) /*this.name = name this.age = age*/ this.price = price } var s1 = new Child('Tom', 20, 15000)
这种方式首先在初始化 Child
之前,会对 Child
进行一个初始化,我们这里涉及这个关于 new
知识点, 在 new
之前会将 this
初始化为 Child.prototype
这个对象,这里使用 Function.prototype.call
来调用Parent,其实就是类似如下代码:
const ChildPrototype = { Parent: function(name, age) { //... } }
这里就获得了Parent中使用 this
初始化的属性和方法, 这里 不能够获取Parent原型链的数据 。使用这种方式有如下优劣:
优点
call
缺点
instanceof
这里使用 Function.prototype.call
方式可以看 这里
组合继承(原型链+子类构造函数) - 1
function Parent(name, age) { this.name = name, this.age = age, this.setAge = function () { } } Parent.prototype.setAge = function () { console.log("111") } function Child(name, age, price) { Parent.call(this, name, age) this.price = price this.setScore = function () { } } Child.prototype = new Parent() Child.prototype.contructor = Child Child.prototype.sayHello = function () { } var s1 = new Child('Tom', 20, 15000) console.log(s1)
这里的话融合了原型链继承和构造函数的的优点。但是缺点很明显,在创建子类的时候会调用调用两次父类的构造函数。
在上面中 Child.prototype.contructor = Child
这里需要进行通过原型链继承修复。这里主要修复之前几个继承问题
- 父类引用共享
- 子类可以获取父类 所有属性和方法
组合继承 - 2
function Parent(name, age) { this.name = name, this.age = age, this.setAge = function () { } } Parent.prototype.setAge = function () { console.log("111") } function Child(name, age, price) { Parent.call(this, name, age) this.price = price this.setScore = function () { } } Child.prototype = Parent.prototype Child.prototype.sayHello = function () { } var s1 = new Child('Tom', 20, 15000) console.log(s1)
这里通过子类的 prototype
重新赋值给 Parent.prototype
, 就可以继承到父类的所有原型链上的属性,例如 setAge
, 在子类函数申明中,通过 Parent.call
继承父类本身的属性和方法。这种方式就可以解决 两次调用构造函数问题 , 但是这里也有一个问题就是,子类构造函数 Child.prototype.constructor
被父类构造函数覆盖, Parent.prototype.construtor
和 Child.prototype.constructor
指向都是 Parent.prototype.constructor
。
组合继承 - 3
function Parent(name, age) { this.name = name, this.age = age, this.setAge = function () { } } Parent.prototype.setAge = function () { console.log("111") } function Child(name, age, price) { Parent.call(this, name, age) this.price = price this.setScore = function () { } } Child.prototype = Object.create(Parent.prototype) Child.prototype.construct = Child Child.prototype.sayHello = function () { } var s1 = new Child('Tom', 20, 15000) console.log(s1)
这里和上面一种区别在于通过创建使用 Object.create()
来把 Parent.prototype
设置为 Child.prototype.__proto__
上, 也就说现在结构会变成如下图:
通过这种方式的继承,目前来看是最完美的一种方式,也解决之前很多问题。
ES6 的 extends 关键字
class Parent { //调用类的构造方法 constructor(name, age) { this.name = name this.age = age } //定义一般的方法 showName() { console.log("调用父类的方法") console.log(this.name, this.age); } } let p1 = new Parent('kobe', 39) console.log(p1) //定义一个子类 class Child extends Parent { constructor(name, age, salary) { super(name, age)//通过super调用父类的构造方法 this.salary = salary } showName() {//在子类自身定义方法 console.log("调用子类的方法") console.log(this.name, this.age, this.salary); } } let s1 = new Child('wade', 38, 1000000000) console.log(s1) s1.showName()
这种方式是目前es6的方式,缺点就是兼容性,不是所有浏览器都完美兼容es6.不过有点也很明显和其他语言保持了一致的继承语法,简单易懂。
实际JS 都是通过原型链继承,所以这里最终会编译成如下代码:
这是Babel编译的结果,可以看到还是通过原型链来实现的。
"use strict"; function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } } function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Test = function Test() { _classCallCheck(this, Test); }; var Test1 = /*#__PURE__*/ function (_Test) { _inherits(Test1, _Test); function Test1() { _classCallCheck(this, Test1); return _possibleConstructorReturn(this, _getPrototypeOf(Test1).apply(this, arguments)); } return Test1; }(Test);
看下面图示:
这里代码和 组合继承3 是类似的哦
更多查看:
以上所述就是小编给大家介绍的《基础学习 - 在JS 中的继承》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- JavaScript 基础:继承
- Java基础知识:继承
- 零基础学习 Python 之继承
- Java 基础 (16):一个例子弄懂什么叫继承?
- java基础(二)-----java的三大特性之继承
- Python语法基础之初始化函数和类的继承
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby on Rails电子商务实战
Christian Hellsten、Jarkko Laine / 曹维远 / 人民邮电出版社 / 2008-4 / 49.00元
《Ruby on Rails电子商务实战》全面讲解了使用Ruby on Rails创建产品级应用程序的过程。书中通过演示构建网上书店的全过程,先后介绍如何使用如TDD的敏捷实践,启动一个项目并建立良好稳定的基础,如何深入Ruby on Rails,实现诸如将应用程序翻译成各种语言对产品进行调试等的普遍需求。其中用到的主要技术包括Ajax、聚合、设置标签和国际化等,还介绍了如何使用ActiveRec......一起来看看 《Ruby on Rails电子商务实战》 这本书的介绍吧!