Blogger Information
Blog 22
fans 0
comment 1
visits 17596
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
第九章深入理解JS作用域
刘静的博客
Original
755 people have browsed it

第九章深入理解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.对于函数来说,执行环境在函数调用时确定的。执行环境包含作用域内的所有的变量和函数的值。在同一个作用域下,不同的调用会产生不同的执行环境,从而产生不同的变量和值。所以执行环境是动态.

Correction status:Uncorrected

Teacher's comments:
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!