有人能解释一下这段JavaScript发生了什么吗?

9

我正在查看一个jQuery平滑滚动教程,并尝试弄清楚它的工作原理,当我遇到了这行代码:

 $target = $target.length && $target || $('[name=' + this.hash.slice(1) +']');

我不明白它的作用。看起来这个人正在将一个字符串赋值给一个变量,但也有点像他在测试那个变量。我不理解这里的 && 和 || 的用法。有人能解释一下吗?
谢谢!
编辑:哇!真是太棒了!不过我需要花点时间去理解,我得打印出来或者什么的来研究。一旦我理解了发生了什么,我就能选择帮助我最多的答案。特别是这部分:
if ($target.length && $target) {
$target = $target;

这个问题困扰着我。程序如何知道将$target分配给$target?这个操作将$target分配给自己的第一个引用(等号左侧),还是分配给自己的第二个引用(&&之后的右侧)?


1
我已经为我认为最佳答案投了赞,但他的逻辑也有问题。你要在检查 $target.length 之前检查 $target 是否为 NULL。这个表达式实际上应该以三元操作符的形式表达会更好。 - Gopherkhan
@Gopherkhan 我有一种感觉$target保证是一个jQuery对象。 - Phil
如果可以保证是一个jQuery对象,那么三元运算符选项就更有意义了。对于这种情况,使用$target = $target.length ? $target : $('[name=' + this.hash.slice(1) +']'); 更加简洁和易读。由于$foo在原始js中仍然是一个有效的名称,如果不能保证它是一个jQuery对象,则存在错误的可能性。最好采取防御性措施,我想说。 - Gopherkhan
1
@StormShadow 简单来说,该程序检查如果 (($target.length 不是 [undefined, null, 任何 falsy 值] 并且 $target.length > 0) 并且 ($target 不是 [undefined, null, 任何 falsy 值])) 那么 (将 $target 分配给它自己(即 $target 的已存在引用)) 否则 (将 $target 分配给 $('[name=' + this.hash.slice(1) +']'))。 - Narendra Yadala
谢谢你 Narendra - 那真的给我澄清了。 - StormShadow
4个回答

8

这是一个简洁(或优雅?)的等价三元操作符版本。

$target = ($target.length && $target) ? $target : $('[name=' + this.hash.slice(1) +']');

如果$target的求值不改变$target的值,则此三元运算符和原始的短路表达式将返回完全相同的值。但是,如果$target的求值更改了$target的值,则SCE和三元运算符将返回不同的值,例如:

var a = 1; b = 2; c = 3;
a && ++b || c
返回3;
//重设b
b = 2
a && ++b ? ++b : c 
返回4;

如果$target的求值更改了$target的值,则SCE的等效三元运算符$target = $target.length && $target || $('[name=' + this.hash.slice(1) +']');将如下所示:

$target = ($result= ($target.length && $target)) ? $result : $('[name=' + this.hash.slice(1) +']');

2
有趣的事实:在Python拥有三元运算符之前,大多数人使用andor,就像问题中的JavaScript片段一样。 - icktoofay
1
那不正确。JavaScript 不会隐式地将逻辑运算转换为布尔值。 - Phil
我从未说过Javascript会评估布尔值。我只是写了一个等效的表达式,因为OP要求简单解释。你能举个例子,证明我的表达式和OP的表达式会给出不同的值吗? - Narendra Yadala
@Narendra 仅仅因为你的表达式和 OP 的结果相同并不意味着它们是等价的。我对你解释中的 ($target.length && $target) 表示异议,因为它是不正确的。 - Phil
我已经清楚地给出了为什么这是错误的例子。a = true; b = false; c = 233; d = a ? b : c 将会把 d 设置为 false,而 OP 的表达式将会把 d 设置为 233...正确的三元运算符应该是 -- a && b ? b : c。 - Narendra Yadala

4

这段代码测试$target(我假设是一个jQuery对象)是否有元素,如果为空,则赋予默认值。

详细解释请参见逻辑运算符

更新

为了解释清楚(如果您不想阅读MDN文档),JavaScript逻辑比较运算符从左到右依次执行。

expr1 && expr2

这将返回expr1,如果它可以转换为false;否则,返回expr2
expr1 || expr2

这将返回expr1,如果它可以转换为true;否则返回expr2
要分解此行���请这样想。
$target = ($target.length && $target) || $('[name=' + this.hash.slice(1) +']');

看第一步操作...

$target.length && $target

如果$target.length可以转换为false(即0),则此操作将返回$target.length,否则它将返回$target

第二个操作看起来像这样...

(operation1) || $('[name=' + this.hash.slice(1) +']')

如果 operation1 的结果可以转换为 true(即$target),则返回该结果,否则返回 $('[name=' + this.hash.slice(1) +']')

1
我也见过这种语法。为什么这个语句不会被评估为布尔值呢? - Jeff
1
实际上应该是 - 如果($target.length && $target){$target = $target;} else {$target = $('[name=' + this.hash.slice(1) +']');}。我想不出$target.length为真且$target为假的情况,但这是精确的翻译。 - Narendra Yadala
@Narendra 这是不正确的。请阅读我回答中链接的 MDN 文档。 - Phil
我指的是顶部的if else。它返回false。请参阅Omeid或MTönnberg提供的if else扩展。 - Narendra Yadala
@Narendra 我明白了。我已经删除了错误的 if .. else 代码块。 - Phil
显示剩余2条评论

2
这被称为短路求值,它与三元运算符不等价。
你的代码等价于以下内容:
if ($target.length && $target) {
    $target = $target;
} 
 else {
    $target = $('[name=' + this.hash.slice(1) +']');
}

在JavaScript和其他大多数弱类型语言中,除了真和假这两个真值之外,返回的值基于“最后一个值”。
如果 a && b 是假的,则返回 a,否则返回 b。
如果 a || b 是真的,则返回 a,否则返回 b。
为了更好地理解“最后一个值”,请看以下示例:
var a = true;
var b = false;
var c = 5;
var d = 0;

return a && b; //returns false
return a && c; //returns 5
return b && c; //returns false
return c && b; //returns false
return c && a; //returns true;
return d && b; //returns 0;
return b && d; //returns false;

return a || b; //returns true
return a || c; //returns true
return b || c; //returns 5
return c || b; //returns 5
return c || a; //returns 5;
return d || b; //returns false;
return b || d; //returns 0;

更新:
三元运算符:
(条件)?(如果条件为真则计算):(如果条件为假则计算)
短路评估:
(如果这个为真,继续执行;否则返回这个)&&(如果这个为真,继续执行;否则返回这个)
可以清楚地看到,在三元操作符中有一个条件,而在短路评估中,值本身的评估就是条件。

我认为你提供的不是等价的表达式。你尝试过 $a&&$b||$c 吗?它返回的不是 false,而是 5。 - Narendra Yadala
它返回最后一个值,所以应该是5。你认为有什么问题吗? - user529649
如果你在 if else 扩展中替换 $a、$b 和 $c,它会返回 false,而不是 5。 - Narendra Yadala
MTönnberg的if else扩展是正确的扩展。请查看一下。 - Narendra Yadala
1
毫无疑问,“三元运算符”在实践中与“if-else”相同,但“短路运算”并不等同于三元运算符。 - user529649
显示剩余6条评论

0

$target = $target.length && $target || $('[name=' + this.hash.slice(1) +']');

在JavaScript中,当赋值变量时,您可以进行内联比较,因此您的示例相当于

if($target.length && $target)
    $target = $target;
else
    $target = $('[name=' + this.hash.slice(1) +']');

更多的例子,请查看http://www.w3schools.com/JS/js_comparisons.asp
例如:

var x= y > 4 ?value1:value2

这意味着如果条件为真(y大于4),x将被赋值为value1,否则它将被赋值为value2。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接