介绍一个简单的JavaScript类框架


Posted in Javascript onJune 24, 2015

 在写work-in-progress JavaScript book一书时,对于javascript继承体系,我花费了相当的时间,并在该过程中研究了各种不同的模拟经典类继承的方案。这些技术方案中,我最为推崇的是base2与Prototype的实现。

从这些方案中,应该能提炼出一个具有其思想内涵的框架,该框架须具有简单、可重用、易于理解并无依赖等特点,其中简单性与可用性是重点。以下是使用示例:
 

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(){
  // Call the inherited version of 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
// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class

 

有几点需要留意:

  •     构造函数须简单(通过init函数来实现),
  •     新定义的类比须继承于已有的类,
  •     所有的‘类'都继承于始祖类:Class,因此如果要创建一个全新的类,该类必须为Class的子类,
  •     最具挑战的一点:父类的被覆写方法必须能访问到(通过配置上下文环境)。
  •     在上面的示例中,你能发现通过this._super()来调用Person父类的init()和dance()方法。

对结果相当满意:使类的定义结构化,保持单一继承,并且能够调用超类方法。

简单的类创建与继承

下面为其实现(便于阅读并有注释),大概25行左右。欢迎并感谢提出建议。
 

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
( function ( ) {
 var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
 // The base Class implementation (does nothing)
 this.Class = function(){};
  
 // Create a new Class that inherits from this class
 Class.extend = function(prop) {
  var _super = this.prototype;
   
  // Instantiate a base class (but only create the instance,
  // don't run the init constructor)
  initializing = true;
  var prototype = new this();
  initializing = false;
   
  // Copy the properties over onto the new prototype
  for (var name in prop) {
   // Check if we're overwriting an existing function
   prototype[name] = typeof prop[name] == "function" &&
    typeof _super[name] == "function" && fnTest.test(prop[name]) ?
    (function(name, fn){
     return function() {
      var tmp = this._super;
       
      // Add a new ._super() method that is the same method
      // but on the super-class
      this._super = _super[name];
       
      // The method only need to be bound temporarily, so we
      // remove it when we're done executing
      var ret = fn.apply(this, arguments);    
      this._super = tmp;
       
      return ret;
     };
    })(name, prop[name]) :
    prop[name];
  }
   
  // The dummy class constructor
  function Class() {
   // All construction is actually done in the init method
   if ( !initializing && this.init )
    this.init.apply(this, arguments);
  }
   
  // Populate our constructed prototype object
  Class.prototype = prototype;
   
  // Enforce the constructor to be what we expect
  Class.prototype.constructor = Class;
  // And make this class extendable
  Class.extend = arguments.callee;
   
  return Class;
 };
})();

其中  “初始化(initializing/don't call init)”与“创建_super方法”最为棘手。接下来,我会对此做简要的介绍,使得大家对其实现机制能更好的理解。

初始化

    为了说明函数原型式的继承方式,首先来看传统的实现过程,即将子类的prototype属性指向父类的一个实例。如下所示:

 

function Person ( ) { }
function Ninja ( ) { }
Ninja. prototype = new Person ( );
// Allows for instanceof to work:
(new Ninja()) instanceof Person

然而,这里具有挑战性的一点,便是我们只想要得到‘是否实例(instatnceOf)'的效果,而不需要实例一个 Person并调用其构造函数所带来的后果。为防止这一点,在代码中设置一个bool参数initializing,只有在实例化父类并将其配置到子类的prototype属性时, 其值才为true。这样处理的目的是区分开真正的实例化与设计继承时这两种调用构造函数之间的区别,进而在真正实例化时调用init方法:
 

if ( !initializing )
 this.init.apply(this, arguments);

    值得特别注意的是,因为在init函数中可能会运行相当费资源的代码(如连接服务器,创建DOM元素等,谁也无法预测),所以做出区分是完全必要的。

超类方法(Super Method)

当使用继承时,最常见的需求便是子类能访问超类被覆写的方法。在该实现下,最终的方案便是提供一个临时方法(._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

实现这一功能需要几步处理。首先,我们使用extend来合并基本的Person实例(类实例,上面我们提到过其构造过程)与字面对象(Person.extend()的函数参数)。在合并过程中,做了简单的检查:首先检查将被合并的的属性是否为函数,如为函数,然后检查将被覆写的超类属性是否也为函数?如果这两个检查都为true,则需要为该属性准备_super方法。

注意,在这里创建了一个匿名闭包(返回的是函数对象)来封装增加的super方法。基于维护运行环境的需要,我们应该将旧的this._super(不管其是否存在)保存起来以备函数运行后重置,这有助于在有相同名称(不想偶然丢失对象指针)的情况下发生不可预知的问题。

然后,创建新的_super方法,该方法对象仅指向超类中被覆写的方法。谢天谢地,不用对_super做任何改动或变更作用域,因为函数的执行环境会随着函数调用对象自动变更(指针this会指向超类).

最后,调用字面量对象的方法,方法执行中可能会使用this._super(),方法执行后,将属性_super重置回其原来状态,之后return退出函数。

以上可以有许多种方案能达到相同的效果(我之前曾见过将super绑定到其自身,然后用arguments.callee访问),但是感觉还是这种方法最能能体现可用性与简洁性的特点。

在我已完成的多个基于javascript原型的工作中,只有这个类继承实现方案是我发表出来与大家分享的。我认为,简洁的代码(易于学习,易于继承,更少下载)更需要提出来让大家探讨,因此,对于学习javascript类构造与继承的人们来说,这套实现方案是一个好的开始。

Javascript 相关文章推荐
jQuery 工具函数学习资料
Apr 29 Javascript
asp.net下使用jquery 的ajax+WebService+json 实现无刷新取后台值的实现代码
Sep 19 Javascript
通过JS来判断页面控件是否获取焦点
Jan 03 Javascript
你可能不知道的JavaScript的new Function()方法
Apr 17 Javascript
JQuery判断radio是否选中并获取选中值的示例代码
Oct 17 Javascript
JavaScript生成随机字符串的方法
Mar 19 Javascript
基于jQuery实现点击弹出层实例代码
Jan 01 Javascript
JavaScript驾驭网页-获取网页元素
Mar 24 Javascript
基于JQuery及AJAX实现名人名言随机生成器
Feb 10 Javascript
js实现微信/QQ直接跳转到支付宝APP打开口令领红包功能
Jan 09 Javascript
node使用promise替代回调函数
May 07 Javascript
详解JS函数防抖
Jun 05 Javascript
jquery分割字符串的方法
Jun 24 #Javascript
jQuery根据元素值删除数组元素的方法
Jun 24 #Javascript
举例详解JavaScript中Promise的使用
Jun 24 #Javascript
jQuery对JSON数据进行排序输出的方法
Jun 24 #Javascript
jQuery中$.extend()用法实例
Jun 24 #Javascript
深入理解JavaScript编程中的同步与异步机制
Jun 24 #Javascript
详解JavaScript中的客户端消息框架设计原理
Jun 24 #Javascript
You might like
php上传文件并显示上传进度的方法
2015/03/24 PHP
浅析Yii2中GridView常见操作
2016/04/22 PHP
PHP类和对象相关系统函数与运算符小结
2016/09/28 PHP
php排序算法实例分析
2016/10/17 PHP
PHP实现的方程求解示例分析
2016/11/11 PHP
分析php://output和php://stdout的区别
2018/05/06 PHP
PHP 获取客户端 IP 地址的方法实例代码
2018/11/11 PHP
Laravel手动返回错误码示例
2019/10/22 PHP
SharePoint 客户端对象模型 (一) ECMA Script
2011/05/22 Javascript
jQuery实现等比例缩放大图片让大图片自适应页面布局
2013/10/16 Javascript
获取3个数组不重复的值的具体实现
2013/12/30 Javascript
javascript计时器事件使用详解
2014/01/07 Javascript
在JavaScript的正则表达式中使用exec()方法
2015/06/16 Javascript
理解JS绑定事件
2016/01/19 Javascript
什么是JavaScript注入攻击?
2016/09/14 Javascript
微信小程序中form 表单提交和取值实例详解
2017/04/20 Javascript
jQuery.validate.js表单验证插件的使用代码详解
2018/10/22 jQuery
[01:14]2014DOTA2展望TI 剑指西雅图newbee战队专访
2014/06/30 DOTA
Django视图之ORM数据库查询操作API的实例
2017/10/27 Python
Python爬虫中urllib库的进阶学习
2018/01/05 Python
Python比较2个时间大小的实现方法
2018/04/10 Python
python中with语句结合上下文管理器操作详解
2019/12/19 Python
双向RNN:bidirectional_dynamic_rnn()函数的使用详解
2020/01/20 Python
Python阶乘求和的代码详解
2020/02/14 Python
Windows下Anaconda和PyCharm的安装与使用详解
2020/04/23 Python
HTML5 语音搜索(淘宝店语音搜素)
2013/01/03 HTML / CSS
英国独特家具和家庭用品购物网站:Cuckooland
2020/08/30 全球购物
UNIX命令速查表
2012/03/10 面试题
J2EE系统只能是基于web
2015/09/08 面试题
英语专业个人求职自荐信
2013/09/21 职场文书
大学生志愿者感言
2014/01/15 职场文书
神农溪导游词
2015/02/11 职场文书
小学教师师德培训心得体会
2016/01/09 职场文书
2016中秋晚会开幕词
2016/03/03 职场文书
js之ajax文件上传
2021/05/13 Javascript
Golang 并发编程 SingleFlight模式
2022/04/26 Golang