欢迎光临
我们一直在努力

JS作用域与预解析

目录

作用域:

一、什么是作用域

(1)、全局作用域

(2)、局部作用域(函数作用域)

(3)、作用域链

面试题:

二、全局执行上下文代码的流程

三、执行上下文栈

预解析:

变量提升与函数提升

变量提升

函数提升

经典面试陷阱

暂时性死区(TDZ)


作用域:

一、什么是作用域

通常来说,一段程序代码中所用到的名字(变量名和函数名)并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

js作用域(es6)之前:全局作用域 局部(函数)作用域,es6后有块级作用域

(1)、全局作用域

直接编写在script标签中或者一个单独的js文件内的Js代码

全局作用域在页面打开时创建,在页面关闭时销毁

在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用它

es5中 在全局作用域中:创建的变量都会作为window对象的属性保存;创建的函数都会作为window对象的方法保存

全局作用域中的变量都是全局变量;在页面的任意部分都可以访问的到

var a=10;
var b=10;
// var c="hello";
console.log(window.c);//加window.,如果没有找到变量c,会undefined,不加的话,会报错

function fun(){
console.log("我是fun函数");
}
window.fun();//创建的函数都会作为window对象的方法保存

(2)、局部作用域(函数作用域)

在函数内部就是局部作用域。这个变量只在函数内部起作用

调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁

每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

在函数作用域中可以访问到全局作用域的变量, 在全局作用域中无法访问到函数作用域的变量

es5的环境下,在函数中如果要访问全局变量,可以使用window对象

var a = 10;
function fun() {
var a = "我是fun函数中的变量a";
//console.log("a="+a);
function fun2() {
// console.log(a);
console.log("a=" + window.a);
}
fun2(); //10
}
fun();

(3)、作用域链

只要是代码,就至少有一个作用域

写在函数内部的局部作用域

如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域

当在函数作用域中操作一个变量时,它会先在自身作用域中寻找,如果有,就直接使用,如果没有,就去它上一级作用域去寻找, 如果全局作用域中依然没找到,则会报错

根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称做是作用域链

面试题:

//第一题
var x = 10;
function fn() {
console.log(x);//10
}
function show(f) {
var x = 20;
f();
}
show(fn);

//第二题
/*说说它们的输出情况*/
var fn = function () {
console.log(fn);//输出函数本身
};
fn(); //函数

var obj = {
fn2: function () {
// 在自己身上找,找不到fn2,然后去全局找,也找不到所以报错
console.log(fn2); //报错
// 通过this在自己身上找,找到了fn2
// console.log(this.fn2)//输出函数本身
},
};
obj.fn2();

二、全局执行上下文代码的流程

 在执行全局代码前将window确定为全局执行上下文顶级对象

对全局数据进行预处理

var定义的全局变量==>undefined, 添加为window的属性

function声明的全局函数==>赋值(fun), 添加为window的方法 

this==>赋值(window)

开始执行全局代码

三、执行上下文栈

1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象

2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)

3. 在函数执行上下文创建后, 将其添加到栈中(压栈)

4. 在当前函数执行完后,将栈顶的对象移除(出栈)

5. 当所有的代码执行完后, 栈中只剩下window


预解析:

变量提升与函数提升

JS代码是由浏览器中的JS解析器来执行的,JS解析器在运行JS代码的时候分为两步:先预解析再代码执行

预解析:js引擎会把js里面所有的var 还有function提升到当前作用域的最前面

代码执行:按照代码书写的顺序从上往下执行

预解析分为变量预解析(变量提升)和函数预解析(函数提升)

变量提升 就是把所有var的变量声明提升到当前的作用域最前面,不提升赋值操作

函数提升 就是把所有的函数声明提升到当前作用域最前面,不调用函数


变量提升

为了理解变量提升,首先我们要将声明与赋值分开

当我们写var name="张三" ;时,JavaScript 引擎其实把这句话看成了两行代码:

  • var name;这是声明(告诉引擎有一个变量叫 name)

  • name="张三";这是赋值(把值放进去)

  • 下面代码的输出结果是什么?

    console.log(a);
    var a = 10;

    按照正常的逻辑(从上往下运行),第一行应该报错才对,因为它这时候还没遇到a的定义

    但在浏览器里运行,它不会报错,而是输出 undefined

    // — 预解析阶段(偷偷做的事)—
    var a; // 1. 把声明提到最上面,默认值是 undefined

    // — 执行阶段(真正运行的事)—
    console.log(a); // 2. 此时 a 存在,但还没赋值,所以是 undefined
    a = 10; // 3. 赋值留在了原地

    结论:var的预解析只提升了名字,把值丢在了原地。所以它不报错,但也记不住值。

    函数提升

    函数声明的待遇比var高级得多。它享有整体提升的特权

    sayHi(); // 我在定义之前就调用了!

    function sayHi() {
    console.log("你好!");
    }

    这段代码是可以正常运行的!输出 "你好!"

    浏览器眼中的代码长这样:

    // — 预解析阶段 —
    function sayHi() { // 整块都被提上来了
    console.log("你好!");
    }

    // — 执行阶段 —
    sayHi(); // 所以这里能找到函数,正常执行


    经典面试陷阱

    这段代码是报错,还是输出我吃饱了?

    eat();

    var eat = function() {
    console.log("我吃饱了");
    };

    报错,在在预解析(Hoisting)的眼里,变量声明和赋值是被强行拆散

    // — 1. 预解析阶段 —
    var eat; // 变量 eat 被提升了,但默认值是 undefined
    // 注意:后面的 function… 并没有跟着上来!

    // — 2. 执行阶段 —
    eat(); // 此时 eat 的值是 undefined。
    // 你试图运行 undefined(),JS 引擎就会怒吼:
    // "TypeError: eat is not a function"

    eat = function() { // 这一步才把函数赋值给 eat,但太晚了,程序上面已经挂了
    console.log("我吃饱了");
    };


    暂时性死区(TDZ)

    var与let和const的区别:以下代码会怎样?

    console.log(name); // 这里会打印 undefined 吗?
    let name = "张三";

    会报错:Uncaught ReferenceError: Cannot access 'name' before initialization

    (无法在初始化之前访问 'name')

    为了搞懂var和let的区别,我们需要把“预解析”拆得更细一点。一个变量的诞生其实分三步:

  • 创建 (Creation):在内存里登记名字“我有个变量叫 name”。

  • 初始化 (Initialization):给它一个默认值(比如 undefined)。

  • 赋值 (Assignment):真正把"张三"赋给它。

  • JS引擎在也会预解析对let进行创建,但不会初始化一个undefined。而是在外面加个锁(暂时性死区),在程序读到正式赋值那一行之前,谁”碰“谁“死”。

    目的:ES6想要把let 设计得更安全、更规范(不像var 那样既污染全局window,又允许在声明前乱用),所以才引入了 TDZ(死区) 机制。

    赞(0)
    未经允许不得转载:171主机测评 » JS作用域与预解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址