JavaScript中六种面试常考继承方式总结

 更新时间:2023年02月13日 09:14:59   作者:mick  
js的几种继承方式在我们面试的时候经常会被问到,所以深入理解js几种继承方式以及它们的优缺点是非常有必要的。本文为大家整理了JavaScript中面试常考的六种继承方式,需要的可以参考一下

js的几种继承方式在我们面试的时候经常会被问到,所以深入理解js几种继承方式以及它们的优缺点是非常有必要的。

原型链继承

之前我们介绍过原型和实例的关系:每一个构造函数都有一个原型prototype,原型对象中的constructor又指回构造函数,实例中有一个内部指针__proto__指向构造函数的prototype。不清楚的可以看这篇 下面看个代码

function Parent() {
  this.name = "mick";
}

Parent.prototype.getName = function () {
  console.log(this.name);
};

function Child() {}

Child.prototype = new Parent();

var child1 = new Child();

console.log(child1.getName());

我们将构造函数Parent的实例赋值给了构造函数Child的原型,实现了Child能够继承Parent的属性和方法。

优点

1.父类的方法可以被复用

缺点

1.父类的所有属性都会被子类共享,只要修改了一个子类的引用类型的属性,其他的子类也会受影响

function Parent() {
  this.names = ["mick", "randy"];
}

function Child() {}

Child.prototype = new Parent();

var child1 = new Child();

child1.names.push("qr");

console.log(child1.names); // ["mick", "randy", "qr"]

var child2 = new Child();

console.log(child2.names); //  ["mick", "randy", "qr"]

2.子类实例不能给父类构造函数传参

盗用构造函数

盗用构造函数的思路其实就是在子类构造函数中通过call或者apply方法调用父类构造函数

function Parent() {
  this.names = ["mick", "randy"];
}

function Child() {
  Parent.call(this);
}

var child1 = new Child();

child1.names.push("qr");

console.log(child1.names); // ["mick", "randy", "qr"]

var child2 = new Child();

console.log(child2.names); //  ["mick", "randy"]

这里我们通过在构造函数Child中通过call调用Parent,此时this就是构造函数Child,其实就是Child的实例被创建的时候都会对Parent进行初始化,相当于每一个实例都拥有了names属性。

我们也可以给父构造函数传参了

function Parent(name) {
  this.name = name;
}

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

var child1 = new Child("randy", 18);

console.log(child1.name); // randy

优点

  • 可以在子类构造函数向父类构造函数传参
  • 父类的实例的引用属性不会被共享

缺点

子类不能访问父类原型上的方法,所以所有方法和属性都写在构造函数中,每次实例创建都会被初始化。

组合继承

组合继承就是综合原型链继承和盗用构造函数继承的优点,从何又对这两种方法的缺点互补。使用原型链继承可以访问父类原型上的属性和方法,通过构造函数继承可以访父类实例的属性和方法。

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name); // 第一次
  this.age = age;
}

Child.prototype = new Parent(); // 第二次
Child.prototype.constructor = Child;

var child1 = new Child("mick", "18");

child1.colors.push("black");

console.log(child1.name); // mick
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]

var child2 = new Child("randy", "20");

console.log(child2.name); // randy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]

优点

  • 父类的方法可以复用
  • 可以在子类构造函数向父类传参
  • 父类构造函数中的引用属性不会共享

缺点

父类构造函数被调用了两次(文章中的注释已经标出)

原型式继承

创建一个临时的构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。其实就是对传入对应进行一次浅复制。

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

其实就是Object.create的模拟实现,将传入的对象作为创建的对象的原型 缺点

1.引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

let person = {
  name: "mick",
  colors: ["red", "blue", "green"],
};

let anotherPerson = createObj(person);
anotherPerson.name = "randy";
anotherPerson.colors.push("black");
console.log(anotherPerson.colors); // ['red', 'blue', 'green', 'black']
let yetAnotherPerson = createObj(person);

