专栏首页水击三千浅谈JavaScript的面向对象程序设计(四)

浅谈JavaScript的面向对象程序设计(四)

  本文继续讲解JavaScript的面向对象程序设计。继承是面向对象语言中的一个基本概念,面向对象语言支持两种继承实现方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。但是在JavaScript中函数时没有签名的,所以无法实现接口继承。JavaScript支持实现继承,而且其实现继承主要是通过原型链继承的。

  • 原型链

JavaScript中有原型链的概念,并将原型链作为实现继承的主要方法。基本实现思想是让一个函数的原型继承另外一个函数的原型的属性和方法。每一个函数都有一个原型对象,原型对象包含一个指向构造函数的指针,实例包含一个指向原型对象的指针。原型链的概念就是,一个原型对象指向另一个函数的原型,同样另一个函数的原型又指向其他函数的原型,层层递进,就构成了原型链。

 1 function SuperType(){
 2                     
 3                 }
 4                 function SubType(){
 5                     
 6                 }
 7                 SubType.prototype=new SuperType();
 8                 function ExtendType(){
 9                     
10                 }
11                 ExtendType.prototype = new SubType();

  上面的代码展示了原型链的概念,ExtendType的原型指向了SubType实例,SubType的原型指向了SuperType实例,SuperType的原型指向了Object的原型。这样就形成了一个原型链。原型链本质上是扩展了前面介绍的原型搜素机制。当访问实例的属性或者方法时,首先搜索实例的属性或者方法,再搜索实例的原型。通过原型链,可以一直向上搜索,直至搜索到Object的原型。

  前面也简单地介绍了确认原型的方法。

1 var extendtype = new ExtendType();
2                 console.log(extendtype instanceof Object);
3                 console.log(extendtype instanceof SubType);
4                 console.log(extendtype instanceof SuperType);

  上面的代码中2、3、4行都输出true,这说明extendtype中能找到函数的原型。也可以通过另外一种方法实现原型的判断,就是通过原型的isPrototypeOf方法。

1 console.log(Object.prototype.isPrototypeOf(extendtype));
2                 console.log(SubType.prototype.isPrototypeOf(extendtype));
3                 console.log(SuperType.prototype.isPrototypeOf(extendtype));

  上面的代码通过isPrototypeOf方法来判断实例的类型。同样,输出都是true。

  通过原型链在JavaScript中实现的继承依然存在一定的问题。原型链会将实例中的所有属性都共享,但是我们在构造函数中定义属性,二不在原型中定义属性就是为了不共享属性

 1 function Super(){
 2                     this.colors=["green"];
 3                 }
 4                 function Sub(){
 5                     
 6                 }
 7                 Sub.prototype= new Super();
 8                 var sub = new Sub();
 9                 sub.colors.push("red");
10                 console.log(sub.colors.toString());//green,red
11                 var sub2 = new Sub();
12                 console.log(sub2.colors.toString());//green.red

  上面的代码定义两个对象super和sub,sub的原型继承了super的实例。我们创建了sub的两个实例,对其中的一个实例colors的属性添加了一个元素,但是我们发现两个实例的属性都改变了。因为该两个实例的colors属性都指向super中的属性。

  • 借用构造函数

  借用构造函数的思想就是在子类型的构造函数中调用父类的构造函数,可以通过apply或者call调用父类构造函数。

 1 function Super(){
 2                     this.colors=["green"];
 3                 }
 4                 function Sub(){
 5                     Super.call(this);
 6                 }
 7                 var sub = new Sub();
 8                 sub.colors.push("red");
 9                 console.log(sub.colors.toString());//green,red
10                 var sub2 = new Sub();
11                 console.log(sub2.colors.toString());//green

  上面的代码在子类的函数中调用了父类的构造函数,通过call。同时,我们实例化了两个子类对象,发现sub的操作并没有影响sub2的结果。每次实例化都会调用父类的构造函数,这样每个sub都有自己的colors属性。

  同时,通过借用构造函数,我们还能传递参数。

 1 function Super(name){
 2                     this.colors=["green"];
 3                     this.name=name;
 4                 }
 5                 function Sub(name){
 6                     Super.call(this,name);
 7                 }
 8                 var sub = new Sub("hehe");
 9                 sub.colors.push("red");
10                 console.log(sub.name);//hehe
11                 var sub2 = new Sub("haha");
12                 console.log(sub2.name);//haha

  上面的代码,我们通过构造函数传递了参数,并通过call方法传递参数给父类的构造函数。借用构造函数和构造函数模式创建对象拥有同样的问题,方法和属性都在构造函数中定义,因为函数无法复用。也无法判断函数的类型。

  • 组合式继承

组合继承是指将原型链和构造函数的技术组合在一起。它的思路是通过原型链实现属性和方法的继续,通过借用构造函数模式实现实例属性的继承。这样在原型链上实现方法,保证函数的复用,同时又保证每个实例有自己的属性。

 1 function Super(name){
 2                     this.name=name;
 3                 }
 4                 Super.prototype.getName=function(){
 5                     return this.name;
 6                 }
 7                 function Sub(name,age){
 8                     Super.call(this,name);
 9                     this.age=age;
10                 }
11                 Sub.prototype= new Super();
12                 var sub = new Sub("haha",18);
13                 console.log(sub.getName());//haha
14                 console.log(sub.age);//18
15                 var sub2 = new Sub("hehe",19);
16                 console.log(sub2.getName());//hehe
17                 console.log(sub2.age);//19

  上面的代码中Super定义了一个属性name和一个原型方法getName,sub定义了一个实例属性age。sub继承了super的实例。也就是sub用super的原型方法,同时能够调用super的实例属性。在后面定义了两个sub实例,他们是不同的实例,拥有不同的实例属性,但是他们共享了原型方法。

  组合继承避免了原型链和构造函数的缺陷,是一种常用的继承实现方法。

  • 原型式继承

  克罗克福德提出了原型式继承的方法。他的方法是借助原型基于已有的对象创建新的对象,同时还不必因此创建自定义类型。

