论坛首页 Web前端技术论坛

简单实现JavaScript继承

浏览 3699 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-09-01  

    看John Resig 的JavaScript实现继承的文章,对大神的实现代码拿来跟大家一起读下源码,因为英语太菜就不翻译了,直接根据源码来分析了。原文地址:http://ejohn.org/blog/simple-javascript-inheritance/

 

首先来看本继承所要达到的效果,知道效果来看源码更能容易理解。(先要知其然,然后知其所以然)。

 

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

 

 继承需要做到以下几点:

1.定义一个简单的结构,有一个初始化方法(生成对象时调用的函数,类似Java的构造方法)

2.子类的生成,必须要继承一个父类。

3.所有类的原型都是派生自Class.(就像Java类最终都派生自Object一样)

4.在子类中提供一种方法能访问到父类中被覆盖的方法。通过this._super().(如:在类Ninja的init方法中调用this._super()就是调用父类Person的init方法)

 

下面来看看代码是怎么实现继承的:

/* 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/ : /.*/;
      //fnTest是一正则表达式,匹配函数里是否有调_super方法。
  // The base Class implementation (does nothing)
  this.Class = function(){}; // 定义一个全局变量Class类/函数。
  
  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    /** 保存当前对象的原型(也就父类的原型),
     *  Class.extend()调用时this是Class,Person.extend调用时this是Person
     *  js里一切都是对象,Class函数也是对象,所以this这里是一个函数。
     */
    var _super = this.prototype;    
    
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    /**父类的实例作为子类的原型(典型的原型继承)
     * 但是这里实例化跟普通的生成对象不一样,这里不调用父类的init方法。
     * 类就是一模板,所以子类在以父类对象为原型的时候,不应调用初始化方法,仅仅是生成一个模板
     * 这边就是用initializing变量来标识实例父类是否是赋值给子类的原型。
     * 用闭包来隐藏了initializing 作为全局变量的污染
     */
    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
      /** 当子类新方法父类中有同名函数,而且子类中调用了父类方法(函数中有_super的调用)
       *  这里使用了代理模式,在调用函数前先将this._super用tmp保存起来(也可能没有_super方法)
       *  在将this._super 赋值为父类中同名函数。调用结束再将原来的this._super还原
       */
      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
      /**如果定义了初始化方法init,则调用init初始化
       * 注意:这里的this不是Class或者Person等类/函数对象
       * 而是实例化后的对象var p = new Person() 这里的this 是p
       */
      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
    //定义类的继承方法,保证每个新类也都有extend.
    //这里也可写成 Class.extend = this.extend;
    Class.extend = arguments.callee;
    
    return Class;
  };
})();

 看这些代码最重要的一点是时刻要清楚this是哪个对象。

 

好了,上面就是文章的全部了,希望对大家有帮助,有疑问请留言,有什么解释不对和不清楚的地方也望大家指正。

 

   发表时间:2012-09-04  

YUI的继承体系也不错,核心代码:

extend:function(subClass,superClass){
			var F = function(){};
			F.prototype = superClass.prototype;
			subClass.prototype = new F();			
			subClass.prototype.constructor = subClass;
			
			subClass.superclass = superClass.prototype;
			if(superClass.prototype.constructor == Object.prototype.constructor) {
				superClass.prototype.constructor = superClass;
			}
		}
 
0 请登录后投票
   发表时间:2012-09-04  
thc1987 写道

YUI的继承体系也不错,核心代码:

extend:function(subClass,superClass){
			var F = function(){};
			F.prototype = superClass.prototype;
			subClass.prototype = new F();			
			subClass.prototype.constructor = subClass;
			
			subClass.superclass = superClass.prototype;
			if(superClass.prototype.constructor == Object.prototype.constructor) {
				superClass.prototype.constructor = superClass;
			}
		}
 

yui的代码精炼啊
0 请登录后投票
   发表时间:2012-09-04  
thc1987 写道

YUI的继承体系也不错,核心代码:

 

extend:function(subClass,superClass){
			var F = function(){};
			F.prototype = superClass.prototype;
			subClass.prototype = new F();			
			subClass.prototype.constructor = subClass;
			
			subClass.superclass = superClass.prototype;
			if(superClass.prototype.constructor == Object.prototype.constructor) {
				superClass.prototype.constructor = superClass;
			}
		}
 

其实2者还是有点不一样的地方:

1. 写法

    YUI是 extend(subClass, superClass);

    这边是任何按照这个定义的类都有 var B = A.extend({...子类新加的函数和变量...});

2. 看你发的这块代码是 subClass继承了superClass,而仅仅是继承了prototype的东西,而subClass里没有继承非在prototype里的方法。

    这边是子类的B.prototype = new A()的,继承了所有的属性。

3. 最后一点差异也是这边代码的亮点,在子类函数中可以引用父类被覆盖的同名函数,只要this._super()即可。

 

这里只是列举出来差异,当然代码没有好与差之分,只有适合不适合,YUI确实写得短小精炼,但功能可能相对John Resig的强大,而且除掉注释代码也非常精炼。(我没看过YUI,只看了你发的这块,可能YUI不止这么少的代码,说的不对的地方还请谅解,欢迎交流,只有交流才能进步,呵呵)

