1.自由变量、执行上下文、作用域、作用域链、this
1-1作用域
作用域:代表了一个变量或者说某个变量的合法使用范围。
- 全局作用域(直接写一个变量没有受到什么函数约束,在全局都可以使用,如window、document对象)
- 函数作用域(在一个函数中定义的变量,只能在函数里使用)
- 块级作用域(ES6新增)
1-2 自由变量
- 一个变量在当前作用域中没有定义,但被使用了
- 向上级作用域,一层一层依次寻找,直至找到为止
- 如果到全局作用域都没找到,则报错 ×× is not defined
1-3 作用域链
作用域链中的下一个变量对象来自外部环境,再下一个变量来自下一外部环境,延申到全局变量。
1-4 执行上下文
JavaScript标准把一段代码(包括函数),执行所需的所有信息定义为:”执行上下文“。
ES3中的执行上下文:
- scope:作用域或作用域链。
- variable object:变量对象,用于存储变量的对象。
- this value:this 值。
ES5中执行上下文:
- lexical environment:词法环境,获取变量时使用。
- variable environment:变量环境,当声明变量时使用。
- this value: this 值。
1-5 作用域与执行上下文
许多开发人员经常混淆作用域和执行上下文的概念,误认为它们是相同的概念,但事实并非如此。我们知道 JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段:
- 词法分析
- 语法分析
- 作用域规则确定
执行阶段:
- 创建执行上下文
- 执行函数代码
- 垃圾回收
JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别是:
执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
1-6this
this场景:
- 作为普通函数
- 使用call apply bind
- 作为对象方法被调用(this指向调用对象)
- 在class方法中调用(this指向调用对象)
- 箭头函数(this指向上一作用域)
//1.作为普通函数调用
function fn1 () {
console.log(this)
}
fn1() //window
//2.使用call apply bind
fn1.call({x: 100})
// {x: 100}
fn1.bind({x: 200})
// {x: 200}
2.理解闭包
闭包:一个绑定了执行环境的函数。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
两种表现形式:
- 函数作为参数被传递
- 函数作为返回值被返回
// 函数作为参数被传递
function print(fn) {
const a = 200
fn() //print函数执行时fn执行,所以是fn执行的地方
}
const a = 100
function fn() {
console.log(a) //函数定义的地方
}
print(fn) //100
function create() {
const a = 100
return function() { //函数定义的地方
console.log(a)
}
}
const fn = create()
const a = 200
fn() //100
闭包总结:
所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方。