JavaScript中常见的继承方式总结

 更新时间:2022年11月15日 11:56:45   作者:beckyyyy  
这篇文章主要和大家详细介绍了JavaScript中常见的几种继承方式,文中的示例代码讲解详细,对我们学习JavaScript有一定帮助,需要的小伙伴可以参考下面文章详细内容

JS和Java中虽然都有对象的概念,但这两种对象却大有不同。Java的对象是基于类创建的,JS的对象却是基于一个特殊的对象——原型对象——创建的,之前看到一个盖房子的比喻,在Java中盖房子是先画好图纸再盖房子,JS中盖房子却是先盖一个样板房再盖其他房子,觉得也挺贴切。

所以JS中的继承和Java中的继承就大有不同了,是基于原型对象的,如果两个对象形成继承关系,那必然是其中一个对象的原型链上存在一个指针指向另一个对象。即使JS中的两个类声明了继承关系,也是表现在原型对象上。比如:

class A {
    say() {
        console.log('say: hello!');
    }
}

class B extends A {
    constructor() {
        super();
    }
}

console.log(A.prototype); // {constructor: ƒ, say: ƒ}
console.log(B.__proto__); // class A {}
console.log(B.prototype); // A {constructor: ƒ}

首先,类是JS中函数的语法糖,并且在JS中函数本身也是对象,也就是说A和B是两个对象,所以extends操作使得B自身的原型属性__proto__指向了A,相当于const B = Object.create(A);

其次,类的继承关系也影响其生成的实例,众所周知,函数本身存在一个特殊的对象属性:prototype,函数经过构造调用产生的实例的原型属性__proto__是指向这个对象的,而extends操作修改了B的prototype对象,所以B实例上的原型属性__proto__也就被修改了,通过B实例的原型属性__proto__能找到A的prototype,即在B实例的原型链上能找到A的prototype。

const b = new B();
console.log(b.__proto__); // A {constructor: ƒ} 即B.prototype
console.log(b.__proto__.__proto__); // {constructor: ƒ, say: ƒ} 即A.prototype

在JS中使用字面量定义的对象时,其默认的原型属性__proto__指向Object的prototype对象,相当于默认继承自Object,所以字面量对象可以调用Object的实例方法。

可以使用isPrototypeOf来判断一个对象是否在另一个对象的原型链上。

由上述可知,JS中的继承关系与原型对象密切相关,为了达到继承的关联关系(共享某些属性和方法),就要从原型对象着手:

1.使用Object.create的方式创建对象,使两个对象直接产生继承关系

const o1 = {
    name: 'o1',
    age: 18,
    walk() {
        console.log('walking...')
    }
};
const o2 = Object.create(o1);
console.log(o2.__proto__); // {name: 'o1', age: 18}
console.log(o2.walk()); // walking...
console.log(o1.isPrototypeOf(o2)); // true

2.使用new操作创建对象,使产生的实例和类(或函数)的原型对象产生继承关系

const b = new B();
console.log(B.prototype); // A {constructor: ƒ}
console.log(b.__proto__); // A {constructor: ƒ} 即B.prototype
console.log(B.prototype.isPrototypeOf(b)); // true

3.使用extends关键字使类形成继承关系,扩展类实例的原型链

class A {
    say() {
        console.log('say: hello!');
    }
}

class B extends A {
    constructor() {
        super();
    }
}

console.log(A.prototype); // {constructor: ƒ, say: ƒ}
const b = new B();
console.log(b.__proto__.__proto__); // {constructor: ƒ, say: ƒ} 即A.prototype
console.log(A.isPrototypeOf(B)); // true
console.log(A.isPrototypeOf(b)); // false
console.log(A.prototype.isPrototypeOf(b)); // true

4.修改函数的prototype属性使函数形成继承关系,扩展函数实例的原型链

function C() {
    this.name = 'c';
    this.operation = function() { return 'printing...'};
}
function D() {}
D.prototype = new C();
const d = new D();
console.log(d.__proto__.__proto__ === C.prototype); // true
console.log(C.prototype.isPrototypeOf(d)); // true
console.log(D.prototype.isPrototypeOf(d)); // true

这里存在一个问题,就是子类实例化时无法向父类的构造函数传参

5.盗用父类构造函数

