以前对函数作用域以及this指向都没有一个系统的认识,偶然一次代码编写运行结果与自己预想结果不一样,这让我决定对Js的作用域和this深层次学习。下面是我当初的一个小错误:
|
|
这里输出的undefined,因为这里的this指向window。只需要修改this指向就可以,方法很多。
|
|
首先需要了解这些重要概念:
- 基础数据类型与引用数据类型
- 内存空间
- 垃圾回收机制
- 执行上下文
- 变量对象与活动对象
作用域
JavaScript中只有全局作用域与函数作用域(因为eval我们平时开发中几乎不会用到它,这里不讨论)。
作用域与执行上下文是完全不同的两个概念。我知道很多人会混淆他们,但是一定要仔细区分
JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。下面请允许我盗一张图:来自波同学。

- 我理解作用域是:函数名以及变量或者anguments所能访问或查找的规则,或者叫所能访问到内存区块。优先级是anguments->function表达式->var。变量在声明它们的函数体内(或嵌套函数内)任意地方都是有定义的。下面看一份代码:
|
|
第一句输出的是: “undefined”,而不是 “global” 第二讲输出的是:”local” 你可能会认为第一句会输出:”global”,因为代码还没执行var scope=”local”,所以肯定会输出“global”。 上面已经讲过,变量在函数体内任意地方都是有定义的。在函数作用域创建阶段,首先会先生成变量对象VO:VO包括anguments、function、var变量的创建。到了函数执行阶段包括变量开始赋值、执行上下文this确定、代码执行。在第一句console.log打印时,函数作用域内只有var scope,scope并没有赋值。可以理解为下面代码:
|
|
在此看来,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。
作用域链
这里来看一下执行上下文的生命周期:从创建–>执行代码–>执行完毕,出栈等待被回收。

这里可以将作用域和作用域链结合起来理解:作用域是一块能够查询到的内存区域或者查询规则;作用域链,是对查询规则的实现或者内存寻址的过程,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。
|
|
因为变量对象在执行上下文进入执行阶段时,就变成了活动对象,这一点在上一篇文章中已经讲过,因此图中使用了AO来表示。Active Object
顺便理解一下闭包,简单来说,假设函数A在函数B的内部进行定义了,并且当函数A在执行时,访问了函数B内部的变量对象,那么B就是一个闭包。概述为:当函数可以记住并访问所在的作用域(全局作用域除外)时,就产生了闭包,即使函数是在当前作用域之外执行。有一下特点:
- 闭包与作用域链息息相关。
- 闭包是在函数执行过程中被确认。
- 通过闭包,我们可以在其他的执行上下文中,访问到其函数的内部变量
闭包案例:利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5
|
|
简单修改为:(相信你可以更多其他方法)
|
|
最后输出的7是setTimeout计时器的ID,计时器一般会生成一个唯一id,用于清除时用。
this的指向理解
this的指向,是在函数被调用的时候确定的
全局对象中的this
关于全局对象的this,我之前在总结变量对象的时候提到过,它是一个比较特殊的存在。全局环境中的this,指向它本身。因此,这也相对简单,没有那么多复杂的情况需要考虑。
|
|
函数中的this
|
|
在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。如果函数独立调用,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。
当obj在全局声明时,无论obj.c在什么地方调用,这里的this都指向全局对象,而当obj在函数环境中声明时,这个this指向undefined,在非严格模式下,会自动转向全局对象
使用call,apply显示指定this
call与applay后面的参数,都是向将要执行的函数传递参数。其中call以一个一个的形式传递,apply以数组的形式传递。这是他们唯一的不同
|
|
实现继承
|
|
在Student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将Person中的代码,在Sudent中复制了一份,其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确,因此就相当于实现了基层。
|
|
构造函数与原型方法上的this
这是面向对象编程的核心要点,直接上代码
|
|
通过new操作符调用构造函数,会经历以下4个阶段。
- 创建一个新的对象;
- 将构造函数的this指向这个新对象;
- 指向构造函数的代码,为这个对象添加属性,方法等;
- 返回新对象
基本上prototype对象指向new操作符生成的实例,这里指向p1。
欢迎转载,但转载请注明来自”勿忘初心”的博客,并保持转载后文章内容的完整性。
本文链接:http://lucylc.com/JavaScript/2017/03/02/Js函数作用域与this的指向问题.html