[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 ...
随机推荐
- 成都普华永道税务开发的offer
首先这是一个.net税务开发的offer,我是做开发的. 有没有人在成都普华永道的,最近收到普华永道的offer,如果有的话请联系我.想知道里面的情况.最想知道里面的加班情况,薪资还是有点诱惑的.毕竟 ...
- AJAX(一)AJAX的简介和基础
本节简介(异步链接服务器对象)XMLHTTPRequest以及AJAX的简介. AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML). AJ ...
- java中的字符串简介,字符串的优化以及如何高效率的使用字符串
简介 String最为java中最重要的数据类型.字符串是软件开发中最重要的对象之一,通常,字符串对象在内存中总是占据着最大的空间块.所以,高效处理字符串,将提高系统的整个性能. 在java语言中,S ...
- SQL-Server 创建数据库,创建表格
use master --使用master权限 create database E_Market--创建新数据库 on primary--指定主数据文件,有且只有一个 ( name='E_Market ...
- HTML5 文件操作API
简介 我常常想,如果网络应用能够读取和写入文件与目录,将会非常方便.从离线转移到在线后,应用变得更加复杂,而文件系统方面的API的缺乏也一直阻碍着网络前进.存储二进制数据或与其进行交互不应局限于桌面. ...
- WeakHashMap和HashMap的区别
看Java源码的时候,看到了 WeakHashMap ,我一直以来使用的 都是 HashMap,于是查了一下两者的区别 (一) 查看API文档,WeakHashmap要点如下: 1. 以弱键 实现的基 ...
- <supports-screens>的用法
<supports-screens android:resizeable=["true"| "false"] android:smallScreens=[ ...
- json2form已改名为AForm
相信大部分程序员都接触过表单,表单是收集用户输入的不二之选,但是表单的开发又是最繁琐.最复杂的,简单地说,开发表单你需要涉及到很多知识: 布局,表单如何布局排版,看起来最清晰整洁,且符合用户体验 控件 ...
- 【POJ 3176】Cow Bowling
题 Description The cows don't use actual bowling balls when they go bowling. They each take a number ...
- 2.Android之按钮Button和编辑框EditText学习
今天学习android一些基本控件:button和edittext使用,我们平时网页或者手机登录一般都会有登录框,这里面就用到这两个部件. 如图: 所对应XML文件: 一般第二个编辑框都是输入密码,所 ...