1 function create(o){
2                     function F(){};
3                     F.prototype=o;
4                     return new F();
5                 }

  在create函数内部,先创建了零时行的函数F,并将F的原型指向参数o,参数o是另一个对象的原型,最后返回F的实例,并且该实例继承了传递进行来的对象的原型。

 1 function create(o){
 2                     function F(){};
 3                     F.prototype=o;
 4                     return new F();
 5                 }
 6                 var Person={
 7                     "name":"haha",
 8                      getName:function(){
 9                          return this.name;
10                      }
11                 }
12                 var oneperson=create(Person.prototype);
13                 oneperson.name="hehe";
14                 console.log(oneperson.name);//hehe
15                 var twoperson = create(Person.prototype);
16                 twoperson.name="jack";
17                 console.log(twoperson.name);//jack

  上面的代码,基于create函数创建了两个对象,这两个对象继承了Person。这就意味着Person中的属性和方法,oneperson中同样拥有。ECMAScript5中定义了新的方法Object.create()方法,该方法有两个参数,一个参数是一个对象原型,另一个参数是需要生成的新属性。与上面的方法类似。

  • 寄生式继承

  寄生式继承与寄生式函数的工厂模式类似,也是创建一个用于封装继承过程的函数。在函数内部以一定的方式增强对象,最后返回对象。

 1 function create(o){
 2                     function F(){};
 3                     F.prototype=o;
 4                     return new F();
 5                 }
 6                 function createPerson(o){
 7                     var f=create(o);
 8                     o.sayHi=function(){
 9                         console.log("hi");
10                     }
11                     return o;
12                 }
13                 var Person={
14                     name:"haa",
15                     age:"8"
16                 }
17                 var one=createPerson(Person);

  上面的one不仅继承了person的属性,同时还拥有增强属性sayHi。

  • 寄生组合式继承

  前面说过最常用的继承方式是组合式继承,但是组合继承,不论什么情况,都要父类构造函数两次。第一次是在子类继承父类的实例时候,第二次是子类实例化过程中。

  寄生组合式继承,通过借用构造函数来继承属性,通过原型链来继承方法。使用寄生式继承来继承父类的原型,不必通过实例化来继承父类,这样减少了调用父类构造函数的次数,只用调用一次。

 1     function create(o){
 2                     function F(){};
 3                     F.prototype=o;
 4                     return new F();
 5                 }
 6                     function SuperType(name){
 7                         this.name=name;
 8                     }
 9                     function Sub(name,age){
10                         SuperType.call(this,name);
11                         this.age=age;
12                     }
13                     Sub.prototype=create(SuperType.prototype);
14                     Sub.prototype.getName=function(){
15                         return this.name;
16                     }
17                     var sub = new Sub("haha",19);
18                     console.log(sub.getName());//
19                     var sub2 = new Sub("hehe",18);
20                     console.log(sub2.getName());//hehe

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 编写优秀 CSS 代码的 8 个策略

    1qjnmmn gbhjnhbgfsjkgff 编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一。然而,我遇到的很多应用程序显然没有人花时...

    企鹅号小编
  • 微信小程序+和风天气完成天气预报

    花半天时间完成简单的小程序应用。适合小程序初学者。

    极乐君
  • JavaScript字符串“三剑客”

    JavaScript字符串方法有很多,其中有三个方法与字符串裁剪有关,他们分别是slice()、substring()和substr(),我把他们统称为“三剑客...

    企鹅号小编
  • 《笨办法学Python》 第41课手记

    《笨办法学Python》 第41课手记 本节课的代码有168行,但是冗长不代表困难,只是print里面的游戏说明内容太多,整体来说是很容易的,你要锻炼自己的耐心...

    Steve Wang
  • 给初学者:JavaScript 的常见注意点

    作者: CarterLi 原文:https://segmentfault.com/a/1190000012730162 上篇说了一些 JS 中数组操作的常见误区...

    企鹅号小编
  • JavaScript闭包详解

    JavaScript闭包详解 闭包就是由函数创造的一个词法作用域,里面创建的变量被引用后,可以在这个词法环境之外自由使用(维基百科)。 闭包,官方对闭包...

    Steve Wang
  • 应用广泛的语言ECMAScript 2018来了,新特性都在这里

    原文:What’s new in ECMAScript 2018 作者:Paul Krill 翻译:不二 译者注:ECMAScript是应用广泛的语言,它常常被...

    企鹅号小编
  • 在.NET Core 中的并发编程

    原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今...

    企鹅号小编
  • Window环境下搭建Vue.js开发环境

    笔者最近在进行前端的学习,在点完了HTML5、CSS3、JavaScript等技能树之后,是时候开始框架的学习了。目前为止前端框架呈现出React、Angula...

    Steve Wang
  • JavaScript严格模式

    "use strict" 指令 "use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。它不是一条语句,但是是一...

    xiangzhihong

扫码关注云+社区

领取腾讯云代金券