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. 【WPF】CommandParameter解决多传参问题

    方法一:传参按钮控件自身绑定的ItemSource 用WAF框架实现MVVM,按钮的点击事件都要通过Command来传递到这个View对应的ViewModel上,再通过ViewModel传递到上层的C ...

  2. 电快速脉冲群EFT(概念)

    电快速瞬变脉冲群 简称EFT(Electrical Fast Transient)是指脉冲群有特定的持续时间(规定为15mS),特定的脉冲周期(300mS)的脉冲, 脉冲群中的单个脉冲有特定的重复周期 ...

  3. Hive Tunning(二)优化存储

    接着上一章我们讲的hive的连接策略,现在我们讲一下hive的数据存储. 下面是hive支持的数据存储格式,有我们常见的文本,JSON,XML,这里我们主要讲一下ORCFile. Built-in F ...

  4. 分享一下自己写的Python 3的各种PDF文档【花了半年时间那】

    这些文档花了我半年的时间去整理.因为是第一次进行整理,希望帮助后来者少走弯路.毕竟是第一次整理.哪些地方不到位,希望大家和我练习,我们一起把它做好,以下就直接给出下载地址了,都是免积分的下载奥.因此. ...

  5. Web API(三):创建Web API项目

    在本篇文章中将讲解如何使用Visual Studio创建一个新的ASP.NET Web API项目. 在Visual Studio中有两种方式用于创建Web API项目: 1.创建带MVC的Web A ...

  6. [Cxf] spring-cxf 配置

    1.在web.xml中配置servlet <!-- spring监听的配置 --> <listener> <listener-class>org.springfra ...

  7. 关于Cocos2d-x中自己定义的类的名字和Cocos2d-x引擎库中的类的名字重复的解决方法

    方法一: 修改自己定义的类的名字,VS2013中可以用Ctrl+H来替换某个特定的单词,Ctrl+F是用来查询某个单词所在的位置或者有没有存在. 方法二: 1.给自己定义的类的.h和.cpp文件的整体 ...

  8. 【转】【Asp.Net】ASP.Net Response.ContentType 详细列表

    不同的ContentType 会影响客户端所看到的效果.默认的ContentType为 text/html 也就是网页格式. 代码如: <% response.ContentType =&quo ...

  9. Spring IO platform 简介

    前提:熟悉Spring基础知识. 简介:Spring IO Platform将 the core Spring APIs 集成到一个Platform中.它提供了Spring portfolio中的大量 ...

  10. 第三百二十一节,Django框架,发送邮件

    第三百二十一节,Django框架,发送邮件 全局配置settings.py EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' ...