数据类型
-
undefined
和null
区别与联系
undefined 表示某个表达式的原始状态。null 表示一个对象被认为重置,切断栈中对象指针与堆中对象的关系,因此,用于释放对象。 -
js中的 string 也属于基本类型,因此数据也是直接存储在栈中。js中也存在包装类,与基本类型最主要的区别就是对象的生存周期。
// 引用类型自定义属性在定义后失效
let s = "hello";
s.color = "red";
alert(s.color); // undefined
// 包装类型会在作用域失效后才会被销毁
let s = new String("hello");
s.color = "red";
alert(s.color); // red
变量提升
定义: JS的引擎工作方式为,先解析代码,获取所有被声明的变量,再进行一行行执行。因此,就出现了后面声明的变量可以被前面的代码访问的情况,也就是所谓的变量提升。
var
关键字定义的变量声明会被提升,同时会被初始化为 undefined 。let
关键字定义的变量声明同样会被提升,但是不会被进行初始化,执行到代码语句处才会被初始化。因此在代码声明前访问时会出现Uncaught ReferenceError: x is not defined
错误。
// 报错说明内部的console输出的是块级作用域中的x,表示该变量已经声明,只是没有初始化
let x = 'global'; // 全局作用域
{
console.log(x); // Uncaught ReferenceError: x is not defined
let x = 1;
}
function
关键字则会在代码执行前将 声明,初始化和赋值 都完成,因此可以在代码语句之前调用。const
关键字与let
相似,只是没有赋值的操作。
参考博客:
https://www.jianshu.com/p/0f49c88cf169
执行上下文
实质: 执行环境栈中的一块栈内存,用于区分函数的作用域。因此每次遇到函数调用,就会创建一个新的执行上下文并压入栈中,用于保护私有变量。
注意:
- 执行上下文只与调用有关,因此同一个函数被调用多次会创建多个上下文。
- 只要当前上下文中的某些内容被其它上下文中的变量占用,那么当前上下文是不能被释放的,直到占用的指针置null后才能释放。这也是闭包出现的必要条件。
闭包
定义:有权访问另一个函数作用域中变量的函数
闭包案例1:(等待队列中放入的是字符)
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i++)
}, 4000);
}
console.log(i) // 5 5 6 7 8 9
// 由于setTimeout需要等待主体代码完成四秒后才会执行,因此,先执行最后一条输出 i 的语句,此时i=5,
// 由于 i 还会在 setTimeout里面使用,因此四秒后其内部的匿名函数继续使用 i=5 来完成后续的操作,来得到后续的结果。
案例2:使用立即执行函数暂存每轮执行的结果,即创建闭包(等待队列中放的是执行完成后的数值)
for(var i=0;i<5;i++){
// 立即执行函数会在完成后单独创建OV来保存当前的i值
(function(x){
setTimeout(function(){
console.log(x++)
}, 4000);
})(i);
}
console.log(i) // 5 0 1 2 3 4
闭包的应用
-
创建访问私有变量的公有方法
- 函数内部定义的变量都可以称为私有变量,函数外部无法访问。创建闭包可以使这些变量暴露出来。
-
模拟块级作用域
编译机制(VO,AO和GO)
VO(变量对象)
VO(G)
定义:全局变量对象,用于保存全局上下文中的所有变量的对象。
AO
定义:活动对象,是函数执行上下文中VO的具体表现。用于保存函数形参、局部变量、自身函数对象引用遍历、argument和this等内容。
GO
定义:全局对象,是预定义对象,表示 window
变量指向的堆内存地址,用于存储内置的属性和方法。
注意:
- 在全局上下文中,基于
var/function
创建的变量本质是给window设置属性(作用于GO)。基于let/const
创建的变量则是存在 VO(G)中的,与window没有关系。 - VO(G)中包含了window变量和自定义的全局变量。
作用域
- 定义:表示一个变量的可访问性与可见性。
- 分类:
- 全局作用域
- 函数作用域
- 块级作用域:声明与大括号内,并且由非
var
关键字声明的变量。
- 作用域链:从自身作用域向上寻找某个变量的过程。在全局作用域中找不到则直接报错。
作用域链的具体体现: 内层函数执行上下文的VO -> 外层函数执行上下文的VO -> 全局执行上下文的VO。通过 [[Scope]] 内部属性来拼接。
参考博客
https://www.cnblogs.com/dasusu/p/10071326.html
https://juejin.cn/post/6961716331297112094(里面有张图很好地解释了VOG与GO的关系)