作用域与上下文
作者:互联网
作用域与上下文
由于
JavaScript
语言的特性,函数可以被自由的调用,这种设计模式衍生出作用域
和执行上下文
(简称上下文)的概念。
-
作用域(scope)是一系列方法内可调用到的变量,对象,方法组成的集合。此种集合的范围基于函数(function-based)。
-
上下文(context)是当前 代码解析和执行 所在环境的一种抽象概念,是方法调用的主体,方法中的 this 所代表的值。该取值基于对象(object-based)。
全局作用域
全局作用域在页面开启时产生,页面关闭时销毁。其他局部作用域的存在可以避免与全局作用域的命名标识符冲突(局部变量和全局变量)。
函数作用域(局部作用域)
函数作用域于函数调用时生成,执行结束后销毁。
块级作用域(es6)
在 {}
体内的执行语句,const
与 let
声明的变量为块级作用域,而 var 声明的变量与 function 声明的函数依旧在其父作用域中。
- 单
{}
语句(非 object) if
、switch
的{}
语句for
、while
、do
的{}
语句try
、catch
的{}
语句with
的{}
语句
全局上下文
底层上下文,如浏览器中的
window
对象和 nodejs 中的global
。
函数上下文
函数调用的主体。函数存在定义时上下文与运行时上下文,通过
call
、apply
改变。
eval 函数的上下文
eval
函数将参数字符串转换为执行脚本eval
函数可以被赋值,当eval
被赋值时,也会把当前eval
所处的变量作用域也赋值过去。
var x = 1,
evalg = eval;
function a() {
var x = 3;
eval('alert(x)'); //3
evalg('alert(x)'); //1
}
- IE9 之前的浏览器没有全局
eval
的概念, 但是可以用execScript
来代替。
var x = 1;
(function() {
(!-[1] ? execScript : eval)('var x = 123;');
})();
console.log(x);
执行栈
执行栈用于存储代码运行时所创建的上下文。与其他语言的调用栈相同,是先进后出的数据结构。
当JavaScript
引擎第一次遇到你的脚本时它会创建一个全局上下文并且压入当前执行栈。
当执行函数时引擎为其增加一个新的局部上下文压入栈的顶。
函数执行结束时,该函数上下文从栈中弹出。
上下文的生命周期
-
创建阶段:
- 创建变量对象
- 建立作用域链
- 确定
this
指向
-
执行阶段:
- 变量赋值
- 函数引用
- 执行代码
-
执行完毕
- 移出执行栈等待回收
变量查找
作用域中的变量查找遵循先局部后全局的规则,当前作用域的变量不存在将在父级函数的作用域查找,依次向上直至顶层作用域。该规则也称为
作用域链
。
闭包
通过一个执行函数返回另一个的自定义函数的程序设计方式。
- 由于
作用域链
的机制,闭包提供了让外部函数访问另个一函数内部变量的功能,从而达到内部封装外部访问的目的,不同场景下的闭包使用根据这一机制也有不同的使用方式。
const getter = (function() {
let a = 1;
return function() {
return a;
};
})();
getter(); // 1
- 由于闭包返回的自定义函数是通过计算获取而非声明,每次获取时都会开辟新的内存地址,且该内存空间将长期占用可能导致内存泄露。
变量提升(预解析)
指变量、函数的声明被提升至作用域的顶部。
- 同一个变量只声明一次,同标识符的声明被忽略。
- 变量赋值不提升。
- 函数声明优先于变量声明,且函数将连带定义一起被提升。(未声明与未定义的区别体现为:未定义的变量返回 undefined,而未声明则抛出异常,如:XX is not defined)
a = 1;
console.log(a);
var a;
//输出为1
var a = 1;
function log() {
console.log(a);
var a;
}
_print();
//输出为undefined print函数将变量a的声明提升,取的是函数内a的值
_print();
function _print() {
console.log('可提升');
}
// 可提升
_print();
var _print = function() {
console.log('不可提升');
};
// Uncaught TypeError: _print is not a function
- let、const 的变量提升
在 es6 之前没有块级作用域,var 变量在声明时将自动赋值 undefined,而 let、const 仅声明不赋值但依旧存在变量提升。
let a = 1;
{
console.log(a);
let a;
}
//Uncaught ReferenceError: Cannot access 'a' before initialization
标签:function,函数,作用域,var,上下文,变量 来源: https://www.cnblogs.com/qingzhao/p/16560913.html