我有以下代码,我试图用来在按钮数组上注册回调。但我似乎无法理解如何绑定回调中需要的字符串。任何建议将不胜感激!
for (var i = 0; i < this.car_types.length; ++i) {
this.select_car_buttons.push($("#button_select_car_" +
this.car_types[i].car_type));
this.select_car_buttons[this.select_car_buttons.length - 1]
.click(function() {
console.log(this.car_types[i].car_type);
}.bind(this));
}
不知何故,this
对象是按钮本身,而不是调用该函数的范围内的对象。
编辑:看起来这个对象确实正确传递了。问题是变量i
不会超出范围,而是通过引用而非值来捕获。我该如何解决这个问题?
此外,JavaScript作为一种语言似乎存在很多这样的问题(考虑到传统的C族语言(如C和C ++)使用的语义是正确的,至少可以归类为一个问题)是否有一些问题我可以读到的文章警告我反对这些类型的问题吗?
另一个编辑:尝试使用值捕获的值i
进行闭包时,我尝试了以下代码
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) {
return function() {
console.log(this.car_types[scoped_i].car_type);
}.bind(this);
}(i)));
但我在Safari中遇到以下错误
TypeError: undefined is not an object (evaluating 'scoped_i')
编辑:相同的代码适用于Firefox和Chrome,但不适用于Safari!
答案 0 :(得分:3)
这是一个范围问题。对于现代浏览器(支持ES6 ),您只需在var
循环中将let
更改为for
即可修复。
for (let i = 0; i < this.car_types.length; ++i)
引用MDN docs
let 语句声明一个块范围局部变量,可选择将其初始化为一个值。
要获得更多全局支持(非ES6支持),请使用立即调用的函数为变量创建额外的范围(将作为参数传递)
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) { // IIF starts here, the new variable is called scoped_i for verbosity
return function() { // your original function code goes here
console.log(this.car_types[scoped_i].car_type); // use the newly scoped variable
}.bind(this);
}.bind(this)(i))); // end and execute the IIF while passing the i variable to it
答案 1 :(得分:0)
是的,这个结构确实会产生大量的闭包,使代码很难阅读。由于你使用jQuery,有一个更好的方法来解决这个问题,它将数据保存在html中:
HTML:
<button class="select-car" data-car-type="CarA">Select CarA</button>
<button class="select-car" data-car-type="CarB">Select CarB</button>
<!-- And a lot of buttons -->
JS:
var selectCarOnClick = function() {
console.info($(this).data('car-type'));
};
$('button.select-car').click(selectCarOnClick);
Live exmaple:http://codepen.io/SCLeo/pen/VaQYjW
如果您要存储许多其他信息,并且想要使用对象来存储它们而不是DOM,则可以保存car-name或car-id而不是car-type。
以下是有关$ .data:https://api.jquery.com/jquery.data/
的文档