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 相关文章推荐
JavaScript CSS修改学习第五章 给“上传”添加样式
Feb 19 Javascript
jquery的ajaxSubmit()异步上传图片并保存表单数据演示代码
Jun 04 Javascript
html的DOM中document对象anchors集合用法实例
Jan 21 Javascript
jQuery获取页面元素绝对与相对位置的方法
Jun 10 Javascript
jQuery中bind(),live(),delegate(),on()绑定事件方法实例详解
Jan 19 Javascript
基于JavaScript实现全选、不选和反选效果
Feb 15 Javascript
JavaScript定义函数_动力节点Java学院整理
Jun 27 Javascript
vue组件开发之用户无限添加自定义填写表单的方法
Aug 28 Javascript
解决vue 绑定对象内点击事件失效问题
Sep 05 Javascript
vue开发拖拽进度条滑动组件
Sep 21 Javascript
jQuery Datatables 动态列+跨列合并实现代码
Jan 30 jQuery
js实现跳一跳小游戏
Jul 31 Javascript
详解微信小程序 登录获取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
漫威DC即将合作联动,而双方早已经秘密开始
2020/04/09 欧美动漫
php中文字符串截取方法实例总结
2014/09/30 PHP
smarty内置函数{loteral}、{ldelim}和{rdelim}用法实例
2015/01/22 PHP
摘自织梦CMS的HTTP文件下载类
2015/08/08 PHP
PHP缓冲区用法总结
2016/02/14 PHP
PHP判断FORM表单或URL参数来的数据是否为整数的方法
2016/03/25 PHP
PHP文件操作详解
2016/12/30 PHP
laravel-admin自动生成模块,及相关基础配置方法
2019/10/08 PHP
PHP过滤器 filter_has_var() 函数用法实例分析
2020/04/23 PHP
克隆javascript对象的三个方法小结
2011/01/12 Javascript
javascript中clone对象详解
2014/12/03 Javascript
js字符串完全替换函数分享
2014/12/03 Javascript
JavaScript中使用Math.PI圆周率属性的方法
2015/06/14 Javascript
使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室
2015/08/21 NodeJs
学做Bootstrap的第一个页面
2016/05/15 HTML / CSS
自己动手制作基于jQuery的Web页面加载进度条插件
2016/06/03 Javascript
jquery制做精致的倒计时特效
2016/06/13 Javascript
jQuery实现订单提交页发送短信功能前端处理方法
2016/07/04 Javascript
javascript容错处理代码(屏蔽js错误)
2017/01/20 Javascript
jQuery弹出层插件popShow(改进版)用法示例
2017/01/23 Javascript
vue 实现动态路由的方法
2020/07/06 Javascript
vantUI 获得piker选中值的自定义ID操作
2020/11/04 Javascript
python中字典(Dictionary)用法实例详解
2015/05/30 Python
浅谈Python的Django框架中的缓存控制
2015/07/24 Python
Python如何实现MySQL实例初始化详解
2017/11/06 Python
Python切片工具pillow用法示例
2018/03/30 Python
python实现桌面壁纸切换功能
2019/01/21 Python
Python常见的pandas用法demo示例
2019/03/16 Python
tensorflow使用range_input_producer多线程读取数据实例
2020/01/20 Python
CSS3 实现倒计时效果
2020/11/25 HTML / CSS
opencv实现图像几何变换
2021/03/24 Python
新郎婚宴答谢词
2014/01/19 职场文书
人事科岗位职责范本
2014/03/02 职场文书
成绩单评语
2015/01/04 职场文书
雾霾停课通知
2015/04/24 职场文书
英语投诉信范文
2015/07/03 职场文书