[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 ...
随机推荐
- Java语言词法分析器
一.实验目的 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解.并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法. 编制一个读单词过程,从输入的源程序中,识别 ...
- Java并发编程-CopyOnWriteArrayList
CopyOnWriteArrayList原理 首先每次写操作,都将数组copy一份,并赋值给arrays 读操作读不加锁 写操作加锁 ReentrantLock 因为每次写都要copy数组,这是一项繁 ...
- Android--TextView 文字显示和修改
一. 新建一个Activity 和 Layout 首先在layout文件夹中新建一个activity_main.xml,在新建工程的时候一般默认会新建此xml文件,修改其代码如下: <Relat ...
- UITableViewdataSourse的协议所有方法
UITableViewDataSource @required- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection ...
- 转-JS中document对象详解
对象属性 document.title //设置文档标题等价于HTML的<title>标签 document.bgColor //设置页面背景色 document.fgColor //设置 ...
- tomcat服务器上webapps里的文件名和项目名称不一样,修改方法
第一种方法:打开工程所在目录,找到一个 .mymetadata的文件,用记事本等打开,内容大致如下: <?xml version="1.0" encoding="U ...
- hive 使用笔记(partition; HDFS乱码)
6. insert 语句 1) 因为目标表有partition, 所以刚开始我使用的语句是 insert overwrite table sa_r_item_sales_day_week_month ...
- groovy–流程控制
在本篇文章中,我们将介绍逻辑分支,循环,以及如何从if-else以及try-catch代码块中返回值. if – elseGroovy 支持Java传统的if-else语法: def x = fals ...
- ubuntu 14.04 vim install youcompleteme
sudo apt-get install vim ; sudo apt-get install vim-youcompleteme ; sudo apt-get install vim-addon-m ...
- Linux文件目录权限浅谈
1.基本权限三种(1)r (read) 读 针对目录,有读(r)权限就代表能对此目录有列表功能,就是可以执行ls命令进行查看,另外还有cp的功能.针对文件,有读(r)权限就代表能对此文件有阅读功能,可 ...