具有 John Resig 简单类模式的静态变量?



我指的是这篇文章。

在其中,他定义了一个看起来像这样的函数:

function makeClass() {
    return function _class() {
        if(this instanceof _class) {
            if(typeof this.init === 'function') {
                this.init.apply(this, arguments);
            }
        } else {
            throw new Error('Constructor called as a function');
        }
    };
}

然后你可以用这样的东西来使用它:

var MyClass = makeClass();
MyClass.prototype = {
    init: function(width, height) { ... },
    clear: function(ctx) {... },
    draw: function(ctx) { ... }
}

但是现在我想初始化一些应该在所有实例之间共享的静态变量。我该怎么做?

最简单的方法是将静态变量定义为原型属性:

MyClass.prototype.xxx: 3, // ...
var t1 = new MyClass();
console.log(t1.xxx); // 3

。但它的行为不像其他语言中的静态属性通常那样:

var t2 = new MyClass();
t2.xxx = 5;
console.log(t1.xxx); // still 3 :(

另一种方法是使用属性也可能附加到函数的事实:

MyClass.xxx = 3;

。但这缩小了我们使用此属性的方式(前面示例中的t1.xxx不能调用它)。

不过,还有另一种方式。可以将静态属性定义为变量,init方法的局部,可通过方法访问,定义...在这个初始化方法中也是如此。) 像这样。

   init: function() {
      var xxx = 3;
      MyClass.prototype.getXXX = function() {
         return xxx;
      };
      MyClass.prototype.setXXX = function(newXXX) {
         xxx = newXXX;
      }
   }

那么所有人都可以简单地使用此属性:

  var t1 = new MyClass();
  var t2 = new MyClass();
  console.log(t1.getXXX()); // 3
  console.log(t2.getXXX()); // 3
  t1.setXXX(5);
  console.log(t1.getXXX()); // 5 now
  console.log(t2.getXXX()); // 5 as well, behold the power of closures!

这是使用的小提琴。

更新:我想,当我们需要使用静态类数据的(某种)容器时,最好使用这种方法,该容器将由所有对象共享 - 但我们不知道确切的内容实际上可以存储在这个容器中。然后我们只使用两个函数 - getStaticsetStatic - 通过字符串键或其他一些标识符来存储和检索数据。这可能看起来有点令人困惑,确实如此,但我认为这可能值得付出努力。)

只需将其添加到MyClass本身即可。

MyClass.myVariable = 42;

它在 Java/C# 意义上并不是真正的静态,但会给你同样的效果。

我通过使用命名约定"解决"了这个问题。

我想要Class.extend({ })语法的便利性,但也想要一种在其中声明"静态"属性的方法。

我选择了一个前导下划线来声明静态属性,尽管你可以做任何你喜欢的事情。

用法:

var myClass = Class.extend({
    _staticProperty: 1337
    , instanceProperty: 'foo'
    , instanceMethod: function() { }
    , ctor: function() {
        this.base();
    }
});

注意 我已经重命名了init并从原始代码this._super()

和代码:

/* Simple JavaScript Inheritance
 * Modified by Andrew Bullock http://blog.muonlab.com to add static properties
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function () {
    var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /bbaseb/ : /.*/;
    // The base Class implementation (does nothing)
    this.Class = function () { };
    // Create a new Class that inherits from this class
    Class.extend = function (prop) {
        var base = this.prototype;
        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;
        // The dummy class constructor
        function Class() {
            // All construction is actually done in the ctor method
            if (!initializing && this.ctor)
                this.ctor.apply(this, arguments);
        }
        // Copy static properties from base
        for (var name in this) {
            if (name.substr(0, 1) == '_')
                Class[name] = this[name];
        }
        // Copy the properties over onto the new prototype
        for (name in prop) {
            // Check if we're overwriting an existing function
            if (typeof prop[name] == "function" && typeof base[name] == "function" && fnTest.test(prop[name])) {
                prototype[name] = (function(name, fn) {
                    return function() {
                        var tmp = this.base;
                        // Add a new .base() method that is the same method
                        // but on the super-class
                        this.base = base[name];
                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this.base = tmp;
                        return ret;
                    };
                })(name, prop[name]);
            } else if (name.substr(0, 1) == '_') {
                Class[name] = prop[name];
            } else {
                prototype[name] = prop[name];
            }
        }
        // Populate our constructed prototype object
        Class.prototype = prototype;
        // Enforce the constructor to be what we expect
        Class.prototype.constructor = Class;
        // And make this class extendable
        Class.extend = arguments.callee;
        return Class;
    };
})();