3. 

0 请登录后投票
   发表时间:2012-09-04  

在子类中是可以调用父类的的方法的.下面有个列子,参考下吧:

<HTML>
 <HEAD>
  <TITLE> New Document </TITLE>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <SCRIPT LANGUAGE="JavaScript">
  <!--
  // ----------------工具类--------------------
  var Class = {
    
    extend:function(subClass,superClass){
        var F = function(){};
        F.prototype = superClass.prototype;
        subClass.prototype = new F();       
        subClass.prototype.constructor = subClass;
        
        subClass.superclass = superClass.prototype;
        if(superClass.prototype.constructor == Object.prototype.constructor){
            superClass.prototype.constructor = superClass;
        }
    }    
}
    // ------------------------------------


    // Demo
    // 声明父类
    var Person = function(param){
        this.name = param.name;
        this.age = param.age;
    }
    Person.prototype.sayName = function(){
        alert("My name is " + this.name);
    }
    Person.prototype.sayAge = function(){
        alert("My age is " + this.age);
    }
    Person.prototype.getAge = function(){
        return this.age;
    }


    // 声明子类
    var Man = function(param){
        // 调用父类的构造函数
       Man.superclass.constructor.call(this,param);
    }
    // 继承父类
    Class.extend(Man,Person);
    
    // 覆盖父类的sayAge方法
    Man.prototype.sayAge = function(){
        alert(this.name + "'s age is " + this.age);
    }
    // 覆盖父类的方法,并且调用父类原来的方法
    Man.prototype.getAge = function(){
        // 先调用父类方法返回年龄
        var age = Man.superclass.getAge.call(this);
        // 年龄+1
        alert(this.name + "'s age is " + (age + 1));
    }


    // 测试
    var man = new Man({name:"Jim",age:22});
    man.sayName(); // 这里调用父类的方法
    man.sayAge();  // 这里调用自己的方法
    man.getAge();
	// 是否属于Person类型
	alert(man instanceof Person)
    // 
  //-->
  </SCRIPT>
 </HEAD>
 <BODY>   
 </BODY>
</HTML>
 
0 请登录后投票
   发表时间:2012-09-04  
其实javascript的继承都是根据自身的特性来模仿出来的.
方法可能有多种,自己选择一种喜欢的就好了

<javascript设计模式>这本书里面就提到过2种继承方法.其中一种就是YUI的
0 请登录后投票
   发表时间:2012-09-04  
thc1987 写道
其实javascript的继承都是根据自身的特性来模仿出来的.
方法可能有多种,自己选择一种喜欢的就好了

<javascript设计模式>这本书里面就提到过2种继承方法.其中一种就是YUI的


可能我刚没表述清楚,呵呵。

var Person = function(param){  
        this.name = param.name;  
        this.age = param.age;  
    }
    Person.prototype.sayName = function(){  
        alert("My name is " + this.name);  
    }  
    Person.prototype.sayAge = function(){  
        alert("My age is " + this.age);  
    }  
    Person.prototype.getAge = function(){  
        return this.age;  
    }  
 

这些方法都是写在原型中的,如果方法不是定义在原型中这样就不能被继承了,刚说的是这个意思,可能之前说的不清楚,抱歉,呵呵。

    var Person = function(param){  
        this.name = param.name;  
        this.age = param.age;  
        this.sayName = function(){   //sayName不是定义在Person.prototype 中
            alert("My name is " + this.name);  
        }  
    }
    /*
    Person.prototype.sayName = function(){  
        alert("My name is " + this.name);  
    }  */
    

 

0 请登录后投票
   发表时间:2012-09-05  

 var Person = function(param){
        this.name = param.name;
        this.age = param.age;
		this.sayName = function(){
			alert("My name is " + this.name);
		}
    }
	/*
    Person.prototype.sayName = function(){
        alert("My name is " + this.name);
    }
	*/
    Person.prototype.sayAge = function(){
        alert("My age is " + this.age);
    }
    Person.prototype.getAge = function(){
        return this.age;
    }

 这样是可以的,试下就知道了.

 

 

  1. extend:function(subClass,superClass){  
  2.    var F = function(){};  
  3.    F.prototype = superClass.prototype;
       // 关键在这句,new F()就是吧父类的实例赋给子类的prototype
       // 包括非prototype申明的方法,这样子类就拥有了父类的prototype方法和非prototype方法.
       // 只不过一个是放在了作用域链上,一个是放在了原型链上.如果要执行一个方法的时候会试图
       // 在作用域上去寻找,如果找到了就执行该方法,不继续往下寻找了;如果找不到,再去原型链上去找
       // 直到找到为止. 貌似有点扯远了.. 
       // 关于作用域链和原型链可以参考<高性能javascript>一书
  4.    subClass.prototype = new F();             
  5.    subClass.prototype.constructor = subClass;  
  6.               
  7.    subClass.superclass = superClass.prototype;  
  8.    if(superClass.prototype.constructor == Object.prototype.constructor) {  
  9.          superClass.prototype.constructor = superClass;  
  10.     }  
  11. }  
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics