【问题标题】:Caching this when using John Resig's javascript inheritance使用 John Resig 的 javascript 继承时缓存它
【发布时间】:2017-06-01 08:29:21
【问题描述】:

我在几个项目中使用John Resig's javascript inheritance pattern (better formatted version)。

使用这种模式,我可以像this.otherMethod()那样从另一个类内部调用一个类方法。

但是,我的类方法通常包含有自己的范围的代码,并定义了自己的 this,例如 ajax 成功处理程序或 $.each 等 jQuery 方法。

这样的事情很典型:

 var AppWidget = Class.extend({
    /**
     * Initialize and set any defaults
     */
    init: function(options) {

        var _this = this;
        var defaults = {
        };
        _this.options = $.extend(true, defaults, options);
        return _this;
    },

    /**
     * Do something involving ajax
     */
    someAjaxyFunction:function(){
        var _this = this;

        $.ajax({
            type: "POST",
            url: "page.php",
            data: dataString,
            success: function(data) {
                _this.someFollowupFunction(data)
            }
        }); 
    },

    /**
     * Do some followup work
     */
    someFollowupFunction:function(data){
        var _this = this;
        $.each(data,function () {
            _this.someOtherFunction(this);
        });
    },

    /**
     * Do some other work
     */
    someOtherFunction:function(thing){
        var _this = this;
         //...
    }
});

它会被称为:

var widget = new AppWidget();
widget.someAjaxyFunction();

这可行,但是,我想知道是否有更好的方法来处理要在方法中使用的缓存 this。在每个类方法的顶部添加var _this = this; 是可行的,但非常麻烦,我在编写新方法时经常忘记它。

是否有一些聪明的方法可以使_this 在所有类方法中自动可用,或者有什么更好的方法让方法引用除this 关键字之外的类?

【问题讨论】:

  • 绑定this 不是一个选项?您能否(如果您愿意)改变 Class.extend 的工作方式,或者您是否正在寻找实现的解决方法?
  • @Icepickle 当然,我愿意修改它的任何部分。
  • 我在想,理论上可以将函数扩展为“注入”一个 _this 变量(这需要序列化函数,然后添加带有参数声明的 new Function 和新的 _this var. 但是,我可以想到这种方法有很多缺点(-> 没有机会进行缩小,使用 strict 会引发错误)。另一个可能的选择是发送一个新的参数,这将是你的上下文(即总是第一个或最后一个),压缩时不容易出错,但也可能有一些缺点,这取决于你想要什么......
  • 第二种方法的一个例子可能是这样的:callback with calldirect callback(理论上你可以做一个类似的测试,比如_super测试并只在实际使用时注入_this)

标签: javascript jquery class inheritance


【解决方案1】:

正如您提到的,您可以修改 John 的 Class.extend 实现,理论上您可以将 _this 变量作为函数参数注入。

这当然很大程度上取决于您希望对当前运行的代码进行多少更改。

在我的示例中,我将_this 参数添加到函数参数列表中,作为所有函数的第一个参数。这将通过以下方式更改您的示例类(完整示例可以在下面的 sn-p 中找到):

var Example = Class.extend({
  method: function(_this, timeout) {
    // some code
  },
  callback: function(_this, arg1) {
    // some code
  }
});

var example = new Example();
example.method(5);

如您所见,_this 现在始终是第一个参数。但是,您调用类中方法的方式不会改变,您仍然可以调用example.method(15),它会使用预填充的_this 参数调用method(通过Class.extend 方法分配给参数列表)

这仍然允许您使用this._super() 实现,它甚至可以提供正确的回调(nl:之后派生类的回调,例如这里)

var Sample = Example.extend({
    method: function(_this, timeout) {
    this._super(timeout);
    console.log('I was called from sample');
  },
  callback: function(_this) {
    console.log('I am supplied from Sample');
  }
});

var sample = new Sample();
sample.method(10);

John 的代码所需的更改(最初来自 John's blog,在 Secrets of a JavaScript Ninja 内部进行了更深入的讨论),类似于以下内容:

/* 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_super\b/ : /.*/;
  // The base Class implementation (does nothing)
  this.Class = function(){};

  function convertArgumentsToArray( args ) {
    return Array.prototype.slice.apply( args );
  }

  // 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, [this].concat( convertArgumentsToArray( arguments ) ));        
            this._super = tmp;

            return ret;
          };
        })(name, prop[name]) :
        (function(fn) {
          return function() {
            var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
            return ret;
          }
        })(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;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();

主要变化是this 上下文自动注入到参数列表中。

仍然可以进行的优化是检查参数列表中是否定义了_this 变量,目前,如果您有任何参数,则应始终将_this 参数添加为第一个参数。

您可以在此处的代码 sn-p 或 jsfiddle here 中查看完整的工作示例

/* 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_super\b/ : /.*/;
  // The base Class implementation (does nothing)
  this.Class = function(){};

  function convertArgumentsToArray( args ) {
    return Array.prototype.slice.apply( args );
  }

  // 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, [this].concat( convertArgumentsToArray( arguments ) ));        
          this._super = tmp;

          return ret;
        };
      })(name, prop[name]) :
      (function(fn) {
        return function() {
          var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
          return ret;
        }
      })(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;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();


var Example = Class.extend({
  method: function(_this, timeout) {
    console.log('timeout defined is ' + timeout);
    setTimeout(function() {
      _this.callback( 15 );
    }, timeout);
  },
  callback: function(_this, arg1) {
    console.log('callback from _this argument, current context: ', this);
    console.log(_this === this);
    console.log(arg1 === 15);
  }
});

var Sample = Example.extend({
  method: function(_this, timeout) {
    this._super(timeout);
    console.log('I was called from sample');
  },
  callback: function(_this) {
    console.log('I am supplied from Sample');
  }
});

var example = new Example();
example.method(5);

var sample = new Sample();
sample.method(10);

【讨论】:

  • 让我想起了 python 以及我正在寻找的东西,谢谢。
猜你喜欢
  • 1970-01-01
  • 2013-02-09
  • 2012-06-19
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2010-09-27
  • 1970-01-01
  • 2016-10-17
相关资源
最近更新 更多