JavaScript 通过函数管理作用域。在函数内部声明的变量只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。

“未声明直接简单使用”,指的是不用 var 关键字来声明变量。这个我们已经非常清楚,避免造成隐式产生全局变量的方法就是声明变量尽量用 var 关键字。

可你以为用了 var 就 ok 了?来看看这个陷进:

function foo() {

var a = b = 0;

// body…

}

也许你期望得到的是两个局部变量,但  b  却是货真价实的全局变量。why? Because  赋值运算是自右往左的  ,所以这相当于:

function foo() {

var a = (b = 0);

// body…

}

所以 b 是全局变量。

解决:变量声明,最好一个个来,别搞批发~_~;

变量声明

先来看陷阱:

myName = “global”;

function foo() {

alert(myName);

var myName = “local”;

alert(myName);

}

foo();

乍看上去,我们预计期望两次 alert 的结果分别为 “global” 与 “local”,但真实的结果是 “undefined” 与 “local”。why? Because 变量在同一作用域(同一函数)中,声明都是被提至作用域顶部先进行解析的。

所以以上代码片段的执行行为可能就像这样:

function foo() {

var myName;

alert(myName); // “undefined”

myName = “local”;

alert(myName); // “local”

}

用另一个陷阱来测试下你是否真的理解了预解析:

if (!(“a” in window)) {

var a = 1;

}

alert(a);

a
 变量的声明被提前到了代码顶端,此时还未赋值。接下来进入 
if
 语句,判断条件中 
"a" in window
 已成立(
a
 已被声明为全局变量),所以判断语句计算结果为 
false
,直接就跳出 
if
 语句了,所以 
a
 的值为 
undefined

var a; // “undefined”

console.log(“a” in window); // true

if (!(“a” in window)) {

var a = 1; // 不执行

}

alert(a); // “undefined”

解决:变量声明,最好手动置于作用域顶部,对于无法当下赋值的变量,可采取先声明后赋值的手法。

函数声明

函数声明也是被提前至作用域顶部,先于任何表达式和语句被解析和求值的

alert(typeof foo); // “function”

function foo() {

// body…

}

可以对比一下:

alert(typeof foo); // “undefined”

var foo = function () {

// body…

};

明白了这个道理的你,是否还会掉入以下的陷阱呢?

function test() {

alert(“1″);

}

test();

function test() {

alert(“2″);

}

test();

运行以上代码片段,看到的两次弹窗显示的都是 “2”,为什么不是分别为 “1” 和 “2” 呢?很简单,test 的声明先于 test() 被解析,由于后者覆盖前者,所以两次执行的结果都是 “2”。

解决:多数情况下,我用函数表达式来代替函数声明,特别是在一些语句块中。

函数表达式

先看命名函数表达式,理所当然,就是它得有名字,例如:

var bar = function foo() {

// body…

};

要注意的是:函数名只对其函数内部可见。如以下陷阱:

var bar = function foo() {

foo(); // 正常运行

};

foo(); // 出错:ReferenceError

解决:尽量少用命名函数表达式(除了一些递归以及 debug 的用途),切勿将函数名使用于外部。

函数的自执行

对于函数表达式,可以通过后面加上 () 自执行,而且可在括号中传递参数,而函数声明不可以。陷阱:

// (1) 这只是一个分组操作符,不是函数调用!

// 所以这里函数未被执行,依旧是个声明

function foo(x) {

alert(x);

}(1);

以下代码片段分别执行都弹窗显示 “1”,因为在 
(1)
 之前,都为函数表达式,所以这里的 
()
非分组操作符,而为运算符,表示调用执行。

// 标准的匿名函数表达式

var bar = function foo(x) {

alert(x);

}(1);

// 前面的 () 将 function 声明转化为了表达式

(function foo(x) {

alert(x);

})(1);

// 整个 () 内为表达式

(function foo(x) {

alert(x);

}(1));

// new 表达式

new function foo(x) {

alert(x);

}(1);

// &&, ||, !, +, -, ~ 等操作符(还有逗号),在函数表达式和函数声明上消除歧义

// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了

true && function foo(x) {

alert(x);

}(1);

解决:这个陷阱的关键在于,弄清楚形形色色函数表达式的实质。

循环中的闭包

以下演示的是一个常见的陷阱:

var links =document.getElementsByTagName(“ul”)[0].getElementsByTagName(“a”);

