javascript代码复用--继承
由于javascript没有类的概念,因此无法通过接口继承,只能通过实现继承。实现继承是继承实际的方法,javascript中主要是依靠原型链要实现。
原型链继承
原型链继承是基本的继承模式,其本质是重写原型对象,使其为新对象的实例。代码实现如下:
function Person(){
this.name = "default";
var temp = "temp";
}
Person.prototype.age=0;
Person.prototype.getName = function(){
return this.name;
}
Person.prototype.getAge = function(){
return this.age;
}
console.log(Person.prototype.age);//
console.log(Person.age);//undefined
console.log(Person.prototype.name);//undefined
console.log(Person.name);//Person, if other property, should be undefined
function Student(){
this.type = "student";
}
//inheritance
Student.prototype = new Person();
console.log(Student.prototype.constructor);//Person(){}
console.log(Student.prototype.name);//default
Student.prototype.constructor = Student;
var student1 = new Student();
console.log(student1.getName());//default
console.log(student1.name);//default
console.log(student1.getAge());//
console.log(student1.age);//
console.log(student1.__proto__.age);//
console.log(student1.temp);//undefined
console.log(student1 instanceof Object);//true
console.log(student1 instanceof Person);//true
console.log(student1 instanceof Student);//true
console.log(Student instanceof Person);//false
以上代码主要注意两个问题:
1.函数局部变量,内部属性及原型属性的区别。var temp定义了一个局部变量,this.name定义了一个内部属性,prototype.age则定义了一个原型属性。
对于局部变量,无法在函数以外的地方调用,包括实例。
之前说过,函数本身的prototype属性仅仅用于函数实例的属性继承,而函数本身不会使用这个关联的prototype,在prototype中设置的属性将直接作用于所有实例。(比如Person的实例Student.prototype和student1,注意Student并不是Person的实例)
而对于函数内部属性,函数实例将直接拥有对应的内部属性(初始值),而无法通过函数本身使用内部属性。这一点其实跟prototype属性有所区别。
2.利用重写原型对象实现继承的时候,Student.prototype = new Person(), Student.prototype将指向了另一个对象Person.prototype,因此此时Student.prototype.constructor将指向Person函数。通过Student.prototype.constructor = Student 可以将其constructor重新指向Student。
通过原型链可以更好的理解上面的代码:

原型链继承的缺点
关于原型链继承的问题,其实就是跟通过原型方式创建对象的问题一样,就是原型中包含引用类型所带来的共享问题。
还有就是创建实例的时候,无法向构造器中传递参数。
构造函数继承
另一种经典的继承便是通过构造函数实现继承,即通过apply()和call()方法在子类构造函数内部调用父类构造函数。具体实现如下:
function Person(name){
this.name = name;
this.friends = new Array();
}
Person.prototype.age = 0;
function Student(name){
Person.call(this, name);
}
var student1 = new Student("Huge");
student1.friends.push("Alan");
console.log(student1.name);//Huge
console.log(student1.age);//undefined
console.log(student1.friends);//["Alan"]
var student2 = new Student("Heri");
student2.friends.push("Amly");
console.log(student2.name);//Heri
console.log(student2.friends);//["Amly"]
console.log(student1 instanceof Person);//false
console.log(student1 instanceof Student);//true
通过构造函数继承的问题除了构造函数模式本身存在的缺点之外(重复实例化方法),也无法类型识别,因此在父类原型中定义的方法和属性无法在子类中调用。
组合继承
由于通过原型链继承和构造函数继承都有其优缺点,因此将这两种继承方式组合起来,使用原型链继承实现原型中方法和属性的继承,通过构造函数继承实现参数传递和引用类型继承,是javascript中最常用的继承模式。代码实现如下:
function Person(name, age){
this.name = name;
this.age = age;
this.friends = new Array();
}
Person.prototype.getName = function(){
return this.name;
}
function Student(name, age){
this.type = "student";
Person.call(this, name, age);
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
var student1 = new Student("Huge", 15);
student1.friends.push("Alan");
console.log(student1.name);//Huge
console.log(student1.age);//
console.log(student1.friends);//["Alan"]
console.log(student1.getName());//Huge
console.log(student1 instanceof Person);//true
console.log(student1 instanceof Student);//true
var student2 = new Student("Heri", 16);
student2.friends.push("Amly");
console.log(student2.name);//Heri
console.log(student2.age);//
console.log(student2.friends);//["Amly"]
console.log(Student.prototype.name);//undefined
console.log(Student.prototype.friends);//[]
从代码可以看出,组合继承会调用两次父类的构造函数:创建子类原型的时候和在子类构造函数内部调用。实际上,第一次创建子类原型的时候,子类已经包含了父类对象的全部实例属性,因此当通过调用子类构造函数创建实例的时候,将会重写这些属性。即同时存在两组属性,一组在实例上,一组在子类原型中,如上代码中Student.prototype.friends返回的空数组。这就是调用两次父类构造函数的结果。
其他继承方式
Crockford曾经提出了prototypal inheritance以及与之结合的parasitic inheritance。通过原型创建基于原有对象的新对象,并为新对象增加功能。
//prototypal inhertance
function createObject(obj){
function F(){}
F.prototype = obj;
return new F();
}
//parasitic inheritance
function enhanceObject(obj){
var enhanceObj = createObject(obj);
enhanceObj.getName = function(){
return this.name;
}
return enhanceObj;
}
var person = {
name : "Alan"
};
var person1 = enhanceObject(person);
console.log(person1.getName());//Alan
更进一步,为了避免组合继承模式两次调用父类构造函数的问题,可以利用parasitic inheritance来继承父类的原型,再将其指定给子类的原型。如下代码:
//prototypal inhertance
function createObject(obj){
function F(){}
F.prototype = obj;
return new F();
}
//parasitic inheritance
function inheritPrototype(superObj, subObj){
var obj = createObject(superObj.prototype);
obj.constructor = subObj;
subObj.prototype = obj;
}
function Person(name, age){
this.name = name;
this.age = age;
this.friends = new Array();
}
Person.prototype.getName = function(){
return this.name;
}
function Student(name, age){
this.type = "student";
Person.call(this, name, age);
}
inheritPrototype(Person, Student);
var student1 = new Student("Huge", 15);
student1.friends.push("Alan");
console.log(student1.name);//Huge
console.log(student1.age);//
console.log(student1.friends);//["Alan"]
console.log(student1.getName());//Huge
console.log(student1 instanceof Person);//true
console.log(student1 instanceof Student);//true
var student2 = new Student("Heri", 16);
student2.friends.push("Amly");
console.log(student2.name);//Heri
console.log(student2.age);//
console.log(student2.friends);//["Amly"]\
console.log(Student.prototype.name);//undefined
console.log(Student.prototype.friends);//undefined
可以看出,子类只调用了父类一次构造函数,避免在子类原型中创建不必要的属性。同时,原型链也保持不便,可以说是实现类型继承的最有效方式。
javascript代码复用--继承的更多相关文章
- C++进阶--代码复用 继承vs组合
//############################################################################ /* * 代码复用: 继承 vs 组合 * ...
- javascript代码复用(四)-混入、借用方法和绑定
这篇继续说js的现代复用模式:混入.借用方法和绑定. 混入 可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in).混入并不是复制一个完整的对象,而是从多个对象中复制出 ...
- javascript代码复用模式(二)
前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承).这篇就继续类式继承. 类式继承模式-借用构造函数 使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量 ...
- javascript代码复用模式
代码复用有一个著名的原则,是GoF提出的:优先使用对象组合,而不是类继承.在javascript中,并没有类的概念,所以代码的复用,也并不局限于类式继承.javascript中创建对象的方法很多,有构 ...
- javascript代码复用模式(三)
前面谈到了javascript的类式继承.这篇继续部分类式继承,及一些现代继承. 类式继承模式-代理构造函数 这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带 ...
- javascript 模式(1)——代码复用
程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...
- 《JavaScript模式》第6章 代码复用模式
@by Ruth92(转载请注明出处) 第6章:代码复用模式 GoF 在其著作中提出的有关创建对象的建议原则: -- 优先使用对象组合,而不是类继承. 传统模式:使用类继承: 现代模式:"类 ...
- 初涉JavaScript模式 (13) : 代码复用 【上】
引子 博客断了一段时间,不是不写,一是没时间,二是觉得自己沉淀不够,经过一段时间的学习和实战,今天来总结下一个老生常谈的东西: 代码复用. 为何复用 JS门槛低,故很多人以为写几个特效就会JS,其实真 ...
- 【前端学习】javascript面向对象编程(继承和复用)
前言 继承,代码复用的一种模式.和其它高级程序语言相比,javascript有点点不一样,它是一门纯面向对象的语言,在JS中,没有类的概念,但也可以通过原型(prototype)来模拟对象 ...
随机推荐
- SPA初试-1
本篇内容是在上一次的基础上进行改进,对状态的定义进行了修改,一个状态的定义如下: function state(stateName, template, templateUrl) { this.sta ...
- UIView的生命周期总结
序言: loadView.viewDidLoad.viewDidAppear.initWithNibName.awakeFromNib等经常出现在UIViewController中的方法介绍. 这些方 ...
- NSDate简单介绍
NSDate简单介绍 一:NSDate是一个日期\时间方面的类,主要用来创建\获取时间 1.NSDate对象的创建: date 创建一个当前系统日期和时间的对象 dateWithTimeInterva ...
- ilter()和find()的区别
这是jQuery里常用的2个方法.他们2者功能是完全不同的,而初学者往往会被误导. 首先 我们看.find()方法:现在有一个页面,里面HTML代码为;程序代码 <div class=" ...
- java多线程之Future和FutureTask
Executor框架使用Runnable 作为其基本的任务表示形式.Runnable是一种有局限性的抽象,然后可以写入日志,或者共享的数据结构,但是他不能返回一个值. 许多任务实际上都是存在延迟计算的 ...
- 转VS快捷键
“文本操作”快捷键 命令名 快捷键 说明 编辑.折叠到定义 Ctrl + M,Ctrl + O 自动确定在代码中创建区域的逻辑边界(如过程),然后隐藏它们. 编辑.注释选定内容 Ctrl + K,Ct ...
- Jquery事件委托之Safari
什么是事件委托 事件委托是Jquery中一种事件绑定的方式,不同于常见的事件绑定方式将事件绑定在目标元素上,而是将事件绑定在父级元素上通过事件冒泡来执行绑定函数. //常见的事件绑定(Jquery) ...
- 文件上传利器SWFUpload入门简易教程
凡做过网站开发的都应该知道表单file的确鸡肋. Ajax解决了不刷新页面提交表单,但是却没有解决文件上传不刷新页面,当然也有其它技术让不刷新页面而提交文件,该技术主要是利用隐藏的iFrame, 较A ...
- php学习笔记4--php中的变量作用域
变量作用域:可以简单地理解为变量的可见区域,变量能被访问的范围.如同其他语言,php中也有全局作用域和局部作用域之分,但是不同的是:php中的全局作用域指的是:只能在函数外部使用,而局部作用域指的是: ...
- Mac下kernel_task占用大量CPU怎么办?
我们都知道要想让电脑运行的快速,那么就要尽量的保持内存和CPU的充足.不过一些MAC用户发现MAC系统中的Kernel_task会占用大量的CPU,导致电脑发热变卡.这个问题该怎么解决呢? 具体操作步 ...