【问题标题】:Emulate super in javascript在javascript中模拟超级
【发布时间】:2011-12-23 08:54:54
【问题描述】:

基本上有一个很好的优雅机制来模拟super,其语法与以下之一一样简单

  • this.$super.prop()
  • this.$super.prop.apply(this, arguments);

坚持的标准是:

  1. this.$super 必须是对原型的引用。即,如果我在运行时更改超级原型,则将反映此更改。这基本上意味着父级有一个新属性,那么这应该在运行时通过super 显示在所有子级上,就像对父级的硬编码引用会反映更改一样
  2. this.$super.f.apply(this, arguments); 必须适用于递归调用。对于在继承链上进行多次超级调用的任何链式继承集,您不能遇到递归问题。
  3. 您不能在您的孩子中硬编码对超级对象的引用。 IE。 Base.prototype.f.apply(this, arguments); 说错了。
  4. 不得使用 X to JavaScript 编译器或 JavaScript 预处理器。
  5. 必须符合 ES5

天真的实现会是这样的。

var injectSuper = function (parent, child) {
  child.prototype.$super = parent.prototype;
};

但是这个breaks condition 2

迄今为止我见过的最优雅的机制是IvoWetzel's eval hack,它几乎是一个 JavaScript 预处理器,因此不符合标准 4。

【问题讨论】:

  • 只是好奇,但是有什么理由为什么您需要在 JavaScript 中模拟 super?考虑到原型链的工作方式,我觉得它完全没有必要。
  • @zzzzBov super 只是用于代码重用的构造。这是我在 ES5 的 OO 糖中唯一缺少的东西。 ES6 带来了super,我很期待
  • 您幼稚的实现也违反了标准 1。
  • injectSuper(P, C); x = new C(); C.prototype = {}; alert(x.$super.constructor === P); y = new C(); alert(y.$super === undefined); 两者都为真。
  • @trinithis 当然可以。条件一只需反映对P 的更改。如果您覆盖C.prototype,那么它会中断。这是预期的行为,编写这样的代码很愚蠢

标签: javascript oop super


【解决方案1】:

对于那些不了解 OP 提出的递归问题的人,这里有一个例子:

function A () {}
A.prototype.foo = function (n) {
    return n;
};

function B () {}
B.prototype = new A();
B.prototype.constructor = B;
B.prototype.$super = A.prototype;
B.prototype.foo = function (n) {
    if (n > 100) return -1;
    return this.$super.foo.call(this, n+1);
};

function C () {}
C.prototype = new B();
C.prototype.constructor = C;
C.prototype.$super = B.prototype;
C.prototype.foo = function (n) {
    return this.$super.foo.call(this, n+2);
};


alert(new C().foo(0)); // alerts -1, not 3

原因:Javascript中的this是动态绑定的。

【讨论】:

  • 这个B.prototype = new A();对我来说很糟糕。
  • @ZenMaster:你还会做什么?这就是你在 JS 中进行原型继承的方式。当然,我会有某种包装功能的功能,但概念是一样的。也就是说,我刚刚意识到我的 $super 是以打破条件 1 的方式实现的。我现在想不出解决办法。
  • @trinithis 正确的是B.prototype = Object.create(A.prototype)
  • 这只是一个问题,如果在示例中,您期望 B.prototype.foo 中的 this 引用 的实例B,而它引用 C 的一个实例。 Function.prototype.bind 在 ES5 中不能使用静态绑定吗?或者你可以使用闭包。
  • 闭包不能解决问题。绑定不能解决问题。它与 this 始终包含对子类方法的引用有关。
【解决方案2】:

John Resig 发布了一个简单但出色的 super 支持的不连贯机制。 唯一的区别是super 指向你调用它的基方法。

看看http://ejohn.org/blog/simple-javascript-inheritance/

【讨论】:

  • 天哪,这太丑了。在你调用方法之前和之后,他会覆盖函数并更改_super 的值。
  • 异常问题的适当修复:try { var ret = fn.apply(this, arguments); } catch (e) { this._super = tmp; throw e; } this._super = tmp;
  • Resig 代码的另一个问题。假设您有类PC,其中C 继承自P。还假设PC 都在它们的原型上定义了自己的foobar 方法。最后,假设P.foo 调用barC.foo 调用_super。调用 new C().foo() 时,会调用 C.bar,而不是 P.bar。除非你真的很聪明,否则这是一个错误。我什至没有尝试过父类方法和子类方法之间的相互递归。我敢打赌这更糟糕,因为我敢打赌它完全破坏了他的_super 机制。
  • @trinithis:JR 的代码肯定不是完美的。但是对于大约 25 行代码,模拟一个适合 90% 需求的良好继承范式就足够了。对于您描述的情况,需要打补丁。
  • 嗯,我觉得应该是 30%,不过还好。