在函数内部通过call或apply调用父类函数(非构造调用),可继承父类实例自身(非原型对象)的属性和方法(相当于把子类实例(即this)传递进父类函数,对这个this做了一遍操作),虽然可在初始化时传递参数给父类,但无法形成原型链

function E() {
    C.call(this);
    this.do = function () { return 'do homework'; }
}
const e = new E();
console.log(E.prototype.isPrototypeOf(e)); // true
console.log(C.prototype.isPrototypeOf(e)); // false
console.log(e); // E {name: 'c', operation: ƒ, do: ƒ}
console.log(e.do()); // do homework

子类产生的实例无法对父类及其原型对象应用instanceof和isPrototypeOf方法。

此时如果父类想共享方法给子类,必须把方法直接在定义在函数内部,绑定到实例上,而无法通过父类的prototype对象共享。

6.结合4和5,使得子类实例可继承父类原型对象的属性和方法,且能形成原型链

function E() {
    C.call(this);
    this.do = function () { return 'do homework'; }
}
E.prototype = new C();
const e = new E();
console.log(E.prototype.isPrototypeOf(e)); // true
console.log(C.prototype.isPrototypeOf(e)); // true
console.log(e); // E {name: 'c', operation: ƒ, do: ƒ}
console.log(e.do()); // do homework

7.用Object.create()替换new父类实例来重写子类的原型对象

function inheritatePrototype(subT, superT) {
  let proto = Object.create(superT.prototype);
  proto.constructor = subT;
  subT.prototype = proto;
}

inheritatePrototype(E, C);

可以舍去new中不需要的操作

8.通过工厂方式共享属性和方式

类似工厂函数,但不是用裸的Object,以某种方式取得对象(如new等返回新对象的函数),对此对象加属性或方法以增强功能,并返回对象。

function createAnother(original) {
  let clone = Object.create(original);
  clone.xx = xxx;
  return clone;
}

适合主要关注对象,而不在乎类型和构造函数的场景

存在的问题: 必须在构造函数中定义方法(属于实例非原型对象的方法),函数不能重用

到此这篇关于JavaScript中常见的继承方式总结的文章就介绍到这了,更多相关JavaScript继承方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈JavaScript原型链

    浅谈JavaScript原型链

    这篇文章主要为大家详细介绍了JavaScript原型链,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2023-04-04
  • JavaScript 短路运算的实现

    JavaScript 短路运算的实现

    本文主要介绍了JavaScript 短路运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • JS实现横向轮播图(中级版)

    JS实现横向轮播图(中级版)

    这篇文章主要为大家详细介绍了JS实现横向轮播图的中级版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • JavaScript实现请求服务端接口方法详解

    JavaScript实现请求服务端接口方法详解

    这篇文章主要介绍了JavaScript实现请求服务端接口方法,JavaScript 中请求服务端接口的代码实现可能会因为使用的方法而有所不同,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • js将控件隐藏的方法及display属性介绍

    js将控件隐藏的方法及display属性介绍

    这篇文章主要介绍了,js中将控件隐藏的方法,以及display的属性,有需要的朋友可以参考一下
    2013-07-07
  • JavaScript创建对象的七种方式(推荐)

    JavaScript创建对象的七种方式(推荐)

    JavaScript创建对象的方式有很多,通过Object构造函数或对象字面量的方式也可以创建单个对象,显然这两种方式会产生大量的重复代码,并不适合量产。接下来介绍七种非常经典的创建对象的方式,他们也各有优缺点
    2017-06-06
  • js实现敏感词过滤算法及实现逻辑

    js实现敏感词过滤算法及实现逻辑

    这篇文章主要介绍了js实现敏感词过滤算法及实现逻辑,文中介绍了dfa算法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07
  • js实现仿百度瀑布流的方法

    js实现仿百度瀑布流的方法

    这篇文章主要介绍了js实现仿百度瀑布流的方法,以完整实例形式分析了js仿百度瀑布流的相关样式与实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • JavaScript通过function定义对象并给对象添加toString()方法实例分析

    JavaScript通过function定义对象并给对象添加toString()方法实例分析

    这篇文章主要介绍了JavaScript通过function定义对象并给对象添加toString()方法,实例分析了javascript中function定义对象及添加方法的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • JavaScript实现轮播图特效

    JavaScript实现轮播图特效

    这篇文章主要为大家详细介绍了JavaScript实现轮播图特效,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04

最新评论