如果你不关心浏览器支持,你也可以使用构造函数/静态属性对的WeakMap。想法是这样的:http://jsfiddle.net/DfNNU/2/。这需要 MyClass.prototype.constructor ,您不应丢弃它。因此,您需要将constructor: MyClass添加回原型。

var statics = (function() {
    var map = new WeakMap;
    return function(inst) {
        var ctor = inst.constructor;
        return map.get(ctor) || map.set(ctor, {});
    };
})();

像这样使用它:

var a = function() {};
var b = function() {};
var inst1 = new a;
var inst2 = new a;
var inst3 = new b;
statics(inst1).foo = 123;
statics(inst3).foo = 456;
console.log( statics(inst1).foo );  // 123
console.log( statics(inst2).foo );  // 123
console.log( statics(inst3).foo );  // 456

我已经修改了 John Resig 的类,以将父级的静态成员复制到新类中,这添加了以下内容:

    for (var name in this) {
        if (!Class[name]) {
            Class[name] = this[name];
        }
    }

这是一个小提琴。

// This is a modified version of John Resig's simple inheritence class to add copying of static methods
// The new code is the for loop commented with "add in the static members"
/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /b_superb/ : /.*/;
 
  // The base Class implementation (does nothing)
  this.Class = function(){};
 
  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;
   
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
   
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
           
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
           
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
           
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
    
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
   
    // Populate our constructed prototype object
    Class.prototype = prototype;
   
    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;
    //add in the static members
    for (var name in this) {
    		if (!Class[name]) {
		        Class[name] = this[name];
        }
    }
    // And make this class extendable
    Class.extend = arguments.callee;
   
    return Class;
  };
})();
function addText(text) {
    document.getElementById('greetings').innerHTML = document.getElementById("greetings").innerHTML + '<br>' + text;
}
//parent class with a prototype method and two static methods
var Parent = Class.extend({
  hello: function () {
    addText('parent.hello');
  }
});  
Parent.static = function() {
  addText('Parent.static');
}
Parent.overrideStatic = function() {
  addText('Parent.overrideStatic');
}
//child class that overrides one of the parent's static methods
var Child = Parent.extend();
Child.overrideStatic = function() {
  addText('Child.overrideStatic');
}
var parent = new Parent();
parent.hello();
Parent.static();
var child = new Child();
child.hello();   //should output parent.hello
Child.static();  //should output Parent.static
Child.overrideStatic();
<div id="greetings"></div>

调用"extend"中传入静态成员的可选列表。此方法将静态属性(如果有)添加到构造函数上的"statics"属性。

代码更改

更改如下。这些行是在"虚拟类构造函数"代码之后添加的:

    if(staticProp) {
        Class.statics = [];
        for (var name in staticProp) {
            !Class.statics[name] && (Class.statics[name] = staticProp[name]);
        }
    }

声明类型时添加了一个附加参数"staticProp",以便允许在此阶段引入静态成员:

Class.extend = function(prop,staticProp) {

小提琴可以在这里找到,包括一些测试。

使用示例

可以在类型声明时定义静态,就像使用第二个可选构造函数参数一样:

var A = Class.extend({},{myStatic:1});

可以在实例方法中访问/定义静态:

var B = Class.extend({test:function(){B.statics.myStatic=2;}});

或者从实例外部:

A.statics.myStatic=3;

使用requirejs的示例:

将 Class.js 放在 baseUrl 文件夹中。示例新类定义。不强制要求将新类的文件命名为与"var C"(即 C.js)相同的名称,但为了可读性可能更好,因此类方法中对 C 名称的引用与对其静态成员的任何外部引用保持一致:

define(['Class'],function($) {
    var C = Class.extend({
        init: function(params){
            C.statics.myStatic++; // access static data
        }
    },{
        myStatic: 123
    });
    return C;
});

D.js 中的另一个类引用类 C 中的静态数据:

define(['Class', 'C'],function($,C) {
    var D = Class.extend({
        init: function(params){
            C.statics.myStatic++; // static data of another class
        }
    },{});
    return D;
});

最新更新