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继承必须首先要了解 ...
随机推荐
- final, finally 和finalize的区别
final 修饰符(关键字) 如果一个类被声明为final,意味着它不能再派生新的子类,不能作为父类被继承.因此一个类不能及被声明为abstract,又被声明为final的.将变量或方法声明为fina ...
- 【弱省胡策】Round #7 Rectangle 解题报告
orz PoPoQQQ 的神题. 我的想法是:给每一个高度都维护一个 $01$ 序列,大概就是维护一个 $Map[i][j]$ 的矩阵,然后 $Map[i][j]$ 表示第 $i$ 根柱子的高度是否 ...
- 这些小众软件让你的效率提升N倍!(必备,收藏)
大部分的我们,电脑买来之后,软件越装越多,电脑越来越卡,导致工作的效率也是越来越低. 同时还可能长期处于软件安装完又卸载的无限恶性循环中.提高工作效率是我们利用电脑办公的一大优势,安装好的软件更是可以 ...
- Android java.net.SocketException四大异常解决方案
java.net.SocketException如何才能更好的使用呢?这个就需要我们先要了解有关这个语言的相关问题.希望大家有所帮助.那么我们就来看看有关java.net.SocketExceptio ...
- Java之sleep和wait的区别
这个问题在面试线程方面的知识时,基本上属于必问的问题.因此这里有必要做一个较为详细的总结. 区别一 首先需要明白的是这两个方法根本来自不同的类,sleep来自Thread,wait来自Object类. ...
- Hibernate java.lang.NoSuchFieldError: INSTANCE
在使用hibernate3.6.2是我遇到了一个有趣的错误java.lang.NoSuchFieldError: INSTANCEat org.hibernate.type.BasicTypeRegi ...
- 【转】G++ 处理 /usr/bin/ld: cannot find -lc
原文网址:http://blog.sina.com.cn/s/blog_67bbb71101010tto.html 用g++编译C++程序时显示出:/usr/lib/ld: cannot find - ...
- 不要直接对Request.Headers["If-Modified-Since"]使用Convert.ToDateTime
不要直接对Request.Headers["If-Modified-Since"]使用Convert.ToDateTime 前一段时间图片处理服务一直报“System.Format ...
- jQuery里面的datepicker日期控件默认是显示英文的,如何显示中文或其他语言呢?
jQuery里面的datepicker日期控件默认是显示英文的,如何让他显示中文或其他呢? [官方的写法]: (1)引入JS文件: <script type="text/javascr ...
- ASP.NET MVC3细嚼慢咽---(2)模板页
这一节演示下MVC3中怎样使用模板页,在传统的webform设计模式中,我们使用masterpage作为模板页,在MVC3 razor视图设计中,我们使用另一种方式作为模板页. 新建一个MVC3项目, ...