JavaScript变量作用域_动力节点Java学院整理


Posted in Javascript onJune 27, 2017

在JavaScript中,用var申明的变量实际上是有作用域的。

如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:

'use strict';

function foo() {
  var x = 1;
  x = x + 1;
}

x = x + 2; // ReferenceError! 无法在函数体外引用变量x

如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:

'use strict';

function foo() {
  var x = 1;
  x = x + 1;
}

function bar() {
  var x = 'A';
  x = x + 'B';
}

由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:

'use strict';

function foo() {
  var x = 1;
  function bar() {
    var y = x + 1; // bar可以访问foo的变量x!
  }
  var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}

如果内部函数和外部函数的变量名重名怎么办?

'use strict';

function foo() {
  var x = 1;
  function bar() {
    var x = 'A';
    alert('x in bar() = ' + x); // 'A'
  }
  alert('x in foo() = ' + x); // 1
  bar();
}

这说明JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

变量提升

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:

'use strict';

function foo() {
  var x = 'Hello, ' + y;
  alert(x);
  var y = 'Bob';
}

foo();

虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是alert显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码相当于:

function foo() {
  var y; // 提升变量y的申明
  var x = 'Hello, ' + y;
  alert(x);
  y = 'Bob';
}

由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量:

function foo() {
  var
    x = 1, // x初始化为1
    y = x + 1, // y初始化为2
    z, i; // z和i为undefined
  // 其他语句:
  for (i=0; i<100; i++) {
    ...
  }
}

全局作用域

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'

因此,直接访问全局变量course和访问window.course是完全一样的。

你可能猜到了,由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:

'use strict';

function foo() {
  alert('foo');
}

foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用

进一步大胆地猜测,我们每次直接调用的alert()函数其实也是window的一个变量:

'use strict';

window.alert('调用window.alert()');
// 把alert保存到另一个变量:
var old_alert = window.alert;
// 给alert赋一个新函数:
window.alert = function () {}

// 恢复alert:
window.alert = old_alert;
alert('又可以用alert()了!');

这说明JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。

名字空间

全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。

减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:

// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
  return 'foo';
};

把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。

许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。

局部作用域

由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的:

'use strict';

function foo() {
  for (var i=0; i<100; i++) {
    //
  }
  i += 100; // 仍然可以引用变量i
}

为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:

'use strict';

function foo() {
  var sum = 0;
  for (let i=0; i<100; i++) {
    sum += i;
  }
  i += 1; // SyntaxError
}

常量

由于varlet申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:

var PI = 3.14;

ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:

'use strict';

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14
Javascript 相关文章推荐
jquery打开直接跳到网页最下面、最低端实现代码
Apr 22 Javascript
JavaScript中Date对象的常用方法示例
Oct 24 Javascript
jQuery实现仿QQ空间装扮预览图片的鼠标提示效果代码
Oct 30 Javascript
Jquery promise实现一张一张加载图片
Nov 13 Javascript
浅谈jQuery hover(over, out)事件函数
Dec 03 Javascript
vue+ElementUI实现订单页动态添加产品数据效果实例代码
Jul 13 Javascript
JS实现微信摇一摇原理解析
Jul 22 Javascript
用React-Native+Mobx做一个迷你水果商城APP(附源码)
Dec 25 Javascript
最后说说Vue2 SSR 的 Cookies 问题
May 25 Javascript
微信小程序实现自定义加载图标功能
Jul 19 Javascript
three.js显示中文字体与tween应用详析
Jan 04 Javascript
详解vue3中组件的非兼容变更
Mar 03 Vue.js
详解微信小程序 登录获取unionid
Jun 27 #Javascript
JavaScript定义函数_动力节点Java学院整理
Jun 27 #Javascript
详解vue项目构建与实战
Jun 27 #Javascript
微信小程序 蓝牙的实现实例代码
Jun 27 #Javascript
微信小程序 开发MAP(地图)实例详解
Jun 27 #Javascript
微信小程序商品到详情的实现
Jun 27 #Javascript
微信小程序的分类页面制作
Jun 27 #Javascript
You might like
PHP常用特殊运算符号和函数总结(php新手入门必看)
2013/02/02 PHP
jQuery+php实现ajax文件即时上传的详解
2013/06/17 PHP
PHP获取数组长度或某个值出现次数的方法
2015/02/11 PHP
php利用ffmpeg提取视频中音频与视频画面的方法详解
2017/06/07 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
2019/09/16 PHP
PHP开启目录引索+fancyindex漂亮目录浏览带搜索功能
2019/09/23 PHP
提高网站信任度的技巧
2008/10/17 Javascript
jQuery+CSS 实现的超Sexy下拉菜单
2010/01/17 Javascript
js中array的sort()方法使用介绍
2014/02/20 Javascript
JS按回车键实现登录的方法
2014/08/25 Javascript
Angularjs注入拦截器实现Loading效果
2015/12/28 Javascript
基于javascript实现checkbox复选框实例代码
2016/01/28 Javascript
JS验证图片格式和大小并预览的简单实例
2016/10/11 Javascript
微信小程序 网络API 上传、下载详解
2016/11/09 Javascript
浅谈angular2的http请求返回结果的subcribe注意事项
2017/03/01 Javascript
JS去掉字符串前后空格、阻止表单提交的实现代码
2017/06/08 Javascript
JavaScript简介_动力节点Java学院整理
2017/06/26 Javascript
Vue iview-admin框架二级菜单改为三级菜单的方法
2018/07/03 Javascript
angular5 子组件监听父组件传入值的变化方法
2018/09/30 Javascript
Nuxt.js开启SSR渲染的教程详解
2018/11/30 Javascript
微信小程序Echarts覆盖正常组件问题解决
2019/07/13 Javascript
Linux下Pycharm、Anaconda环境配置及使用踩坑
2018/12/19 Python
python支付宝支付示例详解
2019/08/22 Python
利用python实现PSO算法优化二元函数
2019/11/13 Python
用python实现一个简单计算器(完整DEMO)
2020/10/14 Python
英国在线泳装店:Simply Swim
2019/05/05 全球购物
专业销售业务员求职信
2013/11/18 职场文书
完美主义个人的自我评价
2014/02/17 职场文书
竞选团支书演讲稿
2014/04/28 职场文书
2014年英语教师工作总结
2014/12/03 职场文书
工程质检员岗位职责
2015/04/08 职场文书
2015年安全员工作总结范文
2015/04/22 职场文书
消防演习通知
2015/04/25 职场文书
PHP中多字节字符串操作实例详解
2021/08/23 PHP
Mysql排查分析慢sql之explain实战案例
2022/04/19 MySQL
MySQL查询日期时间
2022/05/15 MySQL