故不积跬步,无以至千里,与君共勉
我们面试的时候经常会被问到,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']