【问题标题】:Scope of "this" when defining functions inside sub-objects在子对象内定义函数时“this”的范围
【发布时间】:2014-02-25 14:33:11
【问题描述】:

我正在使用关于我在另一个类中评估的页脚的自定义选项扩展主干视图。

看起来像:

var EditUserView = Backbone.View.extend({
    footer: {
        name: "Hello",
        label: function() {
            //Return "Create" if it's a new model without id, or else "Save"
            return this.model.id ? "Save" : "Create"; 
        }
    }
});

如您所见,属性应该能够定义为返回字符串或普通字符串值的函数。 我使用 _.result 在 FooterView 中评估这些选项:

initialize: function(options) {
    //"options" is the footer-object from the view.
    this.data = {
        name: _.result(options, "name"),
        label: _.result(options, "label")
    };
}

但问题是我无法在上面定义的标签函数中访问 EditUserView 的 this。我也无法定义var that = this,因为我在扩展对象时没有放置局部变量的位置。 如何使我在 footer-object 中定义的函数具有 UserEditView 的 this-scope?

我也可以:

footer: {
    name: "Hello",
    label: this.getName
},

getName: function() {
    return this.model.id? "Save":"Create";
}

如果其他方式不可行或者这种方式更容易做到。

【问题讨论】:

  • 你可以使用 .bind(this) 吗?
  • 如果您在我的示例中告诉我在哪里调用它,我认为没有理由不这样做。

标签: javascript backbone.js scope this


【解决方案1】:

一般来说,函数内部的this 仅取决于函数的调用方式(当然,除非你有绑定函数)。鉴于此:

var V = Backbone.View.extend({
    m: function() { console.log(this) }
});
var v = new V;

然后这些做不同的事情:

var f = v.m; f();
v.m();

它们调用相同的函数,但在第一种情况下this 将是全局对象,在第二种情况下它将是v;区别不在于函数本身,区别在于调用方式。

如果我们查看_.result,我们可以看到它是如何调用函数的:

_.result = function(object, property) {
  if (object == null) return void 0;
  var value = object[property];
  return _.isFunction(value) ? value.call(object) : value;
};

注意里面的call,意思是说_.result(obj, 'm')是,如果mobj的函数属性,同说:

obj.m()

将其应用于您的:

_.result(options, "label")

我们看到您实际上是在说:

options.label()

label 函数中的this 将是options

我在上面提到了绑定函数。官方创建绑定函数的方式是使用Function.prototype.bind

bind() 方法创建一个新函数,在调用该函数时,将其 this 关键字设置为提供的值,在新函数被调用时,在任何提供的参数之前都有一个给定的参数序列调用。

这意味着您可以使用bind 来指定函数内部的this 是什么,而不管函数是如何调用的。还可以使用_.bind_.bindAll$.proxy等各种方法在函数上模拟原生bind方法。

在您视图的initialize 中,您可以将footer 中的函数绑定到相应的this。但请注意,您必须克隆整个 footer 以避免意外通过原型共享内容:

initialize: function() {
    var footer = {
        name: this.footer.name,
        label: this.footer.label.bind(this)
    };
    this.footer = footer;
}

使用_.clone_.isFunction 和迭代器的任意组合以显而易见的方式概括,让您开心。

这样做的缺点是,您的视图的每个实例都有自己独特的 footer 副本,如果您有很多实例或 footer 很大,这可能会很浪费。如果这是一个问题,那么您可以编写自己的 _.result 版本,类似这样(未经测试的代码):

_.mixin({
    i_cant_think_of_a_good_name_for: function(object, property) {
        if(object == null)
            return void 0;
        return _.isFunction(property) ? property.call(object) : property;
    }
});

然后说:

_.i_cant_think_of_a_good_name_for(this, options.name);
_.i_cant_think_of_a_good_name_for(this, options.label);

在你看来。请注意,这里的第一个参数是您希望用于函数的this,第二个参数是整个属性,而不仅仅是其名称。

【讨论】:

    猜你喜欢
    • 2018-10-23
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 2020-07-22
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    相关资源
    最近更新 更多