for (var i = 0, l = links.length; i < l; i++) {

links[i].onclick = function (e) {

e.preventDefault();

alert(“You click link #” + i);

}

}

我们预期当点击第 i 个链接时,得到此序列索引 i 的值,可实际无论点击哪个链接,得到的都是 i 在循环后的最终结果:”5”。

解释一下原因:当 alert 被调用时,for 循环内的匿名函数表达式,保持了对外部变量 i的引用(闭包),此时循环已结束,i 的值被修改为 “5”。

解决:为了得到想要的结果,需要在每次循环中创建变量 i 的拷贝。以下演示正确的做法:

var links =document.getElementsByTagName(“ul”)[0].getElementsByTagName(“a”);

for (var i = 0, l = links.length; i < l; i++) {

links[i].onclick = (function (index) {

return function (e) {

e.preventDefault();

alert(“You click link #” + index);

}

})(i);

}

可以看到,
(function () { ... })()
 的形式,就是上文提到的
 函数的自执行 

i
 作为参数传给了 
index

alert
 再次执行时,它就拥有了对 
index
 的引用,此时这个值是不会被循环改变的。当然,明白了其原理后,你也可以这样写:

for (var i = 0, l = links.length; i < l; i++) {

(function (index) {

links[i].onclick = function (e) {

e.preventDefault();

alert(“You click link #” + index);

}

})(i);

}

JavaScript 中的陷阱的更多相关文章

  1. 【JavaScript】JavaScript中的陷阱大集合

    本文主要介绍怪异的Javascript,毋庸置疑,它绝对有怪异的一面.当软件开发者开始使用世界上使用最广泛的语言编写代码时,他们会在这个过 程中发现很多有趣的“特性”.即便是老练的Javascript ...

  2. JavaScript中的陷阱(关于变量声明,函数)

    查看:http://www.css88.com/archives/5347#more-5347

  3. JavaScript中的this陷阱的最全收集

    JavaScript来自一门健全的语言,所以你可能觉得JavaScript中的this和其他面向对象的语言如java的this一样,是指存储在实例属性中的值.事实并非如此,在JavaScript中,最 ...

  4. JavaScript中的this陷阱的最全收集 没有之一

    当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概 念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解 ...

  5. 转:JavaScript中的this陷阱的最全收集

    在其他地方看到的,觉得解释的狠详细,特此分享 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清 ...

  6. JavaScript 类型判断 —— typeof 以及 instanceof 中的陷阱

    JavaScript中基本类型包含Undefined.Null.Boolean.Number.String以及Object引用类型.基本类型可以通过typeof来进行检测,对象类型可以通过instan ...

  7. JavaScript中的this陷阱

    当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解释 ...

  8. JavaScript中for..in循环陷阱介绍

    for...in循环中的循环计数器是字符串,而不是数字它包含当前属性的名称或当前数组元素的索引,下面有个不错的示例大家可以参考下   大家都知道在JavaScript中提供了两种方式迭代对象: (1) ...

  9. JavaScript 中 4 种常见的内存泄露陷阱

    了解 JavaScript 的内存泄露和解决方式! 在这篇文章中我们将要探索客户端 JavaScript 代码中常见的一些内存泄漏的情况,并且学习如何使用 Chrome 的开发工具来发现他们.读一读吧 ...

随机推荐

  1. SpringMVC 多个数据源 配置多个事物管理器 Multiple Transaction Managers

    http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction ...

  2. UTF-8以字节为单位对Unicode进行编码

    UTF-8以字节为单位对Unicode进行编码.从Unicode到UTF-8的编码方式如下: Unicode编码(16进制) UTF-8 字节流(二进制) 000000 - 00007F 0xxxxx ...

  3. Android——TextView属性XML详解

    Android_TextView属性XML详解 博客分类: android   属性名称    描述 android:autoLink    设置是否当文本为URL链接/email/电话号码/map时 ...

  4. R语言两种方式求指定日期所在月的天数

                 R语言两种方式求指定日期所在月的天数 days_monthday<-function(date){ m<-format(date,format="%m& ...

  5. linux 批量创建用户获取8位随机密码

    #创建账号  分组不分组只有一列useradd无法添加三个账号 添加账号  获取密码  执行最后一句echo stu{4..6}|xargs -n 1|sed -r 's#(.*)#useradd \ ...

  6. 上手并过渡到PHP7(5)——轻量级“集合”迭代器-Generator

    轻量级“集合”迭代器-Generator泊学视频链接泊阅文档链接Generator是PHP 5.5加入的新语言特性.但是,它似乎并没有被很多PHP开发者广泛采用.因此,在我们了解PHP 7对Gener ...

  7. C++ 有用的网站

    C++ 有用的网站C++ Programming Language Tutorials ? C++ 编程语言教程.C++ Programming ? 这本书涵盖了 C++ 语言编程.软件交互设计.C+ ...

  8. (转)Linux下/etc/rc.local与/etc/init.d的区别与联系

    Linux下/etc/rc.local与/etc/init.d的区别与联系 2012-10-13 20:14:52|  分类: Linux学习|字号 订阅     1./etc/rc.local 这是 ...

  9. 在 Mac 上搭建 Nginx PHP Mysql 开发环境

    事实上这个过程跟Linux下安装都几乎相同,仅仅是部分命令有区别,大同小异. 网上看到非常多教程都是用 brew 之类的包管理器安装,可是 Mac 自带了 php , 难道还要再装一个第三方的?强迫症 ...

  10. c++ __int64

    C++的64位整数[原]by 赤兔 在做ACM题时,经常都会遇到一些比较大的整数.而常用的内置整数类型常常显得太小了:其中long 和 int 范围是[-2^31,2^31),即-2147483648 ...