全局作用域,函数作用域
分为五个阶段:内部原理分成- 编译,执行,查询,嵌套,异常
编译阶段 :边解释边执行
var a = 2;console.log(a);//2function add(){var b = 3;console.log(a);}console.log(b);// b is not defined
1.1 分词
词法单元: var, a, =, 2,;
{"var":"keyword",//关键字"a" : "indentifier",//标识符"=" : "assignment",//分配"2" :"interger",//整数";" :'eos',//(end of statement)//结束语句}
1.2 解析
抽象语法树(AST Abstract Snatax Tree)
1.3 代码生成
将AST准换成可执行的代码的过程,转换成一组机器指令
简言之,编译过程就是编译器把程序分解成词法单元,将词法单元解析成AST,再把AST转换成机器指令等待执行得过程
var a = 2;console.log(a);console.log(b);
1.引擎运行代码时首先查找当前的作用域,看a变量是否在当前的作用域下,如果是,引擎就会直接使用这个变量;如果否,引擎会继续查找该变量
2.如果找到了变量,就会将2赋值给当前的变量,否则引擎就会抛出异常
查询可分为:LHS查询 和RHS查询
[^有等号赋值的称为叫LHS查询,反之叫RHS查询]:
function foo(a){console.log(a);}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]:
在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者是抵达最外层的作用域(全局作用域)为止
function foo(a){function fo(){console.log(a + b);}fo();}var b = 2;foo(4);
// RHSfunction fn(a){a = b;//b is not defined}fn(2);function fn2(){var b = 0;b();//b is not a function}fn2();function fn(){'use strict';a = 1;}fn();//a is not definedconsole.log(window);console.log(a);
function fn(a){// console内置对象console.log(a);}fn(2);
[^作用域内部原理过程]:
function foo(a){var b = a * 2;function bar(c){console.log(a,b,c);// 2 4 12}bar( b * 3);}foo(2);
作用域查找从运行时所处的最内部作用域开始,逐级向上进行,直到遇到第一个匹配的标识符为止
在多层的嵌套作用域可以定义同名的标识符,这叫做 “遮蔽效应”
var a = 0;function test(){var a = 1;console.log(a);}test();
声明从他们在代码中出现的位置被移动到最上面,这个过程叫做变量提升,预解释
// 预解释a = 2;var a;console.log(a);var a;console.log(a);a = 2;// 声明从他们在代码中出现的位置被移动到最上面,这个过程叫做变量提升,预解释var a;console.log(a);//undefineda = 0;function fn(){var b;console.log(b);//undefinedb = 1;function test(){var c;console.log(c);//undefinedc = 2;}test();}fn();
函数调用在函数声明前,也可以调用到函数
[^函数的声明可以提升,但是函数表达式不能提升]:
var foo;foo();foo = function bar(){console.log(1);}
1.声明提升: 变量声明提升和函数声明提升 变量的声明优先于函数的声明.但是 函数的声明会覆盖未定义的同名的变量
var a;function a(){};console.log(a);var a;function a(){};a = 10;console.log(a);
2.变量的重复声明是无用的,但是函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)
var a;var a;a = 1;a = 10;console.log(a);
3.函数声明提升优先级高于变量的声明提升
var a;function a(){console.log('hello wolrd');}a();
4.后面的函数声明会覆盖前面的函数声明
fn();function fn(){console.log('fn');}function fn(){console.log('fn2');}
[^应该避免在同一作用域中重复声明]:
bar => fn => 全局
查找机制:在当前作用域中发现没有该变量,然后沿着作用域链往上级查找,直到查到对应的变量为止,如果查找不到,直接报错
自由变量:在当前作用域中存在但未在当前作用域中声明的变量
一旦出现自由变量,就肯定会有作用域链,再根据作用域链查找机制,查找到对应的变量
var a = 1;var b = 2;// fn=>全局function fn(x) {var a = 10;// bar => fn => 全局function bar(x) {// 自由变量:在当前作用域中存在但未在当前作用域中声明的变量// 一旦出现自由变量,就肯定会有作用域链,再根据作用域链查找机制,查找到对应的变量// 查找机制:在当前作用域中发现没有该变量,然后沿着作用域链往上级查找,直到查到对应的变量为止,如果查找不到,直接报错var a = 100;b = x + a;return b;}bar(20);bar(200);}fn(0);
每个执行环境都有一个与之关联的变量对象,环境中定义的函数和变量都保存在这个对象
var a = 1;var b = 2;function fn(x) {// arguments// thisvar a = 10;function bar(x) {var a = 100;b = x + a;return b;}bar(20);bar(200);}fn(0);
执行环境栈:其实就是一个出栈和压栈的过程
执行环境 相当于作用域链一样
总结:
1.在js中,除了全局作用域,每个函数都会创建自己的作用域。
2.作用域在函数定义的时候已经确定了,与函数调用无关。
3.通过作用域,可以查找作用域范围内的变量和函数有哪些,却不知道变量的值是什么。所以作用域是静态
4.对于函数来说,执行环境在函数调用时确定的。执行环境包含作用域内的所有的变量和函数的值。在同一个作用域下,不同的调用会产生不同的执行环境,从而产生不同的变量和值。所以执行环境是动态.
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号