作用域
- 作用域 — 每一个函数就会有一个独立的scope,这样函数内的变量,内部函数,参数都可以在这个作用域中找到。
执行上下文(exec context)与作用域(scope)是对等的概念。在函数执行的时候,浏览器会生成一个exec context。JS 是单线程的,意味着同时只能有一个线程运行。当 JS解释器开始执行代码时,默认先进入到 global context。这时起每次函数调用都会创建一个新的 exec context。新创建的 exec context 会被放到execution stack 上。浏览器总是执行栈顶的当前exec context,一旦执行完成,它就出栈,控制权转移到下面的 exec context 上。
exec context 分创建和执行两个阶段。在创建阶段,解释器会先生成一个variable object(在函数中是 activation object),这个函数上有所有的变量,函数声明,以及 arguments->在这里 scope chain接下来会被初始化->最后 this 会被确定。最后在执行阶段,代码被解释和执行。
每一个exec context 都有一个 scope chain 与之共存。Scope chain包含了execution stack 上的每个 exec context 的 variable object。它的主要用途是决定变量的可访问性和标识符解析。每次你试图在一个函数的 exec context 中访问一个变量时,它会先从当前的 variable object 中去查找,如果找不到就会沿着 scope chain往上爬去外层的 exec context 的 variable object上查找,直到最后的 global context。
block scope
JS只有functional scope,没有block scope,2个特例:
with statement( strict mode下禁用)
try/catch catch是block scope
ES6 let 作用域
使用let声明变量,将变量附加在其所在的block({})上
通常显式的创建block,让其范围更明确,方便代码重构
let 让变量为block scope,将变量重新绑定到每次循环,用上次循环的结束时的值
- ES6 const
用const声明的变量,也是block scope
一次性赋值后不能再改变
声明提前(hoisting)
- 变量、函数声明会被提前
- 声明和赋值实际上是两步
上面第一张图片的代码输出结果是undefined,因为声明被提前了,但是复制却没有。执行顺序如第二段代码所示。 - 变量重名时,函数提前优先于变量提前;同名函数后者覆盖前者
this关键字
- this的值是一个对象
- 由函数是怎样被调用的决定:
一个对象的方法被调用时,this 指向这个对象
用 new 调用函数创建新实例,this 指向新创建的对象
当调用一个未绑定函数,this 默认指向 global context, strict mode下为undefined