博客列表 >第九章深入理解JS作用域

第九章深入理解JS作用域

刘静的博客
刘静的博客原创
2020年08月29日 22:10:57752浏览

第九章深入理解JS作用域

作用域内部原理的过程介绍

全局作用域,函数作用域

分为五个阶段:内部原理分成- 编译,执行,查询,嵌套,异常

编译阶段 :边解释边执行

  1. var a = 2;
  2. console.log(a);//2
  3. function add(){
  4. var b = 3;
  5. console.log(a);
  6. }
  7. console.log(b);// b is not defined

编译阶段

1.1 分词

词法单元: var, a, =, 2,;

  1. {
  2. "var":"keyword",//关键字
  3. "a" : "indentifier",//标识符
  4. "=" : "assignment",//分配
  5. "2" :"interger",//整数
  6. ";" :'eos',//(end of statement)//结束语句
  7. }

1.2 解析

抽象语法树(AST Abstract Snatax Tree)

1.3 代码生成

将AST准换成可执行的代码的过程,转换成一组机器指令

执行阶段

简言之,编译过程就是编译器把程序分解成词法单元,将词法单元解析成AST,再把AST转换成机器指令等待执行得过程

  1. var a = 2;
  2. console.log(a);
  3. console.log(b);

1.引擎运行代码时首先查找当前的作用域,看a变量是否在当前的作用域下,如果是,引擎就会直接使用这个变量;如果否,引擎会继续查找该变量

2.如果找到了变量,就会将2赋值给当前的变量,否则引擎就会抛出异常

查询

查询可分为:LHS查询 和RHS查询

[^有等号赋值的称为叫LHS查询,反之叫RHS查询]:

  1. function foo(a){
  2. console.log(a);
  3. }
  4. foo(2);

查询过程如下:

1.foo()对foo函数对象进行RHS引用

2.函数传参a=2对a进行了LHS引用

3.console.log(a);对console对象进行RHS引用,并检查其是否有log()方法

4.console.log(a);对a进行RHS引用,并把得到的值传给了console.log(..)

[^LHS:Left Hand Side; RHS:Right Hand Side]:

作用域变量的查找机制(important)

在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者是抵达最外层的作用域(全局作用域)为止

  1. function foo(a){
  2. function fo(){
  3. console.log(a + b);
  4. }
  5. fo();
  6. }
  7. var b = 2;
  8. foo(4);

异常

  1. // RHS
  2. function fn(a){
  3. a = b;//b is not defined
  4. }
  5. fn(2);
  6. function fn2(){
  7. var b = 0;
  8. b();//b is not a function
  9. }
  10. fn2();
  11. function fn(){
  12. 'use strict';
  13. a = 1;
  14. }
  15. fn();//a is not defined
  16. console.log(window);
  17. console.log(a);
  1. function fn(a){
  2. // console内置对象
  3. console.log(a);
  4. }
  5. fn(2);

[^作用域内部原理过程]:

词法作用域

  1. function foo(a){
  2. var b = a * 2;
  3. function bar(c){
  4. console.log(a,b,c);// 2 4 12
  5. }
  6. bar( b * 3);
  7. }
  8. foo(2);

遮蔽效应

作用域查找从运行时所处的最内部作用域开始,逐级向上进行,直到遇到第一个匹配的标识符为止

在多层的嵌套作用域可以定义同名的标识符,这叫做 “遮蔽效应”

  1. var a = 0;
  2. function test(){
  3. var a = 1;
  4. console.log(a);
  5. }
  6. test();

变量的声明提升

声明从他们在代码中出现的位置被移动到最上面,这个过程叫做变量提升,预解释

  1. // 预解释
  2. a = 2;
  3. var a;
  4. console.log(a);
  5. var a;
  6. console.log(a);
  7. a = 2;
  8. // 声明从他们在代码中出现的位置被移动到最上面,这个过程叫做变量提升,预解释
  9. var a;
  10. console.log(a);//undefined
  11. a = 0;
  12. function fn(){
  13. var b;
  14. console.log(b);//undefined
  15. b = 1;
  16. function test(){
  17. var c;
  18. console.log(c);//undefined
  19. c = 2;
  20. }
  21. test();
  22. }
  23. fn();

函数的声明提升

函数调用在函数声明前,也可以调用到函数

[^函数的声明可以提升,但是函数表达式不能提升]:

  1. var foo;
  2. foo();
  3. foo = function bar(){
  4. console.log(1);
  5. }

声明时的注意事项

1.声明提升: 变量声明提升和函数声明提升 变量的声明优先于函数的声明.但是 函数的声明会覆盖未定义的同名的变量

  1. var a;
  2. function a(){};
  3. console.log(a);
  4. var a;
  5. function a(){};
  6. a = 10;
  7. console.log(a);

2.变量的重复声明是无用的,但是函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)

  1. var a;
  2. var a;
  3. a = 1;
  4. a = 10;
  5. console.log(a);

3.函数声明提升优先级高于变量的声明提升

  1. var a;
  2. function a(){
  3. console.log('hello wolrd');
  4. }
  5. a();

4.后面的函数声明会覆盖前面的函数声明

  1. fn();
  2. function fn(){
  3. console.log('fn');
  4. }
  5. function fn(){
  6. console.log('fn2');
  7. }

[^应该避免在同一作用域中重复声明]:

作用域链

bar => fn => 全局

查找机制:在当前作用域中发现没有该变量,然后沿着作用域链往上级查找,直到查到对应的变量为止,如果查找不到,直接报错

自由变量:在当前作用域中存在但未在当前作用域中声明的变量

一旦出现自由变量,就肯定会有作用域链,再根据作用域链查找机制,查找到对应的变量

  1. var a = 1;
  2. var b = 2;
  3. // fn=>全局
  4. function fn(x) {
  5. var a = 10;
  6. // bar => fn => 全局
  7. function bar(x) {
  8. // 自由变量:在当前作用域中存在但未在当前作用域中声明的变量
  9. // 一旦出现自由变量,就肯定会有作用域链,再根据作用域链查找机制,查找到对应的变量
  10. // 查找机制:在当前作用域中发现没有该变量,然后沿着作用域链往上级查找,直到查到对应的变量为止,如果查找不到,直接报错
  11. var a = 100;
  12. b = x + a;
  13. return b;
  14. }
  15. bar(20);
  16. bar(200);
  17. }
  18. fn(0);

执行上下文环境和执行流

每个执行环境都有一个与之关联的变量对象,环境中定义的函数和变量都保存在这个对象

  1. var a = 1;
  2. var b = 2;
  3. function fn(x) {
  4. // arguments
  5. // this
  6. var a = 10;
  7. function bar(x) {
  8. var a = 100;
  9. b = x + a;
  10. return b;
  11. }
  12. bar(20);
  13. bar(200);
  14. }
  15. fn(0);

执行环境栈

执行环境栈:其实就是一个出栈和压栈的过程

总结

执行环境 相当于作用域链一样

总结:

1.在js中,除了全局作用域,每个函数都会创建自己的作用域。

2.作用域在函数定义的时候已经确定了,与函数调用无关。

3.通过作用域,可以查找作用域范围内的变量和函数有哪些,却不知道变量的值是什么。所以作用域是静态

4.对于函数来说,执行环境在函数调用时确定的。执行环境包含作用域内的所有的变量和函数的值。在同一个作用域下,不同的调用会产生不同的执行环境,从而产生不同的变量和值。所以执行环境是动态.

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议