Ben Cherry的优秀文章充分解释了hoisting in JavaScript。然而,我的问题是,我不能为这个臭名昭着的混淆犯罪者设想一个用例。请解释是否存在实际利用此语言功能的设计模式。
其次,是JavaScript独有的范围提升吗?
更新 ---我正在为满足我好奇心的答案添加赏金:哪些设计模式实际上利用了JavaScript的提升行为?我了解为什么 JavaScript支持提升,但我想知道如何利用此功能。
答案 0 :(得分:19)
吊装最简单的用途之一是可变吊装。如果我们没有变量提升,则会抛出ReferenceError
:
var bar = foo;
var foo;
这似乎没有立即有用,但它允许我们做这样的事情:
var myCoolJS = myCoolJS || {};
这基本上意味着它的样子:myCoolJS
如果存在则为myCoolJS
,如果不存在则为myCoolJS
。如果ReferenceError
尚未存在,则第二个myCoolJS
不会抛出typeof myCoolJS != 'undefined'
,因为此变量声明已提升。
这使我们免于进行尴尬的module
检查。
当将多个脚本合并到一个中时,功能提升特别有用。例如,我创建了CommonJS modules的轻量级构建时实现。这提供了在node.js中找到的相同require
,exports
和require('/foo')
功能。我构建了工具,允许所需的模块由多个文件组成。例如,foo.js
可能会生成一个由两个文件foo.h.js
(“正文文件”)和// dom.h.js
var util = require('util').util;
exports.css = css; // we can do this because "css" is hoisted from below
// ... other exports ...
(“标题文件”)组成的模块。
这允许“body文件”不知道CommonJS模块环境提供的自由变量;所有这些都在标题中处理。这使得代码可以重复使用,并且无需构建即可轻松测试。但是,由于标题预先到正文,我们利用正文文件中的函数提升来允许标题中的导出。例如:
// dom.js
function css(){}; // this would normally just be an object.
css.hasClass = function(element) { ... };
css.addClass = function(element) { ... };
// ...other code...
...
{{1}}
答案 1 :(得分:15)
这是一个用于吊装的用途:
(function() {
var factorial = function(n) {
if(n == 0)
return 1;
return n * factorial(n - 1);
};
})();
没有提升,这不会编译,因为factorial
在函数文字内部还不存在。您必须单独声明变量或使用命名函数。
JavaScript还允许使用以下代码:
var test = function(b) {
if(b) {
var value = 2;
} else {
var value = 5;
}
console.log(value);
};
使用区块范围设定,您必须在value
之前添加另一行以声明if
。
公平地说,这段代码因功能范围而不是吊装而起作用。 JavaScript可能没有提升功能范围。 Ruby更好地处理这个问题:Ruby有变量的方法范围,但变量不存在直到你设置它们:
def test(b)
# unlike JavaScript, value is not accessible here
if b
value = 2
else
value = 5
end
puts value
end
答案 2 :(得分:11)
JavaScript没有块范围(现在让我们忘记let
),因此任何变量声明都声明整个函数,其中JavaScript 具有范围。
如果您这样考虑,JavaScript提升可能会更有意义。
如果你还记得提升,它不应该是错误和混乱的根源。这只是你必须理解和记住的一个怪癖。
我不确定吊装是否仅限于JavaScript。我从未在其他地方听说过,但这并不一定意味着它在其他语言中不存在。
答案 3 :(得分:9)
该文章中的前两个例子写得很糟糕。糟糕的代码显然会导致错误和混乱。让我给你这些例子的重构版本。你会发现这里没有混淆......
示例1 - 原始代码
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo);
}
bar();
示例1 - 重构代码(删除混淆)
var foo = 1;
function bar() {
var foo;
if ( !foo ) {
foo = 10;
}
alert( foo );
}
bar();
警报显示“10”,很明显为什么。这里没有混淆。
示例2 - 原始代码
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
示例2 - 重构代码(删除混淆)
var a = 1;
function b() {
var a = function () {};
a = 10;
return;
}
b();
alert( a );
警报显示“1”。明显。这里也没有混淆。
答案 4 :(得分:6)
“hoisting”不是ECMAScript标准的一部分,但它确实说函数内部的变量是在函数的开头声明的,无论函数在代码中的位置如何。
实施例
(function() {
alert(myvar); // undefined
var myvar = 'local value';
})();
内部Javascript会在警报之前声明myvar,显示警报,然后将myvar指定为“本地值”。
因此Javascript会将该代码插入:
(function() {
var myvar;
alert(myvar); // undefined
myvar = 'local value';
})();
这就是为什么“Java the Good parts”有一个指导原则,说明你应该在函数的顶部声明变量。
来源:http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/
“请解释是否存在实际利用此语言功能的设计模式。” “hoisting”不是一个特性,而是Javascript解释器如何构造代码的结果,因为该语言使用了函数作用域。
“哪种设计模式实际上利用了JavaScript的提升行为?” 答:没有。
答案 5 :(得分:5)
我认为提升有用的一个领域是由于函数被视为第一类对象。例如:
function foo()
{
function a()
{
//...
}
function b()
{
//...
}
}
也可以写成:
function foo()
{
var a = function ()
{
//...
}
var b = function ()
{
//...
}
}
如果不提升,以下内容将导致错误:
function foo()
{
var a = function ()
{
b();
}
a(); //Error in function since b is not yet defined
var b = function ()
{
//...
}
}
我认为他们可能只有悬挂的功能对象,但我认为这与功能应该被视为语言中的一等公民的哲学不一致。
答案 6 :(得分:4)
这是一个真实的用例(虽然简化为伪代码),来自那些真正想要在野外使用吊装的好处。
我最近编写了这个脚本来处理简单的表单验证和提交。每个函数声明尽可能调用以下内容。这有两个主要的可读性好处:
$( function emailHandler(){ var $form = … var $email = … var $feedback = … var value = … var validation = … // All initialisation is right here. Executes immediately. // Therefore, all future code will only ever be invoked // by call stacks passing through here. void function bindEvents(){ $email.on( 'input', filterInput ); $form.on( 'submit', preSubmit ); }(); function filterInput( inputEvent ){ if( inputEvent && inputEvent.which === '13' ){ return presubmit( inputEvent ); } return validate(); } function validate(){ var presentValue = $email.val(); if( validation.done || presentValue === value ){ return; } else if( presentValue === placeholder || presentValue === '' ){ validation.message = 'You must enter an email address to sign up'; validation.valid = false; } else if( !validation.pattern.test( presentValue ) ){ validation.message = 'Please enter a valid email address'; validation.valid = false; } else { validation.message = ''; validation.valid = true; } validation.ever = true; clearFeedback(); } function preSubmit( inputEvent ){ if( inputEvent instanceof $.Event ){ inputEvent.preventDefault(); } if( !validation.ever ){ validate(); } if( validation.pending || validation.done ){ return; } else if( validation.valid ){ return submit(); } else { return feedback(); } } function submit(){ $form.addClass( 'pending' ); // Create an XHR base on form attributes $.ajax( { cache : false, url : $form.attr( 'action' ), data : $form.serialize(), type : $form.attr( 'method' ).toUpperCase() } ) .done( success ) .fail( failure ) .always( feedback ); } function success( response ){ validation.message = response.message; validation.valid = response.valid; } function failure(){ validation.message = 'Our server couldn\'t sign you up. Please try again later.'; validation.error = true; } function feedback(){ clearFeedback(); if( validation.message ){ $feedback .html( validation.message ) .appendTo( $placeholder ); } if( !validation.valid || validation.error ){ $form.addClass( 'invalid' ); $email.trigger( 'focus' ); } else { $form.addClass( 'done' ); validation.done = true; } validation.pending = false; } function clearFeedback(){ $form.removeClass( 'invalid pending done' ); } } );
答案 7 :(得分:1)
我喜欢这个问题的风格,基于对语言的好奇心。显然没有人应该使用吊装作为特征,除非他们绝对确定以后可能会使用它的人不能发现他们的家庭住址。
我只能想象一些微不足道的案例。要利用的基本属性是变量可以声明(但未定义),然后只在一行代码中分配,但事件在两个不同的不同点解释。
使用循环结束时的声明(当然不是.forEach设置范围),您可以使用它来检测第一次迭代。
var notfirst = true; // this is actually never referenced.
(function () {
var number, stack = [1, 2, 3, 4, 5];
while (number = stack.pop()) {
if (notfirst) console.log(number);
var notfirst = true;
}
})();
清空堆栈的输出为4,3,2,1.5被拒绝。
再次。不要这样做!
答案 8 :(得分:0)
如果你考虑其他语言的编写方式(C ++ / Java)以及如何使用它们的类模式,可以利用提升来编写类似的模式来构建原型。