prototype.js 是如何實現JS的類以及類的相關屬性和做用

實現類的步驟javascript

  • 第一步是使用Class.create新建類,初始化的固定函數是initialize,不能使用其它名稱
  • 子類也是使用Class.create新建,父類放在第一個參數中,如var Cat = Class.create(Animal,{});
  • 子類中與父類的同名方法,若是須要在父類的同名方法上拓展,在須要在第一個參數中使用$super,而後在方法體內使用$super(args);
  • 若是須要在類的外面增長方法,可使用addMethods方法
// 使用 Class.create 建立類
    var Person = Class.create({
        // 初始函數固定爲 initialize, 若是不設置,會默認建立一個空函數給 initialize
        initialize:function(name) {
            this.name = name;
            this.friends = ['jack', 'mark'];
        },
        getName: function(){
            console.log("My name is " + this.name);
        },
        setFriends:function(friend){
            this.friends.push(friend);
        },
        getFriends:function(){
            console.log(this.friends)
        }
    });

    // 使用 addMethods 給在類的初始構建以外添加方法,子類能夠繼承該方法
    Person.addMethods({
        getAge:function(age){
            console.log("My age is " + age);
        }
    })

    // 子類經過 Class.create 建立類,第一個參數爲父類的名字
    var Chinese = Class.create(Person,{
        // 使用 $super 爲第一個參數,表示當前函數在父類的同名函數上拓展
        initialize:function($super, name, addr){
            $super(name);
            this.addr = addr;
        },
        getAddr:function(){
            console.log("My address is " + this.addr);
        }
    });

    var Japanese = Class.create(Person, {
        initialize:function($super, name){
            $super(name);
        }
    })

    // 實例化類
    var men = new Chinese('allen', 'BeiJing');
    men.getName(); // My name is allen
    men.getAge(23); // My age is 23
    men.getAddr(); // My address is BeiJing

    // 如下驗證 - 子類繼承父類的屬性,修改了以後,其餘子類再次繼承父類,父類的屬性的值爲什麼不會改變
    var allen = new Person();
    allen.getFriends(); // ["jack", "mark"]

    var women = new Japanese();
    women.setFriends("lisa");
    women.getFriends(); // ["jack", "mark", "lisa"]

    var men = new Chinese();
    men.setFriends('peter');
    men.getFriends(); //["jack", "mark", "peter"]

    var wallen = new Person();
    wallen.getFriends(); //["jack", "mark"]

JS是如何實現類的方法,有幾個重要的問題須要搞清楚java

  • JS是如何建立類的
  • 子類是如何實現繼承父類屬性和方法的
  • 子類繼承父類的屬性,修改了以後,其餘子類再次繼承父類,父類的屬性的值爲什麼不會改變
  • 子類和父類的同名函數,在同名函數中使用$super,是如何作到在子類中共存的
  • 如何實現,不在類中,而是使用addMethods往類中添加方法的

下面來經過prototype.jsclass來具體分析web

var Class = (function() {

  function subclass() {};
  function create() {
    // ... 
  }

  function addMethods(source) {
    // ...
  }

  // 暴露給外部的接口
  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();

內部實現其實很簡單,Class是一個當即執行函數,裏面只有三個函數,並且subclass仍是個空函數segmentfault

/* Based on Alex Arnell's inheritance implementation. */

/**
 *  Refer to Prototype's web site for a [tutorial on classes and
 *  inheritance](http://prototypejs.org/learn/class-inheritance).
**/
var Class = (function() {

  function subclass() {};
  function create() {
    // $A 函數就是把參數轉化成數組
    var parent = null, properties = $A(arguments);
    // 若是第一個參數是函數,就把他看成父類
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      // klass 是新建的類,把傳入的參數都綁定到 klass 的 initialize 方法中
      this.initialize.apply(this, arguments);
    }

    // 把經過 extend 方法,把 Class.Methods 的方法添加到 klass 中
    Object.extend(klass, Class.Methods);
    // 這裏有指定 klass 的父類是哪個
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      // 這裏經過把父類的原型方法,都繼承到當前類中
      // 經過中間變量 subclass 來傳遞 prototype 來防止因爲子類的修改而致使父類的屬性或者方法也被修改
      subclass.prototype = parent.prototype;
      // 每次子類都會建立一個新的中間變量來傳遞,因此不管子類怎麼修改屬性,都不會影響到父類
      klass.prototype = new subclass;
      // 把當前類添加到父類的子類中
      parent.subclasses.push(klass);
    }

    for (var i = 0, length = properties.length; i < length; i++)
      // 前面把 addMethods 方法添加到 klass 中,這裏就可使用 addMethods 把傳入參數中的方法,添加到 klass 中了
      klass.addMethods(properties[i]);

    // 若是 klass 沒有初始化函數,就設置一個空函數
    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    // 把 klass 的構造函數指向自身
    klass.prototype.constructor = klass;
    return klass;
  }

  
  // source 是全部要添加進來方法的集合
  function addMethods(source) {
    var ancestor   = this.superclass && this.superclass.prototype,
        properties = Object.keys(source);

    for (var i = 0, length = properties.length; i < length; i++) {
      // value 就是單個的方法
      var property = properties[i], value = source[property];
      // 若是參數中的第一個參數是 $super,就須要把父類的同名方法,傳遞進來
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames()[0] == "$super") {
        // 把最初的 value 使用 method 存起來
        var method = value;
        // 繼承父類的同名方法,而後把當前參數傳進去
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method);
          // wrap 是把父類的同名方法,添加當前類的同名方法中

        // We used to use `bind` to ensure that `toString` and `valueOf`
        // methods were called in the proper context, but now that we're
        // relying on native bind and/or an existing polyfill, we can't rely
        // on the nuanced behavior of whatever `bind` implementation is on
        // the page.
        //
        // MDC's polyfill, for instance, doesn't like binding functions that
        // haven't got a `prototype` property defined.
        // 將 valueOf 的方法綁定到 method 中
        value.valueOf = (function(method) {
          return function() { return method.valueOf.call(method); };
        })(method);

          // 將 toString 的方法綁定到 method 中
        value.toString = (function(method) {
          return function() { return method.toString.call(method); };
        })(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }

  // 暴露給外部的接口
  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();

上面使用到的wrap函數,摘抄在下面數組

function wrap(wrapper) {
    var __method = this;
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

// 這裏就是把 args 中的屬性,都添加到 array 中
function update(array, args) {
    var arrayLength = array.length, length = args.length;
    while (length--) array[arrayLength + length] = args[length];
    return array;
  }

JS面向對象系列app

相關文章
相關標籤/搜索