由于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代码复用--继承的更多相关文章

  1. C++进阶--代码复用 继承vs组合

    //############################################################################ /* * 代码复用: 继承 vs 组合 * ...

  2. javascript代码复用(四)-混入、借用方法和绑定

    这篇继续说js的现代复用模式:混入.借用方法和绑定. 混入 可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in).混入并不是复制一个完整的对象,而是从多个对象中复制出 ...

  3. javascript代码复用模式(二)

    前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承).这篇就继续类式继承. 类式继承模式-借用构造函数 使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量 ...

  4. javascript代码复用模式

    代码复用有一个著名的原则,是GoF提出的:优先使用对象组合,而不是类继承.在javascript中,并没有类的概念,所以代码的复用,也并不局限于类式继承.javascript中创建对象的方法很多,有构 ...

  5. javascript代码复用模式(三)

    前面谈到了javascript的类式继承.这篇继续部分类式继承,及一些现代继承. 类式继承模式-代理构造函数 这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带 ...

  6. javascript 模式(1)——代码复用

    程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...

  7. 《JavaScript模式》第6章 代码复用模式

    @by Ruth92(转载请注明出处) 第6章:代码复用模式 GoF 在其著作中提出的有关创建对象的建议原则: -- 优先使用对象组合,而不是类继承. 传统模式:使用类继承: 现代模式:"类 ...

  8. 初涉JavaScript模式 (13) : 代码复用 【上】

    引子 博客断了一段时间,不是不写,一是没时间,二是觉得自己沉淀不够,经过一段时间的学习和实战,今天来总结下一个老生常谈的东西: 代码复用. 为何复用 JS门槛低,故很多人以为写几个特效就会JS,其实真 ...

  9. 【前端学习】javascript面向对象编程(继承和复用)

    前言       继承,代码复用的一种模式.和其它高级程序语言相比,javascript有点点不一样,它是一门纯面向对象的语言,在JS中,没有类的概念,但也可以通过原型(prototype)来模拟对象 ...

随机推荐

  1. hadoop2 作业执行过程之map过程

    在执行MAP任务之前,先了解一下它的容器和它容器的领导:container和nodemanager NodeManager NodeManager(NM)是YARN中每个节点上的代理,它管理Hadoo ...

  2. 减肥App计划

    写在前面   最近公司需求不多,正好研究一下 App 瘦身的办法,写了点小总结. 如果你不知道下面几个问题,不妨可以看看文章. 使用 .xcassets 有什么好处? @1x .@2x 和 @3x 会 ...

  3. 基于python3的手机号生成脚本

    今天利用业余,自己突发想法来写个手机号码生成的脚本,其实自己用的方法很简单,想必肯定又不少人写的比我的好,我只是自己闲来无聊搞一下, #作者:雷子 #qq:952943386 #日期:2016年7月1 ...

  4. C#三大支柱之多态

    1.包含与委托---has-a 2.嵌套类型(枚举.类.接口.结构等) 由于只有嵌套类才可声明为私有,通过嵌套类则可以完全控制内部类型的访问级别. 嵌套类型是包含类的成员,所有可以访问包含类的私有成员 ...

  5. [Javascript] Try...Catch和异常处理

    在JavaScript可以使用try...catch来进行异常处理.例如:  try    {     foo.bar(); }   catch  (e)   {     alert(e.name  ...

  6. 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码

    来自:http://www.cnblogs.com/lcxu2/archive/2011/01/16/2004016.html 正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain 或 ...

  7. 140个google面试题

    某猎头收集了140多个Google的面试题,主要是下面这些职位的. Product Marketing Manager Product Manager Software Engineer Softwa ...

  8. XHTML文档基本结构

    1.什么是XHTML 即是通常所说的DIV+CSS XHTML (可扩展文本标签语言)英文表示(eXtensiblet HeperText Markup Language) (XHTML是一个扮演着H ...

  9. Centos7.5 java环境的安装配置

    1.查看系统中的java环境 [root@localhost ~]# java -version openjdk version "1.8.0_101" OpenJDK Runti ...

  10. 开篇,UVA 755 && POJ 1002 487--3279 (Trie + DFS / sort)

    博客第一篇写在11月1号,果然die die die die die alone~ 一道不太难的题,白书里被放到排序这一节,半年前用快排A过一次,但是现在做的时候发现可以用字典树加深搜,于是乐呵呵的开 ...