我正在查看操纵HTML A标记的javascript代码,而我无法理解它如何设置“onclick”属性。它似乎告诉它用索引变量j更新ytplayer_playitem然后调用ytplayer_playlazy(1000)
但是所有的括号怎么样? javascript语法中的哪些细节允许它像这样设置?
var a = document.createElement("a");
a.href = "#ytplayer";
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
答案 0 :(得分:4)
基本上,onclick
的值是一个在单击元素时将被调用的函数。无论您在用户点击元素时想要发生什么,都会进入函数体内。
您可以创建一个命名函数,然后将其分配给元素的onclick属性:
function youClickedMe() {
...
}
a.onclick = youClickedMe
但是使用从未在其他地方引用的函数名称来混淆命名空间。在您需要的地方创建匿名功能更清晰。通常情况下,这将是这样的:
a.onclick = function() { ... }
但是,如果我们尝试使用您的具体示例:
a.onclick = function() {
ytplayer_playitem = something; // ??
ytplayer_playlazy(1000);
}
我们发现它会对播放的something
进行硬编码。我假设原始代码是从一个循环中获取的,它生成了几个可点击的链接来播放;使用上面的代码,所有这些链接都会起到同样的作用,这可能不是你想要的。
到目前为止,如此直截了当,但下一次飞跃是它变得棘手的地方。解决方案似乎很明显:如果你处于循环中,为什么不在函数体内使用循环变量呢?
// THIS DOESN'T WORK
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
}
看起来它应该可以工作,但不幸的是,当函数被称为时,函数内的i
引用变量i
的值,而不是当它< EM>创建。当用户点击链接时,创建所有链接的循环将完成,i
将具有其最终值 - 可能是列表中的最后一项或大于该项的索引,取决于如何写循环。无论它的价值是什么,你都会再次遇到所有链接都在同一个项目中的情况。
代码中的解决方案通过使用返回值为另一个函数的函数获得一点元数据。如果将循环控制变量作为参数传递给生成函数,则它创建的新函数可以引用该参数并始终获取最初传入的值,无论外部参数变量的值发生了什么,因为:
function generate_onclick(j) {
// no matter when the returned function is called, its "j" will be
// the value passed into this call of "generate_onclick"
return function() { ytplayer_playitem = j; ytplayer_playlazy(1000); }
}
要使用它,请在循环内调用它:
a.onclick = generate_onclick(i);
每个生成的函数都有自己的j
变量,它永远保持其值,而不是在i
时更改。所以每个链接都是正确的;任务完成了!
这正是您发布的原始代码正在做的事情,只有一个小区别:就像我解释的第一步一样,作者选择使用匿名函数而不是定义命名函数。另一个不同之处在于它们在定义后立即调用该匿名函数。这段代码:
a.onclick = (function (j) { ... })(i)
是此代码的匿名版本:
function gen(j) { ... }
a.onclick = gen(i)
由于JavaScript的分号插入规则,需要匿名版本周围的额外数据; function (y) {...}(blah)
编译为独立的函数定义,后跟括号中的独立表达式,而不是函数调用。
答案 1 :(得分:3)
“但是所有括号的内容是什么?”
大多数括号只是做你期望的事情。
还有一个技术上不需要的额外设置,但通常用作提示函数被调用的提示。
// v-v---these are part of the function definition like normal
a.onclick = (function (j) {
// ^-----------this and...v
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
// v---...this are technically not needed here, but are used as a hint
})(i);
// ^-^---these invoked the function like normal
“javascript语法中的哪些细节允许它像这样设置?”
结果是该函数立即被调用,并传递i
,以便其值被立即调用的函数中的j
参数引用。
这会创建一个变量范围,返回函数将继续有权访问。这样它始终可以访问j
变量,而不是在循环中被覆盖的i
。
这些内联函数被滥用了一点IMO。如果你只是让它成为一个命名函数就会变得更清楚。
for(var i = 0; i < 10; i++) {
// create the new element
a.onclick = createHandler(i);
// append it somewhere
}
function createHandler (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
}
生成的处理程序完全相同,但代码不那么神秘。
答案 2 :(得分:1)
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
这会创建一个“闭包”,以确保绑定到处理程序的i
的值是当时i
“的值,而不是i
的值
在您的代码中,()
内的函数是一个表达式,执行并传递变量i
。这是您在最后部分看到的(i)
。在此执行的函数表达式中,i
成为局部变量j
。此执行的函数表达式返回一个处理函数,该函数将绑定onclick
事件,该事件的值j
当时为i
“
如果我没有使用闭包:
//suppose i is 1
var i = 1;
a.onclick = function () {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
//and changed i
i = 2;
//if you clicked the <a>, it would not be 1 onclick but 2 because you
//did not assign the value of i "at that time". i is "tangible" this way
答案 3 :(得分:1)
是的,我猜猜周围的代码是这样的:
for (var i = 0; i < playitems.length; i++) {
// above code here
}
现在,你可以在这里做一件显而易见的事情,然后像这样分配onclick
属性:
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
然而效果不佳,因为i
的值会发生变化。无论点击哪个链接,最后一个都是激活的链接,因为此时i
的值将是列表中的最后一个。
所以你需要防止这种情况发生。您需要通过创建一个新范围来完成此操作,该范围是通过创建一个额外的函数来完成的,该函数会立即被调用:
(function (j) {
// some code here
})(i);
因为已将i
传递给函数,所以传递的值不是对保留变量的引用。这意味着您现在可以定义一个函数,该函数将引用正确的值。所以你得到额外的功能来返回点击处理功能:
a.onclick = (function (j) { // j is the right number and always will be
return function () { // this function is the click handler
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
因此每个a
元素都有自己的单击处理函数,每个函数都有自己的j
变量,这是正确的数字。因此,单击链接时,将执行您希望它们执行的功能。
答案 4 :(得分:0)
a.onclick = (function (j) { return function () { ytplayer_playitem = j; ytplayer_playlazy(1000); }; })(i);
这里有一个自我调用的匿名函数。让我们分解它,首先用更简单的东西替换函数体(return j + 1;
):
function( j ) { return j + 1; }
这是一个普通的匿名函数或闭包。这行代码是一个表达式,因此它有一个值,该值是一个函数。现在我们可以这样做:
var foo = function( j ) { return j + 1; }
foo( 5 ); // => 6
您认识到这一点,我敢肯定 - 我们将匿名函数分配给变量foo
,然后使用参数i
按名称调用函数。但是,不是创建一个新变量,因为闭包是一个表达式,我们可以这样称之为:
( function( j ) { return j + 1; } )( 5 ); // => 6
结果相同。现在,它只是返回j + 1
,但在你的代码中它返回其他东西:另一个匿名函数:
return function() { /* ... */ }
当我们有一个返回函数的自调用匿名函数时会发生什么?结果是返回的“内部”函数:
a.onclick = ( function( j ) {
return function() {
ytplayer_playitem = j;
ytplayer_playlazy( 1000 );
}
}
)( i );
如果i
等于9
,那么a.onclick
现在将拥有与此相当的函数:
function() {
ytplayer_playitem = 9;
ytplayer_playlazy( 1000 );
}
正如其他人所指出的,这样做的有用之处在于,当调用( function( j ) { /* ... */ } )( i )
时,您正在捕获当时i
的值 并将其放入j
而不是创建引用到值i
保持,这可能(可能会)稍后更改。