javascript进阶(三)— — 变量提升
2016-05-28 15:19
465 查看
outline
引子
Scoping in JavaScript
Hoisting in JavaScript
ES6
原文参考https://segmentfault.com/a/1190000003114255
最近在阮一峰的ES6,里面在讲let的时候提到了变量提升,看了下,感觉可以解释清楚不少以前没有理解的东西.
先看个例子
有人认为这里!foo是false,那么赋值语句应该不执行.那么输出结果应该是1,however,结果是10!
再来一个例子
看到这个例子,大家会认为,哎,这里里面的a = 10会覆盖外面的a = 1,觉得答案是10.但是~,答案是1!!
估计这两个例子,可能很多人都会觉得不理解,不理解就对了,往下看!
关于JavaScipt中的作用域问题,可以参考我的这篇文章JavaScript没有块级作用域.这里我就提下,在JavaScript中只有两种作用域:全局作用域与function作用域.
类似于上面的C代码中的依靠
类似的代码却得不到一样的结果的根源在于在上面的JS代码中,
正如上面所说的JS中只有global和function两种作用域,那么这里,我们也可以通过function作用域来隔离
这里我们用一个function作用域来隔离内外的x,避免内部x赋值对外部的x的影响.
在Javascript中,变量进入一个作用域可以通过下面四种方式:
语言自定义变量:所有的函数作用域中都存在this和arguments这两个默认变量
函数形参:函数的形参存在函数作用域中
函数声明:
变量定义:
JS代码真正执行过程中,在代码运行前,函数声明和变量定义通常会被解释器移动到其所在作用域的最顶部.
这就是所谓的变量提升,对于
举个例子:
对于这段代码JS会这样认为:
对于
对于JS中函数定义,我们可以采取
这里例子等价于
从这个等价函数中,我们可以很轻松的看出函数bar被提升,所以后面可以正常的执行.但是
结合JS作用域以及JS变量提升,回头来看之前引子中的例子
这里例子等价于
在函数bar外面foo为1,但在函数bar内,定义一个新的foo,这个时候foo是undefined,
另外一个例子:
等价于
这里函数b的外部定义了a,值为1.函数b内,声明函数a的过程被提升(hoist),之后a被赋值为10;由于是函数b中新定义了a,所以对函数b外部的a并不造成影响,最后得console.log(a)输出的值还是1;
在ES6中,通过语法糖可以帮助开发者避免一些作用域以及变量提升带来的问题.比如let的使用.这里举两个例子,一个关于变量的,一个关于函数的
用let替换之前例子中的var
babel编译后生成:
执行结果是1;
ES5执行结果是
ES6以babel编译为例,编译出来结果是
babel编译自动把里面同名函数f变成了_f.执行结果是
引子
Scoping in JavaScript
Hoisting in JavaScript
ES6
引子
原文参考https://segmentfault.com/a/1190000003114255最近在阮一峰的ES6,里面在讲let的时候提到了变量提升,看了下,感觉可以解释清楚不少以前没有理解的东西.
先看个例子
1 [code]2 3 4 5 6 7 8 | var foo = 1; [code]function bar() { if (!foo) { var foo = 10; } console.log(foo); } bar(); |
再来一个例子
1 2 3 4 5 6 7 8 | var a = 1; function b() { a = 10; return; function a() {} } b(); console.log(a); |
估计这两个例子,可能很多人都会觉得不理解,不理解就对了,往下看!
Scoping in JavaScript
关于JavaScipt中的作用域问题,可以参考我的这篇文章JavaScript没有块级作用域.这里我就提下,在JavaScript中只有两种作用域:全局作用域与function作用域.1 2 3 4 5 6 7 89 | #include <stdio.h> int main() { int x = 1; printf("%d\n", x); // 1 if (1) { int x = 2; printf("%d\n", x); // 2 } printf("%d\n", x); // 1 } |
{}来形成作用域在JavaScript中是不成立的.来看一段跟上面功能类似的JavaScript代码
1 2 3 4 5 6 7 | var x = 1; console.log(x);//1 if(1){ var x = 2; console.log(x);//2 } console.log(x);//2 |
{}中
var x = 2和
{}外面的
var x = 1;其实是同一个x,
{}中修改
{}里面的x影响了
{}外面的x的.
正如上面所说的JS中只有global和function两种作用域,那么这里,我们也可以通过function作用域来隔离
{}内外两个x
1 2 3 4 5 6 7 89 | var x = 1; console.log(x);//1 if(1){ (function(){ var x = 2; console.log(x);//2 })() } console.log(x);//1 |
Hoisting in JavaScript
在Javascript中,变量进入一个作用域可以通过下面四种方式:语言自定义变量:所有的函数作用域中都存在this和arguments这两个默认变量
函数形参:函数的形参存在函数作用域中
函数声明:
function foo() {}
变量定义:
var foo
JS代码真正执行过程中,在代码运行前,函数声明和变量定义通常会被解释器移动到其所在作用域的最顶部.
这就是所谓的变量提升,对于
var a = 1这句话而言,我们可以拆分成两部分来看变量定义与变量赋值.不管
var a = 1在作用域的什么地方,变量定义会被提升到到当前作用域的顶部来执行.
举个例子:
1 2 3 4 | function foo() { bar(); var x = 1; } |
1 2 3 45 | function foo(){ var x; bar(); x = 1; } |
var x = 1,
var x被提到
bar()之前的过程就是变量提升(hoist),这里要主要提升不仅包含
var a这种变量,还包括
function a(){};
对于JS中函数定义,我们可以采取
function foo(){}或者采取
var foo = function(){}(这里我们不谈var foo = new Function()这么烦的方式).这两种方式有什么区别?
1 2 3 4 5 6 7 89 | function test() { foo(); // TypeError "foo is not a function" bar(); // "this will run!" var foo = function () { // function expression assigned to local variable 'foo' console.log("this won't run!"); } function bar() { // function declaration, given the name 'bar' console.log("this will run!"); } } test(); |
1 2 3 4 5 6 7 89 | function test() { //hoist variable foo and function bar var foo; //undefined function bar() { console.log("this will run!"); } foo(); bar(); foo = function () { console.log("this won't run!"); } } test(); |
var foo = function(){}的方式提升的是foo这个变量,并且初始值为undefined,所以后面执行的时候就相当于执行foo()的时候,就会出现 foo is not a function().
function foo(){}提升的是整个函数定义.
var foo = function(){}提升的是foo变量的定义.
结合JS作用域以及JS变量提升,回头来看之前引子中的例子
1 2 3 4 5 6 7 8 | var foo = 1; function bar() { if (!foo) { var foo = 10; } console.log(foo); } bar(); |
1 2 3 4 5 6 7 89 | var foo; foo = 1; function bar(){ var foo;//undefined if(!foo){ foo = 10; } console.log(foo); } bar(); |
!foo就变成了true,执行赋值操作,foo=10;这个时候输出的foo是函数bar内刚定义的foo,输出的值为10;
另外一个例子:
1 2 3 4 5 6 7 8 | var a = 1; function b() { a = 10; return; function a() {} } b(); console.log(a); |
1 2 3 4 5 6 7 89 | var a; a = 1; function b(){ function a(){}; a = 10; return; } b(); console.log(a); |
ES6
在ES6中,通过语法糖可以帮助开发者避免一些作用域以及变量提升带来的问题.比如let的使用.这里举两个例子,一个关于变量的,一个关于函数的
ES6中关于变量的例子:
用let替换之前例子中的var1 2 3 4 5 6 7 8 | let foo = 1; function bar() { if (!foo) { let foo = 10; } console.log(foo); } bar(); |
1 2 3 4 5 6 7 8 | var foo = 1; function bar() { if (!foo) { var _foo = 10; } console.log(foo); } bar(); |
ES6中关于函数的例子:
1 2 3 45 | function f() { console.log('I am outside!'); } if(true) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f(); |
I am inside!,两次function f(){}的声明都被提升了,赋值取最后一次赋值.所以执行结果是
I am inside!
ES6以babel编译为例,编译出来结果是
1 2 3 4 5 6 7 89 | 'use strict'; function f() { console.log('I am outside!'); } if (true) { // 重复声明一次函数f var _f = function _f() { console.log('I am inside!'); }; } f(); |
I am outside!
原文链接:http://warjiang.github.io/devcat/2016/05/21/JavaScript变量提升/
相关文章推荐
- 深入理解js中this的用法
- javascript进阶(二)— — 异步编程
- fastjson过滤属性,重点在于PropertyFilter 这个东西(应用场景,hibernate懒加载过滤不要的属性)
- JavaScript 变量声明提升
- javascript高级函数
- 借助 SublimeLinter 编写高质量的 JavaScript & CSS 代码
- JavaScript初学者难以知道的事(初级篇三)
- js常用操作
- JavaScript 函数中的值传递
- 理清JavaScript正则表达式--下篇
- JavaScript 字符串不变性
- 《Web开发过滤Javascript、HTML的方法》
- Js作用域与作用域链详解
- 用JS写Ajax的请求函数(1)
- 不同js异步函数同步的实现方法
- js获取键盘键值
- JSP---web.xml中设置前后台不同的头部和底部
- 4类 JavaScript 内存泄漏及如何避免
- javaScript取得当前元素的下一个元素
- js 给json添加新的字段,或者添加一组数据,在JS数组指定位置删除、插入、替换元素