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

【翻译】简单的JavaScript继承

阅读更多
原文地址:http://ejohn.org/blog/simple-javascript-inheritance/
译者注:cocos2d-html5使用了这种类继承方式

简单的JavaScript继承


关于JavaScript继承,最近我做了很多事,主要是为了我的JavaScript书能顺利出版,同时也检验了多种JavaScript经典继承方式的模拟技术。其中,我最感兴趣的是base2(http://code.google.com/p/base2/)和Prototype(http://prototypejs.org/)。

我想把这几种技术的精华以简单、可复用、容易理解并没有任何依赖的形式展现出来。并且,我希望结果简单而高可用。下面是一个例子:
var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});
var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    //调用dance()的继承版本
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

var p = new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

//应该都是true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class



接下来对这种实现方式做一些解释:
1.创建构造函数很简单(本例中通过使用init方法实现)
2.为了创建一个新的“类”,你需要extend一个已经存在的类
3.所有类继承自同一祖先:Class。所以,如果你想创建一个新类,那它必须是Class的子类
4.最有挑战的是:这里提供了重载方法实现。参考上面的代码,可以通过在init()和dance()中调用this._super()实现。

我对结果很满意:它强制类为一个结构,维护简单的继承,并且可以调用父类方法。

简单的类创建和继承
下面是具体实现(做了格式化并添加了注释)——大概25行,欢迎反馈。
/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// 受base2和Prototype启发
(function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  //基类实现(什么都没做)
  this.Class = function(){};
  
  //创建一个新类,继承自class
  Class.extend = function(prop) {
    var _super = this.prototype;
    
    // 初始化一个基类(但是只创建了实例,没有调用init构造函数)
    initializing = true;
    var prototype = new this();
    initializing = false;
    
    //将属性复制到新的prototype
    for (var name in prop) {
      //检查是否覆盖了已有的方法
      prototype[name] = typeof prop[name] == "function" && 
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
            
            // 在父类增加一个相同的._super()方法
            this._super = _super[name];
            
            //方法只是临时绑定,所以执行后把它移除了
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
            
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
    
    //构造函数
    function Class() {
      //所有构造过程在init方法中完成
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
    
    //我们构造的protype对象
    Class.prototype = prototype;
    
    //强制构造函数为我们需要的内容
    Class.prototype.constructor = Class;

    //然后使该类可继承
    Class.extend = arguments.callee;
    
    return Class;
  };
})();


我觉得最有技巧的地方是“初始化/不要调用init”和“创建_super方法”。我想简要地介绍一下这些部分,这样你就可以更好地理解我们在这个方法中实现了什么。

初始化
为了使用一个函数prototype模拟继承,我们使用传统的技术来创建父类方法的实例,并且把它赋给prototype。不使用以上技术它应该看起来像这样:
function Person(){}
function Ninja(){}
Ninja.prototype = new Person();
//允许instanceof可用
(new Ninja()) instanceof Person


然而挑战在哪里,我们需要的就是“instanceof”的好处吗,这不值得初始化一个Person对象并运行它的构造函数。为了抵消损失,我在代码中用了一个变量,initializing,在我们仅想初始化一个类用于prototype时,设置为true。
if ( !initializing )  
    this.init.apply(this, arguments);


尤其重要的是init方法可以运行任何耗费资源的启动代码(连接服务器,创建DOM对象,谁知道呢)可以有效避免无法正常工作的。

Super方法
当你继承的时候,创建一个类,然后从父类继承一些功能,大家最希望有的功能就是可以访问被覆盖的父类方法。最终,在这个实现方案中是通过一个临时方法(._super)来实现的,它只能在父类方法中访问,引用父类相关的方法。

例如,如果你想调用父类的构造函数,你可以使用以下技术实现。
var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  }
});
var Ninja = Person.extend({
  init: function(){
    this._super( false );
  }
});

var p = new Person(true);
p.dancing; // => true

var n = new Ninja();
n.dancing; // => false 



实现该功能需要多步操作。首先,用来扩展已存在类(例如传递给Person.extend的这个)的对象需要合并到基类new Person实例中(构建过程前面我们讲了)。在合并过程中,我们做个简单的检查:我们要合并的属性是个方法吗,我们要覆盖的也是个方法吗?如果是这样,我们需要找到一种方法使父类方法可用。

我们创建了一个匿名的闭包(返回一个方法)来封装新的加强后的父类方法。我们需要保存原来的this._super(不管是否真的存在)的引用,完成后再恢复。这样会帮助我们解决同名变量已经存在的问题(而不是意外地弄丢它)。

接下来,我们创建一个新的_super方法,只是对父类prototype已经存在方法的一个引用。谢天谢地,我们不用再做其他改变或者修改作用域之类的,如果它是我们对象的一个属性,功能的上下文会自动设置(this是指与父类相对的实例)。

最后,我们调用最开始的方法,它做自己的事(很可能也是利用_super),之后我们恢复_super到原始状态并从方法返回。

现在有很多方法实现上面的类似功能(我见过另外一种实现方法是将super方法绑定到方法本身,从过arguments.callee访问),但是我认为这种方法实现了可用性和简单性最好的结合。

我会在我的书中深入介绍JavaScript prototype的本质,但是我希望把这个类实现方式单独剥离出来分享给大家。在简化代码上还有很多可以讨论的地方(容易学,容易扩展,下载量下),所以这个实现是个很好的开始用来学习JavaScript类的创建和继承。
分享到:
评论

