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条:当心命名函数表达式笨拙的作用域的更多相关文章

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

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

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

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

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

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  5. [Effective JavaScript 笔记]第3章:使用函数--个人总结

    前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...

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

    嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x) ...

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

    分号可以省略 js可以在语句结束不强制加分号.(建议还是添加,不添加分号往往会出现不易发现的BUG) function Point(x,y){ this.x=x||0; this.y=y||0; } ...

  8. [Effective JavaScript 笔记] 第11条:熟练掌握闭包

    理解闭包三个基本的事实 第一个事实:js允许你引用在当前函数以外定义的变量. function makeSandwich(){ var magicIngredient=”peanut butter”; ...

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

    function wrapElements(a){ var res=[],i,n; for(i=0,n=a.length;i<n;i++){ res[i]=function(){return a ...

随机推荐

  1. Redis入门学习(一)——安装配置

    最近马上要找实习了,听学长说他们公司里用了redis来解决缓存问题,在大三结束前来学习一下. 问:Redis是什么? 答:(官方)REmote DIctionary Server(Redis)是一个K ...

  2. 你会swap吗,按值传递还是按引用?

    问题 1.Java到底是按值传递(Call by Value),还是按引用传递(Call by Reference)? 2.如下面的代码,为什么不能进行交换? public CallBy swap2( ...

  3. IT男的”幸福”生活"续1

    IT男的”幸福”生活"续1 我和LL开始下步追妹计划...... 知彼知已,方能把握机会.没想到孙子兵法太给力了,据LL了解,MM(她)是湖北人,找对象一般不会考虑外省的.高度165左右,相 ...

  4. WPF开发时光之痕日记本——终于完工了。。晒晒截图(三)(已上传安装包)

    由于是业余时间学习的 WPF 的相关开发且不怎么会使用 Blend 软件,所以开发这个客户端着实花费了我很长时间,比如文本编辑器的开发,最初是在 Simple.HtmlEditor 的基础上做的修改, ...

  5. Java学习笔记(十五)——javadoc学习笔记和可能的注意细节

    [前面的话] 这次开发项目使用jenkins做持续集成,PMD检查代码,Junit做单元测试,还会自动发邮件通知编译情况,会将javadoc生成的文档自动发到一个专门的服务器上面,每个人都可以看,所以 ...

  6. nginx + Lua 实现自定义WAF

    文章摘自:https://github.com/unixhot/waf wget git@github.com:unixhot/waf.git

  7. strncmp很好的函数

    strcmp比较的是所有的长度,而strncmp可以比较前几个长度 strncmp(s1,s2,n);这样就比较了s1,s2,前n个长度的大小.

  8. Mysql-日期转换

    一.字符串转日期 下面将讲述如何在MYSQL中把一个字符串转换成日期: 背景:rq字段信息为:20100901 1.无需转换的: SELECT * FROM tairlist_day WHERE rq ...

  9. 【UESTC 482】Charitable Exchange(优先队列+bfs)

    给你n个物品交换,每个交换用r,v,t描述,代表需要用r元的东西花费t时间交换得v元的东西.一开始只有1元的东西,让你求出交换到价值至少为m的最少时间代价.相当于每个交换是一条边,时间为边权,求走到价 ...

  10. Vijos1889 天真的因数分解

    描述 小岛: 什么叫做因数分解呢?doc : 就是将给定的正整数n, 分解为若干个素数连乘的形式.小岛: 那比如说 n=12 呢?doc : 那么就是 12 = 2 X 2 X 3 呀.小岛: 呜呜, ...