js作用域与变量提升

故不积跬步,无以至千里,与君共勉

我们面试的时候经常会被问到,var、let、const的区别?函数的类型? 可是往往知其形而非其意,这篇记录也是阅读你不知道的js上的个人总结。

一、作用域

作用域是一个概念,在了解它之前,我们先简单的去了解代码在执行的时候的顺序:

词法分析===>语法分析===>代码生成

其中在词法分析中,我们可以用var a=1,进行分析

1、词法作用域

var a = 1;

在词法分析的过程中 ,会将这代码分解为:

1、var a = 1

2、在内存中创建一个a空间,通过赋值将1放入

注: var a 这部分简称为LHS(赋值的目标) , 等号右边的1简称为RHS(赋值的源头)

简单了解-欺骗词法:

1、eval

2、with

两者的区别是,eval是在原有的基础上去改变,而with是创建了一个新的变量去改变,都不建议用,因为性能问题,这里做简单的了解。

2、函数类型简单介绍

对函数做一点简单的扩展方便下文理解,函数大致分为下面三种

1、声明式函数

2、函数表达式

3、构造函数

1、声明式函数:

function foo() {
  var a=3
 console.log('我是函数声明',a)
}

foo()  //3

简单的理解就是,function前没有任何其他的符号

2、函数表达式:

函数表达式包含了===> 立即执行函数

1、立即执行函数

写法一:

(function(str){
 console.log('hellow' + str)
})('我是立即执行函数')

输出:
hellow 我是立即执行函数

可以简单的看出,第二个圆括号里面是对miming函数的调用,括号里面就是传参

写法二:

(function foo(){
  console.log('我是函数表达式')
}())

写法一与二只是写法上的区别

立即执行函数的作用:

1、创建一个独立的作用域,外部访问不到,避免污染全局变量

2、闭包和私有数据

2、非立即执行函数

var foo = function () {
  console.log('我也是函数表达式')
}
foo()

以上都是函数表达式

3、构造函数

var a = new function()

简单理解就是new出来的函数就是构造函数,对于new的理解,在另一篇博客中有记录

二、变量提升与函数声明提升

一、变量提升

直接如下列:

console.log(a) //undefined
var a = 10
console.log(a) //10

可将上列解析为:

var a // a变量得到了提升,在该变量的作用域最上方
console.log(a) // a变量已被声明,但是没有被赋值,undefined
a = 10 // a变量原有的位置上进行赋值操作
console.log(a) // 此时a变量已被声明并且被赋值

以上就是一个简单的变量提升的过程,同理函数声明也是差不多的。

注意: 只有var具有变量提升的效果,const、let没有变量提升

二、函数声明提升

注意: 只有声明式函数具有函数提升,其他类型的函数没有

如下列:

列一

foo() // 10
function foo(){
 console.log(10)
}

列二

foo() // 这里直接报错 (报错原本以下代码全部不执行,但是先不考虑)
console.log(foo) // undefinde
var foo =function (){
   console.log(10)
}

解析:

列一

function foo(){
 console.log(10)
} // 这一步,函数声明进行了函数提升
foo() 

列二

var foo
foo() // 这里会直接报错
console.log(foo) // 变量已被声明,但是还没被赋值,所以为undefinde
foo = function () {
console.log(10)
}

通过简单的对比,可以发现只有函数声明才会出现提升的效果,这里有个有趣的地方,变量声明和函数声明都是提升在作用域的最上方,但是谁在顶层呢?

函数声明提升>变量声明提升

列:

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

解析

function a() {
console.log(10)
}
var a
console.log(a) //输出的是函数的本身
a = 10
console.log(a) //a的值被覆盖掉, 10

这里可看出,函数声明提升实际上是大于变量的声明的提升。

ES6碎片知识

一、var、let、const

在写代码的过程中,我们经常用到这个三个关键字,我这里简单的说下区别

var

1、var 声明的变量具有变量提升效果,下面是一些例子

console.log(a) // undefinde
var a = 10
console.log(a) //10

为什么第一个是undefinde,可以看我另一篇博文,这里不再介绍,这就是最简单的变量提升

注意的是:

<script>
      for (var i = 0; i < 10; i++) {
        setTimeout(() => {
          console.log(i);
        });
      }
    </script>

这里输出的i都会是10,这里做出简单的解释

首先我们得先知道,

1、执行上下文

执行上下文中的作用域有全局作用域、函数作用域等,在for循环中有个箭头函数,箭头函数的this指向是和所在的外层函数相关(this指向问题另一篇博客中有记录),在这里指向的是全局变量

2、任务队列

使用setTimeout的时候并不会马上把每一次输出操作执行,而是会放在任务队列中,如下图:
在这里插入图片描述

上面就是简单的理解

这里想要正常的输出,方法可以如下:

<script>
      for (let i = 0; i < 10; i++) {
        setTimeout(() => {
          console.log(i);
        });
      }
    </script>
let

let和const是ES6提出的块级作用域,let我们注意的点如下:

1、let、const不存在变量提升,故不能先使用变量再声明

2、var a=10,let a=2,是错误的,一个变量被定义后,不能再用let进行声明

3、let、const都需要在{ }内进行使用

关于第一点,这里叫做暂时性死区

暂时性死区的理解列子如下

{
 a = 10
 console.log(a) //会报错
 let a
 a =2
 console.log(a) //2 
}

可以看出来就是简单的变量提升问题,这里对let进行了简单的介绍

const

const也是块级作用域,主要是与let的异同点,区别如下:

1、let 在声明一个变量后可以再去改变其值的内容,const定义一个值的内容后不能改变

2、const 和 let 都不存在变量提升

关于第一点原理简单的来说,const 保存的变量,保存的是指向变量中的指针,由指针指向保存变量的地址,而const只能保证指针是不变的,队于内存地址中的内容就不管了,下面举出一些简单的列子:

const a = 10
const a = 2 //这里就会报错

const b =[]
b.push('a') // 这样就不会报错,打印出来就是['a']

猜你喜欢

转载自blog.csdn.net/qq_43376559/article/details/121700027