2023-05-12 开启多语言插件支持……

向ES6靠齐的Class.js

javascript 苏 demo 2431℃ 0评论

写在前面

在2008年的时候,John Resig写了一 Class.js ,使用的方式如下:

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;
  }
});

init为构造函数,通过this._super()访问 父类同名方法

这种看上去很酷很方便的继承方式,居然有一个致命的缺陷。那就是:

当父类A有一个方法a,子类B也有一个方法a的时候,仅仅只有子类B中的方法a才能访问父类A中的方法a,子类B中的其他方法从此就无法访问到父类A中的方法a。虽然这种场景很少,但是不完美啊不完美!!所以就有了今天向ES6看齐的Class.js。

ES6 class

先来看看ES6中的class继承:

Class之间可以通过extends关键字,实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。

class ColorPoint extends Point {}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。

class ColorPoint extends Point {

  constructor(x, y, color) {
    super(x, y); // 等同于parent.constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 等同于parent.toString()
  }

}

上面代码中,constructor方法和toString方法之中,都出现了super关键字,它指代父类的实例(即父类的this对象)。

上面来自ruanyifeng的es6入门: http://es6.ruanyifeng.com/#docs/class

Class.js

下面是向ES6靠齐的Class.js

//所有类的基类
var Class = function () { };
//基类增加一个extend方法
Class.extend = function (prop) {
  var _super = this.prototype;
  //父类的实例赋给变量prototype
  var prototype = new this();
  //把要扩展的属性复制到prototype变量上
  for (var name in prop) {
    //下面代码是让ctor里可以直接访问使用this._super访问父类构造函数,除了ctor的其他方法,this._super都是访问父类的实例
    prototype[name] = name == "ctor" && typeof prop[name] == "function" &&
      typeof _super[name] == "function" ?
      (function (name, fn) {
        return function () {
          //备份一下this._super
          var tmp = this._super;
          //替换成父类的同名ctor方法
          this._super = _super[name];
          //执行,此时fn中的this里面的this._super已经换成了_super[name],即父类的同名方法
          var ret = fn.apply(this, arguments);
          //把备份的还原回去
          this._super = tmp;
          return ret;
        };
      })(name, prop[name]) :
      prop[name];
  }
  //假的构造函数
  function Class() {
    //执行真正的ctor构造函数
    this.ctor.apply(this, arguments);
  }
  //继承父类的静态属性
  for (var key in this) {
    if (this.hasOwnProperty(key) && key != "extend")
      Class[key] = this[key];
  }
  // 子类的原型指向父类的实例
  Class.prototype = prototype;
  //这里一定要用new this
  //不能Class.prototype._super = prototype;(这里明显错误,prototype这时已经被copy进去了新的属性)
  //或者Class.prototype._super = _super;(这里会导致_super instanceof 不准确 )
  Class.prototype._super = new this();
  //覆盖父类的静态属性
  if (prop.statics) {
    for (var name in prop.statics) {
      if (prop.statics.hasOwnProperty(name)) {
        Class[name] = prop.statics[name];
        if (name == "ctor") {
          Class[name]();
        }
      }
    }
  }
  Class.prototype.constructor = Class;
  //原型可扩展
  Class.extendPrototype = function (prop) {
    for (var name in prop) {
      prototype[name] = prop[name];
    }
  };
  //任何Class.extend的返回对象都将具备extend方法
  Class.extend = arguments.callee;
  return Class;
};

不多说。看注释…

测试

var Animal = Class.extend({
  statics: {
    TestStaticsProperty: 1,
    TestStaticsProperty2: 2,
    TestStaticsMethod: function () {
      return 2;
    },
    TestStaticsMethod2: function () {
      return 33;
    }
  },
  ctor: function (age) {
    this.age = age;
    this.testProp = "animal";
  },
  eat: function () {
    return "nice";
  },
  dirnk: function () {
    return "good";
  }
})
var Pig = Animal.extend(
{
  statics: {
    TestStaticsProperty2: 3,
    TestStaticsMethod2: function () {
      return 3;
    }
  },
  ctor: function (age, name) {
    this._super(age);
    this.name = name;
  },
  climbTree: function () {
    return this._super.eat();
  },
  eat: function () {
    return "very nice"
  }
});
var BigPig = Pig.extend({
  ctor: function () {
    //测试通过this._super访问父类构造函数
    console.log(typeof this._super === "function");
  },
  getSuper: function () {
    return this._super;
  }
})
//测试静态属性
console.log(Animal.TestStaticsProperty === 1);//true
//测试静态方法
console.log(Animal.TestStaticsMethod() === 2);//true
//测试子类访问父类静态属性
console.log(Pig.TestStaticsProperty === 1);//true
//测试子类重写父类静态属性
console.log(Pig.TestStaticsProperty2 === 3);//true
//测试子类访问父类静态属性
console.log(Pig.TestStaticsMethod() === 2);//true
//测试子类重写父类静态属性
console.log(Pig.TestStaticsMethod2() === 3);//true
//测试父类静态方法未被覆盖
console.log(Animal.TestStaticsMethod2() === 33);//true
//测试父类静态属性未被覆盖
console.log(Animal.TestStaticsProperty2 === 2);//true
var animal = new Animal(11);
//测试构造函数
console.log(animal.age === 11);//true
console.log(animal.eat() === "nice");//true
var pig = new Pig(1, 11);//true
console.log(pig.testProp === "animal");//true
console.log(pig.climbTree() === "nice");//true
console.log(pig.eat() === "very nice");//true
console.log(pig instanceof Pig)//true
var bigPig = new BigPig();
//测试孙类访问祖先方法(原型链)
console.log(bigPig.dirnk() === "good");//true
//测试孙类访问祖先属性
console.log(bigPig.testProp === "animal");//true	 
console.log(bigPig instanceof BigPig);//true
//测试通过this._super访问父类
console.log(bigPig.getSuper() instanceof Pig);//true
console.log(bigPig instanceof Animal);//true

使用的ES6一样,除了构造函数ctor方法可以直接通过this. super方法parent.ctor,其余的方法都需要通过this. super.方法名(xxxx)去访问父类方法。

欢迎使用,玩得愉快。

打赏

转载请注明:苏demo的别样人生 » 向ES6靠齐的Class.js

   如果本篇文章对您有帮助,欢迎向博主进行赞助,赞助时请写上您的用户名。
支付宝直接捐助帐号oracle_lee@qq.com 感谢支持!
喜欢 (0)or分享 (0)