哪种devise模式利用了JavaScript的提升行为?

Ben Cherry的优秀文章充分说明了JavaScript的提升 。 然而,我的问题是,我不能想象这个臭名昭着的混淆的肇事者的用例。 请说明是否有一种devise模式实际上利用了这种语言function。

其次,JavaScript的独特之处在于范围吗?

更新 —我添加一个满足我的好奇心的答案赏金: 哪个devise模式实际上利用了JavaScript的提升行为? 我明白为什么 JavaScript支持提升,但我想知道如何利用此function

variables提升

吊装的一个最简单的用途是可变吊装。 如果我们没有variables提升,这会抛出一个ReferenceError

 var bar = foo; var foo; 

这似乎并不是很有用,但是它允许我们做这样的事情:

 var myCoolJS = myCoolJS || {}; 

这基本上意味着它看起来像: myCoolJSmyCoolJS如果它存在,或者一个新的对象,如果它不存在。 如果myCoolJS不存在,第二个myCoolJS不会抛出一个ReferenceError ,因为这个variables声明被挂起。

这节省了我们做一个尴尬的typestypeof myCoolJS != 'undefined'检查。

function吊装

将多个脚本合并为一个时,函数提升可能特别有用。 例如,我创build了一个CommonJS模块的轻量级构build时实现。 这提供了在node.js中find的相同modulerequireexportsfunction。 我构build的工具允许所需的模块由多个文件组成。 例如, require('/foo')可能会导致由两个文件foo.js (“body file”)和foo.h.js (“header file”)组成的模块。

这允许“主体文件”不知道由CommonJS模块环境提供的自由variables; 所有这些都在头文件中处理。 这使得代码可重用,并且易于testing而无需构build。 但是,由于标题是预先添加到主体的,因此我们利用主体文件中的函数提升来允许标题中的导出。 例如:

 // dom.h.js var util = require('util').util; exports.css = css; // we can do this because "css" is hoisted from below // ... other exports ... 

 // dom.js function css(){}; // this would normally just be an object. css.hasClass = function(element) { ... }; css.addClass = function(element) { ... }; // ...other code... 

以下是吊装的用途:

 (function() { var factorial = function(n) { if(n == 0) return 1; return n * factorial(n - 1); }; })(); 

没有提升,这不会编译,因为factorial文字内部还没有存在factorial 。 您必须单独声明variables或使用命名函数。

JavaScript也允许像下面这样的代码:

 var test = function(b) { if(b) { var value = 2; } else { var value = 5; } console.log(value); }; 

使用块范围,您必须添加另一行来在if之前声明value

公平的说,这个代码因为function范围而不起作用。 而JavaScript可以有function的范围,而不是提升。 Ruby更好地处理了这个问题:Ruby有variables的方法范围,但是variables在你设置之前不存在:

 def test(b) # unlike JavaScript, value is not accessible here if b value = 2 else value = 5 end puts value end 

JavaScript没有块范围(现在让我们忘记let ),因此任何variables声明都是为整个函数声明的,其中JavaScript 确实有范围。

如果你这样想,JavaScript的提升可能会更有意义。

如果你还记得提起的话,它不应该是一个错误和混乱的来源。 这只是你必须理解和记住的怪癖之一。

我不确定提升是否仅限于JavaScript。 我从来没有听说过其他地方,但这并不一定意味着它不存在于其他语言。

这篇文章的前两个例子写得很糟糕。 不好的代码显然会导致错误和困惑。 让我给你这些例子的重构版本。 你会看到这里没有混乱

示例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”。 明显。 这里也没有混淆。

“提升”不是ECMAScript标准的一部分,但它确实声明函数内部的variables是在函数的开始处声明的,而不pipe函数在代码中的位置。

 (function() { alert(myvar); // undefined var myvar = 'local value'; })(); 

内部JavaScript会在alert之前声明myvar,显示alert,然后将myvar赋值为'local value'。

所以JavaScript会把这些代码插入为:

 (function() { var myvar; alert(myvar); // undefined myvar = 'local value'; })(); 

这就是为什么“Java好的部分”有一个指导,说你应该在你的函数的顶部声明variables。

资料来源: http : //net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/

“请解释一下,是否有一种devise模式可以充分利用这种语言function。” “提升”不是一个特征,而是一个结果,因为语言使用函数范围,Javascript解释器是如何构造代码的。

“哪种devise模式实际上利用了JavaScript的提升行为?”答案:没有。

我认为有一个地方起重是有用的,因为function被视为一stream的对象。 例如:

 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 () { //... } } 

我想他们可能只是提升function对象,但我相信这与function在语言上应该被视为一等公民的理念是不一致的。

这里有一个真正的用例(尽pipe简化为伪代码),对于那些实际上想要利用野外提升的好处的人来说。

我最近写了这个脚本来处理简单的表单validation和提交。 每个函数声明尽可能调用以下内容。 这对于可读性有两个主要的好处:

  1. 逻辑顺序 :有一个顺序stream向代码,这意味着函数将始终在声明之前被调用。 这是一个好处,当它使用低复杂度的函数时,它保持相对平坦的东西,给你一个函数的调用上下文来源之前不久。 您只需要向下滚动(从不上)以遵循代码,并且尽可能地不滚动滚动条。
  2. 低参考开销 :我喜欢将所有variables声明保留在每个范围的顶部,以便读者在读取其主体之前知道函数需要的所有移动部分,但是没有人想要读取每个调用函数的源代码以获得想法当前范围的作用 使用这种方法,您将永远不会遇到一个函数引用之前,其声明。 这听起来很愚蠢,但是它实际上减less了认知开销:你从来没有被赋予一个函数的来源,而隐式地记住这个 – 我们稍后会用到它 – 相反,只有当你知道被调用的上下文在。
 $( 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' ); } } ); 

基于对语言的好奇,我喜欢问题的风格。 显然没有人应该使用提升作为一个function,除非他们绝对肯定他们的家庭地址不能被以后使用的人发现。

我只能想象一些微不足道的情况。 要利用的基本属性是variables可以声明(但未定义),然后只分配一行代码,但事件解释在两个不同的不同点。

在一个循环结束时声明(而不是当作set scope),你可以用它来检测第一次迭代。

 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被拒绝。

再次。 不要这样做!

如果考虑其他语言的编写方式(C ++ / Java)以及如何使用它们的类模式,可以利用提升来编写类似的模式来构build原型。