你想知道的多种 js 原生继承方式以及他们的优缺点

一次面试被面试官问到:你简历上写你对 js 深入的理解,请你说一下js继承有几种方式?我心里暗喜:我当然知道啦,我 5 年的前端开发经验又不是虚的,然后就列了以下 3 种模式:

1.通过 call/apply 将父构造函数 this 指向子构造函数

2.通过给子构造函数的 prototype 属性赋值父构造函数实例

3.直接给子构造函数的 prototype 属性赋值父构造函数 prototype 属性

然后面试官:嗯,那你知道它们之间的差异和优缺点吗?

我:没有深入了解.....

毫无悬念面试挂了,吸取笔者的教训,以后简历就不要轻易写 “深入理解”’,除非你真的对其有很深入的研究,不然就是给自己挖坑!还有千万不要写“精通”某某语言,相信那些技术大牛也不敢说自己 “精通” 哪门语言。扯远了,回归正题js原生继承有几种方式它们的优缺点分别是什么?

一、构造函数绑定

就是上面说的通过 call/apply 将父构造函数 this 指向子构造函数看下面的例子

// 人类的构造函数 
function Person(species){
   this.species = species;
}
Person.prototype.sex = 'man'
//中国人的构造函数
function Chinese(species,name,skin){
    Person.apply(this, arguments);
     this.name = name;
   this.skin = skin ;
}

 var chinese = new Chinese("会说话","小明","黄色");
 console.log(chinese.species); // 会说话
  console.log(chinese.sex); //undefined

优点:
1.简单快捷
2.创建子类实例时,可以向父类传递参数
3.可以实现多继承(apply 多个父类对象)

缺点:
1.实例只是子类的实例不属于父类实例
2.只能继续父类属性/方法不能继承其原型属性/方法

二、原型链继承

function Person(){
   this.species = "会说话";
}

//中国人的构造函数
function Chinese(name,skin){
     this.name = name;
   this.skin = skin ;
}

 Chinese.prototype = new Person();
  Chinese.prototype.constructor = Chinese

var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话

有同学可能会对这句 Chinese.prototype.constructor = Chinese 感到疑惑,为什么要重新赋值 Chinese.prototype 的 constructo r呢,因为 Chinese.prototype=new Person() 会把 Chinese.prototype 的 constructor 指向了 Person,如果不手动改过来就会导致会导致继承链的紊乱。

优点:

1.非常纯粹的继承关系,实例是子类的实例,也是父类的实例
2.父类新增原型方法/原型属性,子类都能访问到
3.简单,易于实现

缺点:

1.性能低,建立 Person 的实例,浪费内存

2.来自原型对象的所有属性被所有实例共享,改变实例的属性会同时改变原型属性

三、prototype继承

function Person(){
}

Person.prototype.species = "会说话"
function Chinese(name,skin){
     this.name = name;
   this.skin = skin ;
}

Chinese.prototype = Person.prototype;
Chinese.prototype.constructor = Chinese

var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话
Chinese.prototype.species = "好客" 
console.log(Person.prototype.species)// 好客

优点:效率比较高(不用执行和建立 Person 的实例了),比较省内存

缺点: Person.prototype 和 Chinese.prototype 现在指向了同一个对象,Chinese.prototype 的修改,会修改到 Person.prototype。

四、拷贝继承

function Person(){
}

Person.prototype.species = "会说话"
function Chinese(name){
  var person = Person.prototype;
  for(var p in person){
    Chinese.prototype[p] = person[p];
  }
  Chinese.prototype.name = name || 'Tom';
}

var chinese = new Chinese();
console.log(chinese.species); //会说话
console.log(chinese instanceof Person); // false
console.log(chinese instanceof Chinese); // true

特点:

1.支持多继承

2.解决Person.prototype 和 Chinese.prototype 共享一个指针问题

缺点:

1.效率较低,内存占用高(因为要拷贝父类的属性)

2.无法获取父类不可枚举的方法(不可枚举方法,不能使用 for in 访问到)

五、Object.create

function Person(){
}

Person.prototype.species = "会说话"
function Chinese(name,skin){
     this.name = name;
   this.skin = skin ;
}

Chinese.prototype = Object.create(Person.prototype);
Chinese.prototype.constructor = Chinese

var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话
Chinese.prototype.species = "好客" 
console.log(Person.prototype.species)// 会说话

这种方式是目前常用的一种继承方式,效率高同时解决了 Chinese.prototype 和 Person.prototype 共享指针内存问题

原文参考

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html 

https://www.cnblogs.com/humin/p/4556820.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值