JS继承的几种方式
JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。
既然要实现继承,那么我们先定义一个父类:
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
alert(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
alert(this.name + '正在吃:' + food);
};
1、原型链继承
核心: 将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
alert(cat.name);
alert(cat.eat('fish'));
alert(cat.sleep());
alert(cat instanceof Animal); //true
alert(cat instanceof Cat); //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
缺点:
- 要想为子类新增属性和方法,必须要在
new Animal()这样的语句之后执行,不能放到构造器中 - 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造函数传参
2、借用构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
alert(cat.name);
alert(cat.sleep());
alert(cat instanceof Animal); // false
alert(cat instanceof Cat); // true
特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)(不完美,没有父类方法)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承
核心:通过调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,继承父类属性和方法,实现函数复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
alert(cat.name);
alert(cat.sleep());
alert(cat instanceof Animal); // true
alert(cat instanceof Cat); // true
特点:
- 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
- 可以实现多继承(同上)
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)(仅仅多消耗了一点内存)
4、原型式继承
核心:继承已有的对象而不是函数
//对传入的对象执行一次浅复制
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//例子
var Animal = {
name:"cat",
friend:["aaron","cassic"]
}
var cat=object(Animal);
cat.name="sss";
cat.friend.push("aseaff");
alert(cat.name);
alert(cat.friend);
特点:
- 没有必要创建构造函数,只是想让一个对象与另一个对象保持类似
缺点:
- 与原型模式一样,包含引用类型的属性值会共享
5、寄生式继承
核心 : 与原型式继承紧密相关,只是多了方法
//对传入的对象执行一次浅复制
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//增强对象
function createobject(o){
var clone=object(o);
clone.sayname=function(){
alert("hi");
};
return clone;
}
//例子
var Animal = {
name:"cat",
friend:["aaron","cassic"]
}
var cat=createobject(Animal);
cat.sayname();
特点:
- 没有必要创建构造函数,只是想让一个对象与另一个对象保持类似
- 与原型式继承相比,有了自己的方法
缺点:
- 与原型式继承一样,包含引用类型的属性值会共享
6、寄生组合继承
核心:通过寄生方式,创建空函数,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
function aaron(o,y){
var Super = function(){};
Super.prototype=y.prototype;
o.prototype=new Super();
o.prototype.constructor=o;
}
aaron(Cat,Animal);
// Test Code
var cat = new Cat();
alert(cat.name);
cat.sleep();
alert(cat instanceof Animal); // true
alert(cat instanceof Cat); //true
特点:
- 堪称完美
缺点:
- 实现较为复杂
- 不能实现多继承(不完美,同上)
下面介绍多继承
7 拷贝继承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
alert(cat.name);
cat.sleep();
alert(cat instanceof Animal); // false
alert(cat instanceof Cat); // true
特点:
支持多继承
缺点:
效率较低,内存占用高(因为要拷贝父类的属性)(使用 for in 能遍历原型方法)
8 组合使用构造函数和拷贝实现多继承
function Parent1(name,age){
this.name = name;
this.age = age;
this.height=180;
}
Parent1.prototype.say = function(){
alert('hi');
}
function Parent2(name,age,weight){
this.name = name;
this.age = age;
this.weight = weight;
this.height = 170;
this.skin='yellow';
}
Parent2.prototype.walk = function(){
alert('walk');
}
function Child(name,age,weight){
Parent1.call(this,name,age);
Parent2.call(this,name,age,weight);
}
for(var i in Parent1.prototype){Child.prototype[i] = Parent1.prototype[i]}
for(var i in Parent2.prototype){Child.prototype[i] = Parent2.prototype[i]}
var c1 = new Child('xiaoming',10,8);
console.log(c1); //Child { name="xiaoming", age=10, height=170, 更多...}
console.log(c1.constructor);//Child(name,age,weight)
特点:javascript的多继承其实就是前面介绍的js循环拷贝继承的多次使用
总结:javascript是可以利用call方法和prototype属性来实现多继承的。继承方法与单继承相似,只是将需要继承的多个父类依次实现,另外对于属性或共有方法重命的时候,以最后继承的属性和方法为主。因为会覆盖前面的继承。
最好的两个继承是寄生组合继承和循环拷贝继承,但是寄生组合继承不能完美实现多继承,会漏掉父类原型方法,只有循环拷贝继承可以完美实现多继承。
JS继承的几种方式的更多相关文章
- js 继承的几种方式
JS继承的实现方式: 既然要实现继承,那么首先我们得有一个父类,代码如下: function Animal(name) { // 属性 this.name = name || '小白'; // 实例方 ...
- 20. js继承的6种方式
想要继承,就必须要提供个父类(继承谁,提供继承的属性) 一.原型链继承 重点:让新实例的原型等于父类的实例. 特点: 1.实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性.( ...
- 理解js继承的6种方式
想要继承,就必须要提供个父类(继承谁,提供继承的属性) 一.原型链继承 重点:让新实例的原型等于父类的实例. 特点:1.实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性.(新 ...
- js继承的6种方式
想要继承,就必须要提供个父类(继承谁,提供继承的属性) 一.原型链继承 重点:让新实例的原型等于父类的实例. 特点:1.实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性.(新 ...
- js实现继承的5种方式 (笔记)
js实现继承的5种方式 以下 均为 ES5 的写法: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承 ...
- javascript(js)创建对象的模式与继承的几种方式
1.js创建对象的几种方式 工厂模式 为什么会产生工厂模式,原因是使用同一个接口创建很多对象,会产生大量的重复代码,为了解决这个问题,产生了工厂模式. function createPerson(na ...
- js 实现继承的几种方式
//js中实现继承的几种方式 //实现继承首先要有一个父类,先创造一个动物的父类 function Animal(name){ this.name = name; this.shoot = funct ...
- js 实现继承的6种方式(逐渐优化)
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...
- JS继承的原理、方式和应用
概要: 一.继承的原理 二.继承的几种方式 三.继承的应用场景 什么是继承? 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展.继承的过程,就是从一般到特殊的过程.要了解JS继承必须首先要了解 ...
随机推荐
- mvvm windows app DataBinding
前台html 绑定(view):<div id="aa" data-win-bind="innerText:UserData.word" style=&q ...
- 敏捷开发的价值观(转自MBAlib)
敏捷开发的价值观 实际上敏捷开发运动在数年前就开始了,但它正式开始的标志是2001年2月的“敏捷宣言”(Agile Manifesto),这项宣言是由17位当时称之为“轻量级方法学家”所编写签署的,他 ...
- js对象序列化JSON.stringify()与反序列化JSON.parse()
草稿: parse用于从一个字符串中解析出json对象,如 var str = '{"name":"huangxiaojian","age" ...
- GDB多进程调试(转)
http://www.cnblogs.com/ggjucheng/archive/2011/12/15/2288710.html GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强 ...
- jQuery 在IE下对表单中input type="file"的属性值清除
对一个文件域(input type=file)使用了验证后,我们总会希望把文件域中的值给清空了,在IE中,由于安全设置的原因,是不允许更改文件域的值的,接下来为大家介绍一下解决方法 一般来说,在对一个 ...
- Mysql 批量建表存储过程
最近项目中用到了使用存储过程批量建表的功能,记录下来: USE db_test_3; drop procedure if EXISTS `createTablesWithIndex`; create ...
- Uva 12361 File Retrieval 后缀数组+并查集
题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合. 问最多能形成多少个不同的集合.集合不能为空. 分析:用 ...
- [codility]Grocery-store
http://codility.com/demo/take-sample-test/hydrogenium2013 用Dijkstra求最短路径,同时和D[i]比较判断是不是能到.用了优先队列优化,复 ...
- RichEdit 各个版本介绍
RichEdit是开发中经常使用到的控件,其版本自1.0起,历经好几年,好几次的更新,在此引用一篇介绍RichEdit版本的博文(http://blogs.msdn.com/b/murrays/arc ...
- gdb 调试c/c++的一些小技巧
ptype obj/class/struct 查看obj/class/struct的成员,但是会把基类指针指向的派生类识别为基类 set print object on 这个选项可以看到派生对象的 ...