博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javascript 作用域和变量提升
阅读量:6814 次
发布时间:2019-06-26

本文共 3375 字,大约阅读时间需要 11 分钟。

hot3.png

首先大家看一下下面的代码,猜猜会输出什么结果?

var foo = 1;function bar() {    if (!foo) {        var foo = 10;    }    alert(foo);}bar();

答案是10!

你是否会疑惑条件语句if(!foo)并不会执行,为什么foo会被赋值为10

再来看第二个例子

var a = 1;function b() {    a = 10;        return;        function a() {}}b();alert(a);

答案还是10吗?显然不是,alert输出了1

如果你仍然对上面两个输出结果摸不着头脑,那么请认真阅读这篇文章!

Scoping in Javascript

的作用域已经是老生常谈的问题了,但是不一定每个人都能准确理解。

我们先来看一下C语言的一个例子:

#include 
int main() {    int x = 1;    printf("%d, ", x); // 1    if (1) {        int x = 2;        printf("%d, ", x); // 2    }    printf("%d\n", x); // 1}

程序依次输出了1,2,1

为什么第三个输出了1而不是2呢?因为在C语言中,我们有块级作用域(block-level scope)。在一个代码块的中变量并不会覆盖掉代码块外面的变量。我们不妨试一下Javascript中的表现

var x = 1;console.log(x); // 1if (true) {    var x = 2;    console.log(x); // 2}console.log(x); // 2

输出的结果为1,2,2 if代码块中的变量覆盖了全局变量。那是因为JavaScript是一种函数级作用域(function-level scope)所以if中并没有独立维护一个scope,变量x影响到了全局变量x

C,C++,C#和都是块级作用域语言,那么在中,我们怎么实现一种类似块级作用域的效果呢?答案是闭包。

function foo() {    var x = 1;    if (x) {        (function () {var x = 2;// some other code        }());    }        // x is still 1.}

上面代码在if条件块中创建了一个闭包,它是一个立即执行函数,所以相当于我们又创建了一个函数作用域,所以内部的x并不会对外部产生影响。函数级作用域指的是,只要创建一个新的函数,就可以将变量分隔开,他们之间相互独立。C语言的块级作用域则是值,一对{ }括号就可以将变量分隔开,使{ }内外的数据相互独立。

Hoisting in Javascript

在Javascript中,变量进入一个作用域可以通过下面四种方式:

  1. 语言自定义变量:所有的作用域中都存在this和arguments这两个默认变量

  2. 函数形参:函数的形参存在函数作用域中

  3. 函数声明:function foo() {}

  4. 变量定义:var foo

其中,___在代码运行前,函数声明和变量定义通常会被解释器移动到其所在作用域的最顶部___,如何理解这句话呢?

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

上面这段在吗,被代码解释器编译完后,将变成下面的形式:

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

我们注意到,x变量的定义被移动到函数的最顶部。然后在bar()后,再对其进行赋值。

再来看一个例子,下面两段代码其实是等价的:

function foo() {    if (false) {        var x = 1;    }        return;        var y = 1;}
function foo() {    var x, y;    if (false) {        x = 1;    }        return;    y = 1;}

所以变量的上升(Hoisting)只是其定义上升,而变量的赋值并不会上升。

我们都知道,创建一个函数的方法有两种,一种是通过函数声明function foo(){}

另一种是通过定义一个变量var foo = function(){} 那这两种在代码执行上有什么区别呢?

来看下面的例子:

function test() {    foo(); // TypeError "foo is not a function"    bar(); // "this will run!"        var foo = function () { // function expression assigned to local variable 'foo'        alert("this won't run!");    }        function bar() { // function declaration, given the name 'bar'        alert("this will run!");    }}test();

在这个例子中,foo()调用的时候报错了,而bar能够正常调用

我们前面说过变量会上升,所以var foo首先会上升到函数体顶部,然而此时的foo为undefined,所以执行报错。而对于函数bar, 函数本身也是一种变量,所以也存在变量上升的现象,但是它是上升了整个函数,所以bar()才能够顺利执行。

再回到一开始我们提出的两个例子,能理解其输出原理了吗?

var foo = 1;function bar() {    if (!foo) {            var foo = 10;    }    alert(foo);}bar();

其实就是:

var foo = 1;function bar() {    var foo;    if (!foo) {        foo = 10;    }    alert(foo);}bar();
var a = 1;function b() {    a = 10;        return;        function a() {}这里建立了一个匿名函数对象,typeof(a)=function}b();alert(a);

其实就是:

var a = 1;function b() {        function a() {}//这里建立了一个匿名函数对象,typeof(a)=function    a = 10;        return;}b();alert(a);

实验一下:

var a =1;function b(){	function a(){a=100;}	/*a =10;*/	alert(a);	alert(typeof(a));	return;}b();alert(a);

会弹出

173522_S4ml_2392809.png

function

1

这就是为什么,我们写代码的时候,变量定义总要写在最前面。

ES6有何区别

在ES6中,存在let关键字,它声明的变量同样存在块级作用域。

而且函数本身的作用域,只存在其所在的块级作用域之内,例如:

function f() { console.log('I am outside!'); }if(true) {   // 重复声明一次函数f   function f() { console.log('I am inside!'); }}f();

上面这段代码在ES5中的输出结果为I am inside!因为f被条件语句中的f上升覆盖了。

在ES6中的输出是I am outside!块级中定义的函数不会影响外部。

转载于:https://my.oschina.net/donngchao/blog/634480

你可能感兴趣的文章
python中的面相对象
查看>>
Spring缓存注解@Cache使用
查看>>
去除wordpress的category各方法对比
查看>>
traceroute
查看>>
精通汇编语言,有兴趣一起搞破解的请进!
查看>>
C#缺省参数可以让代码变得更加简洁明了与时俱进心里敞亮了很多了
查看>>
【自然框架】js版的QuickPager分页控件 V2.0
查看>>
poj-2049 Finding Nemo *
查看>>
模块化编程本质探讨
查看>>
利用博客与视频分享和交流知识和经验
查看>>
js操作dom对象
查看>>
Windows2003服务器安全配置:先关闭不需要的端口(转自)
查看>>
HDU1247 Hat’s Words 【trie树】
查看>>
iOS开发--动画篇之layout动画深入
查看>>
WorldWind源码剖析系列:视景体类Frustum
查看>>
(转)完整java开发中JDBC连接数据库代码和步骤
查看>>
Redis Lua脚本原理
查看>>
有时间测试dism
查看>>
/Users/alamps/AndroidStudioProjects/Demo10ScrollView
查看>>
【Swift】iOS UICollectionView 计算 Cell 大小的陷阱
查看>>