【解决方案3】:

看看Classy 库;它使用this.$super提供类和继承以及对覆盖方法的访问

【讨论】:

  • 呃!这会用新函数覆盖方法,这些函数在前后将this.$super 的值设置为“正确值”。它还吞下错误:(
【解决方案4】:

我认为你提到的“递归超级”问题没有“免费”的方法。

我们不能乱用this,因为这样做要么会迫使我们以非标准方式更改原型,要么将我们移到原型链上,从而丢失实例变量。因此,当我们进行 super-ing 时,必须知道“当前类”和“超类”,而不是将责任传递给 this 或其属性之一。

我们可以尝试做很多事情,但我认为都会有一些不受欢迎的后果:

  • 在创建时向函数添加超级信息,使用 arguments.calee 或类似的邪恶来访问它。
  • 调用超级方法时添加额外信息

    $super(CurrentClass).method.call(this, 1,2,3)
    

    这迫使我们复制当前的类名(这样我们就可以在一些超级字典中查找它的超类),但至少它没有复制超类名那么糟糕,(因为如果耦合继承关系,如果比与类自己的名称的内部耦合更糟糕)

    //Normal Javascript needs the superclass name
    SuperClass.prototype.method.call(this, 1,2,3);
    

    虽然这远非理想,但至少有一些来自2.x Python 的历史先例。 (他们为 3.0 “修复”了 super,因此它不再需要参数,但我不确定这涉及到多少魔法以及它对 JS 的可移植性)


编辑:工作fiddle

var superPairs = [];
// An association list of baseClass -> parentClass

var injectSuper = function (parent, child) {
    superPairs.push({
        parent: parent,
        child: child
    });
};

function $super(baseClass, obj){
    for(var i=0; i < superPairs.length; i++){
        var p = superPairs[i];
        if(p.child === baseClass){
            return p.parent;
        }
    }
}

【讨论】:

  • 哦,我喜欢与类名而不是超类名耦合。
  • 如果你对超类有一个闭包,你可以避免使用 CurrentClass。在这两种情况下,您最终都必须知道超类,为什么不呢?
【解决方案5】:

请注意,对于以下实现,当您在通过 $super 调用的方法中时,在父类中工作时访问属性永远不会解析为子类的方法或变量,除非您访问的成员是直接存储在对象本身上(而不是附加到原型上)。这避免了一系列混乱(解读为细微的错误)。

更新: 这是一个没有__proto__ 的实现。问题是使用$super 与父对象的属性数量成线性关系。

function extend (Child, prototype, /*optional*/Parent) {
    if (!Parent) {
        Parent = Object;
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    for (var x in prototype) {
        if (prototype.hasOwnProperty(x)) {
            Child.prototype[x] = prototype[x];
        }
    }
    Child.prototype.$super = function (propName) {
        var prop = Parent.prototype[propName];
        if (typeof prop !== "function") {
            return prop;
        }
        var self = this;
        return function () {
            var selfPrototype = self.constructor.prototype;
            var pp = Parent.prototype;
            for (var x in pp) {
                self[x] = pp[x];
            }
            try {
                return prop.apply(self, arguments);
            }
            finally {
                for (var x in selfPrototype) {
                    self[x] = selfPrototype[x];
                }
            }
        };
    };
}

以下实现适用于支持__proto__ 属性的浏览器:

function extend (Child, prototype, /*optional*/Parent) {
    if (!Parent) {
        Parent = Object;
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    for (var x in prototype) {
        if (prototype.hasOwnProperty(x)) {
            Child.prototype[x] = prototype[x];
        }
    }
    Child.prototype.$super = function (propName) {
        var prop = Parent.prototype[propName];
        if (typeof prop !== "function") {
            return prop;
        }
        var self = this;
        return function (/*arg1, arg2, ...*/) {
            var selfProto = self.__proto__;
            self.__proto__ = Parent.prototype;
            try {
                return prop.apply(self, arguments);
            }
            finally {
                self.__proto__ = selfProto;
            }
        };
    };
}

例子:

function A () {}
extend(A, {
    foo: function () {
        return "A1";
    }
});

function B () {}
extend(B, {
    foo: function () {
        return this.$super("foo")() + "_B1";
    }
}, A);

function C () {}
extend(C, {
    foo: function () {
        return this.$super("foo")() + "_C1";
    }
}, B);


var c = new C();
var res1 = c.foo();
B.prototype.foo = function () {
    return this.$super("foo")() + "_B2";
};
var res2 = c.foo();

alert(res1 + "\n" + res2);

【讨论】:

  • .__proto__ 是非标准的。恐怕我不能使用这个解决方案。
  • 如果我愿意,我会尝试获得更多想法并发布它们。
  • 您使用相同的覆盖函数并在 john resig 使用的技巧之前和之后更改 super 的值。除了稍微有点混乱
  • @Raynos:Resig 的代码不允许您从超类调用任意函数。还要考虑以下几点:在父类P 和子类C 中定义foobarP.foo 致电 bar。现在假设C.foo 使用超级机制调用foo。我还没有测试过,但我相信 Resig 的代码会有P.foo 调用C.bar。这是冒险的国际海事组织。我不会写测试用例,但如果有人写,请告诉我结果。
  • 好的,我测试过了。 Resig 的代码有我推测的缺陷,我认为这是非常不可取的。
【解决方案6】:

super 的主要困难在于你需要找到我称之为here 的对象:包含生成超级引用的方法的对象。这对于获得正确的语义是绝对必要的。显然,拥有here 的原型也一样好,但这并没有太大的区别。以下是静态解决方案:

// Simulated static super references (as proposed by Allen Wirfs-Brock)
// http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super

//------------------ Library

function addSuperReferencesTo(obj) {
    Object.getOwnPropertyNames(obj).forEach(function(key) {
        var value = obj[key];
        if (typeof value === "function" && value.name === "me") {
            value.super = Object.getPrototypeOf(obj);
        }
    });
}

function copyOwnFrom(target, source) {
    Object.getOwnPropertyNames(source).forEach(function(propName) {
        Object.defineProperty(target, propName,
            Object.getOwnPropertyDescriptor(source, propName));
    });
    return target;
};

function extends(subC, superC) {
    var subProto = Object.create(superC.prototype);
    // At the very least, we keep the "constructor" property
    // At most, we preserve additions that have already been made
    copyOwnFrom(subProto, subC.prototype);
    addSuperReferencesTo(subProto);
    subC.prototype = subProto;
};

//------------------ Example

function A(name) {
    this.name = name;
}
A.prototype.method = function () {
    return "A:"+this.name;
}

function B(name) {
    A.call(this, name);
}
// A named function expression allows a function to refer to itself
B.prototype.method = function me() {
    return "B"+me.super.method.call(this);
}
extends(B, A);

var b = new B("hello");
console.log(b.method()); // BA:hello

【讨论】:

  • 您的静态解决方案是一个合理的示例,但由于您将 "me" 硬编码到其中,因此尚不清楚您是如何概括它的。我还认为super(B).methodme.super.method 更好。
  • 另外,如果 B 隐藏了 A 的方法并且 A.prototype.method 引用了该方法,应该调用 A 的方法还是 B 的方法?哪一个是正确的?
  • @Raynos:“我”只存在于函数内部,因此它也适用于多种方法。试试看:(function me() { console.log(me) }()); console.log(me);
  • @Raynos:至于应该使用 A 的方法还是 B 的方法——这取决于。 OO 编程中的规范是 B 的方法(因为它会覆盖)。如果你想做一个“横向引用”,你可能应该在闭包中使用一个函数。
【解决方案7】:

JsFiddle:

这有什么问题?

'use strict';

function Class() {}
Class.extend = function (constructor, definition) {
    var key, hasOwn = {}.hasOwnProperty, proto = this.prototype, temp, Extended;

    if (typeof constructor !== 'function') {
        temp = constructor;
        constructor = definition || function () {};
        definition = temp;
    }
    definition = definition || {};

    Extended = constructor;
    Extended.prototype = new this();

    for (key in definition) {
        if (hasOwn.call(definition, key)) {
            Extended.prototype[key] = definition[key];
        }
    }

    Extended.prototype.constructor = Extended;

    for (key in this) {
        if (hasOwn.call(this, key)) {
            Extended[key] = this[key];
        }
    }

    Extended.$super = proto;
    return Extended;
};

用法:

var A = Class.extend(function A () {}, {
    foo: function (n) { return n;}
});
var B = A.extend(function B () {}, {
    foo: function (n) {
        if (n > 100) return -1;
        return B.$super.foo.call(this, n+1);
    }
});
var C = B.extend(function C () {}, {
    foo: function (n) {
        return C.$super.foo.call(this, n+2);
    }
});

var c = new C();
document.write(c.foo(0) + '<br>'); //3
A.prototype.foo = function(n) { return -n; };
document.write(c.foo(0)); //-3

使用特权方法而不是公共方法的示例。

var A2 = Class.extend(function A2 () {
    this.foo = function (n) {
        return n;
    };
});
var B2 = A2.extend(function B2 () {
    B2.$super.constructor();
    this.foo = function (n) {
        if (n > 100) return -1;
        return B2.$super.foo.call(this, n+1);
    };
});
var C2 = B2.extend(function C2 () {
    C2.$super.constructor();
    this.foo = function (n) {
        return C2.$super.foo.call(this, n+2);
    };
});

//you must remember to constructor chain
//if you don't then C2.$super.foo === A2.prototype.foo

var c = new C2();
document.write(c.foo(0) + '<br>'); //3

【讨论】:

    【解决方案8】:

    我想我有一个更简单的方法....

    function Father(){
      this.word = "I'm the Father";
    
      this.say = function(){
         return this.word; // I'm the Father;
      }
    }
    
    function Sun(){
      Father.call(this); // Extend the Father
    
      this.word = "I'm the sun"; // Override I'm the Father;
    
      this.say = function(){ // Override I'm the Father;
        this.word = "I was changed"; // Change the word;
        return new Father().say.apply(this); // Call the super.say()
      }
    }
    
    var a = new Father();
    var b = new Sun();
    
    a.say() // I'm the father
    b.ay() // I'm the sun
    b.say() // I was changed
    

    【讨论】:

    • -1:这个答案不是很有用。它根本不使用原型设计。特别是。 new Father().say.apply(this) 的解决方法很糟糕。
    • 您所说的精神是正确的。您的实施远未接近。理解这是一个非常人为的例子,正确的实现在this jsbin
    【解决方案9】:

    本着完整性的精神(也感谢大家对这个帖子的关注,它是一个很好的参考点!)我想在这个实现中折腾。

    如果我们承认没有好的方法可以满足上述所有标准,那么我认为这是 Salsify 团队的一项英勇努力(我刚刚找到)found here。这是我见过的唯一实现避免递归问题,但也让.super 成为对正确原型的引用,而无需预编译。

    因此,我们打破标准 1,而不是打破标准 5。

    该技术取决于使用Function.caller(不符合 es5,尽管它在浏览器中得到广泛支持,并且 es6 消除了未来的需求),但它为所有其他问题提供了非常优雅的解决方案(我认为)。 .caller 让我们获得方法引用,它让我们定位我们在原型链中的位置,并使用 getter 返回正确的原型。它并不完美,但与我在这个领域看到的解决方案大不相同

    var Base = function() {};
    
    Base.extend = function(props) {
      var parent = this, Subclass = function(){ parent.apply(this, arguments) };
    
        Subclass.prototype = Object.create(parent.prototype);
    
        for(var k in props) {
            if( props.hasOwnProperty(k) ){
                Subclass.prototype[k] = props[k]
                if(typeof props[k] === 'function')
                    Subclass.prototype[k]._name = k
            }
        }
    
        for(var k in parent) 
            if( parent.hasOwnProperty(k)) Subclass[k] = parent[k]        
    
        Subclass.prototype.constructor = Subclass
        return Subclass;
    };
    
    Object.defineProperty(Base.prototype, "super", {
      get: function get() {
        var impl = get.caller,
            name = impl._name,
            foundImpl = this[name] === impl,
            proto = this;
    
        while (proto = Object.getPrototypeOf(proto)) {
          if (!proto[name]) break;
          else if (proto[name] === impl) foundImpl = true;
          else if (foundImpl)            return proto;
        }
    
        if (!foundImpl) throw "`super` may not be called outside a method implementation";
      }
    });
    
    var Parent = Base.extend({
      greet: function(x) {
        return x + " 2";
      }
    })
    
    var Child = Parent.extend({
      greet: function(x) {
        return this.super.greet.call(this, x + " 1" );
      }
    });
    
    var c = new Child
    c.greet('start ') // => 'start 1 2'
    

    您还可以调整它以返回正确的方法(如在原始帖子中),或者您可以通过将名称传递给超级函数(而不是使用 getter)来消除使用名称注释每个方法的需要

    这是一个演示该技术的工作小提琴:jsfiddle

    【讨论】:

      【解决方案10】:

      我想出了一种方法,允许您通过更改执行上下文来使用伪关键字 Super(我还没有看到这里介绍的方法。)我发现我不满意的缺点根本就是它不能将“超级”变量添加到方法的执行上下文中,而是将其替换为整个执行上下文,这意味着使用该方法定义的任何私有方法都变得不可用......

      此方法与提供的“eval hack”OP 非常相似,但它不对函数的源字符串进行任何处理,只是在当前执行上下文中使用 eval 重新声明函数。由于这两种方法都有上述相同的缺点,所以让它变得更好一点。

      很简单的方法:

      function extend(child, parent){
      
          var superify = function(/* Super */){
              // Make MakeClass scope unavailable.
              var child = undefined,
                  parent = undefined,
                  superify = null,
                  parentSuper = undefined,
                  oldProto = undefined,
                  keys = undefined,
                  i = undefined,
                  len = undefined;
      
              // Make Super available to returned func.
              var Super = arguments[0];
              return function(/* func */){
                  /* This redefines the function with the current execution context.
                   * Meaning that when the returned function is called it will have all of the current scopes variables available to it, which right here is just "Super"
                   * This has the unfortunate side effect of ripping the old execution context away from the method meaning that no private methods that may have been defined in the original scope are available to it.
                   */
                  return eval("("+ arguments[0] +")");
              };
          };
      
          var parentSuper = superify(parent.prototype);
      
          var oldProto = child.prototype;
          var keys = Object.getOwnPropertyNames(oldProto);
          child.prototype = Object.create(parent.prototype);
          Object.defineProperty(child.prototype, "constructor", {enumerable: false, value: child});
      
          for(var i = 0, len = keys.length; i<len; i++)
              if("function" === typeof oldProto[keys[i]])
                  child.prototype[keys[i]] = parentSuper(oldProto[keys[i]]);
      }
      

      制作类的例子

      function P(){}
      P.prototype.logSomething = function(){console.log("Bro.");};
      
      function C(){}
      C.prototype.logSomething = function(){console.log("Cool story"); Super.logSomething.call(this);}
      
      extend(C, P);
      
      var test = new C();
      test.logSomething(); // "Cool story" "Bro."
      

      前面提到的缺点的一个例子。

      (function(){
          function privateMethod(){console.log("In a private method");}
      
          function P(){};
      
          window.C = function C(){};
          C.prototype.privilagedMethod = function(){
              // This throws an error because when we call extend on this class this function gets redefined in a new scope where privateMethod is not available.
              privateMethod();
          }
      
          extend(C, P);
      })()
      
      var test = new C();
      test.privilagedMethod(); // throws error
      

      另请注意,此方法不是“超级”子构造函数,这意味着 Super 对它不可用。我只是想解释这个概念,而不是制作一个工作库:)

      另外,刚刚意识到我满足了所有 OP 的条件! (虽然确实应该有关于执行上下文的条件)

      【讨论】:

        【解决方案11】:

        这是我的版本:lowclass

        这是来自 test.js 文件的super 意大利面条汤示例(编辑:变成运行示例):

        var SomeClass = Class((public, protected, private) => ({
        
            // default access is public, like C++ structs
            publicMethod() {
                console.log('base class publicMethod')
                protected(this).protectedMethod()
            },
        
            checkPrivateProp() {
                console.assert( private(this).lorem === 'foo' )
            },
        
            protected: {
                protectedMethod() {
                    console.log('base class protectedMethod:', private(this).lorem)
                    private(this).lorem = 'foo'
                },
            },
        
            private: {
                lorem: 'blah',
            },
        }))
        
        var SubClass = SomeClass.subclass((public, protected, private, _super) => ({
        
            publicMethod() {
                _super(this).publicMethod()
                console.log('extended a public method')
                private(this).lorem = 'baaaaz'
                this.checkPrivateProp()
            },
        
            checkPrivateProp() {
                _super(this).checkPrivateProp()
                console.assert( private(this).lorem === 'baaaaz' )
            },
        
            protected: {
        
                protectedMethod() {
                    _super(this).protectedMethod()
                    console.log('extended a protected method')
                },
        
            },
        
            private: {
                lorem: 'bar',
            },
        }))
        
        var GrandChildClass = SubClass.subclass((public, protected, private, _super) => ({
        
            test() {
                private(this).begin()
            },
        
            reallyBegin() {
                protected(this).reallyReallyBegin()
            },
        
            protected: {
                reallyReallyBegin() {
                    _super(public(this)).publicMethod()
                },
            },
        
            private: {
                begin() {
                    public(this).reallyBegin()
                },
            },
        }))
        
        var o = new GrandChildClass
        o.test()
        
        console.assert( typeof o.test === 'function' )
        console.assert( o.reallyReallyBegin === undefined )
        console.assert( o.begin === undefined )
        <script> var module = { exports: {} } </script>
        <script src="https://unpkg.com/lowclass@3.1.0/index.js"></script>
        <script> var Class = module.exports // get the export </script>

        尝试无效的成员访问或无效使用_super 将引发错误。

        关于要求:

        1. this.$super 必须是对原型的引用。即,如果我在运行时更改超级原型,则将反映此更改。这基本上意味着它的父级有一个新属性,那么这应该在运行时通过 super 显示在所有子级上,就像对父级的硬编码引用会反映变化一样

          不,_super 助手不返回原型,只返回一个带有复制描述符的对象,以避免修改受保护和私有原型。此外,从中复制描述符的原型保存在Class/subclass 调用的范围内。有这个就好了。 FWIW,本机 classes 的行为相同。

        2. this.$super.f.apply(this, arguments);必须适用于递归调用。对于任何在继承链上进行多个超级调用的链式继承集,都不能遇到递归问题。

          是的,没问题。

        3. 您不能在您的孩子中硬编码对超级对象的引用。 IE。 Base.prototype.f.apply(this, arguments);失败了。

          是的

        4. 您不得使用 X to JavaScript 编译器或 JavaScript 预处理器。

          是的,所有运行时

        5. 必须符合 ES5

          是的,它包括一个基于 Babel 的构建步骤(例如,lowclass 使用 Wea​​kMap,它被编译为非泄漏的 ES5 形式)。我不认为这违反了要求 4,它只是允许我编写 ES6+,但它仍然应该在 ES5 中工作。诚然,我没有在 ES5 中对此进行太多测试,但是如果您想尝试一下,我们绝对可以解决我的任何构建问题,从您的角度来看,您应该能够在没有任何构建步骤的情况下使用它.

        唯一未满足的要求是 1。这很好。但也许换掉原型是不好的做法。但实际上,我确实有一些用途,我想换掉原型以实现元内容。 '如果在本机 super(它是静态的 :( )中拥有此功能,那就太好了,更不用说在此实现中了。

        为了仔细检查要求 2,我将基本递归测试添加到我的 test.js 中,该测试有效(编辑:制成运行示例):

        const A = Class((public, protected, private) => ({
            foo: function (n) { return n }
        }))
        
        const B = A.subclass((public, protected, private, _super) => ({
            foo: function (n) {
                if (n > 100) return -1;
                return _super(this).foo(n+1);
            }
        }))
        
        const C = B.subclass((public, protected, private, _super) => ({
            foo: function (n) {
                return _super(this).foo(n+2);
            }
        }))
        
        var c = new C();
        console.log( c.foo(0) === 3 )
        <script> var module = { exports: {} } </script>
        <script src="https://unpkg.com/lowclass@3.1.0/index.js"></script>
        <script> var Class = module.exports // get the export </script>

        (对于这些小类来说,类头有点长。我有几个想法可以减少如果不是预先需要所有帮助器的话)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-03-19
          • 1970-01-01
          • 1970-01-01
          • 2018-10-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多