作用域:作用域是指在程序中定义变量的区域,该变量在该区域内可被访问。
1、关于作用域的两种查询
在JavaScript中编译器会用两种查询方式进行查询
一种是LHS查询;
一种是RHS查询;
俩个查询的含义是,当变量出现赋值操作在左侧时进行LHS查询,出现在右侧时进行RHS查询。
详细的讲就是RHS查询就是简单的查找某个变量的值,而LHS查询则是查询变量的容器本身,从而对其赋值。
当然在代码查询中对它俩的区别也很重要.
在变量还没有声明(在任何作用域都无法找到该变量)的情况下,这两种查询的行为是不一样是。
参考下面的代码:
function foo(a){
consle.log(a+b);
b=a;
}
foo(2);
第一次b进行RHS查询时是无法找到该变量的。也就是说,这是一个“未声明”的变量,因为在任何相关的作用域中都无法找到它,那么RHS查询就会让引擎就会抛出ReferenceError异常
相较之下,当引擎执行LHS查询时,如果在顶层(全局作用域)中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎。
function foo(a) {
var b=a*2;
function bar(a) {
console.log(a,b,c);
}
bar(b*3);
}
foo(2); //2,4,12
一个简单的小程序;但这个例子中有3个逐级嵌套的作用域;
(1)包含整个全局作用域,只有一个标识符:foo;
(2)包含foo所创建的作用域,只有三个标识符:a,bar,b;
(3)包含着bar所创建的作用域,只有一个标识符:c;
2、欺骗语法
词法作用域完全由写代码期间函数所声明的位置来定义,能在运行时来“修改”词法作用域的方法叫做欺骗;下面用两个关键字来做欺骗。
注意:欺骗词法作用域会导致性能下降,所以在代码使用这两种机制并不是什么好主意。
2.1 eval
eval(..)函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。
function foo(str,a){
eval(str);//欺骗
console.log(a,b);
}
var b=2;
foo("var b=3;",1);//1,3
eval(..)调用中的“var b=3;”这段代码会被当作本来就在那里一样来处理。由于那段代码声明了一个新的变量b,因此它对已经存在的foo(..)的词法作用域进行了修改。事实上,和前面提到的原理一样,这段代码实际上在foo(..)内部创建了一个变量b,并遮蔽了外部(全局)作用域中的同名变量。
当consle.log(..)被执行时,会在foo(..)的内部同时找到a和b,但是永远也无法找到外部的b,因此会输出“1,3”而不是正常情况下会输出的“1,2”。
2.2 with
JavaScript中;另一个难以控制的用来欺骗词法作用域的功能的with关键字。
with通常被当作重复引用同一个对象中多个属性的快捷方式,可以不需要重复引用对象本身。
比如:
var obj={
a:1,
b=2,
c:3
};
//单调乏味的重复"obj"
obj.a;
obj.b;
obj.c;
//简单的快捷方式
with(obj){
a=3;
b=4;
c=5;
}
但实际上这不仅仅是为了方便地访问对象属性。考虑下面代码:
function foo(obj){
with(obj){
a=2;
}
}
var o1={
a:3
};
var o2={
b:3
};
foo(o1);
consle.log(o1.a); // 2
foo(o2.a);
consle.log(o2.a); //undefined
console.log(a); // 2---a被泄露到全局作用域上了
这个例子中创造了o1和o2两个对象。其中一个具有a属性,另一个没有。foo(..)接受了obj的参数,该参数是一个对象引用,并对这个对象引用执行了with(obj){..}。在with块内部,我们写的代码看起来只是对变量a进行简单的词法引用,实际上就是一个LHS查询所引用的,并将2赋值给它。
当我们将o1传递进去,a=2赋值操作找到了o1.a并将2赋值给它,这在后面的console.log(o1.a)中可以体现。而当o2传递进去,02并没有a属性,因此不会创建这个属性,o2.a保持undefined。
总结:eval(..)函数如果接受了一个或多个声明的代码,就会修改其所处的词法作用域,而with声明实际上是根据你传递给它的对象凭空创建了一个全新的词法作用域。
3、函数作用域
函数作用域的含义:属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
举个例子
function foo(a) {
var b=2;
function bar() {
//.....
}
var c=3;
}
由于标识符a、b、c和bar都附属于foo(..)的作用域,因此无法从foo(..)的外部对它们进行访问。也就是说,这些标识符全都无法从全局作用域中进行访问,因此下面的代码会导致ReferenceError错误:
bar(); //失败 console.log(a,b,c); //三个全都失败
3.1 隐藏和避免冲突
JavaScript中的隐藏和C语言中的主函数与子函数差不多
特征一:引申于最小特权原则(指在软件设计中,应该最小限度地暴露必要内容,而将其他内容都“隐藏”起来)就是创建一个作用域将一段代码嵌套起来,使这段代码所声明的变量变成私有;
特征二:避免同名标识符之间的冲突;
标签:java,变量,..,作用域,查询,---,obj,foo From: https://blog.csdn.net/qq_63698880/article/details/136722123