[Effective JavaScript 笔记] 第14条:当心命名函数表达式笨拙的作用域
js函数会根据上下文改变其含义。
function double(x){return x*2;}
这是一个函数声明,也可以是一个命名函数表达式(named function expression),取决于它出现的地方。
声明一个函数,并绑定一个当前作用域的变量。
同一段函数代码也可以作为一个表达式。
var f=function double(x){return x*2;}
根据ECMAScript规范,该函数绑定到变量f,而不是变量double。这里给函数表达式命名并不是必要的的,可以直接使用匿名的函数表达式。
var f=function(x){return x*2;}
匿名和命名函数表达式的官方区别在于后者会绑定到与其函数名相同的变量上,该变量将作为函数内的一个局部变量。
可以用来写递归函数表达式
var f=function find(tree,key){
if(!tree){
return null;
}
if(tree.key === key){
return tree.value;
}
return find(tree.left,key)||find(tree.right,value);
}
注意:变量find的作用域只在其自身函数中。不像函数声明,命名函数表达式不能通过其内部的函数名在外部被引用。
find(myTree,”foo”);//error:find is not defined
使用命名函数表达式来进行递归似乎没有必要,因为使用外部作用域的函数名也可以达到同样的效果
var f=function(tree,key){
if(!tree){
return null;
}
if(tree.key === key){
return tree.value;
}
return f(tree.left,key)||f(tree.right,value);
}
或
function find(tree,key){
if(!tree){
return null;
}
if(tree.key === key){
return tree.value;
}
return find(tree.left,key)||find(tree.right,value);
}
var f=find;
命名函数的真正的用处是进行调试
大多数现代的JS环境都提供对Error对旬的栈跟踪功能。在栈跟踪中,函数表达式的名称通常作为其入口作用。用于检查栈的设备调试器对命名函数表达式有类似的使用。
命名函数表达式是作用域和兼容性问题的来源。ES规范的错误,在ES3中就已经存在,JS引擎被要求将命名函数表达式的作用域表示为一个对象,像有问题的with结构。该作用域对象只含有单个属性,该属性将函数名和函数自身绑定起来。该作用域对象也继承了Object.prototype的属性。这意味着仅仅是给函数表达式命名也会将Object.prototype中的所有属性引用到作用域中。结果出问题如:
var constructor=function(){return null;};
var f=function f(){
return constructor();
};
f();//{}(in ES3 environments)
ES5修正了这个问题。运行如下:

但有些JS环境仍然使用过时的对象作用域,有些环境更不符合标准,对匿名函数表达式使用对象作为作用域。
在系统中避免对象污染函数表达式作用域的最好方式是避免任何时候在Object.prototype中添加属性,以及避免使用任何与标准Object.prototype属性同名的局部变量。
在流行的JS引擎中的另一个缺陷是对命名函数表达式的声明进行提升。
var f=function g(){return 17;}
g();//17(在不标准的环境中)
注意这是不符合标准的行为。
有一些JS环境甚至把f和g这两个函数作为不同的对象,从而导致不必要的内存分配。这种行为的一个合理的解决办法是创建一个与函数表达式同名的局部变量并赋值为null。
var f=function g(){return 17;}
var g=null;
即使在没有错误地提升函数表达式声明的环境中,使用var重声明变量能确保仍然会绑定变量g。设置变量g为null能确保重复的函数被垃圾回收。
合理的结论
- 命名函数表达式由于会导致很多问题,所以并不值得使用。
- 一个不太严肃的回应是开发阶段使用命名函数表达式用作调试,在发布前通过构建工具去把所有函数表达式转化为匿名的。
- 要明确自己要发布的平台的JS环境。
提示
- 在Error对象和调试器中使用命名函数表达式改进栈跟踪
- 在ES3和有问题的JS环境中,函数表达式作用域会被Object.prototype污染
- 错误百出的JS环境中会提升命名函数表达式声明,并导致命名函数表达式的重复存储
- 考虑避免使用命名函数表达式或在发布前删除函数命名
- 确保JS代码到正确实现ES5环境中,就不用再担心这些问题了
[Effective JavaScript 笔记] 第14条:当心命名函数表达式笨拙的作用域的更多相关文章
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- [Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域
嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x) ...
- [Effective JavaScript 笔记] 第6条:了解分号插入的局限
分号可以省略 js可以在语句结束不强制加分号.(建议还是添加,不添加分号往往会出现不易发现的BUG) function Point(x,y){ this.x=x||0; this.y=y||0; } ...
- [Effective JavaScript 笔记] 第11条:熟练掌握闭包
理解闭包三个基本的事实 第一个事实:js允许你引用在当前函数以外定义的变量. function makeSandwich(){ var magicIngredient=”peanut butter”; ...
- [Effective JavaScript 笔记] 第13条:使用立即调用的函数表达式创建局部作用域
function wrapElements(a){ var res=[],i,n; for(i=0,n=a.length;i<n;i++){ res[i]=function(){return a ...
随机推荐
- 微软发布独立Android模拟器 为开发者提供测试
微软发布了 Visual Studio 2015 正式版,除了免费的社交版之外,另外也有付费的专业版.这套工具除了提供 Windows 应用程序的整合环境之外,你也可以利用它来开发 Android 程 ...
- nginx的笔记
nginx 的安装 下载地址: http://nginx.org/download/nginx-1.4.2.tar.gz 安装准备: nginx依赖于pcre库,要先安装pcre yum instal ...
- 编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]
前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托 ...
- [AaronYang]C#人爱学不学[5]
这世上有三样东西是别人抢不走的:一是吃进胃里的食物,二是藏在心中的梦想,三是读进大脑的书 --Aaronyang的博客(www.ayjs.net) 1. 数组-的疑惑? 1.1 多维数组 ...
- git 命令的学习
我们在安装好gitlab 之后就是怎么使用它了,这里我选择http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b806 ...
- JAVA成员变量为什么不能在类体中先定义后赋值
package dx; public class Test1 { int a111;//定义成员变量(全局变量) // a = 1;//此处若给变量赋值,会报错,JAVA所有的除定义或声明语句之外的任 ...
- JS~json日期格式化
起因 对于从C#返回的日期字段,当进行JSON序列化后,在前台JS里显示的并不是真正的日期,这让我们感觉很不爽,我们不可能为了这东西,把所有日期字段都变成string吧,所以,找了一个JS的扩展方法, ...
- json2form已改名为AForm
相信大部分程序员都接触过表单,表单是收集用户输入的不二之选,但是表单的开发又是最繁琐.最复杂的,简单地说,开发表单你需要涉及到很多知识: 布局,表单如何布局排版,看起来最清晰整洁,且符合用户体验 控件 ...
- ActiveRecord中andFilterWhere使用
查询数据库时 $model; if(!empty($name)) { $model->andWhere(['name'=>$name]); } 可以用andFilterWhere,自动的把 ...
- 19.Android之文件存储方法学习
Android开发中会用到文件存储,今天来学习下. 先改下布局界面: <?xml version="1.0" encoding="utf-8"?> ...