你是个富二代吗?——JavaScript作用域链
理解词法作用域和动态作用域
作用域
作用域是指程序源代码中定义变量的区域。 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。 JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
静态作用域与动态作用域
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了,而词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
理解 JavaScript 的作用域和作用域链
什么是作用域?
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用于决定了代码区块中变量和其他资源的可见性。 作用域就是一个独立的地盘,让变量不会外泄,暴露出去。 作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。 ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了’块级作用域’,可通过新增命令 let 和 const 来体现。
全局作用域和函数作用域
最外层函数和最外层函数外面定义的变量拥有全局作用域
所有未定义直接赋值的变量自动声明为拥有全局作用域
所有 window 对象的属性拥有全局作用域
全局作用域有个弊端:如果我们写了很多 JS 代码,变量定义都没有用函数包括,那么他们就全部都在全局作用域中。这样就会污染全局命名空间,容易引起命名冲突。
作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。
值得注意的是:块语句(大括号”{}”中间的语句), 如 if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域。
块级作用域
在一个函数内部创建
在一个代码块(由一对花括号包裹)内部
块级作用域有以下几个特点
声明变量不会提升到代码块顶部
禁止重复声明
循环中的绑定块作用域
作用域链
作用域中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
理解 JavaScript 的执行上下文栈,可以应用堆栈信息快速定位问题
什么是执行上下文?
简而言之,执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。 每当 JavaScript 代码运行的时候,它都是在执行上下文中运行。
执行上下文类型
JavaScript 中有三种执行上下文类型
全局执行上下文
这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置this的值等于这个全局对象。一个程序中会有一个全局执行上下文。
函数执行上下文
每当一个函数被调用时,都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序执行一系列步骤。
Eval 函数执行上下文
执行在 eval 函数内部的代码也会有它属于自己的执行上下文。
运行期上下文
当函数执行时,会创建一个称为执行器上下文的内部对象,一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
this 的原理以及几种不同使用场景的取值
JavaScript 中 this 含义完全取决于函数调用方式
JavaScript 中函数的调用有以下几种方式
作为对象方法调用
作为函数调用
作为构造函数调用
使用 apply 或 call 调用
作为对象调用
在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,this 被自然绑定到该对象。
作为函数调用
函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。
作为构造函数调用
JavaScript 支持面向对象式编程,不过 JavaScript 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式。【构造函数以大写字母开头】。 在内部函数中,即声明在另外一个函数体内的函数,这种绑定到全局对象的的方式会产生另外一个问题。我们需要在内部函数中创建变量存下外部函数的 this,该变量一般命名为 that。
在 call 或者 apply 中调用
他们允许切换函数执行上下文环境(context),即 this 绑定的对象。
最后更新于