做用域的概念对于初学者来讲可能比较难,它涉及到变量,函数等基础知识,理解做用域对于理解做用域链和闭包是很是重要的,今天闲来一块儿复习下做用域:闭包
一、定义
做用域(scope)指的是变量可访问的范围,在JavaScript中只有两种做用域:一个全局做用域,另外一个是函数做用域。全局做用域指变量在任何函数外声明(若是在函数内部声明的时候没有用var关键字,那就成了隐式全局声明了,也是全局变量,在严格模式下是禁止的),那么这样的话,在当前做用域下的任何地方均可以访问到这个全局变量;至于函数做用域,指的是变量在函数内部显式声明,这个变量只能在函数内部内被访问到,函数外部没法访问;函数
在全局声明的变量成为全局变量,在函数内部定义的变量成为局部变量,局部变量只能在函数内部读取;学习
var v = 1; function f(){ console.log(v); } f()// 1
在上面的代码中,变量v是全局中声明,为全局变量,在函数内就能读取到,全部输出结果为1spa
然鹅,在函数内部定义的变量为局部变量,在函数外没法读取哦,以下:code
function f(){ var v = 1; }
//输出变量v console.log(v) // ReferenceError: v is not defined
在上面的代码中,因为变量v是在函数f内部定义的,因此在函数外部没法读取到,所以会报undefined哦;blog
若是在函数外部和内部同时出现同名变量呢?在函数内部读取的应该是全局的仍是局部的呢?答案是局部变量,函数内部的局部变量会覆盖同名全局变量:ip
var v = 1;//全局变量 function f(){ var v = 2;//局部变量 console.log(v); } f() // 2 console.log(v); // 1
在上面的代码中,在全局和函数f中都有定义变量v,可是因为两个变量同名了,并且在f内部读取的变量v,全部局部变量v会覆盖掉全局变量v;作用域
这里有个点须要注意下,前面我一直说的是显式声明变量,若是在函数内部隐式声明了变量,即没有用var命令去声明变量,这个时候这个变量就是全局变量,在开发中,必定要避免隐式声明变量,解决办法是在采起严格模式,严格模式下禁止隐式声明变量,会报错;开发
function fn(){
x = 5;
}; console.log(x);// 5
在上面代码中,就是读取了一个函数内部的隐式全局变量,这个变量在哪里均可以访问到;io
二、变量提高
JavaScript引擎的工做方式是,在代码正式执行以前,会有预解析的过程,先解析(通读)代码,获取全部被声明的变量和函数,而后再一行一行地运行。这形成的结果,就是全部的变量的声明语句和全部的函数在其做用域内,都会被提高到代码的头部,这就叫作变量提高(hoisting),看看栗子:
console.log(a);//undefined var a = 1;
上面的代码输出结果为undefined,为何?由于在预解析阶段,变量a声明被提高到了顶部,到执行console.时,a只是声明了,并无到赋值(js代码从上到下依次执行),此时不会报错,由于存在变量提高,真正是这样运行的:
var a; console.log(a);//undefined a = 1;
最后的结果是显示undefined
,表示变量a
已声明,但还未赋值。
请注意,变量提高只对var
命令声明的变量有效,若是一个变量不是用var
命令声明的,就不会发生变量提高,由于没有用var声明,这个变量就是隐式全局变量,在全局均可以访问到,不会提高声明:
console.log(b);//1
b = 1;
好,接下来,咱们看看函数的声明提高,先看看函数有常见的几种声明方式:
看第一个,function命名声明函数,f1为函数名,这种方法声明的函数在预解析阶段也会发现声明提高,注意和变量不一样的是,函数声明提高的时候,会同时赋值函数体:
f1();
function f1(){ //some code here };
以上代码,虽然函数f1在声明以前就调用了,但正常执行,没毛病老铁,其实这么写就至关于下面这样:
function f1(){ //some code here }; f1();
这是和变量声明不同的地方;
看第二种,第二种采用变量赋值的方式,这个时候就当函数体是一个值,并且函数在JavaScript中就是一个值,能够被任意赋值,那么就和普通变量声明提早同样了,在声明提高的时候不会发生赋值行为:
f1();//Uncaught TypeError: f1 is not a function var f1 = function(){
console.log("啊哈");
};
结果是f1报错undefined;
至于第三种,和第二种同样,也是变量赋值形式,普通的变量提高;
三、函数自己的做用域
函数自己也是一个值,也有本身的做用域。它的做用域与变量同样,就是其声明时所在的做用域,与其运行时所在的做用域无关。
var a = 1; var x = function () { console.log(a); }; function f() { var a = 2; x(); } f(); // 1
上面的代码中,因为x函数是定义在f函数外部的,因此x函数做用域绑定在外层,内部变量a
不会到函数f
体内取值,因此输出1
,而不是2
。
总之,函数执行时所在的做用域,是定义时的做用域,而不是调用时所在的做用域。
很容易犯错的一点是,若是函数A
调用函数B
,却没考虑到函数B
不会引用函数A
的内部变量。
再来看个具体栗子:
var x = function () { console.log(a); }; function y(f) { var a = 2; f(); } y(x);// ReferenceError: a is not defined
上面代码将函数x
做为参数,传入函数y
。可是,函数x
是在函数y
体外声明的,做用域绑定外层,所以找不到函数y
的内部变量a
,致使报错。
一样的,函数体内部声明的函数,做用域绑定函数体内部。
function foo() { var x = 1; function bar() { console.log(x); } return bar; } var x = 2; var f = foo(); f() // 1
上面代码中,函数foo中定义了一个函数bar,在取出函数bar并调用的时候,bar内部的x变量访问的是外层函数foo中的变量x,而不是同名全局变量x,这也是前面讲的,内部同名变量会覆盖全局变量,这也是理解闭包的基础哦!
今天到这里了,学习不停。。。。