OurJS


OurJS-我们的JS, 我们的技术-IT文摘; 专注JS相关领域;
我们热爱编程, 我们热爱技术;我们是高大上, 有品味的码农;

欢迎您订阅我们的技术周刊


我们会向您分享我们精心收集整理的,最新的行业资讯,技术动态,外文翻译,热点文章;
我们使用第三方邮件列表向您推送,我们不保存您的任何个人资料,注重您的隐私,您可以随时退订,

欢迎分享您的观点,经验,技巧,心得

让我们一起找寻程序员的快乐,探索技术, 发现IT人生的乐趣;


本网站使用缓存技术每次加载仅需很小流量, 可在手机中流畅浏览;
如果您发现任何BUG,请即时告知我们: ourjs(at)ourjs.com

JavaScript变量作用域(Variable Scope)和闭包(closure)的基础知识


分享到


分类 JS学习   关键字 JavaScript   发布 ourjs  1430183200081
注意 转载须保留原文链接,译文链接,作者译者等信息。  
在这篇文章中,我会试图讲解JavaScript变量的作用域和声明提升,以及许多隐隐藏的陷阱。为了确保我们不会碰到不可预见的问题,我们必须真正理解这些概念。

基本定义


作用范围是个“木桶”,里面装着变量。变量可以是局部或者全局性的,但在子范围中定义的变量是可以访问父范围的,这一点可能会造成一些困扰。

在JavaScript中使用"var"关键字声明变量。一旦在父范围宣声明,就会作为各自子范围的一部分。即在本地范围内有效,但本地定义的变量不可在全局范围内访问。

让我们来看一个例子。执行下面的代码,你会发现,你能打印出全局范围定义的变量,而全局范围无法访问局部范围定义的变量。


var agloballydefinedvariable = 'Global';
function someFunction() {
  var alocallydefinedvariable = 'Local';
  console.log(agloballydefinedvariable); // Global
}
console.log(alocallydefinedvariable);
// Uncaught ReferenceError: alocallydefinedvariable is not defined

作用域链(Scope Chain)


如果你忘记使用“var”的关键字来定义局部变量,事情可能会变得非常糟糕。为什么会这样呢?因为JavaScript会首先在父作用域内搜索一个未定义的变量,然后再到全局范围进行搜索。在下面的例子中,JavaScript知道变量“a”是someFunction()的一个局部变量,在anotherFunction()中它会寻找它父作用域内的变量。


var a = 1;
function someFunction() {
  var a = 2;
  function anotherFunction() {
    console.log(a); // 2
  }
}


更复杂的情况是,在下面的例子中,一个变量没有在函数中进行作用域的限定。

在someFunction()中调用了一个没有在函数范围内定义的变量 a=2; 这个分配将覆盖全局变量的值。
后续引用将指向全局变量的值。


var a = 1;
function someFunction() {
  a = 2;
  function anotherFunction() {
    console.log(a); // 2
  }
  anotherFunction();
}
someFunction();
console.log(a); //2


声明提升(Hoisting)


Hoisting会将在函数或全局范围内的变量“提升”到顶部声明的过程。请记住,只有量声明被提升了,初始化或值分配等等没有变化,在下面的代码的情况下,第一个输出将不确定...但它不会抛出任何错误。

console.log(a); //undefined
var a = 1;
console.log(a); //1


Window范围


在基于浏览器的JavaScript中,定义为全局范围内的一部分变量实际上是所谓的“Window”对象的属性。这里的Window是指“容器”。换句话说,当你想从一个局部范围修改全局定义的变量,你也可以通过修改Window对象的相应的属性来做到这一点。


var myVariable = 'Global Scope';
function myFunction() {
  window.myVariable = 'Something Else';
}
myFunction();
console.log(myVariable); // Something Else


可能的陷阱


如果在函数内部分配一个以前没有被定义的变量的值,它会自动成为全局范围的一部分。

function myFunction() {
  myVariable = 'JavaScript';
}
myFunction();
console.log(myVariable); //JavaScript


如果你不小心忘记定义了一个局部变量,你的整个脚本可能会运行混乱。

var city="LA";
var team="Lakers";

function showTeam () {
    console.log (city + " " + team);
}

function showCity () {
    city = "Moscow";
    console.log (city);
}

showTeam(); // LA Lakers
showCity(); // Moscow

/*
因为上面的 showCity 中定义的变量 "city" 没有使用 "var" 声明,全局范围内的变量被覆盖了。因此会导致下面的问题 :)
*/
showTeam(); // Moscow Lakers


内部函数依然会存储局部变量即使它的外部函数已经执行完毕

