做网站用的软件是什么了,做高铁在哪个网站买,爱站工具维护,wordpress商城主题手机一、什么是作用域#xff1f;
作用域指一个变量的作用范围。
每个作用域都是一个独立的地盘#xff0c;目的就是为了保证当前作用域内的变量不会外泄#xff0c;且不会和其他作用域中的同名变量冲突。
在JavaScript中的作用域类型
全局作用域函数作用域(局部作用域)块级…一、什么是作用域
作用域指一个变量的作用范围。
每个作用域都是一个独立的地盘目的就是为了保证当前作用域内的变量不会外泄且不会和其他作用域中的同名变量冲突。
在JavaScript中的作用域类型
全局作用域函数作用域(局部作用域)块级作用域(let/const ES6新增
二、作用域详解
2.1 全局作用域
写在script标签内的最外层变量和函数都是全局作用域。当页面打开时全局作用域会自动创建而当页面关闭时就会销毁。所有全局作用域对象都可以通过window对象的属性进行访问。
//示例
scriptconsole.log(window);var a 1;function aa(){console.log(aa)
}var person {name:张三};window.aa();
console.log(window.a);
console.log(window.person.name);/script
2.2 函数作用域
· 定义于函数内部的变量或函数。也称为局部作用域。在函数调用时被创建函数执行完毕后自动销毁。函数每调用一次都会创建一个新的函数作用域他们之间是相互独立的。
· 函数作用域中可以访问全局作用域的变量和函数反之则不行。
· 在函数内部访问一个变量或函数时会现在当前作用域中查找。如果没找到就到上一级的作用域中查找直到全局作用域。
· 函数内部也相当于一个小的全局作用域所以定义在函数内部的变量和函数也存在变量提升。
//示例
scriptvar name 张三;
function aa(){var name 李四;console.log(name) //李四console.log(window.name) //张三
}
aa();/script2.3 块级作用域ES6
带“{}”的基本都为块级作用域如if(){ }、for(){ }、catch(err){}。var和function没有块级作用域的概念只有let和const有块级作用域概念。
//示例
scriptif(true){let a 10;console.log(a) //10
} for(let i0;i0;i){console.log(i) //0
}console.log(a) //ReferenceError: a is not defined
console.log(i) //ReferenceError: i is not defined/script 三、什么是作用域链 当一个块或函数嵌套在另一个块或函数中时就发生了作用域嵌套。因此在当前作用域中没有找到某个变量时引擎就会在外层嵌套的作用域中继续查询直到找到该变量。如果抵达最外层的作用域(全局作用域)还找不到就会抛出ReferenceError异常。 四、 作用域原理
1.1 编译过程 作用域的内部原理分为5个阶段
编译阶段 编译器将代码分解成词法单元并将词法单元解析成一个数结构AST执行阶段 编译器询问作用域中是否存在某变量,不存在就创建并为引擎生成运行时所需要的代码查询阶段引擎询问作用域中是否存在某变量如果存在引擎就会使用该变量并为其赋值不存在就继续向上查找嵌套阶段当前作用域不存在某变量会继续向上查询异常阶段直到顶层作用域window也没有找到某变量就抛出异常ReferenceError 例var a 2;的执行过程 首先引擎会认为这里有两个完成不同的声明一个由编译器在编译时处理(var a)另一个则由引擎在运行时处理( 2)。 编译器会进行两步操作 语法分析阶段 1将这段代码分解成词法单元 2将词法单元解析成一个数结构。 代码执行阶段 1遇到var a编译器会询问作用域是否已经有一个名称为a的变量存在于同一作用域的集合中。如果是编译器会忽略该声明继续进行编译。否则它会要求作用域在当前作用域的集合中声明一个新的变量命名为a。 2接下来编译器会为引擎生成运行时需要的代码这些代码被用来处理a 2这个赋值操作引擎运行时会首先询问作用域在当前作用域集合中是否存在一个 叫做a的变量。如果存在引擎就会使用该变量如果否引擎会继续在上一层作用域中查找。如果引擎最终找到了a变量就会将2赋值给它。否则引擎就会举手示意并抛出一个异常。 总结变量的赋值操作会执行两个动作首先编译器会在当前作用域中声明一个变量(变量未声明过时)然后在运行时引擎会在作用域中查找该变量如果能找到就给它赋值。 1.2 详解LHS和RHS LHS和RHS的含义为“赋值操作的左侧或右侧”但是并不意味着就是“”赋值操作符的左侧或右侧。赋值操作还有其他几种形式因此在概念上最好将其理解为
LHS(查询变量的容器本身目的是为某个变量赋值setRHS(查询变量的源值目的是获取某个变量的值get 例 console.log( a ); //这里的a没有赋予任何值所以需要查找并取得a的值所以是RHS查询。 a 2 //这里的a则是LHS查询因为我们并不关心当前的值是什么只是要为 2这个赋值操作找到一个目标。 总结LHS 和 RHS 查询都会在当前执行作用域中开始如果有需要也就是说它们没有找到所 需的变量就会向上级作用域继续查找目标变量这样每次上升一级作用域最后抵达全局作用域顶层无论找到或没找到都将停止。 不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式 地创建一个全局变量非严格模式下该变量使用 LHS 引用的目标作为标识符或者抛 出 ReferenceError 异常严格模式下
五、词法作用域 词法作用域就是定义在词法阶段的作用域。是根据代码中变量和块作用域的位置所决定的因此当词法分析器处理代码时会保持作用域不变(大部分情况,eval和with特殊)。 在下列示例中编译器在词法(分词)阶段会创建三个不同的作用域他们三个作用域是严格包含的 /*foo方法为全局作用域在foo这个函数作用域中又存在a、b、bar在bar函数作用域中存在c
*/function foo(a) {var b a * 2;function bar(c) {console.log( a, b, c );}bar( b * 3 );
}
foo( 2 ); // 2, 4, 12 六、变量提升 在作用域原理中第一阶段也就是编译阶段有一部分工作就是找到所有的变量和函数在内的所有声明并用合适的作用域将他们关联起来。这个处理工作是在代码执行阶段之前就处理完成的。 在JavaScript中var a2; js引擎会将其看成两个声明var a和a2。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。 函数声明变量声明 函数声明和变量声明都会被提升但是函数会首先被提升。 七、立即执行函数IIFE
(function foo(){console.log(我被执行了)
})();//或者
(function(){ console.log(我被执行了)
}());//或者
(function IIFE(){ console.log(我被执行了)
})()。 由于函数被包含在一对()括号内部因此成为了一个表达式通过在末尾加上另外一个()就可以立即执行这个函数比如(function foo(){ ... })()。第一个()将函数变成表达式第二个()执行了这个函数。 IIFE用法1:把他们当作函数调用并传递参数进去。
scriptvar a 10;
(function IIFE(global){var a 3;console.log(a) //3console.log(global.a) //10
})(window)/script IIFE用法2: 倒置代码的运行顺序将需要运行的函数放在第二位在IIFE执行之后当作参数传递进去。
script/*函数表达式 def 定义在片段的第二部分然后当作参数这个参数也叫作 def被传递进IIFE 函数定义的第一部分中。最后参数 def也就是传递进去的函数被调用并将window 传入当作 global 参数的值。
*/var a 10;
(function IIFE(def){def(window)
})(function def(global){var a 3;console.log(a) //3console.log(global.a) //2
})/script 七、作用域闭包 函数内部可以访问所以外部作用域中的变量,但是反之则不行这就形成了闭包闭包在我们的代码中其实很常见比如在定时器、事件监听器、ajax请求或者任何其他的异步(或者同步)任务重只要使用了回调函数实际上就是在使用闭包。 延迟函数的回调会在循环结束时才执行。