Js对象继承

更新日期: 2019-08-18阅读: 1.9k标签: 继承

JavaScript对象继承的方法有很多,这里总结一下几种比较常用的方法。
现在有一个"动物"对象的构造函数

function Animal(){
    this.species = "动物";
}
Animal.prototype.voice = function(){
    console.log('voice');
}

还有一个"猫"对象的构造函数。

function Cat(name, color){
    this.name = name;
    this.color = color;
}

怎样才能使"猫"继承"动物"呢?


一、原型链和构造函数继承

使用call/apply和Object.create()

第一种方法使用call或apply方法,改变了 this 的指向而实现继承,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:

function Cat(name, color){
    Animal.apply(this, arguments); 
    this.name = name;
    this.color = color;
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

到目前为止一切看起来都还行,但是我们很快会发现我们遇到问题了,当我们调用cat1.voice()的时候报错了,显然Cat并没有继承Animal的原型对象里的方法。

我们已经定义了一个新的构造器,这个构造器默认有一个空的原型属性。我们并没有让Cat从Animal的原型对象里继承方法。

Cat.prototype = Object.create(Animal.prototype);

这里我们的老朋友create()又来帮忙了——在这个例子里我们用这个函数来创建一个和Animal.prototype一样的新的原型属性值(这个属性指向一个包括属性和方法的对象),然后将其作为Cat.prototype的属性值。这意味着Cat.prototype现在会继承Animal.prototype的所有属性和方法。

接下来,在我们动工之前,还需要完成一件事 — 现在Cat()的prototype的constructor属性指向的是Aniaml(),这是由我们生成Animal()的方式决定的。(这篇 Stack Overflow post 文章会告诉您详细的原理)

Cat.prototype.constructor = Cat;

此时再输入Cat.prototype.constructor就会得到Cat()。

顺带一提,这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

    o.prototype = {};
    o.prototype.constructor = o;

对上面构造函数继承代码做一个封装:

function extend(C, P) {
    function C(){
        P.apply(this, arguments); 
    }
    C.prototype = Object.create(P.prototype);
    C.prototype.constructor = C;
}


不使用call或apply方法

第二种方法更常见,但是不使用apply和call,而是使用prototype属性。

如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。

    var Cat = function(){};
    Cat.prototype = new Animal();
    Cat.prototype.constructor = Cat;

    var cat1 = new Cat("大毛","黄色");
    console.log(cat1.species); // => 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。
它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是此时Cat.prototype.constructor依然指向Animal的构造函数,所以需要将其指回Cat。

对prototype继承代码做一个封装:

function extend(C, P) {
    var C = function(){};
    C.prototype = new P();
    C.prototype.constructor = C;
}


原型式继承(prototypal inheritance)(推荐)

利用一个空对象作为中介。

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。

alert(Animal.prototype.constructor); // Animal

我们将上面的方法,封装成一个函数,便于使用。

function extend(C, P) {
    var F = function(){};
    F.prototype = Parent.prototype;
    C.prototype = new F();
    C.prototype.constructor = Child;
    C.super = P.prototype;
}

使用的时候,方法如下

  extend(Cat,Animal);
  var cat1 = new Cat("大毛","黄色");
  alert(cat1.species); // 动物

这个extend函数,就是YUI库如何实现继承的方法。

另外,说明一点,函数体最后一行

  Child.super = Parent.prototype;

意思是为子对象设一个super属性,这个属性直接指向父对象的prototype属性。这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。


"非构造函数"的继承

什么是"非构造函数"的继承?

比如,现在有一个对象,叫做"中国人"。

  var Chinese = {
    nation:’中国'
  };

还有一个对象,叫做"医生"。

  var Doctor ={
    career:’医生’
  }

请问怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个"中国医生"的对象?

这里要注意,这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现"继承"。


深拷贝继承(推荐)

提到拷贝继承,不得不提到浅拷贝,浅拷贝只能拷贝一层深度,但是很多时候我们碰到的对象结构不止一层,所以浅拷贝并不适合我们实际开发中的使用,这里也就不多做说明了。

所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。

  function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

使用的时候这样写:

  var Doctor = deepCopy(Chinese);

现在,给父对象加一个属性,值为数组。然后,在子对象上修改这个属性:

  Chinese.birthPlaces = ['北京','上海','香港'];
  Doctor.birthPlaces.push('厦门');

这时,父对象就不会受到影响了。

  alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
  alert(Chinese.birthPlaces); //北京, 上海, 香港

目前,jquery库使用的就是这种继承方法。


extends 关键字实现继承

这个是 ES6 的语法糖,下面看下es6实现继承的方法

class Parent {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Children extends Parent {
  constructor(name, age, job) {
    this.job = job; // 这里会报错
    super(name, age);
    this.job = job; // 正确
  }
}

上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。子类Children的构造函数之中的super(),代表调用父类Parent的构造函数。这是必须的,否则 JavaScript 引擎会报错。

注意,super虽然代表了父类Parent的构造函数,但是返回的是子类Children的实例,即super内部的this指的是Children,因此super()在这里相当于Parent.prototype.constructor.call(this)。


链接: https://fly63.com/article/detial/5988

js原型继承、构造函数继承、组合继承法

原型继承缺点:1、不能由子对象像父对象传递参数,2、对于引用型的属性修改之后会印象其他的实例对象;构造函数继承缺点:1、不能继承父对象原型上的方法 2、每次实例化对象会重新构建函数,浪费内存。

各种实现js继承的方法总结

学过java的同学应该都知道,继承是java的重要特点之一,许多面向对象的语言都支持两种继承方式:接口继承和实现继承,接口继承只继承方法签名,而实现继承则继承实际的方法,在js中,由于函数没有签名,因此支持实现继承,而实现继承主要是依靠原型链来实现的,那么,什么是原型链呢?

css的继承性

在面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法。那么我们现在主要研究css,css就是在设置属性的。不会牵扯到方法的层面。

js原型继承的几种方式

1. 原型链继承;2,构造函数继承(对象冒充继承);3,组合继承(原型链继承+构造函数继承);4,原型式继承;5. 寄生组合式继承,为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术

Js继承背后的场景-prototype,__proto__, [[prototype]]

prototype是构造函数的一个属性,它决定了在构造出来的对象上__proto__属性将是什么样的。如上图所示,理解JavaScript中的继承的关键是要理解母鸡如何产蛋的过程。

Js常用的继承方式

JavaScript常用继承方式主要分为(7种):原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承以及继承多个对象。原型链继承(核心:将父类的实例作为子类的原型

JS之继承(ES5 & ES6)

继承到底是什么?继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”。继承可以使得子类具有父类别的各种属性和方法

Js继承总结

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。如果让原型对象指向另一个类型的实例.....有趣的事情便发生了.

ES5 的构造函数原型链继承

构造函数,就是专门用来生成实例对象的函数。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。为了与普通函数区别,构造函数名字的第一个字母通常大写。

Js实现继承的几种方法及其优缺点

要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链;每一个构造函数都有prototype属性(显示原型),用来显示修改对象的原型

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!