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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索javascript
框架
javascript基础知识、javascript的基础知识、javascript基础知识点、java基础知识框架总结、逻辑学基础知识框架图,以便于您获取更多的相关知识。

时间: 2016-06-24

介绍一个简单的JavaScript类框架_基础知识的相关文章

介绍一个简单的JavaScript类框架

  这篇文章主要介绍了一个简单的JavaScript类框架,有助于初学者理解JS类的创建与继承,需要的朋友可以参考下 在写work-in-progress JavaScript book一书时,对于javascript继承体系,我花费了相当的时间,并在该过程中研究了各种不同的模拟经典类继承的方案.这些技术方案中,我最为推崇的是base2与Prototype的实现. 从这些方案中,应该能提炼出一个具有其思想内涵的框架,该框架须具有简单.可重用.易于理解并无依赖等特点,其中简单性与可用性是重点.以下

使用JavaScript制作一个简单的计数器的方法_基础知识

设计思想 该方法的关键是Cookie技术和动态图像特性的综合运用.使用Cookie,可以在用户端的硬盘上记录用户的数据,下次访问此站点时,即可读取用户端硬盘的Cookie,直接得知来访者的身份和访问次数等有关信息.JavaScript中通过document.cookie属性访问Cookie,这个属性包括名字.失效日期.有效域名.有效URL路径等.用等号分开的名字和其值是Cookie的实际数据,本例中用来存储该访问者访问该页面的次数.通过把Web页中的图像映射到一个Images数组,一定条件下修改

一个简单的javascript类定义例子_js面向对象

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

javascript第一课_基础知识

作为第一课,只是给大家一个简单的认识,东西不是太多.主要有以下三个方面: 1.在页面添加javascript 2.javascript的数据类型 3.javascript最基本的三个对话框 关于怎么添加javascript,我想大家都知道,就是 <script language="javascript"> //这行是javascript脚本标记,斜杠后面的就是注释了 document.write("在页面显示的javascript") //在页面显示一句

深入理解javascript变量声明_基础知识

相对于C/C++来说,ECMAScript里的for循环并不能创建一个局部的上下文. 复制代码 代码如下: for (var k in {a: 1, b: 2}) {   alert(k); } alert(k); // 尽管循环已经结束但变量k依然在当前作用域 任何时候,变量只能通过使用var关键字才能声明.   上面的赋值语句:   a = 10; 这仅仅是给全局对象创建了一个新属性(但它不是变量)."不是变量"并不是说它不能被改变,而是指它不符合ECMAScript规范中的变量概

javascript RegExp 使用说明_基础知识

一:正则表达式的创建方式   1.文字格式,使用方法如下:   /pattern/flags(即:/模式/标记)   2.RegExp构造函数,使用方法如下:   new RegExp("pattern"[,"flags"])(即:new RegExp("模式"[,"标记"]))      参数:   pattern(模式):表示正则表达式的文本   flags(标记):如果指定此项,flags可以是下面之一:      g:

怎样在JavaScript里写一个swing把数据插入数据库_基础知识

最终的目标是想这样的,在JavaScript里写一个swing来实现确定取消,来决定是否执行这个功能的,但是在执行的过程中,出现了一点问题,每次执行时,都是直接就会插入把数据插入数据库,不能控制了,想要知道应该怎样来解决这个问题,详情要参考下面的代码详情: 复制代码 代码如下: <% boolean foo=false; if (((theqingjiadays<3)&&(thetiqiandays>=1))||((theqingjiadays<10) &&

常用一些Javascript判断函数_基础知识

1.<DIV id=div1><h1>This is an DIV</h1></div> <button onclick=alert(div1.outerHTML)>outerHTML</button> <button onclick=alert(div1.innerHTML)>innerHTML</button> <button onclick=alert(div1.innerText)>inne

javascript 函数使用说明_基础知识

什么是函数(Function) function sum(a,b){       return a+b;  }  其实通俗的说就是一个有名称的代码段,方便重用. 要注意的是: 1.Javascript 的函数语法,因为Javascript本身就是区分大小写的,所以function不能写作Function或FUNCTION. 2.sum是函数的名称,这个并不是必须的,等以后我们会说到. 3.return是返回,如果不写的话,函数的返回是undefined.如果要返回多个值,可以返回个数组或者对象(