前言:
想学好一门语言,从了解该语言的词法结构和编译执行原理下手小编感觉再合适不过了。这里我就会写一点关于JS中的作用域的一些理解。
大佬别喷,直接划走。嘻嘻(#^.^#)
三大作用域(全局、局部、块级):
全局
var a;
写在最外层(window)时为全局作用域,无论在哪个地方调用它。谁都可以用访问得到。
局部
但是当var a;
写在函数体内的时候就只有该函数体内可用(包括函数内部的子函数),私有作用域就是当前函数自己的地盘(自己的执行上下文)用来存放自己的私有变量不受外界影响,私有作用域中变量不被外界访问。
父级作用域是不能取子级作用域的字符的,子级作用域是可以调用父级作用域的字符。
块级
那let
和const
是啥?
这就是JS中的块作用域用法和var
相同,但是被Let
声明的变量只在代码块内有效,对该块的外部无影响。
const
为不可修改的。
例如:
var a = 15
for(var a =0; a< 10; a++){
console.log(a)}
console.log(a)//外部再打印一次a为10,影响了外部属性,a属性就会挂在外部的作用域上
var a = 15
for(let a =0; a< 10; a++){
console.log(a)}
console.log(a)//用let外部再打印一次a为15,对块外部无影响。同样外部也访问不到内部。
查找
遮蔽效应:如果下面这个函数fy想得到a的值,结果会是多少呢
var a = 11
function fn(){
var a = 12
function fp(){
var a = 13
function fy(){
console.log(a)
}
fy()
}
fp()
}
fn()//为13
查找方式是从本函数内作用域由内而外查找的,对于函数fy来说,fp→fn→windo,都是可访问可查找可调用的区域。由内而外找到第一个a的值即完成该语句,其后面的属性被第一个遮蔽从而无法查找。
欺骗词法作用域(会影响性能):
1.eval()用法
词法作用域就是再编译时划分该词法作用的区域。在你写代码时将变量和块作用域写在哪里来决定,也就是词法作用域是静态的作用域,在你书写代码时就确定了。
我们知道了词法作用域是静态不可变的,有没有什么方法来修改(欺骗)词法作用域呢?
例如:
function foo(str, a){
eval(str);
console.log(a, b)
}
var b = 2;
foo(‘var b = 3’, 1)//执行结果为1,3
JavaScript 中的 eval(..) 函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码并 运行,就好像代码是写在那个位置的一样。
eval(..) 调用中的 "var b = 3;" 这段代码会被当作本来就在那里一样来处理。由于那段代码声明了一个新的变量 b,因此它对已经存在的 foo(..) 的词法作用域进行了修改。事实上,这段代码实际上在 foo(..) 内部创建了一个变量 b,并遮蔽了外部(全局)作用域中的同名变量。
在严格模式下'use strict'让浏览器引擎进入严格模式,无法改变作用域。
例如:
function foo(str){
‘use strict’;
eval(str)
console.log(a)//a is not defind
}
foo(‘var a = 2’)//此处调用
eval是未执行吗?
不是的,eval()
已经执行完了,str存在在eval()
内部,没有改变foo()
函数的词法作用域。
with()
with 可以重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象 本身。
可以这么用:
var obj = { a: 1};
obj.a = 2;// 单调乏味的重复
with (obj) { a = 3; }// 简单的快捷方式
还可以这么用:
function foo(obj) {
with (obj) { a = 2; }
}
var o1 = { a: 3 };
var o2 = { b: 3 };
foo( o1 );
console.log( o1.a ); // 2
但是呢,如果用with()
给没有该属性的函数修改值,该值会被泄漏到全局window上。
function foo(obj) {
with (obj) { a = 2; }
}
var o2 = { b: 3 };
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!
这里O2对象中没有a属性,with()无法修改会直接把a丢进全局。