满满干货,js作用域和声明提升,小白也能看懂,保姆级教学

899 阅读6分钟

js作用域和声明提升 !!!

哈喽哈喽,我是新手小白金樽清酒。相信大家在初学js的时候的总会遇到一个奇怪的问题,那就是变量提升。那什么是变量提升呢,就是在我们在我们将输出语句放在用var声明的一个变量之前编译器还能正常执行。有接触过其他语言的的大佬就知道,如c,c++,java等语言这样不就会报错嘛。因为程序是自上而下运行的,输出语句怎么会在声明语句之前呢?诶,别着急,这就是js的特别之处,让我们来一探究竟吧。

首先js是一门弱数据类型的语言

什么叫弱数据类型的语言呢?就是声明的时候不带上数据类型的,在我们给变量赋值之前,我们并不知道它是什么类型的。就在下面的这串代码,我声明了四个变量,在未赋值之前是不知道这些变量是什么数据类型的。相较于其他语言,如c,c++定义之前是不是就确定了变量的数据类型呢。比如,int a=10。前面的int 就会告诉我们它是整型。

image.png

那js是如何知道数据类型的呢?诶,别急,它有自己的小秘书。在执行之前,js首先会请秘书编译一下看不懂的东西。所以在执行之前它会进行编译,但是秘书只会编译老板需要的东西,所以啊,有一定的工具区间,不然看到老板的隐私就不好啦。诶,那就是作用域。

什么是作用域呢?

作用域就是限制一个变量在程序中的使用范围。一般分为三种,全局作用域,函数体作用域和块级作用域。 为什么要叫作用域呢?因为在js中分为两步编译和执行。编译和执行的顺序不同,所执行出来的结果也不同,我们就用作用域来解释。比如下面的代码。

1.jpg

大家觉得这个代码的结果是什么呢?大家可以自己去试一下,相信有很多小伙伴都会说执行了foo()函数,将a的值修改为2,最后的结果应该是2吧。其实结果是1。

我们来分析一下吧,前面我们提到先编译再执行首先编译全局变量,a=1,和foo函数名,但未执行函数,然后执行调用的foo()函数,foo是什么呢?诶,又得叫小秘书编译一下,是个函数就会形成自己的小作用域叫函数作用域,里面有什么呢?编译一下,a=2。诶,再往下执行,有个输出语句,输出那个a呢,是全局变量的,还是局部变量呢?看这个输出的位置是定义在全局的,所以输出的也是全局的变量a=1.因为我们要牢记,作用域只能从内到外不能从外到内。假如我们把输出放在函数里面,那结果就大不一样了。

image.png

image.png

从结果我们能大概理解一下啊全局作用域和函数作用域吧。让我们看一下从作用域从内到外吧

image.png

我们在函数体并没有声明变量a,但是输出变量a会发生什么结果呢?

image.png

结果是全局变量的a=1,因为在函数作用域没找到变量,就会从内向外找,于是找到了全局的变量a。所以我们要牢记,作用域只能从内向外,不能从外向内。接下来让我们看一下进阶版的吧。新手小白自己思考一下。

2.jpg

var的声明提升

刚才我们简单的了解了一下作用域,现在呀,我们就可以揭开声明提升的面目啦。声明提升到底是啥? 我们先看一个简单的例子

image.png

我们先执行,再声明会发生什么事情呢?我们会发现,根本不会报错,输出10。 为什么呢?因为var定义的变量会声明提升,等同于下面的代码

var a=10
console.log(a)//变量提升

再来一个更复杂一点的代码,来将作用域和声明提升结合一下 函数作用域var定义的也会变量提升

foo()
function foo(){ 
  
    console.log(a)
    var a=10
}

但是结果是undefined,为什么呢? 因为这里的变量提升只会将变量提升下去,而值再输出之后,所以是undefined,等同于下面的代码

foo() 
function foo(){ 
var a
console.log(a)
a=10
}

通过上面的例子我们会明白了var定义的变量存在变量提升,那么有无解决办法呢?

用let定义取代var可以避免变量提升

let和var一样是来声明变量的,那么两者有什么区别呢?

  • 1.let 不会声明提升
  • 2.let 不能重复声明同一变量

让我们来举例一一看一下吧。同样的代码

console.log(a)
let a=10

执行结果:

image.png js出现报错,为声明变量a,说明let声明的变量不会变量提升

再让我们看一下声明同一个变量

image.png

image.png

这是var声明,重复声明同一变量会覆盖

image.png

image.png

这是let声明的,会报错,提示重复定义,这更符合语法要求。

const 定义也不会发生声明提升

首先const定义有以下几个特点,我们简单的了解一下

  • 1.不会声明提升

  • 2.不能重复声明同一变量

  • 3.声明的是一个常量,值不能被修改,必须初始化 让我们看下面的代码

    image.png

image.png 由此可见,const也不会变量提升

欺骗词法作用域

  • 1.eval() 让原本不属于这里的代码变成就是写在这里的代码
  • 2.with 当修改对象中不存在的属性时,该属性会泄露到全局变量 我们先来看一下eval()
function foo(str){
    eval(str)
    var a=1
    console.log(a,b)
}
foo('var b=2')

函数内并没有变量b,但通过eval()函数写过去了,让不属于这里的代码出现

再让我们看一下啊with


var obj={
    a:1,
    b:2,
    c:3,

}
with(obj){
    a=2,
    b=3,
    c=4
}
console.log(obj)

image.png

但是,with定义的变量在对象中没有的话会泄露到全局的位置


var obj={
    a:1,
    b:2,
    c:3,

}
with(obj){
    a=2,
    b=3,
    c=4
    d=5
}
console.log(d)

执行结果:

image.png

变量d是定义在with里门的,但对象里面并没有d对象,已经泄露到全局了,所以可以输出

结语

在成为大佬的路上你我相伴,一份耕耘一份收获。在一次次的尝试中,总能发现新的知识,在千万次的代码敲打中,你我也在不断的变强。加油代码人。