相关推荐

    lingoes 翻译 中文+javascript

    此外,JavaScript的面向对象编程(OOP)概念,如类、构造函数、原型链、继承、封装和多态,也是JavaScript进阶学习的重点。Lingoes的翻译功能使得这些复杂的概念能以中文的形式呈现,便于理解。 在实际应用中,...

    JavaScript英文翻译

    此外,原型继承是JavaScript中实现面向对象编程的一种方式,而ES6引入的类和模块系统则让JavaScript的面向对象编程更加符合传统的类继承模型。 总的来说,JavaScript是一种强大且灵活的语言,不仅限于网页开发,还...

    外文翻译:学用JavaScript设计模式

    ### 外文翻译:学用JavaScript设计模式 #### 序言与重要性 设计模式作为软件工程中的一个重要组成部分,其核心在于提供了一套标准的方法论,帮助开发人员以优雅、高效的方式解决常见的软件设计问题。《学用...

    编写可维护面向对象的JavaScript代码[翻译]

    在JavaScript中,虽然传统意义上没有类的概念,但它支持基于原型(Prototype)的继承机制,也支持使用ES6引入的类语法来更好地支持面向对象编程。 面向对象编程具有几个核心概念,包括封装(Encapsulation)、继承...

    JavaScript参考手册中文版

    原型链和原型继承是JavaScript实现面向对象编程的关键机制。 **5. 数组** 数组是有序元素的集合,可以存储任意类型的值。JavaScript提供了多种操作数组的方法,如`push`、`pop`、`shift`、`unshift`、`splice`、`...

    JavaScript权威指南中文第6版--淘宝团队翻译

    《JavaScript权威指南中文第6版》是JavaScript编程领域的一本经典著作,由淘宝前端团队精心翻译,确保了内容的准确性和专业性。本书全面覆盖了JavaScript语言的各种核心概念和技术,对于初学者和经验丰富的开发者来...

    JavaScript 权威指南 中文第6版 (完整目录 淘宝前端团队 译)

    《JavaScript 权威指南》是JavaScript编程领域的一本经典之作,中文第六版由淘宝前端团队翻译,为读者提供了全面、深入的JavaScript知识体系。这本书详细介绍了JavaScript的核心语法、对象、函数、类、DOM操作、BOM...

    [JavaScript进阶]Professional JavaScript for Web Developers(3rd)

    Zakas所著的JavaScript领域的专业书籍,是《Professional JavaScript for Web Developers》第三版的中文翻译版。这本书旨在帮助读者深入理解JavaScript编程语言,从基础到高级特性,再到最佳实践,涵盖了JavaScript...

    JavaScript忍者禁术

    Secrets of the JavaScript Ninja(翻译:JavaScript忍者禁术)适用有JS基础的,这本书以函数为中心(函数也确实是js的核心),对函数的讲解非常全面细致,涉及到函数的定义、扩展、重载、curry化、闭包、重定向、继承...

    JavaScript高级程序设计第三版中文

    《JavaScript高级程序设计》是学习这一语言的经典教材,现在已经更新到第三版,并提供了完整的中文翻译,对于国内开发者来说,无疑是一份宝贵的资源。这本书深入浅出地讲解了JavaScript的核心概念和高级特性,帮助...

    《javascript设计模式》学习笔记二:Javascript面向对象程序设计继承用法分析

    javascript中的继承有三种(不同的书上对这三中的名称翻译都不一样,我按照我的理解来命名吧): a.构造函数继承(类式继承,组合继承,伪经典继承) b.原型继承 c.原型赋值(遍历)继承(寄生式继承) 2.构造函数...

    JavaScript权威指南(第6版)完整源码

    《JavaScript权威指南(第6版)》是JavaScript学习者必读的经典著作,由淘宝前端团队精心翻译,确保了中文版本的准确性和权威性。这本书全面深入地探讨了JavaScript语言的各个方面,包括其语法特性、对象模型、DOM...

    javaScript秘密花园

    尽管实现类继承模型在JavaScript上相对容易,但要在JavaScript中实现原型继承则复杂得多。 在JavaScript中,对象是数据类型,并且可以作为哈希表使用,保存命名的键与值的对应关系。对象可以通过对象字面量(使用{}...

    JavaScript宝典(第6版)-带书签

    《JavaScript宝典(第6版)》是一本深入探讨JavaScript编程语言的重要著作,由Danny Goodman和Michael Morrison共同撰写,并由张文波翻译成中文。这本书是JavaScript开发者和学习者的宝贵资源,涵盖了广泛的JavaScript...

    JavaScript_Garden_CN

    众多贡献者,包括Caio Romão(拼写校对)、Andreas Blixt(语言修正),以及中文翻译三生石上,共同使得这份文档得以完善并广泛传播。根据MIT许可协议,文档在GitHub上开放,鼓励社区成员参与修正和改进。 #### ...

    JavaScript The Definitive Guide 中文第四版

    中文第四版是针对这个备受赞誉的指南的最新中文翻译,旨在帮助中国读者深入理解和掌握JavaScript这门强大的脚本语言。 这本书详细介绍了JavaScript的核心概念和语法,包括数据类型、变量、操作符、语句、函数、对象...

    clean-code-javascript

    类是面向对象编程的核心概念之一,在JavaScript中,虽然它不是原生支持的,但可以通过构造函数和原型继承来实现。 ##### 1. 封装 确保类的内部状态被良好地封装,并通过公共方法暴露对外的行为。 ##### 2. 继承 ...

    讲解JavaScript的面向对象的编程

    本人一行注释一行代码翻译了该大师的艺术作品--目的说明它是在第1,2阶段文档演示的JavaScript面向对象的书写方式的进一步改进,它是现代JavaScript面向对象编程方式(使用基本类来编码)的过渡代码--没有它就没有当今...

Global site tag (gtag.js) - Google Analytics