基础学习 - 在JS 中的继承
栏目: JavaScript · 发布时间: 6年前
内容简介:最近在使用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语法基础之初始化函数和类的继承
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Iterative Methods for Sparse Linear Systems, Second Edition
Yousef Saad / Society for Industrial and Applied Mathematics / 2003-04-30 / USD 102.00
Tremendous progress has been made in the scientific and engineering disciplines regarding the use of iterative methods for linear systems. The size and complexity of linear and nonlinear systems arisi......一起来看看 《Iterative Methods for Sparse Linear Systems, Second Edition》 这本书的介绍吧!