[Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域

嵌套函数声明。没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明

  function f(){return "global"}  function test(x){      var result=[];      function f(){return "local";}//block-local     if(x){          result.push(f());       }       result.push(f());       return result;    }    test(true);//["local","local"]    test(false);//["local"]
    

如果我们把函数f移动到局部块里。

  function f(){return "global"}  function test(x){      var result=[];      if(x){         function f(){return "local";}//block-local         result.push(f());       }       result.push(f());       return result;    }    test(true);//?    test(false);//?
    

js没有块级作用域,所以内部函数f的作用域应该是整个test函数。下面的这个例子的合理猜测结果是["local","local"]和["local"]。事实上,一些js环境的确如此执行。并不是所有js环境都这样,其他一些环境在运行时根据包含函数f的块是否被执行来有条件地绑定函数f。(这使代码更难理解,而且致使性能降低)
下面为chrome控制台的执行结果:

关于这点ES标准中几乎没有定义。ES5,js标准才承认局部块函数声明的存在。官方指定函数声明只能出现在其他函数或者程序的最外层。ES5建议把在非标准环境的函数声明转变成警告或错误。
一些js实现在严格模式下将这类函数报告为错误(具有局部块函数声明的处于严格模式下的程序报告一个语法错误)。有助于检测出不可移植的代码,并为未来的标准版本给局部块函数声明指定更明智和可移植的主义开辟了一条路。
编写可移植的函数的最好方式是始终避免将函数声明置于局部块或子语句中
如果你想写嵌套函数声明,应该将它置于其父函数的最外层,如示例1,如果需要有条件的选择函数,最好的方法是使用var声明和函数表达式来实现

function f(){return "global";} function test(x){    var g=f,result=[];    if(x){       g=function(){return "local";}       result.push(g());    }    result.push(g());   return result; }

消除内部变量作用域的神秘性。无条件地作为局部变量绑定,而仅仅只有赋值语句是有条件的。

提示

  • 始终将函数声明置于程序或被包含的函数的最外层以避免不可移植的行为
  • 使用var声明和有条件的赋值语句替代有条件的函数声明
时间: 2024-12-19 13:38:02

[Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域的相关文章

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

[Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof "s";//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.t

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

[Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法

前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数arguments对象.它是一个类数组对象,并不是一个标准的数组,所以无法使用数组原型中的方法,因此无法使用arguments.forEach这样的形式来遍历每一个参数.这里我们必须使用call方法来对使用forEach方法. function highlight(){ [].forEach.call(argume

[Effective JavaScript 笔记] 第13条:使用立即调用的函数表达式创建局部作用域

function wrapElements(a){ var res=[],i,n; for(i=0,n=a.length;i<n;i++){ res[i]=function(){return a[i]}; } return res; } var wrapped=wrapElements([10,20,30,40,50]); var f=wrapped[0]; f();//undefined 这个可以由之前的闭包来讲,res里的每个函数都是一个闭包,它们都可以访问上一个函数的作用域内的变量,所以每

[Effective JavaScript 笔记]第63条:当心丢弃错误

管理异步编程的一个是错误处理.同步代码中只要使用try语句块包装一段代码很容易一下子处理所有的错误. try{ f(); g(); h(); } catch(e){ //这里用来下得出现的错误 } try语句块 但对于异步的代码,多步的处理通常会被分隔到事件队列的单独轮次中,因此,不可能将它们包装在一个try语句块中.事实上异步的API甚至根本不可能抛出异常,因为,当一个异步的错误发生时,没有一个明显的执行上下文来抛出异常!相反,异步的API倾向于将错误表示为回调函数的特定参数,或使用一个附加的

[Effective JavaScript 笔记] 第14条:当心命名函数表达式笨拙的作用域

js函数会根据上下文改变其含义. function double(x){return x*2;} 这是一个函数声明,也可以是一个命名函数表达式(named function expression),取决于它出现的地方. 声明一个函数,并绑定一个当前作用域的变量. 同一段函数代码也可以作为一个表达式. var f=function double(x){return x*2;} 根据ECMAScript规范,该函数绑定到变量f,而不是变量double.这里给函数表达式命名并不是必要的的,可以直接使用

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

分号可以省略 js可以在语句结束不强制加分号.(建议还是添加,不添加分号往往会出现不易发现的BUG) function Point(x,y){ this.x=x||0; this.y=y||0; } Point.prototype.isOrigin=function(){ return this.x===0 && this.y===0 } 上面代码可以运行,是由于js可以自动插入分号,它是一种程序解析技术.能推断出某些上下文中省略的分号,然后有效地自动地将分号"插入"到

[Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.txt',function(file){ console.log('file:'+file); }); 基于promise的API不接收回调函数作为参数.相反,它返回一个promise对象,该对象通过其自身的then方法接收回调函数. var p=downloadP('file.txt'); p.th