JavaScript:变量提升&&作用域

作用域是JavaScript中听上去感觉很简单,其实比较麻烦的一个特性,什么是作用域?我看书籍有一个相对的官方解释:作用域(scope,或译有效范围)就是变量和函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期,是在代码运行时中某些特定部分或代码块中变量,函数和对象的可访问性,也就是说作用域决定了代码区块中变量等其它的可见性或可访问性。我们先看一个简单的例子:

function func() {
    var num = 1;
}
func();
console.log(num);     // Uncaught ReferenceError: num is not defined

可以看到,打印num显示的是'not defined',因为变量num在全部作用下没有任何声明,所以会报错。通过这个例子可以得到一个基本的结论:函数作用域是一块独立的区域,可以防止变量暴露在全局。

  • 变量提升

在讲述作用域之前,先来看下JS中变量的提升,看一个例子:

a = 1;
var a;
console.log(a);  // 1

根据我们对JS执行机制的了解,它是从上而下的执行机制,也就是说,理论上这里打印的变量a应该是undefined,但是这里打印的却是1,为什么呢?因为在JS中它不是严格的自上而下的执行机制,上面的代码,实际在JS中会被编译成如下代码:

var a;
a = 1;
console.log(a); //1

再看一个例子:

console.log(a);   // undefined
var a = 1;

神奇的事情发生了,当你看到打印结果的那一刻,惊不惊喜!!!意不意外!!!这里应该打印"a is not defined"的啊,其实不然,你的眼睛欺骗了你,这个真实的执行机制是:

var a;
console.log(a);
a = 1;

 

为什么会有变量提升现象呢?因为JavaScript跟其它的变成语言一样,在运行的时候,会经历两个过程:编译 --- 执行,在它编译的时候,会对全局进行搜索,搜索所有的变量并且会将变量的声明全部提前,而其它的语句不会改变它们的顺序,所以在编译的时候,先执行了变量的声明,其次在执行阶段才会到赋值时候才去执行它该做的事儿。我们再看一个例子:

func();
function func(){
    console.log('func');
}
var func = 1;

上面执行的结果就是''func'',为什么呢?这个不是跟之前说的变量提升有冲突吗?其实不然,当有函数和变量一起声明的时候,会优先声明函数,因为函数声明高于一切,我们之前介绍函数的定义及特性的时候,也说到了函数时JS的一等公民,所以当然要给它最高规格的待遇。我们来总结一下关于变量提升的几个特点:

  • JavaScript提升变量会将变量的声明提升到JS的顶部执行;
  • 变量提升的本质就是因为在JavaScript引擎在函数运行编译的时候,已经将所有的变量声明过一次了,所以在执行的时候,变量已经声明;
  • 当存在多个同名的变量声明时,函数会高于一切,而覆盖其它的声明,如果有多个同名函数声明时,则是最后一个函数声明会覆盖之前的所有声明;

OK,介绍完函数的变量提升后,我们来说一下作用域的问题,先简单说一下词法作用域动态作用域:

词法作用域又称为静态作用域,官方解释是词法作用域也就是在词法阶段定义的作用域,也就是说词法作用域在代码书写时就已经确定了。也就是说所谓的词法作用域就是你在写代码时将变量或者作用域写在哪儿,作用域就在哪儿,在JS处理代码的时候,作用域会保持不变,看以下示例代码:

function foo(a) {
    var b = a * 2;
    function bar(c) {
        console.log(a, b, c);
    }
    bar(b * 3);
}
foo(2); // 2, 4, 12

上面的代码就包含三个作用域:全局作用域(即全部函数foo),函数foo创建的作用域(即变量a,b,函数bar),函数bar创建的作用域(即变量c);作用域是严格的包含关系,即没有任何一个函数可以同时出现两个父级函数中。

而所谓的动态作用域,实际上是不存在的,只是函数的this的执行机制让作用域表现的像一个动态作用域,而this的绑定是在代码执行的时候确定的。


再来看一下全局作用域局部作用域,所谓全局作用域就是在代码中任何地方都能访问到的对象,基本上有三种情形下的变量定义具有全局作用域的特性:

不在任何函数内定义的变量;

window对象的一些内置属性,例如window.location,window.name等等;

所有末定义直接赋值的变量自动声明为拥有全局作用域;

前面两个好理解,第三个是什么意思呢?来看下面的代码:

function func() {
    a = 1;
    var b = 2;
}
func();
console.log(a); // 1
console.log(b); // b is not defined

看代码可以知道,就是说当一个变量没有声明的时候,直接赋值的话,JS运行编译时会将该变量自动声明为具有全局作用域的变量;

所谓局部作用域,就是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,称为函数(局部)作用域,和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部。例如:

function func() {
    var a = 1;
    function say() {
        console.log(a);
    }
    say();
}
console.log(a);    // a is not defined
say();       // say is not defined

不过这里需要注意的是,当变量被包裹在块语句中时(即被{}包裹),这个块语句不会像函数一样创建一个新的作用域,如下:

if(1 == 1) { 
    var a = 1;
}
console.log(a); // 1

因为在块语句中所定义的变量将会保留在块语句当前存在的作用域中。

说完全局作用域和局部作用域后,再来说一下ES6中所提到的一个新的作用域---块级作用域。块级作用域可以通过ES6中新增的命令let和const来声明变量,这两种命令所声明的变量在指定的块级作用域外是无法被访问的,创建块级作用域的情况有:

在一个函数内部;

在一个块语句内部;

块级作用域有以下几个特点:

  • 声明变量不会提升到代码的顶部
function getValue(condition) {
    if (condition) {
        let value = "blue";
        return value;
    } else {
        return value;
    }
    return value;
}
getValue();   // value is not defined
  • 禁止重复声明
var a = 30;
let a = 40; // Uncaught SyntaxError: Identifier 'a' has already been declared

 

作用域暂且介绍到这里,后续会给大家带来更多的介绍!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值