作用域
词法作用域
作用域
域表示的就是范围, 即作用范围
就是一个名字在什么地方能使用, 在什么地方不能使用
块级作用域
块级别的作用范围
// 在 c , java 等编程语言中,下面的语法报错
{
var num = 123; // 应该用int , 这里是伪代码
{
console.log(num); // 123
}
console.log(num); // 报错
}
在 js 中采取词法作用域
词法 (代码) 作用域, 就是代码在编写过程中体现出来的作用范围, 代码一旦写好, 不用执行, 他的作用范围就已经确定好了, 这个就是所谓的词法作用域
在 js 中的词法作用域规则
函数允许方位函数外的数据
整个代码结构中只有函数可以限定作用域
作用规则首先是提升规则分析
就近原则如果当前作用规则有了名字, 就不考虑外面的名字
在 js 中作用域分析方法
先进行预解析, 分析预解析过程
程序在执行过程, 会先将代码读取到内存中检查. 会将所有的声明在此时进行标记. 所谓的标记就是
让 js 解释器知道有这个名字, 后面在使用名字的时候, 不会出现未定义的错误. 这个标记过程就是提升.
声明
名字的声明, 标识符的声明 (变量名声明)
名字的声明就是让我们的解释器知道有这个名字
名字没有任何数据与之对应
函数的声明
函数声明包含两部分
函数声明与函数表达式有区别, 函数声明是单独写在一个结构中, 不存在任何语句, 逻辑判断等结构中
首先函数声明告诉解释器有这个名字存在. 该阶段与名字声明一样
告诉解释器, 这个名字对应的函数体是什么 (函数名和函数体绑定链接)
再进行代码执行过程
常见的简单作用域问题
例子 1:
var num = 123;
function foo() {
console.log(num);
}
foo(); // 输出 123
分析
预解析
变量num变量名提升函数foo函数名提升
代码执行
num赋值123,
函数内区域为独立区域,
可以使用外部数据,
但是现在变量名相同,
覆盖外面的值函数foo执行函数区域中变量num变量名提升输出num为undefined num赋值456输出num为456
例子 2:
if (false) {
var num = 123;
}
console.log(num); // 输出undefined
分析
预解析
变量名num提升
执行代码
if判断为false,
不进入输出num,
num定义未赋值,
为undefined
例子 3:
var num = 123;
function foo() {
var num = 456;
function fn() {
console.log(num); // 输出456
};
fn();
}
foo();
分析
预解析
变量名num和函数名foo声明提升,
函数名foo和函数体链接
执行代码
num = 123,
赋值执行函数,
函数内部声明提升,
变量名num和函数名fn变量num = 456,
执行函数fn输出num,
fn函数内部没有num,
向上级寻找num = 456,
输出num
例子 4:
var num = 123;
function foo1() {
var num = 456;
function foo2() {
num = 789;
function foo3() {
console.log(num); // 输出789
}
foo3();
}
foo2();
}
foo1(); // 输出456
console.log(num); // 输出123
分析
预解析
变量名num和函数名foo1声明提升,
函数名foo1和函数体绑定
执行代码
num = 123,
赋值执行函数foo1,
变量名num和函数名foo2声明提升,
函数名foo2和函数体绑定num = 456,
赋值执行函数foo2,
num在foo2函数中没有,
访问上级foo1得到num,
num = 789,
赋值函数名foo3声明提升并且绑定函数体执行函数foo3,
输出num,
foo3中没有num,
访问上级,
得到num = 789输出num为789跳出函数,
输出num为123
例子 5:
if (!'a' in window) {
var a = 123;
}
console.log(a); // undefined
分析
预解析
变量a声明提升
执行代码
判断window中是否存在a,
a存在判断为false,
不执行
if中的代码输出a,
为undefined
复杂的作用域问题
例子 1
if (true) {
function f1() {
console.log('true');
}
} else {
function f1() {
console.log('false');
}
}
f1();
分析
新版浏览器
预解析
无,
函数f1被浏览器认为是函数表达式,
不进行变量名提升
执行代码
if判断进入true执行函数表达式f1输出结果为true
旧版浏览器
预解析
函数f1变量声明提升,
f1函数名和最后一个函数体连接在一起
执行代码
if判断进入true执行f1输出false
例子 2
if (false) {
function f1() {
console.log('true');
}
} else {
function f1() {
console.log('false');
}
}
f1();
分析
新版浏览器
预解析
无,
f1为函数表达式,
没有进行声明提升
执行代码
if判断执行f1输出false
旧版浏览器
预解析
函数名f1,
声明提升,
函数名和最后一个函数体链接在一起
执行代码
if判断执行f1输出false
例子 3
var num = 123;
function f1() {
console.log(num);
}
function f2() {
var num = 456;
f1();
}
f2();
分析
预解析
声明提升,
变量num,
函数名f1,
f2,
函数名和函数体链接
执行代码
num = 123执行函数f2变量num赋值456执行函数f1输出num,
函数里面没有,
向上寻找,
上级为 (0级作用域链),
num = 123,
输出num为123
例子 4
var num = 123;
function f1() {
console.log(num);
}
function f2() {
num = 456;
f1(num);
}
f2();
分析
预解析
变量名num,
函数名f1,
f2声明提升,
函数名和函数体链接一起
代码执行
num = 123执行函数f2 num = 456,
f2中没有num,
向上级寻找得到num为123,
将num = 123赋值,
num = 456执行f1,
f1中没有num,
向上级寻找得到num为456输出num,
num = 456,
输出num456
来源: http://www.cnblogs.com/liu666/p/5747667.html