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

前面介紹了prototype.jsMootools.js是如何實現類,及其類的屬性和做用的。今天介紹的klass.js就是單純的實現面向對象的庫,只有90多行,也照例分析吧。javascript

實現類的步驟java

  • 第一步是使用klass新建類,初始化的固定函數是initialize,不能使用其它名稱
  • 子類使用ParentClassName.extend{// 子類方法}繼承
  • 子類中與父類的同名方法,若是須要在父類的同名方法上拓展,須要在子類的方法體中使用this.supr(args)
  • 若是須要在類的外面增長方法,可使用methods方法
// 使用 klass 建立類
    var Person = klass({
        // 初始函數固定爲 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)
        }
    });

    // 使用 methods 給類添加方法,子類能夠繼承該方法
    Person.methods({
        getAge:function(age){
            console.log("My age is " + age);
        }
    })

    // 子類直接經過 ParentClassName.extend 來繼承父類
    var Chinese = Person.extend({
        initialize:function(name, addr){
            // 使用 this.supr(args)來使用父類的同名函數
            this.supr(name);
            this.addr = addr;
        },
        getAddr:function(){
            console.log("My address is " + this.addr);
        }
    });

    // 子類直接經過 ParentClassName.extend 來繼承父類
    var Japanese = Person.extend({
        initialize:function(name){
            this.supr(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是如何實現類的方法,有幾個重要的問題須要搞清楚segmentfault

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

下面來經過klass.js來具體分析app

!function (name, context, definition) {
  if (typeof define == 'function') define(definition)
  else if (typeof module != 'undefined') module.exports = definition()
  else context[name] = definition()
}('klass', this, function () {
  var context = this
    , f = 'function'
      // 是否使用了 supr 來調用父類的同名函數
    , fnTest = /xyz/.test(function () {xyz}) ? /\bsupr\b/ : /.*/
    , proto = 'prototype'

    // o 爲建立類時,類的方法
  function klass(o) {
    return extend.call(isFn(o) ? o : function () {}, o, 1)
  }

  // 判斷對象是不是函數
  function isFn(o) {
    return typeof o === f
  }

  // 將父類的方法/屬性,附加到子類中
  function wrap(k, fn, supr) {
    return function () {
      var tmp = this.supr
        // 當前函數的 supr,就是父類的同名函數
      this.supr = supr[proto][k]
      var undef = {}.fabricatedUndefined
      var ret = undef
      try {
        // this 是當前類,fn 是父類的函數,fn 的上下文是綁定在當前類中,因此就等於父類的方法就繼承到子類中了
        ret = fn.apply(this, arguments)
      } finally {
        this.supr = tmp
      }
      return ret
    }
  }

  // 子類繼承父類的屬性,若是有同名函數,就使用 wrap 方法處理,若是沒有,就徹底繼承該屬性
  // what : child; o : parent; supr: parentClass
  function process(what, o, supr) {
    for (var k in o) {
      if (o.hasOwnProperty(k)) {
        what[k] = isFn(o[k])
          && isFn(supr[proto][k])
          && fnTest.test(o[k])
          ? wrap(k, o[k], supr) : o[k]
      }
    }
  }

  // 實現繼承的主函數, o建立類時的全部函數,fromSub 爲1,不明白爲何不把fromSub設置爲true/false
  function extend(o, fromSub) {
    // must redefine noop each time so it doesn't inherit from previous arbitrary classes
    function noop() {}
    noop[proto] = this[proto]
    var supr = this
      , prototype = new noop()
      , isFunction = isFn(o)
      , _constructor = isFunction ? o : this
      , _methods = isFunction ? {} : o
    function fn() {
      // 若是當前類設置類 initialize 函數,就把傳給當前類的參數傳遞給該函數
      if (this.initialize) this.initialize.apply(this, arguments)
      else {
        // 若是沒有設置 initialize ,傳入類的參數也能被其它函數使用
        fromSub || isFunction && supr.apply(this, arguments)
        _constructor.apply(this, arguments)
      }
    }

    // 使用 methods 添加方法到當前類,o 爲使用 methods 內的方法
    fn.methods = function (o) {
      process(prototype, o, supr)
      fn[proto] = prototype
      return this
    }

    // 指定 fn 的 constructor
    fn.methods.call(fn, _methods).prototype.constructor = fn

    // 使用 ParentClassName.extend 來實現繼承時候的
    fn.extend = arguments.callee
      // 使用 implement 來重寫父類的方法或者拓展父類的方法
      // o : 函數名 ; optFn : 函數
    fn[proto].implement = fn.statics = function (o, optFn) {
      o = typeof o == 'string' ? (function () {
        var obj = {}
        obj[o] = optFn
        return obj
      }()) : o
      // 使用 process 把 implement 中的函數添加到當前類中
      process(this, o, supr)
      return this
    }

    return fn
  }

  return klass
});

JS面向對象系列函數

相關文章
相關標籤/搜索