在子对象内定义函数时“this”的范围

时间:2014-02-01 22:59:53

标签: javascript backbone.js scope this

我正在使用关于页脚的自定义选项扩展Backbone View,我在另一个类中评估。

看起来像:

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。我也无法定义var that = this因为我正在扩展对象而没有局部变量的位置。 如何使我在footer-object中定义的函数具有UserEditView的这个范围?

我也可以:

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

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

如果不可能采用其他方式,或者这种方式更容易。

1 个答案:

答案 0 :(得分:0)

通常,函数内的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')m的函数属性,则说obj,就像说:

obj.m()

将其应用于:

_.result(options, "label")

我们看到你有效地说:

options.label()

this函数中的label将为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,第二个参数是整个属性而不仅仅是它的名称。