yetAnotherPerson.colors.push("yellow");
console.log(yetAnotherPerson.name); // mick
console.log(yetAnotherPerson.colors); // ['red', 'blue', 'green', 'black', 'yellow']

修改了anotherPerson.name的值,yetAnotherPerson.name没有发生变化,这是因为anotherPerson.nameanotherPerson添加了name的值,并不是修改了原型上的值。

寄生式继承

寄生式继承背后类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。我们继续使用原型式继承创建的方法

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

function createAnother(original) {
  let clone = createObj(original);
  clone.sayHi = function () {
    console.log("hi");
  };
  return clone;
}

缺点

跟盗用构造函数一样的,方法在每次创建对象都会重新创建一遍

寄生式组合继承

我们首先回看下组合继承有个缺点就是父类构造函数会调用两次,那如何优化这个缺点呢?

寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。也就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();

var child1 = new Child("mick", "18");

console.log(child1);

封装一下

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

function prototype(child, Parent) {
  var prototype = createObj(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}
function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

prototype(Child, Parent)

var child1 = new Child("mick", "18");

console.log(child1);

这种方式的高效率体现它只调用了一次Parent构造函数,并且因此避免了在Parent.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceofisPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

以上就是JavaScript中六种面试常考继承方式总结的详细内容,更多关于JavaScript继承方式的资料请关注脚本之家其它相关文章!

相关文章

  • 利用策略模式与装饰模式扩展JavaScript表单验证功能

    利用策略模式与装饰模式扩展JavaScript表单验证功能

    这篇文章主要介绍了利用策略模式与装饰模式扩展JavaScript表单验证功能,需要的朋友可以参考下
    2017-02-02
  • Bootstrap Chart组件使用教程

    Bootstrap Chart组件使用教程

    图表组件Chart.js是Bootstrap比较好用的组件之一,与一款收费的组件highchart类似,效果上来看免费与收费的产品相差还是有一点点的,不过功能上差不多能满足我们项目的需要,本文给大家介绍Bootstrap Chart组件使用,需要的朋友参考下吧
    2016-04-04
  • js实现砖头在页面拖拉效果

    js实现砖头在页面拖拉效果

    这篇文章主要为大家详细介绍了js实现砖头在页面拖拉效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • JS实现的3D拖拽翻页效果代码

    JS实现的3D拖拽翻页效果代码

    这篇文章主要介绍了JS实现的3D拖拽翻页效果,通过鼠标事件结合时间函数动态操作页面元素实现拖拽翻页的效果,需要的朋友可以参考下
    2015-10-10
  • 基于JavaScript简单实现一下新手引导效果

    基于JavaScript简单实现一下新手引导效果

    这篇文章主要为大家详细介绍了如何基于JavaScript简单实现一下新手引导效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-03-03
  • 浅析JS原始值和引用值问题

    浅析JS原始值和引用值问题

    如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址,这篇文章主要介绍了JS原始值和引用值 ,需要的朋友可以参考下
    2021-12-12
  • 基于javascript原生判断DOM是否加载完毕

    基于javascript原生判断DOM是否加载完毕

    这篇文章主要介绍了基于javascript原生判断DOM是否加载完毕,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • javascript制作游戏开发碰撞检测的封装代码

    javascript制作游戏开发碰撞检测的封装代码

    这篇文章主要介绍了javascript制作游戏开发碰撞检测的封装代码,需要的朋友可以参考下
    2015-03-03
  • Bootstrap 轮播(Carousel)插件

    Bootstrap 轮播(Carousel)插件

    Bootstrap 轮播(Carousel)插件是一种灵活的响应式的向站点添加滑块的方式。下面通过本文给大家介绍Bootstrap 轮播(Carousel)插件,非常不错,需要的朋友参考下吧
    2016-12-12
  • js+div实现图片滚动效果代码

    js+div实现图片滚动效果代码

    本篇文章主要是对js+div实现图片滚动效果的示例代码进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-02-02

最新评论