解析John Resig Simple JavaScript Inheritance代码


Posted in Javascript onDecember 03, 2012

由于作者翻译会加入 自己的理解 以便自己学习和使用, 如果英文好的同学可看下面   如文章中有翻译错误还请留言. 交流并改正. (:
======================Enein翻译=========================

        John Resig 写了一篇关于 JavaScript 里 类似其它语言的 "继承", 灵感来自于  base2 and PrototypeJS.  他为文章起名为"Simple JavaScript Inheritance" . 他使用的一些很巧妙的技术来实现 super 方法.
        你还可以看原文也会有详细的说明, 他也在他的 "Secrets of a JavaScript Ninja"里有所介绍. 在书中可能方法有一些不同, 它在Object中加入了subClass 方法, 而不是创建一个全局变量.
Original Script - John Resig Simple JavaScript Inheritance
下面是原谅代码, 我移除了一些注释使用它看起来更清晰.

(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
this.Class = function(){};
Class.extend = function(prop) {
var _super = this.prototype;
initializing = true;
var prototype = new this();
initializing = false;
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;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
function Class() {
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
Class.prototype = prototype;
Class.constructor = Class;
Class.extend = arguments.callee;
return Class;
};
})();

Breakdown of the Simple Inheritance script
下面我们来分析一下, 它是如何实现和有哪些技术被使用.

(function(){ // ... })();

首先我们创建一个自执行匿名函数, 为代码创建一个作用域.

      
var initializing = false

这 initializing 变量意思很直接, 它是boolean来检查Class Function(稍后介绍)什么时候被调用. 在创建实例时设置 initializing 为true/false 或者只是返回一个对象指向当前的原型链上来达到"继承"的目的.

如果我们创建一个实例(initializing == false), 正好Class有一个init方法, 这样 init 会自动执行。 再或者, 如果我们仅仅将它分配给原型上(initializing == true), 将不会发生什么, init 方法不会被执行。这样做是为了避免 每次调用构造方法都要执行 init 方法. (var prototype = new this());.

fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

          这个fnTest的目的就是为了验证 class method 中是否使用了 "_super()" 调用. 这种技术叫做 " function decompilation(函数反编译)" 也叫做 "function serialisation(函数序列化)", Function serialisation 是在一个函数被转换成字符串时发生的. 现在很多浏览器都支持 toString 方法。

测试 Function serialisation, fnTest 使用一个匿名函数 funciton(){xyz;} 设置内容为 "xyz", 在转变成字符串后使用正则对 "xyz" 进行查找. 它将返回true (如果浏览器支持 function serialisation) 因为 函数将转变成字符串所以 "xyz" 也民属于字符串的一部分. 在这个例子中 fnTest 将返回 "/\b_super\b/", 另一种则返回 "/.*/" 如果浏览器不支持 function serialisation 则始终返回 true。(这个指的是原始代码中的fnTest.test)使用 fnTest 正则, 和 函数序列化技术, 我们能很容易方法中是否使用了 "_super" 如果它们使用, 则执行一些特殊方法. 反之正常.  这个特殊方法是为了避免在 父类与子类中同时出现同一个方法. 父类将会被覆盖. 

        浏览器不支持 Function serialisation 将会始终返回 true, 那么会始终对 _super 进行额外的操作, 导致这些新的方法不能在 _super 中使用. 这会有一些小的性能消耗. 但能保证在所有浏览器中 正常执行.

this.Class = function(){};

        创建一个空的构造方法, 放到全局变量中. 这将会是最上层的构造方法. 它没有定义内容, 或一个原型对象. 除了下面的 extends 方法. this 指的是window对象. 使 Class 变量为全局对象.

      
Class.extend = function(prop) { // ...}

        加入 extends 方法和一个简单的 prop(一个对象) 参数. 它将返回 新构造方法的原型 + 父对象的原型; 

var _super = this.prototype;

        将当前对象的原型对象存储在 _super中. this.prototype是被扩展对象的原型, 它可以访问父级方法在你需要的地方,  这个变量叫什么 _super , 是因为 super 是保留字. 尽管现在还没有应用起来.

initializing = true;var prototype = new this();initializing = false;

        实例 class 对象存储在 prototype 变量中, 但不执行 init 方法. 之前设置 initializing 为 true 所以在 new Class的时候 不会 fire init 方法. prototype变量分配后, initializing 被设置回 false, 为了下一步可以正常工作. (e.g 当想要创建一个真正的实例的时候)

    
for (var name in prop) { // ...}

        使用一个 for 循环, 我们迭代出 prop 里的属性和方法. 该属性是通过 extends 方法传递进来的, 除了一些对 _super 的特殊处理, 我们将值赋给 prototype 属性.

    
prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() {  // special handling for _super }; })(name, prop[name]) : prop[name];

        当我们遍历 prop 里的每个对象时, 如果 满足 (typeof prop[name] == "function")  (typeof _super[name] == "function") (fnTest.test(prop[name]) == true)
我们将会加入新的方法来处理 绑定到 父类 新的方法 以及 原始方法.
        以上方式代码 看起来可能很有些 混乱 下面改使用 一种清晰的方式查看一下.

     
if (typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name])) { prototype[name] = (function(name, fn){ return function() {  // special handling for _super }; })(name, prop[name]);} else { // just copy the property prototype[name] = prop[name];}

        另一个自执行匿名函数, 在处理 super 中的 name prop[name] 被使用 . 没有这个闭包. 当返回这个function时 这个变量的引用将会出错.(e.g 它始终会返回 循环的最后一个)
        遍历所有, 我们将返回一个新的函数, 这个函数来处理 原生方法(via super) 和 新方法.

        
// special handling for supervar tmp = this._super;this._super = _super[name];var ret = fn.apply(this, arguments);this._super = tmp;return ret;

        对 super 的特殊处理, 我们首先要存储 已存在 _super 属性和类的一些参数. 存储在 临时 tmp 里, 这是为了防止 _super 中已存在的方法被重写
完事儿后我们将 tmp 在赋给 this._super 这样它就可以正常工作了.
         下一步, 我们将 _super[name] 方法赋给 当前对象的 this._super, 这样当 fn 通过 apply 被执行的时候 this._super()就会指向 父类方法, 这个
父类方法中的 this 也同样可以访问 当前对象.
         最后我们将返回值存储在 ret 中, 在将 _super 设置回来后返回该对象.
        下面有个简单的例子,  定义个简单的 Foo , 创建继承对象 Bar:

var Foo = Class.extend({ qux: function() { return "Foo.qux"; }});var Bar = Foo.extend({ qux: function() { return "Bar.qux, " + this._super(); }});

         当 Foo.extends 被执行, 在 qux 方法中由于存在 this._super 所以 Bar原型上的qux 实际上应该是这样的:

       
Bar.prototype.qux = function () { var tmp = this._super; this._super = Foo.prototype.qux; var ret = (function() { return "Bar.qux, " + this._super(); }).apply(this, arguments); this._super = tmp; return ret;}

        在脚本中完成这步后, 构造方法将被调用

function Class() { if ( !initializing && this.init ) this.init.apply(this, arguments);}

        这段代码调用 Class 创建一个新的构造方法, 这不同于之前创建的 this.Class, 作为本地的 Class.extend. 这个构造方法返回 Class.extend 的调用(比如之前 Foo.extends).  new Foo() 实例后这个构造方法将被执行.
        构造方法将会自动执行 init() 方法(如果存在的话) 正好上面说的那样, 这个 initializing 变量来控制 init 是否被执行.

       
Class.prototype = prototype;

        最后这个 prototype,  从父类的构造方法返回一个混合后的 父类原型对象. (e.g var prototype = new this()), 这个结果是通过 extend 函数里的for循环.

Class.constructor = Class;
        因为我们重写了整个原型对象, 在这个类型中存储这个 原生的构造方法,  让它在一个实例的构造方法中能保持默认形为.

        
Class.extend = arguments.callee;

        将赋其自身, 通过  arguments.callee, 在本例中表示 “自身” 其实这里我们可以 避免使用 arguments.callee , 如果我们修改一下我的原生方法(e.g Class.extend = function extend(prop)) 之后我们就可以通过 使用

        
Class.extend = extend;.return Class;

        实例之后会返回, 一个原型对象, 一个构造属性, 一个 extend 方法 和一个可自执行的 方法 init.!!!

Javascript 相关文章推荐
JavaScript Event学习第十章 一些可替换的事件对
Feb 10 Javascript
jquery三个关闭弹出层的小示例
Nov 05 Javascript
利用jquery动画特效和css打造的侧边弹出垂直导航
Apr 04 Javascript
js中this的用法实例分析
Jan 10 Javascript
JavaScript中逗号运算符介绍及使用示例
Mar 13 Javascript
JS实现选择TextArea内文本的方法
Aug 03 Javascript
JS简单实现城市二级联动选择插件的方法
Aug 19 Javascript
跟我学习javascript的Date对象
Nov 19 Javascript
AngularJs ng-change事件/指令的用法小结
Nov 01 Javascript
vuex实现的简单购物车功能示例
Feb 13 Javascript
js 获取扫码枪输入数据的方法
Jun 10 Javascript
基于vue+element实现全局loading过程详解
Jul 10 Javascript
cookie在javascript中的使用技巧以及隐私在服务器端的设置
Dec 03 #Javascript
js函数调用常用方法详解
Dec 03 #Javascript
JS随即打乱数组实现代码
Dec 03 #Javascript
JS图片预加载 JS实现图片预加载应用
Dec 03 #Javascript
输入密码检测大写是否锁定js实现代码
Dec 03 #Javascript
js操作textarea 常用方法总结
Dec 03 #Javascript
javascript object array方法使用详解
Dec 03 #Javascript
You might like
PHP 危险函数解释 分析
2009/04/22 PHP
PHP自定义大小验证码的方法详解
2013/06/07 PHP
PHP版本如何选择?应该使用哪个版本?
2015/05/13 PHP
wordpress之js库集合研究介绍
2007/08/17 Javascript
jQuery+ajax实现鼠标单击修改内容的方法
2014/06/27 Javascript
谈谈基于iframe、FormData、FileReader三种无刷新上传文件的方法
2015/12/03 Javascript
jQuery实现横向带缓冲的水平运动效果(附demo源码下载)
2016/01/29 Javascript
node.js实现端口转发
2016/04/14 Javascript
JS获取及验证开始结束日期的方法
2016/08/20 Javascript
巧用canvas
2017/01/21 Javascript
微信小程序 参数传递实例代码
2017/03/20 Javascript
Vue中如何实现轮播图的示例代码
2017/07/27 Javascript
用node开发并发布一个cli工具的方法步骤
2019/01/03 Javascript
vue component 中引入less文件报错 Module build failed
2019/04/17 Javascript
Openlayers实现扩散的动态点(水纹效果)
2020/08/17 Javascript
Python中for循环详解
2014/01/17 Python
Python实现动态添加类的属性或成员函数的解决方法
2014/07/16 Python
Python深入学习之上下文管理器
2014/08/31 Python
在Python的Django框架中显示对象子集的方法
2015/07/21 Python
Django 生成登陆验证码代码分享
2017/12/12 Python
用Python3创建httpServer的简单方法
2018/06/04 Python
对python3标准库httpclient的使用详解
2018/12/18 Python
解决pycharm工程启动卡住没反应的问题
2019/01/19 Python
Python单例模式的四种创建方式实例解析
2020/03/04 Python
基于Python和C++实现删除链表的节点
2020/07/06 Python
python复合条件下的字典排序
2020/12/18 Python
HTML5 video标签(播放器)学习笔记(二):播放控制
2015/04/24 HTML / CSS
分享29个基于Bootstrap的HTML5响应式网页设计模板
2015/11/19 HTML / CSS
工商管理专业实习大学生自我鉴定
2013/09/19 职场文书
个性大学生自我评价
2013/12/04 职场文书
出国留学介绍信
2014/01/13 职场文书
食品工程专业求职信
2014/06/15 职场文书
个人专业技术总结
2015/03/05 职场文书
董事会决议范本
2015/07/01 职场文书
导游词之上海杜莎夫人蜡像馆
2019/11/22 职场文书
SpringBoot+Vue+JWT的前后端分离登录认证详细步骤
2021/09/25 Java/Android