介绍一个简单的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 相关文章推荐
浅析js中2个等号与3个等号的区别
Aug 06 Javascript
JS如何将数字类型转化为没3个一个逗号的金钱格式
Jan 27 Javascript
jQuery的css() 方法使用指南
May 03 Javascript
解读ES6中class关键字
Nov 20 Javascript
vue-cli配置环境变量的方法
Jul 09 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变rgba的a值实现)
Jan 24 Javascript
Vue中JS动画与Velocity.js的结合使用
Feb 13 Javascript
Node.js如何对SQLite的async/await封装详解
Feb 14 Javascript
VUE脚手架的下载和配置步骤详解
Apr 01 Javascript
electron-vue利用webpack打包实现多页面的入口文件问题
May 12 Javascript
判断JavaScript中的两个变量是否相等的操作符
Dec 21 Javascript
如何基于JS截获动态代码
Dec 25 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写UltraEdit插件脚本实现方法
2011/12/26 PHP
ueditor 1.2.6 使用方法说明
2013/07/24 PHP
PHP截断标题且兼容utf8和gb2312编码
2013/09/22 PHP
php中get_object_vars()方法用法实例
2015/02/08 PHP
php 实现301重定向跳转实例代码
2016/07/18 PHP
ThinkPHP中session函数详解
2016/09/14 PHP
php array_map使用自定义的函数处理数组中的每个值
2016/10/26 PHP
删除PHP数组中头部、尾部、任意元素的实现代码
2017/04/10 PHP
YII框架实现自定义第三方扩展操作示例
2019/04/26 PHP
TNC vs RR BO3 第一场 2.14
2021/03/10 DOTA
JSON 教程 json入门学习笔记
2020/09/22 Javascript
通过jquery的$.getJSON做一个跨域ajax请求试验
2011/05/03 Javascript
createElement与createDocumentFragment的点点区别小结
2011/12/19 Javascript
js选取多个或单个元素的实现代码(用class)
2012/08/22 Javascript
js Date概念详细介绍
2013/11/22 Javascript
javascript数组操作(创建、元素删除、数组的拷贝)
2014/04/07 Javascript
js实现3D图片逐张轮播幻灯片特效代码分享
2015/09/09 Javascript
AngularJS入门教程之多视图切换用法示例
2016/11/02 Javascript
深入理解JavaScript中的预解析
2017/01/04 Javascript
js中的数组对象排序分析
2018/12/11 Javascript
Vue项目实现简单的权限控制管理功能
2019/07/17 Javascript
国内常用的js类库大全(CDN公共库)
2020/06/24 Javascript
jQuery冲突问题解决方法
2021/01/19 jQuery
利用Python如何生成随机密码
2016/04/20 Python
Python注释详解
2016/06/01 Python
Python标准库06之子进程 (subprocess包) 详解
2016/12/07 Python
python 多线程重启方法
2019/02/18 Python
Flask框架重定向,错误显示,Responses响应及Sessions会话操作示例
2019/08/01 Python
python 基于opencv实现图像增强
2020/12/23 Python
高中生第一学年自我鉴定
2014/09/12 职场文书
党员自我评议对照检查材料
2014/09/27 职场文书
2014年客户经理工作总结
2014/11/20 职场文书
2015初中团支部工作总结
2015/07/21 职场文书
音乐研修感悟
2015/11/18 职场文书
Nginx已编译的nginx-添加新模块
2021/04/01 Servers
一篇文章带你掌握SQLite3基本用法
2022/06/14 数据库