作用域
JavaScript中的作用域分为全局作用域,函数作用域,块作用域。
全局作用域
对于全局范围内声明的变量,可以在任何地方访问。
var messge = 'Hello'
function say(){
console.log(message)
}
say(); // Hello
函数作用域
在函数内声明的变量,只能从该函数内部访问。
function say() {
var messge = 'Hello';
console.log(messge);
}
say(); // Hello
console.log(greeting); // ReferenceError: messge is not defined
块作用域
ES6 引入了let
和const
关键字,它们声明的变量只能从该代码块内访问。
{
let messge = 'Hello';
console.log(messge);
}
console.log(messge); // ReferenceError: messge is not defined
静态作用域
在词法分析时(编译时)确定。
let number = 42;
function printNumber() {
console.log(number);
}
function log() {
let number = 54;
printNumber();
}
log(); // 42
作用域链
JavaScript运行时,引擎首先会在当前范围内查找变量,如果找不到,会向父作用域查一层一层向上查找,一直找到顶层全局作用域,如果还是找不到就返回undefined
。
var g = 'Global hello'
function f1() {
var g1 = 'G1 hello';
function f2() {
var g2 = 'G2 hello'
function f3() {
console.log(g, g1, g2)
}
f3()
}
f2();
}
f1(); // Global hello G1 hello G2 hello
运行分析
执行上下文
执行上下文是执行一段 JavaScript 的环境,它存储了执行代码的一些必要信息。执行上下文分全局执行上下文和函数执行上下文。
-
全局执行上下文,一个程序中只会有一个,函数之外的代码都在全局执行上下文中运行。
-
函数执行上下文,函数在每次调用时都会创建一个对应的函数执行上下文。
执行上下文的包含变量环境(Variable environment
),作用域链(Scope chain
),this
指向。
- 变量环境,函数内部所有的变量和对象引用和调用参数。
- 作用域链,当前函数之外的变量的引用组成。
this
指向。
从JavaScript运行时的内存结构来看,调用栈就是存储执行上下文的集合。
案例
案例一
这是一个普通的JavaScript例子,运行流程分析:
var msg = 'Global hello'
function say() {
var msg = 'Hello'
return msg;
}
var s = say();
console.log(message) // Hello
- 创建全局执行上下文,然后入调用栈。
- 调用函数
say()
,创建say()
的函数执行上下文,并入调用栈。 - 执行完
say()
函数将结果返回,更新全局执行上下文里的s
变量。 - 将
say
函数的执行上下文弹出栈。
案例二
这个例子跟上面不同的是返回值是函数,这个匿名函数也称闭包,它访问了函数外部的变量,即使外部函数执行上下文被弹出栈后,它依然可以持有外部变量的引用。运行分析如下:
var msg = 'Global hello'
function say() {
var msg = 'Hello'
return function() {
console.log(msg)
};
}
var s = say();
s()
- 创建全局执行上下文,然后入调用栈。
- 调用函数
say()
,创建say()
的函数执行上下文,并入调用栈。 - 执行完
say()
函数将结果返回,更新全局执行上下文里的s
变量,s
是函数类型,它依然持有say
var = msg
引用。 - 将
say
函数的执行上下文弹出栈。 - 执行
s()
,创建s()
的函数执行上下文。 - 将
s()
函数的执行上下文弹出栈。
思考题
在脑海里动态执行下面代码。
题目一:
var msg = 'Global hello'
function getMsgFunc() {
var msg = 'Hello';
function getMsg() {
return msg;
}
return getMsg();
}
console.log(getMsgFunc());
题目二:
var msg = 'Global hello'
function getMsgFunc() {
var msg = 'Hello';
function getMsg() {
return msg;
}
return getMsg;
}
console.log(getMsgFunc()());
题目三:
var msg = 'Global hello'
var obj = {
msg : 'Hello',
getMsgFunc : function(){
return function(){
return this.msg;
};
}
};
console.log(obj.getMsgFunc()());
题目四:
var msg = 'Global hello'
var obj = {
msg : 'Hello',
getMsgFunc : function(){
var that = this
return function(){
return that.msg;
};
}
};
console.log(obj.getMsgFunc()());