下层基础决定上层建筑2-js作用域
下层基础决定上层建筑,今天认识一下JavaScript的作用域
先有鸡还是先有蛋?
我们先来看两段代码
a = 2
var a;
console.log(a);//是否会报错,如果不报错那输出什么?
console.log(a);//是否会报错,如果不报错那输出什么?
var a = 2;
答案在结尾,等你看完文章自会明白。
变量和函数在声明前就调用,以我们熟悉的java,python,C++,C#来说它是会报错的,我们认为代码是由上而下一行一行执行的,但实际上这并不完全正确没有一种特殊情况导致这个假设是错误的。
到底发生了什么?看来我们面对的是一个先有鸡还是先有蛋的问题。到底是先声明(蛋)在前还是赋值(鸡)在前?
编译的步骤
1.词法分析
2.声明提升
1.变量声明,声明提升
2.函数声明,整体提升
3.解析
4.生成代码
我们逐个分析
// 1.词法分析,声明提升
var a = 1 ->var , a , = , 1
//2.声明提升
1.变量声明,声明提升
2.函数声明,整体提升
//声明提升前
console.log(a)
var a = 1
foo()
function foo(){
console(1)
}
//声明提升后
var a;
function foo(){
console(1)
}
console.log(a) //会打印出undefined
var a = 1
foo()
// 3. **解析**:这一步骤通常涉及构建语法树或抽象语法树(AST),以便更好地理解代码的结构和逻辑。
// 4. **生成代码**:基于解析的结果,生成目标代码,例如机器码或字节码,以便计算机能够执行。
域
1.全局作用域:在整个程序中都可以访问到的变量和函数处于全局作用域。例如在页面中直接声明的变量通常是全局变量。
2.函数作用域:在函数内部定义的变量和函数,只能在该函数内部访问。
3.作用域的规则:内部作用域可以访问外部作用域,反之则不行
整个程序就是一个全局作用域,这个全局作用域里面的函数就可以形成一个函数作用域,内部作用域可以访问外部作用域,反之则不行,这就是一个外部不能访问内部,所以打印结果为1.
这就是内部作用域可以访问外部作用域的例子
这是一个三层的域,最外面是全局作用域,第二层是函数作用域,第三层是函数作用域里面的作用域,a,b都在第二层的函数作用域里面,根据规则是可以访问到的。
词法作用域
变量声明的地方,也可以说是上一层的域
以这个为例,bar的词法作用域就是foo的作用域
词法欺骗作用域
1.eval() 让原本不属于这里的代码,变得好像天生就定义在这里一样,它的主要作用是将字符串作为 JavaScript 代码进行动态求值执行。
function foo(a, str){
eval(str)
console.log(a,b)//输出5 2
}
foo(5,'var b = 2')
2.with() {} 用于临时扩展作用域链。 缺点:当修改的对象中不存在的属性时,这个属性会被泄漏到全局,变成全局变量
let person = { name: 'John', age: 30 };
with (person) {
console.log(name); //打印John
console.log(age); //打印30
}
缺点的例子
function foo(obj){
with(obj){
a = 2;
}
}
var ol = {
b: 3
}
foo(ol);
console.log(a); //2
console.log(ol.a);//undefind
var vs let
var和let
变量提升:
-
var存在变量提升,即可以在声明之前使用,值为undefined。 -
let不存在变量提升,在声明之前使用会报错。 重复声明: -
var可以重复声明同一个变量。 -
let不允许在同一作用域内重复声明。
块级作用域:
var不具备块级作用域,在块内声明的变量在块外也可访问。let具有块级作用域,只在其所在的块内有效。
暂存死区:
-
let存在暂存死区,在其声明之前访问会引发错误。// var 的情况 console.log(a); // undefined var a = 10; // let 的情况 console.log(b); // 报错 let b = 10; // var 重复声明 var c = 1; var c = 2; // let 不允许重复声明 let d = 1; // let d = 2; // 报错
块级作用域
1.{} + let
2.函数内部又有一对花括号 {}
3.const(const声明的是常量,不允许修改值)
案例1 -({} + let):
{
let blockVar = '块级变量';
}
console.log(blockVar)//报错
案例2 - ({} + let)-特殊例子:
{
let a = 1;
var b = 2;
}
console.log(b);//打印2,`var` 定义的变量 `b` 并不受块级作用域严格限制。
案例3 - (函数内部又有一对花括号 `{}`):
function testFunc() {
{
function blockFunction() {
console.log('块级函数执行');
}
}
// 无法直接在这里调用 blockFunction
//解释:在 `testFunc` 函数内部又有一对花括号 `{}`,这就构成了一个块级区域。
}
testFunc();
案例4 - (const):
console.log(a) //报错
const a = 2 ;