ES5 如何实现 Class

原文地址:https://github.com/catchonme/blog/issues/3

我们知道Class最大的作用就是能够实现继承,这样子类通过继承父类,从而尽可能的对代码进行复用。

那么问题来了

  • 如何实现子类继承父类的属性和函数,
  • 子类继承父类时,为什么子类的同名函数不会覆盖父类的同名函数

我们通过分析prototype.js 来看看ES5 是如何实现Class

prototype.js 书写Class 的方法

var Animal = Class.create({
  initialize: function(name) {
    this.name = name;
  }
})

var myAnimal = new Animal('jack'); 
console.log(myAnimal.name);// jack

var Cat = Class.create(Animal, {
  initialize: function($super, name, age) {
    $super(name);
    this.age = age;
  }
})

var myCat = new Cat('mia', 13); 
console.log(myCat.name); // mia
console.log(myCat.age); // 13
复制代码

创建类:通过Class.create创建Classinitialize 函数初始化Class 的属性

继承:子类在Class.create 的第一个参数传入父类名称,在函数中第一个参数传入$super ,函数内使用$super(args) 来调用父类的同名函数

然后我们来看prototype.js 内部实现Class ,以下是大致的结构,主体是三个函数,包含一个空函数

var Class = (function(){
  var subclass() {}; // 空函数
  function create() { // code... } 
  function addMethods() { // code... }  
  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  }  
})();
复制代码

我们挨个解析,create 函数做了什么呢?

function create() {
  var parent = null, properties = [].slice.call(arguments);

  // 传入的第一个参数是否是函数,如果是,就说明是当前类的父类
  if (isFunction(properties[0]))
    parent = properties.shift();

  // 新建 klass 函数,并执行initialize 函数
  function klass() {
    this.initialize.apply(this, arguments);
  }

  // extend 函数将 Class.Methods的属性赋给 klass
  extend(klass, Class.Methods);
  // 设定当前类为 parent
  klass.superclass = parent;
  klass.subclasses = [];

  // 如果当前类有父类,就需要把父类的属性和函数赋给当前类
  if (parent) {
    // 父类的 prototype 赋给 空函数 subclass.prototype
    subclass.prototype = parent.prototype;
    // 通过实例化 subclass 再赋给 klass.prototype,这样 klass 就能够拥有父类的属性和函数了
    klass.prototype = new subclass;
    parent.subclasses.push(klass)
  }

  // addMethods 为将所有传入的参数赋给 klass 的 prototype 中,该函数作用后面讲解
  for (var i=0, length=properties.length; i<length; i++) {
    klass.addMethods(properties[i]);
  }

  if (!klass.prototype.initialize) {
    klass.prototype.initialize = emptyFunction
  }

  klass.prototype.constructor = klass;
  return klass
}
复制代码
  • create 函数做了哪些事呢
    • 新建klass 函数,执行initialize 函数
    • 判断是否传入了父类,如果是,就将父类的属性和函数赋给当前类
    • 使用addMethods 方法,将用户的函数赋给klassprototype

继承父类的作用已经实现了,用户创建类时的方法需要通过addMethods 赋给当前类的prototype 中,接下来的问题就是,父类和子类的同名函数,该如何执行呢,父类的同名函数已经赋给子类的prototype 中了,子类增加同名函数,为什么不会覆盖掉呢?

我们看看addMethods 函数

function addMethods(source) {
  var ancestor = this.superclass && this.superclass.prototype,
      properties = Object.keys(source);

  // 遍历传递过来的函数,分别赋给当前的 prototype 中
  for (var i=0, length=properties.length; i<length; i++) {
    var property = properties[i], value = source[property];

    // 判断当前类中的函数,第一个参数否是是 $super
    if (ancestor && isFunction(value)
        && value.argumentNames()[0] == "$super") {

      var method = value;

      // 这里是为什么子类能够执行父类的同名函数的关键地方
      value = (function (m) {
        return function () {
          // 通过将函数名传递给父类,父类执行一次该同名函数,然
          return ancestor[m].apply(this, arguments);
        }
      })(property).wrap(method); // wrap(method) 又执行一次当前类的同名函数

      // 重写当前函数的 valueOf
      value.valueOf = (function (method) {
        return function () {
          return method.valueOf.call(method);
        }
      })(method);

      // 重写当前函数的 toString
      value.toString = (function (method) {
        return function () {
          return method.toString.call(method);
        }
      })(method);
    }

    // 将函数赋给当前类的 prototype
    this.prototype[property] = value;
  }
}
复制代码
  • addMethods 又做了哪些事呢?
    • 遍历传过来的函数
      • 如果该函数的第一个参数为$super ,就调用父类的同名函数执行一次,在调用当前类的同名函数执行一次,这样就能够实现使用$super 调用父类的函数了
      • 如果该函数的第一个参数不为$super ,就直接赋给当前类的prototype

上述函数中使用的其他工具函数,如isFunction, extend 等,因为我已经把prototype,js 中的Class 单独剥离出来了,通过点击 这里 查看,里面会有更详细的注释。

所以我们回到最初的问题

  • 如何继承父类的属性和函数?
    • 通过中间变量实例化父函数,然后赋给子函数的prototype 中,这样子函数就拥有父函数的属性和函数了
  • 子类为何不会覆盖掉父类的同名函数?
    • 子函数设定属性superclass 为父函数,在当前类中所有自定义的函数赋给当前类的prototype 时,会通过superclass 找到父类的同名函数,这样执行子类的同名函数时,即为执行父类的同名函数一次,子类的同名函数一次。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值