在 JavaScript 中,“域”通常指的是作用域。作用域是可访问变量的集合,它决定了变量和函数在代码中的可见性和可访问性。
JavaScript 中的作用域分为全局作用域和局部作用域。全局作用域是指在函数外部定义的变量和函数,它们可以在整个程序中被访问。局部作用域是指在函数内部定义的变量和函数,它们只能在函数内部被访问。
“预”通常指的是预解析。预解析是 JavaScript 引擎在执行代码之前进行的一项操作,它会将变量和函数的声明提升到当前作用域的顶部,以便在代码执行时能够正确地访问这些变量和函数。
“译”可能指的是翻译或解释。在 JavaScript 中,代码需要被翻译成机器能够理解的指令,然后才能被执行。这个过程通常由 JavaScript 引擎来完成。
总的来说,“域”、“预”、“译”都是 JavaScript 中非常重要的概念,理解它们对于编写正确的 JavaScript 代码非常重要。
一、作用域的概念
1、作用域有三种:全局作用域、函数作用域、块级作用域。(模块作用域)
(1)全局作用域:这时最高级别的作用域,在这定义的函数以及变量可以在代码的所有地方被访问。在浏览器的环境中,全局变量实际是window对象的属性。例如:
var a = 123
function foo(){}
//在全局中定义了变量a 函数foo()
(2)函数作用域(局部作用域):在每个函数内部声明的变量(未使用const、let关键字)、function声明的函数,这些对象具有局部作用域,它们只可以在函数内部访问。例如:
function foo(){
var a;
function add(){}
}
(3)块级作用域:这个作用域是在ES6引入了let和const关键字,避免因var声明的变量的变量提升(接下来会进行解释)现象,导致的让人匪夷所思的行为。这个作用域可以简单的理解为:{} + let/const例如:
if(...){
let a = 6;
const b = 6
}
while(...){
let a = 6;
const b =6
}
for(){
let a = 6;
const b =6;
}
function foo(){
let a = 6;
const b =6;
}
注:(1) 以上作用域所指的是全局内、函数体内、块内的域,而词法作用域则是变量声明的地方,注意不是调用的地方。例如:我们的寝室是一个域,则该域的词法作用域就是这一栋寝室楼。
(2)欺骗词法作用域:
eval
:可以让原本不属于这里的代码,变得好像天生被定义了在这一样。
function foo(a,str){
eval(str) //相当于var b = 2;
console.log(a,b)
}
foo(1,'var b = 2')
whit(){}
当修改对象中不存在的属性时,这个属性会被定义在全局,变为全局变量,造成数据泄露。
function foo(obj){
with(obj){
a = 2
}
}
var o2= {
b :1
}
foo(o2)
console.log(a) //输出a = 2,此时的a为全局变量
2、声明提升的概念与示例
概念:在变量声明和函数声明在代码执行前被提升,或者说移到,其包含的作用域的顶部的过程,发生在JS的编译阶段,导致了变量和函数可以在被声明之前就被访问。
console.log(a); //变量a只是声明被提示,输出undefined
var a = 6;
//编译器会将代码整理为
//var a;
//console.log(a);
//a = 6;
即使函数foo()
在调用之后声明,但由于声明提升,他在执行开始时,就可以使用了,因此能正常输出。
foo()
function foo(){
console.log('你好')
}
二、“预备与编译”--预编译的概念
1、预编译发生在代码被执行之前,是JS引擎对代码的预处理,保证了变量和函数在使用之前已经被正确的设置。
2、全局预编译:
(1)创建全局执行上下文GO(Global Object)。
(2)寻找变量声明,变量名作为GO的属性名,值为undefined。
(3)在全局找函数声明,函数名作为GO的属性名,值为函数体
画图实例:
3、函数中的预编译
(1)创建函数的执行上下文对象AO(Activation Object)。
(2)找到形参和变量声明,将形参和变量声明作为AO的属性名,值为undefined。
(3)将实参和形参统一
(4)在函数体内找到函数声明,将函数名作为AO的属性名,值为函数体。
画图实例:
注: 以上的执行上下文是被放在一个栈内,AO对象被放在变量环境中。在执行上下文中除了变量环境外,还有一个词法环境,用于let
和 const
声明的变量的存储,这保证了它们可以遵守块级作用域的规则,处于临时死区,直到声明被执行才可被访问。这个栈叫做调用栈。
画图实例: