介绍一个简单的JavaScript类框架

在写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类构造与继承的人们来说,这套实现方案是一个好的开始。

(0)

相关推荐

  • javascript跟随滚动条滚动的层(浮动AD效果)

    其实这个效果在很多网站中都能见到,其主要表现为网页两侧的浮动广告.看起来感觉很难做,但其实原理是很简单的,使用定时器没0.1秒检测层的位置并将其置在指定的位置(相对于窗口).写了一个简单的代码: function scrollImg(){ var posX,posY; if (window.innerHeight) { posX = window.pageXOffset; posY = window.pageYOffset; } else if (document.documentElement

  • JavaScript实现网页上的浮动广告的简单方法

    漫游于网络之间,你会发觉,因特网不但是信息的海洋,也是广告的海洋.除了普通的Gif Banner.Flash外,浮动广告也是时下网上较为流行的广告形式之一.当你拖动浏览器的滚动条时,这种在页面上浮动的广告,可以跟随屏幕一起移动.尽管这种效果对于广告展示有相当的实用价值,但对浏览你网页的人来讲,这则是个既妨碍阅读,又影响阅读兴趣的东西,因此一定不能滥用.不过,如果你能善用的话,它就能发挥出极大的作用. 要做出浮动式广告的效果并不困难,如果你有JS基础的可以自己写一个,如果连写都懒得写的话,到网上下

  • JavaScript实现的浮动层框架用法实例分析

    本文实例讲述了JavaScript实现的浮动层框架用法.分享给大家供大家参考.具体如下: 这是一个JavaScript编写的浮动层框架,作为框架来说,骨干已经完成了.可以实现"类"."构造函数"."继承"(木有实现多继承)."静态方法"."重载"(木有实现多态),对我来说主要的意义就是,我喜欢这种代码风格,可以非常简单的重用和二次开发. 运行效果截图如下: 在线演示地址如下: http://demo.jb5

  • JavaScript实现的圆形浮动标签云效果实例

    本文实例讲述了JavaScript实现的圆形浮动标签云效果.分享给大家供大家参考.具体如下: 这里介绍的JS标签云效果,在鼠标的作用下会自动转动,整体上围绕成一个圆形,各个标签之间无需Div代码,直接文字+链接的形式,有多少就显示多少,JavaScript会自动调整显示数量,让视觉效果最佳. 运行效果如下图所示: 具体代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://

  • 如何制作浮动广告 JavaScript制作浮动广告代码

    如果有一定的JavaScript基础,制作浮动广告还是比较容易的.直接上代码了: 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"

  • javascript实现div浮动在网页最顶上并带关闭按钮效果实例

    复制代码 代码如下: <html> <head> <title>javascript实现div浮动在网页最顶上并带关闭按钮效果实例</title> <style type="text/css"> <!-- body { margin: 0px;padding: 0px;text-align: center;} TD {FONT-SIZE: 12px; COLOR: #333;} #toubiao {BORDER-BOTT

  • javascript 表格排序和表头浮动效果(扩展SortTable)

    一.SortTable说明 SortTable version 2 7th April 2007 Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ Instructions: Download this file Add <script src="sorttable.js"></script> to your HTML Add class="sortable"

  • JavaScript 浮动定位提示效果实现代码第1/2页

    这个效果本身难度不大,主要在程序结构和扩展中下了些功夫,务求用起来更方便,能用在更多的地方.程序特点 1,同一个提示框用在多个触发元素时,只需一个实例: 2,显示和隐藏分别有点击方式和触发方式选择: 3,能设置延时显示和隐藏: 4,有25种预设定位位置: 5,可在预设定位基础上,再自定义定位: 6,可设置自适应窗口定位: 程序说明 [Tip对象] Tip对象就是用来显示提示信息的容器,程序用Tip属性表示.这个没什么要求,程序初始化时会对它进行一些设置. 首先进行下面设置: 复制代码 代码如下:

  • JavaScript点击按钮后弹出透明浮动层的方法

    本文实例讲述了JavaScript点击按钮后弹出透明浮动层的方法.分享给大家供大家参考.具体分析如下: 这里实现点击后页面变灰色,并用JS弹出一个居中的浮动层提示窗口,这个窗口是透明的,可以设置透明度,网上已经有很多类似的JavaScript代码,你可以借鉴一下. <HTML> <HEAD> <TITLE>浮动层居中的对话框效果演示</TITLE> <META http-equiv=Content-Type content="text/htm

  • 介绍一个简单的JavaScript类框架

    在写work-in-progress JavaScript book一书时,对于javascript继承体系,我花费了相当的时间,并在该过程中研究了各种不同的模拟经典类继承的方案.这些技术方案中,我最为推崇的是base2与Prototype的实现. 从这些方案中,应该能提炼出一个具有其思想内涵的框架,该框架须具有简单.可重用.易于理解并无依赖等特点,其中简单性与可用性是重点.以下是使用示例: var Person = Class. extend ( { init: function (isDan

  • 一个简单的javascript类定义例子

    复制代码 代码如下: <script> //定义一个javascript类 function JsClass(privateParam/* */,publicParam){//构造函数 var priMember = privateParam; //私有变量 this.pubMember = publicParam; //公共变量 //定义私有方法 function priMethod(){ return "priMethod()"; } //定义特权方法 //特权方法可以

  • Lua教程(六):绑定一个简单的C++类

    本文是最后一篇C/C++与Lua交互的教程,在此之后,我们会结合Cocos2D-X来介绍Lua绑定.本文主要介绍如何绑定一个简单的C++类到Lua里面,并且提供Lua的面向对象访问方式. 绑定C++类 定义C++类 首先,我们定义一个Student类,它拥有名字(字符串类型)和年龄(整型),并且提供一些getter和setter,最后还提供了一个print方法.这里有Student类的定义和实现:Student.h和Student.cpp 编写绑定代码 首先,让我们编写在Lua里面创建Stude

  • 分享一个简单的java爬虫框架

    反复给网站编写不同的爬虫逻辑太麻烦了,自己实现了一个小框架 可以自定义的部分有: 请求方式(默认为Getuser-agent为谷歌浏览器的设置),可以通过实现RequestSet接口来自定义请求方式 储存方式(默认储存在f盘的html文件夹下),可以通过SaveUtil接口来自定义保存方式 需要保存的资源(默认为整个html页面) 筛选方式(默认所有url都符合要求),通过实现ResourseChooser接口来自定义需要保存的url和资源页面 实现的部分有: html页面的下载方式,通过Htt

  • Python实现一个简单的MySQL类

    本文实例讲述了Python实现一个简单的MySQL类.分享给大家供大家参考. 具体实现方法如下: 复制代码 代码如下: #!/usr/bin/env python # -*- coding:utf-8 -*- # Created on 2011-2-19 # @author: xiaoxiao import MySQLdb import sys __all__ = ['MySQL'] class MySQL(object):     '''     MySQL     '''     conn

  • 一个简单的JavaScript Map实例(分享)

    用js写了一个Map,带遍历功能,请大家点评下啦. //map.js Array.prototype.remove = function(s) { for (var i = 0; i < this.length; i++) { if (s == this[i]) this.splice(i, 1); } } /** * Simple Map * * * var m = new Map(); * m.put('key','value'); * ... * var s = ""; *

  • 一个简单的javascript图片放大效果代码

    一个简单的图片放大效果: 参数说明: 1.berviary:String,需要被放大的图片id 2.expand:object,放大区域,如果不设置此项或者此项的id未设置,则默认为光标跟随模式,会自动创建用于显示放大区域的div. 3.clip:object,裁剪区域的大小,即鼠标移动到需要放大的图片上时,突出显示需要被放的大区域 4.opacity:float,放大时图片被遮罩的区域的透明度 效果1: 调用的js代码: 复制代码 代码如下: $E({berviary:"currentPic&

  • 一个简单横向javascript日期控件

    具体要求就是: 1.日期表格横向占满页面. 2.每个月的日期列表横向显示在一排,而不是像网上的很多日期控件显示一个方块. 3.要求只有年月日可选,选择年或月后自动更新对应日期(这个每个日期控件都有的). 4.默认显示当前年月,高亮当前日期,并显示当前的周次(本年第几周)和星期几. 5.选择某个日期后高亮显示当前日期,并自动更新周次和星期显示. 6.提供接口,可以设定特定日期的显示样式. 6.其他就是一些界面展示的问题了. 想着就是一个日期控件,做着也比较简单,不过比较特别是横向的,第一次听说这种

  • 一个简单的JavaScript 日期计算算法

    复制代码 代码如下: <script type="text/javascript"> var today=new Date(); //定义当天日期对象 var year = today.getYear(); //获取年份 var month = today.getMonth(); //获取月份 var date = today.getDate(); //获取日期值 try{ //定义下个日期对象,日期值加上30天 var nextDay = new Date(year,mo

  • php编写一个简单的路由类

    类代码: 复制代码 代码如下: <?php class Router { public function getRouter($types = 1) { if ( isset($_SERVER['PATH_INFO']) ) { $query_string = substr(str_replace(array('.html','.htm', '.asp', '//'), '',$_SERVER['PATH_INFO']),1); } else { $query_string = str_repl

随机推荐