这听起来可能有点怪异,看一个例子,就会更容易理解。解释这一点的最好办法是使用一个简单的“Hello World”的例子。


function greet(who) {
    var iterations = 0;
    return function () {
        console.log(++iterations);
        return 'Hello ' + who + '!';
    };
}

var greeting = greet('World');
console.log(typeof greeting); //function
console.log(typeof greeting()); //string & iterations=1
console.log(greeting()); //Hello World! & iterations=2
console.log(greeting("Universe")); //Hello World! & iterations=3
//输出不是 Hello Universe. world 被闭包封闭保存了起来


注*  在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
闭包的概念出现于60年代,最早实现闭包的程序语言是Scheme。之后,闭包被广泛使用于函数式编程语言如ML语言和LISP。很多命令式程序语言也开始支持闭包。  引自:Wiki


正如你上面看到的那样,greet() 返回一个被称为“闭包”的内部函数。闭包除了会储存他们自己本地作用域内部的封闭起来的函数和变量外,还会存储外部引用的参数。参看我们的具体例子,参数 who 和 iterations 就是被闭包封闭起来的局部变量。

这意味着,greeting已成为一个包含who和iterations在内的函数(直接返回的匿名函数)。- 它不会再次执行greet,它只会执行闭包而且返回结果永远是 "Hello World!"。 


原文地址: 点此
社区评论 ( Beta版 )
OnceDoc 您自己的企业内容管理系统——文档、流程、知识库、报表、网盘All In One

访问404页面,寻找丢失儿童
 热门文章 - 分享最多
  1. NativeScript的工作原理:用JavaScript调用原生API实现跨平台
  2. 6款基于Node.JS的开源内容管理和静态网站生成系统
  3. NativeScript的竞争者React Native:又一个用JavaScript写原生iOS应用的项目
  4. 2015年度开发者调查:JS最流行;ObjC工资最高;最想学Android
  5. Session劫持与Session-ID的安全长度
  6. 用原生HTML5控件实现输入框自动提示(下拉列表补全)功能
  7. 编写高性能HTML网页应用
  8. Image Lazy Load:那些延时加载图片的开源插件(jQuery)
  9. 微软,IBM,ARM等大公司先后加入对Node.js/io.js的支持
  10. 使用Node.JS监听文件夹变化
  11. 用 OnceAir 搭建个人Git/Svn/照片备份服务器,每年电费7块钱

 相关阅读 - JS学习
  1. 用jQuery为跳转链接锚点添加平滑滚动动画效果(如回到顶部按钮)
  2. 用纯css改变下拉列表select框的默认样式
  3. Atom编辑器嵌入Node.JS引擎实践
  4. AngularJS和$scope.$apply()用法的最佳实践
  5. 微软,IBM,ARM等大公司先后加入对Node.js/io.js的支持
  6. 用原生HTML5控件实现输入框自动提示(下拉列表补全)功能
  7. 让我们写快速的JavaScript,JS性能优化小窍门
  8. 通过Web Audio API可视化输出MP3音乐频率波形
  9. 用页面可见性(Page Visibility)API创建用户体验良好的网站
  10. 理解Node.js的事件循环(Event Loop)和线程池

 关键字 - JavaScript
  1. 少年,不要滥用箭头函数啊:JS中lambda表达式的优缺点和使用场景
  2. 用JavaScript获取当月第一天和最后一天
  3. 让pre和textarea等HTML元素去掉滚动条自动换行自适应文本内容高度
  4. ES6中的Map与JSON的相互转化(序列和持久化)
  5. Facebook发布全新JavaScript引擎Hermes:越来越像Java字节码,JS要统一全端?
  6. 在嵌入式设备树莓派上编译QuickJS教程:一个C语言编写的极简JavaScript引擎
  7. 为什么我不建议你将JavaScript作为主力语言
  8. 使用JavaScript的Proxy监听对象属性变化并进行类public/private的访问控制
  9. Node.JS中UDP打洞穿透内网路由,架设内网服务器技术详解及源码
  10. JavaScript求一个字符串的字节长度

 欢迎订阅 - 技术周刊

我们热爱编程, 我们热爱技术; 我们是高端, 大气, 上档次, 有品味, 时刻需要和国际接轨的码农; 欢迎您订阅我们的技术周刊; 您只需要在右上角输入您的邮箱即可; 我们注重您的隐私,您可以随时退订.
加入我们吧! 让我们一起找寻码农的快乐,探索技术, 发现IT人生的乐趣;


 关注我们

我们的微信公众号: ourjs-com
打开微信扫一扫即可关注我们:
IT文摘-程序员(码农)技术周刊

ourjs官方微信号