JS作用域的本质:
谈谈JS作用域的本质之前,先谈一下编程语言的功能是什么;
编程语言的功能是什么,用来操作计算机,那么怎样操作计算机呢?无非就是让计算机按照我们的想法,去进行计算,然后得到结果;
那么计算又是计算什么呢?当然是数据呀,那么数据存在哪里呢,当然是内存;
因此,我将编程语言的功能理解为两部分:1.存取数据(将数据存入内存或者从内存中读出) 2.根据内存中的数据指导计算机做运算
现在我们谈一下JS的作用域:
上面说到:编程语言的剧本功能之一就是数据的存取,比如 var a=10,可以理解为在内存中开辟一块空间,命名为a,里面存着数字10。
那么,如果在此基础上写function test(){ var a=100;a=1000; };JS引擎又该怎样处理我函数内部的 var a=10呢;是覆盖?还是重新开辟
空间进行保存?a=1000又怎样处理?
因此,我们需要一套规则来管理变量的存取,于是就引入了作用域的概念
作用域:顾名思义:变量的作用范围。可理解为一个一个的圈子,规则就是:
圈子内可以看见圈子外的数据,圈子外的无权访问圈子内的数据【除非提供相应的出口 例如 闭包】,如果当前圈子内没有找到目标数据,
就会向外层的圈子寻找,直到全局作用域,如果依旧没找到,就报错
例子:
function out(){
var a=10;
function inner1(){
console.log(a);
}
function inner2(){
var a=100;
console.log(a);
}
inner1();//输出10
inner2();//输出100
console.log(r)//报错
}
out();
作用域分类:
词法作用域:写代码时就已经决定了变量的作用范围【我这颗保姆心啊,多想给它解释清楚,哭了】
当然,我们可以通过eval()函数动态插入一些变量来改变词法作用域
比如:function foo(str,a){
eval(str);
console.log(a,b);
}
var b=10;
foo(’var b=100;‘,1);//这里传入的第一个参数就改变了词法作用域
解释:在我们书写的时候,词法分析器会认为 函数foo内部的b是指向外部的【于是很可能会做一些优化】;
但当执行函数时,词法作用域被改变了,指向了临时定义的b=100,这样使得之前的优化毫无意义。
因此,尽量不要使用eval函数
函数作用域:基于函数的作用域,没什么特别的,就是圈子规则;为了不污染全局作用域,防止命名冲突,隐藏实现细节而设计(个人理解)。
值得注意的是,在函数内部访问变量时,如果到全局也没有找到;对于LHS(左查询),则会在全局创建变量;
如果是RHS(右查询),则会报错。
例如:
var x=100;
function test(x){
a=x;//会在全局创建一个变量a,并将x的值赋给a;
}
test(x);
console.log(window.a)//输出100
----------------------
function test2(){
var m;
m=t;//报错,因为右查询找不到t
}
test2();
注意:对于左查询和右查询,不了解的伙伴百度一下,很简单的
块级作用域:当我们在使用循环结构,选择结构时,后面的花括号或小括号里变量的作用范围。
当我们在使用循环结构,选择结构时,希望后面的花括号里能形成一个单独的作用域,而不污染到当前结构所在的作用域,
于是就引入了块级作用域
例如:在全局作用域里写 for( var a=0;a<10;a++){
console.log(a)//输出 0-9
var b=100;
}
console.log(a)//输出10
console.log(b)//输出100
解释:为什么会输出10,因为 我们是用了 var 来定义了a,而使用var来定义,是没有块级作用域这一说的,
于是就相当于 a是在全局中定义的;b也一样;这样的话,无意间就污染了全局作用域;那应该怎么做呢?
我们可以将var 换成let 来定义,就出现了块级作用域,let定义的变量就会在自己的块级作用域中,
进而解决污染问题。
for(let a=0;a<10;a++){
console.log(a);//输出0-9
let b=100;
}
console.log(a);//报错
console.log(b);//报错