前言
JavaScript作为一种动态类型、解释型的脚本语言,其作用域和上下文的概念对于开发者来说至关重要。本文将探讨JavaScript中的对象特性、函数属性、作用域、作用域链及预编译等核心概念,并通过实例分析,帮助读者构建清晰的理解框架。
基础知识
对象
对象特点: 可以放键值对
let a = {
age: 18,//键名:键值,,,,age为属性
}
函数也是一种特殊的对象,也可以拥有属性 以test函数为例
function test() {
}
函数天生的属性有:
test.name 函数名
test.length 传入的实参的个数
test.prototype 函数的原型
test.[[scope]] 作用域属性,给js引擎访问的,我们拿不到 ---隐式属性
当test()被调用的时候,引擎才会来看里面写了什么。此时引擎会创建一个上下文对象AO对象(Action Object)出来,用来记录有效标识符,执行完后会被回收。
AO对象被回收后,再次调用test(),又会创建一个新的AO对象
概念
作用域
函数身上的属性[[scope]],用于存储函数中的有效标识符
作用域链
作用域是执行器上下文对象的集合,这种集合呈链式连接,我们把这种链状关系称之为作用域链
为什么内层可以访问外层?---->循着作用域链查找
我们用下面这个例子帮助我们理解:
function a() {
function b() {
b = 33;
}
var a = 4399;
b();
console.log(a);
}
var glob = 100;
a();
a的定义
a.[scope]指向全局
a定义在全局,引擎读到a时,一定读到了全局作用域
a的执行
创建a的上下文对象AO
先在AO找然后再去全局找,AO优先然后是GO
这个东西就是a的作用域链:
接下来我们看b
b在a里,所以b一定能访问a
现在我们就很容易理解作用域链的发生机制了
预编译
代码在执行前需要进行编译操作,用于确定代码之间的各种关联
简单来说,开始写程序的时候,预编译器已经帮我们做好了很多事情,让程序写起来更快,也更容易理解。就像我们在做手工之前,先准备好所有的材料和工具一样。
变量声明
代码在执行前是一定会被编译的,不编译引擎就不会识别出来,会出现诸多问题比如变量提升
我们来看个例子:
console.log(a);
var a = 1
变量在编译过后会声明提升,代码在引擎眼中是这样的:
var a
console.log(a); //输出undefined
a = 1
函数声明
我们来举个函数的例子
test()
function test() {
var a = 123
console.log(a);
}
同样,函数声明也会整体提升:
function test() {
var a = 123
console.log(a);
}
test()//会输出123,不会报错
例子
好的,理解了这两个概念之后我们来看下面这行代码:
var a = 1;
function fn(a) {
var a = 2
function a() { }
console.log(a);
}
fn(3)
看到这段代码的第一反应:
咱先分析一下:
- 预编译发生在全局:
- 创建全局GO对象
- 找变量声明,将变量名作为GO的属性名,值为undefined
- 在全局找函数声明,将函数作为GO的属性名,值为函数体
- 预编译发生在函数体内:
-
创建一个AO对象
-
找形参和变量声明,将形参和变量名作为AO的属性名,值为undefined
-
形参和实参统一
-
在函数体内找函数声明,将函数名作为AO的属性名,值为该函数体
-
//全局作用域
GO:{
a:undefined-->1
fn:function(){}
}
var a = 1;
function fn(a) {
var a = 2
function a() { }
console.log(a);
}
//函数调用
AO:{
a:undefined-->3-->function(){}
}
fn(3)//输出为2
再来分析一段:
function fn(a) {
console.log(a);
var a = 4399
console.log(a);
function a() { }
console.log(a);
var b = function () { }
console.log(b);
function c() { }
var c = a
console.log(c);
}
步骤一:全局预编译
GO:{
fn:function(){}//全局就只有一个fn
}
步骤二:全局代码执行,遇到了函数调用,进行函数编译
AO: {
a: undefined-- > 1-- > function a() { }
b: undefined
c: undefined-- > function c() { }
}
步骤三:函数编译完了之后再执行
AO: {
a: undefined-- > 1-- > function()-->4399
b: undefined -->function () { }
c: undefined-- > function c() { }-->4399
}
输出结果为
这个输出结果符合我们的预期
结语
好滴,以上就是本文全部内容啦,本文我们深入理解了JavaScript作用域与上下文的工作原理以及预编译机制,包括变量提升和函数声明提升等概念。希望这些知识能帮助大家写出更高效的代码。感谢阅读,期待未来更多精彩分享~