`
123003473
  • 浏览: 1043352 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Javascript继承机制总结

阅读更多
Javascript本身是从Perl语言的语法演变而来的,本质上是脚本语言,随着版本的更新逐渐加入的对面向对象的模拟。我认为Js的面向对象模拟总体上做得还是不错的,因为我们不能盲从任何一种理念,不能纯粹的为了OOP而OOP,我们需要抓住的是面向对象的好处到底是什么?为了这些优点去OOP,才是最明智的选择,所以说Js做得还不错。

Js的继承在很多书里面细致的分了很多种类型和实现方式,大体上就是两种:对象冒充、原型方式。这两种方式各有优点和缺陷,这里我先列举出来,再从底层分析区别:

(一)对象冒充

JScript code
function A(name){
    this.name = name;
    this.sayHello = function(){alert(this.name+” say Hello!”);};
}

function B(name,id){
    this.temp = A;
    this.temp(name);        //相当于new A();
    delete this.temp;        //防止在以后通过temp引用覆盖超类A的属性和方法
     this.id = id;   
    this.checkId = function(ID){alert(this.id==ID)};
}


当构造对象B的时候,调用temp相当于启动A的构造函数,注意这里的上下文环境中的this对象是B的实例,所以在执行A构造函数脚本时,所有A的变量和方法都会赋值给this所指的对象,即B的实例,这样子就达到B继承了A的属性方法的目的。之后删除临时引用temp,是防止维护B中对A的类对象(注意不是实例对象)的引用更改,因为更改temp会直接导致类A(注意不是类A的对象)结构的变化。

我们看到了,在Js版本更新的过程中,为了更方便的执行这种上下文this的切换以达到继承或者更加广义的目的,增加了call和apply函数。它们的原理是一样的,只是参数不同的版本罢了(一个可变任意参数,一个必须传入数组作为参数集合)。这里就以call为例子,解释一下用call实现的对象冒充继承。


JScript code
function Rect(width, height){
    this.width = width;
    this.height = height;
    this.area = function(){return this.width*this.height;};
}

function myRect(width, height, name){
    Rect .call(this,width,height);
    this.name = name;
    this.show = function(){
    alert(this.name+” with area:”+this.area());
    }
}


关于Call方法,官方解释:调用一个对象的一个方法,以另一个对象替换当前对象。
call (thisOb,arg1, arg2…)

这也是一种对象冒充的继承,其实在call方法调用的时候发生的事情也是上下文环境变量this的替换,在myRect函数体中this肯定是指向类myRect对象的实例了,然而用这个this作为上下文环境变量调用名字叫Rect方法,即类Rect的构造函数。于是此时调用Rect时候对this的赋值属性和方法都实际上是对一个myRect的对象进行。所以说尽管call和apply并不是仅仅为了继承而新增的方法,但用它们可以模拟继承。

对象冒充继承就是这么一回事,它可以实现多重继承,只要重复做这一套赋值的流程就可以了。不过目前真正大规模使用得并不多,为什么呢?因为它有一个明显的性能缺陷,这就要说道OO的概念了,我们说对象是成员+成员方法的集合,构造对象实例的时候,这些实例只需要拥有各自的成员变量就可以了,成员方法只是一段对变量操作的可执行文本区域而已,这段区域不用为每个实例而复制一份,所有的实例都可以共享。现在回到Js利用对象冒充模拟的继承里,所有的成员方法都是针对this而创建的,也就是所所有的实例都会拥有一份成员方法的副本,这是对内存资源的一种极度浪费。其它的缺陷比如说对象冒充无法继承prototype域的变量和方法就不用提了,笔者认为前一个致命缺陷就已经足够。不过,我们还是需要理解它,特别是父类的属性和方法是如何继承下来的原理,对于理解Js继承很重要。

(二)原型方式
第二种继承方式是原型方式,所谓原型方式的继承,是指利用了prototype或者说以某种方式覆盖了prototype,从而达到属性方法复制的目的。其实现方式有很多中,可能不同框架多少会有一点区别,但是我们把握住原理,就不会有任何不理解的地方了。看一个例子(某一种实现):


JScript code
function Person(){
    this.name = “Mike”;
    this.sayGoodbye = function(){alert(“GoodBye!”);};
}

Person.prototype.sayHello = function(){alert(”Hello!”);};

function Student(){}

Student.prototype = new Person();




关键是对最后一句Student原型属性赋值为Person类构造的对象,这里笔者解释一下父类的属性和方法是如何copy到子类上的。Js对象在读取某个对象属性的时候,总是先查看自身域的属性列表,如果有就返回否则去读取prototype域(每个对象共享构造对象的类的prototype域所有属性和方法),如果找到就返回,由于prototype可以指向别的对象,所以Js解释器会递归的去查找prototype域指向对象的prototype域,直到prototype为本身,查找变成了一种循环,就停止,此时还没找到就成undefined了。

这样看来,最后一句发生的效果就是将父类所有属性和方法连接到子类的prototype域上,这样子类就继承了父类所有的属性和方法,包括name、sayGoodbye和sayHello。这里与其把最后一句看成一种赋值,不如理解成一种指向关系更好一点。这种原型继承的缺陷也相当明显,就是继承时父类的构造函数时不能带参数,因为对子类prototype域的修改是在声明子类对象之后才能进行,用子类构造函数的参数去初始化父类属性是无法实现的,如下所示:


JScript code
function Person(name){
    this.name = name;
}

function Student(name,id){
    this.id = id;
}

Student.prototype = new Person(this.name);




两种继承方式已经讲完了,如果我们理解了两种方式下子类如何把父类的属性和方法“抓取”下来,就可以自由组合各自的利弊,来实现真正合理的Js继承。下面是个人总结的一种综合方式:


JScript code
function Person(name){
    this.name = name;
}

Person.prototype.sayHello = function(){alert(this.name+“say Hello!”);};

function Student(name,id){
    Person.call(this,name);
    this.id = id;
}

Student.prototype = new Person();
Student.prototype.show = function(){
    alert(“Name is:”+ this.name+” and Id is:”+this.id);
}


总结就是利用对象冒充机制的call方法把父类的属性给抓取下来,而成员方法尽量写进被所有对象实例共享的prototype域中,以防止方法副本重复创建。然后子类继承父类prototype域来抓取下来所有的方法。如想彻底理清这些调用链的关系,推荐大家多关注Js中prototype的constructor和对象的constructor属性

分享到:
评论

相关推荐

    Javascript继承机制详解

    学完了Javascript类和对象的创建之后,现在总结一下Javascript继承机制的实现。Javascript并不像Java那样对继承机制有严格明确的定义,它的实现方式正如它的变量的使用方式那样也是十分宽松的,你可以设计自己的方法...

    JavaScript不使用prototype和new实现继承机制

    此方法并非笔者原创,笔者只是在前辈的基础上,加以总结,得出一种简洁实用的JavaScript继承方法。  传统的JavaScript继承基于prototype原型链,并且需要使用大量的new操作,代码不够简洁,可读性也不是很强,貌似...

    JavaScript常见继承模式实例小结

    JavaScript中并没有传统的面向对象语言中的类的概念,但是却实现了特殊的继承机制。 (阅读此文您首先需要知道原型的知识) 先来说说第一种继承方式,原型链继承。 一. 原型链继承 所谓原型链继承,就是让父类的一个...

    Javascript数组操作高级心得整理

    3. 继承机制实现 27 (1) 继承的方式 27 (2) 继承方式1—对象冒充 27 (3) 继承方式2—call()方法与apply()方法 28  call()方法 28  apply()方法 28 (4) 继承方式3—原型链(prototype chaining) 29 (5) 继承方式...

    C++ Template 基础篇(一):函数模板详解

    泛型编程弥补了这个缺点,通过把通用逻辑设计为模板,摆脱了类型的限制,提供了继承机制以外的另一种抽象机制,极大地提升了代码的可重用性。 注意:模板定义本身不参与编译,而是编译器根据模板的用户使用模板时...

    es6面向对象详细总结,超全超详细

    包含es6的超详细内容,是自己学习总结来的,适合于和我一样的学习前端的人,可以深入了解或者复习一下es6,这个word文档中包含《 类、构造函数和原型对象、原型链查找机制、this指向问题、继承、扩展内置对象、es5...

    庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术

    1.4 可继承控件基类介绍 2 1.5 运行一个简单的控件 7 1.5.1 开发一个简单控件 7 1.5.2 部署和运行 11 1.6 控件生命周期 12 1.6.1 概述 12 1.6.2 用代码跟踪控件生命周期过程 14 1.7 本章总结 17 第2章 ...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part1

    1.4 可继承控件基类介绍 2 1.5 运行一个简单的控件 7 1.5.1 开发一个简单控件 7 1.5.2 部署和运行 11 1.6 控件生命周期 12 1.6.1 概述 12 1.6.2 用代码跟踪控件生命周期过程 14 1.7 本章总结 17 第2章 ...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part2

    1.4 可继承控件基类介绍 2 1.5 运行一个简单的控件 7 1.5.1 开发一个简单控件 7 1.5.2 部署和运行 11 1.6 控件生命周期 12 1.6.1 概述 12 1.6.2 用代码跟踪控件生命周期过程 14 1.7 本章总结 17 第2章 ...

    庖丁解牛纵向切入ASP.NET 3.5控件和组件开发技术.pdf

    如果扎实地掌握了asp.net控件的运行机制,开发一个页面级的asp.net应用程序会变得非常简单。本书宗旨就是让开发人员真正理解asp.net技术,帮助开发人员提高asp.net开发的技术水平。学完本书后您不仅能够掌握控件开发...

    AngularJS全局scope与Isolate scope通信用法示例

    1、AngularJS中,子作用域一般都会通过JavaScript原型继承机制继承其父作用域的属性和方法。但有一个例外:在directive中使用scope: { … },这种方式创建的作用域是一个独立的”Isolate”作用域,

    Web-interview:《前端面试小册》从面试前简历的准备到知识点的总结,全面复盘大前端面试知识点(实时更新,实时补充):writing_hand::writing_hand::writing_hand:

    2020年1月15日开始写JavaScript基础知识部分。 2020/1/16写数据类型中的七大模块部分。 2020/1/17写这篇,闭包等重点部分。 2020/1/30写消息循环机制的原理 2020/02/02补充new的实现原理 2020/02/03补充继承等...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    Chrome Frame 会把最新版的Chrome Webkit 内核和JavaScript 引擎注入到IE中, IE浏览器将获得Chrome的性能和功能 目录 摘要 I ABSTRACT II 专业名词清单 III 第一章 绪论 1 1.1 研究背景与意义 1 1.2国内外相关...

    asp.net知识库

    完整的在.net后台执行javascript脚本集合 ASP.NET 中的正则表达式 常用的匹配正则表达式和实例 经典正则表达式 delegate vs. event 我是谁?[C#] 表达式计算引擎 正式发布表达式计算引擎WfcExp V0.9(附源码) 运算...

    Java面试宝典2010版

    28、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 21 29、super.getClass()方法调用 21 30、String是最基本的数据类型吗? 22 31、String s = "Hello...

    python入门到高级全栈工程师培训 第3期 附课件代码

    05 迭代器协议和for循环工作机制 06 迭代器补充 07 三元运算,列表解析,生成器表达式 第19章 01 生成器函数 02 生成器函数的好处 03 母鸡下蛋的传说 04 生成器特性阐释 05 生产者消费者模型 06 第三次作业讲解 ...

    亮剑.NET深入体验与实战精要2

    本书既考虑到实际开发中经常遇到的困惑和难题,也分析了解决问题的思路和方法,更总结出项目开发中不可或缺的技术点及思想。读者可以在欣赏一个个有趣例子的过程中,不知不觉具备开发真正商业项目的能力。 本书集...

    亮剑.NET深入体验与实战精要3

    本书既考虑到实际开发中经常遇到的困惑和难题,也分析了解决问题的思路和方法,更总结出项目开发中不可或缺的技术点及思想。读者可以在欣赏一个个有趣例子的过程中,不知不觉具备开发真正商业项目的能力。 本书集...

    PHP和MySQL WEB开发(第4版)

    8.2.7 表格类型的总结 8.3 Web数据库架构 8.4 进一步学习 8.5 下一章 第9章 创建Web数据库 9.1 使用MySQL监视程序 9.2 登录到MySQL 9.3 创建数据库和用户 9.4 设置用户与权限 9.5 MySQL权限系统的介绍 9.5.1 最少...

    PHP和MySQL Web开发第4版pdf以及源码

    8.2.7 表格类型的总结 8.3 Web数据库架构 8.4 进一步学习 8.5 下一章 第9章 创建Web数据库 9.1 使用MySQL监视程序 9.2 登录到MySQL 9.3 创建数据库和用户 9.4 设置用户与权限 9.5 MySQL权限系统的介绍 ...

Global site tag (gtag.js) - Google Analytics