系列文章:javascript
本质上是一套规则,用于肯定在何处以及如何查找变量(标识符)。java
做用域是能够嵌套的,引擎从当前做用域开始查找,若是找不到,就会向上一级继续查找,当抵达到最外层的全局做用域查找后,不管找到仍是没找到,都会中止。 bash
有如下两种方式:闭包
⚠️注意:只会查找一级标识符,比 如foo.bar.baz,只会试图找到 foo 标识符,找到后,对象属性访问规则后分别接管对 bar、baz 的属性访问。模块化
举🌰:函数
function foo(a) {
console.log(a + b);
}
var b = 2;
foo(3);
引擎:做用域,我须要为 b 进行 LHS引用,这个你见过吗?
全局做用域:见过见过!刚才编译器声明它来着,给你。
引擎:谢谢大哥,如今我要把2赋值给 b
引擎:做用域啊,还有个事,我要对 foo 进行 RHS 引用,你见过没啊?
全局做用域:见过呀,它是个函数,给你。
引擎:好的,我如今执行一下 foo
引擎:哥啊,我须要对 a 进行 LHS 引用,这个你见过没?
全局做用域:这个也见过,是编译器把它声明成 foo 的一个形参了,拿去吧。
引擎:太棒了,如今我把3赋值给 a 了
引擎:foo 做用域啊,我要对 console 进行 RHS 引用,你见过没啊?
foo做用域:这我也有,是个内置对象,给你
引擎:你老是那么给力,如今我要看看这里有没有 log(),找到了,是个函数。
引擎:哥,我要对 a 进行 RHS 引用,虽然我记得好像有这个值,可是想让你帮我确认如下。
foo做用域:好,这个值没变过,你拿走吧。
引擎: 哥,我还要对 b 进行 RHS 引用,你找找呗
foo做用域:我没听过啊,你问问个人上级吧:
引擎:foo 的上级做用域兄弟,你见过 b 没啊?
全局做用域:见过 b 啊,等于2,拿走不谢!
引擎:真棒,我如今把 a + b ,也就是5,传递进 log(...)
复制代码
主要有两种:post
eval():能够接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。性能
function foo(str, a) {
eval(str);
console.log(a, b)
}
var b = 3;
foo("var b = 4", 2); // 2, 4
复制代码
with:经过将一个对象的引用看成做用域来处理,将对象的属性看成做用域中的标识符来处理,从而建立了一个新的词法做用域。优化
function foo(obj) {
with(obj) {
a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo(o1);
console.log(o1.a) //2
foo(o2)
console.loh(o2.a) //undefined;
console.log(a)
//2 在o2中,对a进行LHS引用,没有找到,
//在o2中不会创造a这个属性
//由于是非严格模式,因此会在全局做用域中建立一个变量 a,并赋值给2
复制代码
⚠️注意:这两个机制只在非严格模式下有效,严格模式下会抛出 Reference 错误。还会致使性能降低,引擎没法在编译时对其进行优化,因此会变慢。ui
做用域有三种:
在javascript中,定义一个函数有四种方式,分别是:
函数声明:function 关键字出如今声明中第一个词,它的调用能够先于声明。
函数表达式:在执行到达时建立,并只有从那时起才能够用。
ES6 中的箭头函数
new Function()
⚠️注意:函数声明和函数表达式最大的区别就是他们的名称标识符将会被绑在何处。
var a = 2;
// 函数声明,被绑定在所在做用域中,能够直接经过 foo() 来调用它
function foo() {
var a = 3;
console.log(a); //3
}
foo();
// 函数表达式,foo2被绑定在函数表达式自身的函数中,而不是所在的做用域中
// 也就是,只能在函数内部里被访问foo2,外部做用域内不能访问
(function foo2() {
var a = 3;
console.log(a) //3
}
)()
console.log(a) //2
复制代码
在函数表达式中的当即执行函数表达式(IIFE)使咱们不用主动调用函数,它会本身调用,对于作模块化、处理组件是很是有用的,IIFE通常使用匿名函数表达式。
⚠️注意:调用函数最简单的方法就是加一对小括号,但函数声明不能直接调用的缘由是:
解决办法:不让 function 关键字出如今行首
function fn() {
console.log(1);
}(); //报错
const fn1 = function() {
console.log('表达式执行');
}(); //执行函数
复制代码
在 ES6 以前,js中也是有块做用域概念的,但只限于个别具体的语法中:
在 ES6 中,引入了新的块做用域
⚠️注意:提高是指声明会被视为存在于其所出现的做用域的整个范围内。var容许变量声明提高,但不容许赋值或其余运行逻辑提高。函数声明会被先提高,而后才是变量。
var scope = "global";
function scopeTest() {
console.log(scope);
var scope = "local" ;
}
scopeTest(); //undefined
复制代码
当函数能够记住而且访问所在的词法做用域时,而且函数是在当前词法做用域以外执行,此时该函数和声明该函数的词法环境的组合。
直接看代码吧,用语言来描述过于空洞。
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
复制代码
这是一个高频率会看到的题,咱们指望的结果是:分别输出数字1 - 5,每秒一个,每次一个。但实际上,会以每秒一次的频率输出五次6。
那代码中到底有什么缺陷致使它的行为同语义所暗示的不一致呢?缺陷是咱们试图假设循环中每一个迭代在运行时,都会为本身"捕获"一个 i 的副本。可是实际上,尽管这五个函数是在各个迭代中分别定义的,可是它们都被封闭在一个共享的全局做用域中,所以只有一个i。
若是想要返回的预期结果,能够经过如下方法。
在迭代内,使用 IIFE 会为每一个迭代都生成一个新的做用域,使得延迟函数的回调能够将新做用域封闭在每一个迭代内部。
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, i * 1000)
})(i);
}
复制代码
let语法本质上是将一个块转换成一个能够被关闭的做用域,let声明的变量在每次迭代都会声明。
for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000) }
最后,若是以为文章还不错,请点个赞吧~👍