作用域与作用域链
作用域(Scope)
作用域产生于程序源代码中声明变量的区域,在程序编译
阶段就确定了。帮助在程序执行
阶段"查询并规定变量的可见性与可访问性。
作用域共两种工作模型:静态作用域
、动态作用域
。
Javascript运用的是静态作用域,函数作用域在声明时确定;Bash脚本运用的是动态作用域,函数作用域在调用时确定。
案例
let a = 1 function foo() { console.log(a) } function bar() { let a = 2 foo() } bar() 复制代码
静态作用域: 函数作用域在声明时确定,foo
函数内部不存在变量a
,依据作用域链向上寻找到全局作用域中的变量a
,输出结果1
。
动态作用域: 函数作用域在调用时确定,foo
函数内部不存在变量a
,因foo
在bar
中调用寻找bar
作用域内的变量a
,输出结果2
。
Javascript中可分为三种作用域:全局作用域
、函数作用域
、块级作用域
。
全局作用域
程序中任何地方都有权访问全局作用域下的声明的变量。
注意: 多人协作下,定义在全局的变量容易引发命名冲突、污染全局的问题。为了避免污染有以下两种方式。
创建私有的命名空间,可一定程度的降低被污染的风险。
let object = { name: '瑾行', getName: function() {} } 复制代码
创建立即调用函数表达式(IIFE),Jquery就是这么干的...????,利用函数作用域私有化变量起到隔离变量的作用。
(function(obj) { var name = '瑾行' var getName = function() {} })(window) console.log(name) //输出为空 console.log(getName) //输出为空 复制代码
函数作用域
函数内部定义的变量,外层作用域无权进行访问。当然,耍些手段可达到目的:闭包
。
例子:
function closure() { let name = '瑾行', return hello() { console.log('hello,' + name) } } let func = closure() func() // hello,瑾行 复制代码
块级作用域
ES6 新增let
和const
命令,使声明的变量仅在当前函数内部或代码块中访问。
块级作用域有如下特点:
与
var
相比,不存在变量提升。不允许
重复定义
。全局定义
不绑定在window上
。
来看下,利用块级作用域解决的经典面试题
for(var i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 5个5 }) } // 将i绑定到for循环的块作用域 for(let i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 0,1,2,3,4 }) } 复制代码
作用域链
上文提到作用域
是在定义时就确定了。原因是函数有个内部属性[[scopes]]
,当函数创建时,会保存所有的父变量对象;当函数被调用时,关联当前函数的活动对象(初始化arguments)与定义时的所有父变量对象确定完整的作用域链。[[scopes]]
如图所示。
作用域链的最前端是当前环境的变量对象,末端是全局window环境的变量对象。
例子
let msg = 'hello,' function say() { let name = '瑾行' return function hello() { console.log(msg + name) } } let hello = say() hello() // hello, 瑾行 复制代码
name
在Closure变量对象中,msg
在 顶层Script变量对象中(因let 声明,未绑定在window的变量对象)。
作用域与执行上下文
作用域与执行上下文的概念经常被混为一谈。
作用域
上文提到在程序编译阶段确定,但执行上下文
是在代码执行阶段确定,是执行代码时所在环境的抽象概念,主要通过引擎创建执行上下文栈来管理执行上下文 。
执行上下文生命周期:创建阶段
、执行阶段
、回收阶段
。
创建执行上下文,包含三个属性:变量对象、作用域链、this指向。
执行变量赋值、函数引用,执行其余代码。
垃圾回收。
执行上下文类型:全局执行上下文
、函数执行上下文
、Eval函数执行上下文
。
全局上下文:当开始执行程序时,就会创建全局上下文压入栈中,结束程序,从栈中弹出。
函数执行上下文:当每个函数被调用时,都会创建一个对应的执行上下文,函数调用结束,从栈中弹出
Eval函数执行上下文:执行eval函数时创建。
举个????。
let a = 1 function foo() { console.log(a) } function bar() { let a = 2 foo() } bar() 复制代码
作者:花哨
链接:https://juejin.cn/post/6993628835543515172