执行函数时出错,未找到表(深入理解函数执行上下文this)
JavaScript 中的 this 是什么
关于 this ,我们得先从执行上下文说起 。我们知道:执行上下文中包含了变量环境 、词法环境 、外部环境 ,当然也包括 this ,具体你可以参考下图:
从图中可以看出 ,this 是和执行上下文绑定的 ,也就是说每个执行上下文中都有一个 this 。执行上下文主要分为三种——全局执行上下文、函数执行上下文和 eval 执行上下文 ,所以对应的 this 也只有这三种——全局执行上下文中的 this 、函数中的 this 和 eval 中的 this。
不过由于 eval 我们使用的不多 ,所以本文我们对此就不做介绍了 ,如果你感兴趣的话 ,可以自行搜索和学习相关知识 。
那么接下来我们就重点讲解下全局执行上下文中的 this和函数执行上下文中的 this 。
全局执行上下文中的 this
首先我们来看看全局执行上下文中的 this 是什么 。
你可以在控制台中输入console.log(this)来打印出来全局执行上下文中的 this,最终输出的是 window 对象 。所以你可以得出这样一个结论:全局执行上下文中的 this 是指向 window 对象的 。这也是 this 和作用域链的唯一交点 ,作用域链的最底端包含了 window 对象 ,全局执行上下文中的 this 也是指向 window 对象 。
函数执行上下文中的 this
现在你已经知道全局对象中的 this 是指向 window 对象了,那么接下来 ,我们就来重点分析函数执行上下文中的 this 。还是先看下面这段代码:
我们在 foo 函数内部打印出来 this 值 ,执行这段代码,打印出来的也是 window 对象 ,这说明在默认情况下调用一个函数 ,其执行上下文中的 this 也是指向 window 对象的 。估计你会好奇 ,那能不能设置执行上下文中的 this 来指向其他对象呢?答案是肯定的 。通常情况下 ,有下面三种方式来设置函数执行上下文中的 this 值。
1. 通过函数的 call 方法设置
你可以通过函数的call方法来设置函数执行上下文的 this 指向 ,比如下面这段代码 ,我们就并没有直接调用 foo 函数 ,而是调用了 foo 的 call 方法 ,并将 bar 对象作为 call 方法的参数 。
执行这段代码 ,然后观察输出结果,你就能发现 foo 函数内部的 this 已经指向了 bar 对象 ,因为通过打印 bar 对象 ,可以看出 bar 的 myName 属性已经由“name1 ”变为“name2 ”了,同时在全局执行上下文中打印 myName ,JavaScript 引擎提示该变量未定义 。
其实除了 call 方法 ,你还可以使用bind和apply方法来设置函数执行上下文中的 this,仅仅是语法稍有不同。
2. 通过对象调用方法设置
要改变函数执行上下文中的 this 指向 ,除了通过函数的 call 方法来实现外 ,还可以通过对象调用的方式 ,比如下面这段代码:
在这段代码中 ,我们定义了一个 myObj 对象 ,该对象是由一个 name 属性和一个 showThis 方法组成的 ,然后再通过 myObj 对象来调用 showThis 方法 。执行这段代码 ,你可以看到 ,最终输出的 this 值是指向 myObj 的 。
所以 ,你可以得出这样的结论:使用对象来调用其内部的一个方法,该方法的 this 是指向对象本身的。
其实 ,你也可以认为 JavaScript 引擎在执行myObject.showThis()时 ,将其转化为了:
myObj.showThis.call(myObj)
接下来我们稍微改变下调用方式,把 showThis 赋给一个全局对象 ,然后再调用该对象 ,代码如下所示:
执行这段代码,你会发现 this 又指向了全局 window 对象 。
所以通过以上两个例子的对比 ,你可以得出下面这样两个结论:
在全局环境中调用一个函数 ,函数内部的 this 指向的是全局变量 window 。 通过一个对象来调用其内部的一个方法 ,该方法的执行上下文中的 this 指向对象本身 。3. 通过构造函数中设置
你可以像这样设置构造函数中的 this ,如下面的示例代码:
在这段代码中 ,我们使用 new 创建了对象 myObj ,那你知道此时的构造函数 CreateObj 中的 this 到底指向了谁吗?
其实 ,当执行 new CreateObj() 的时候 ,JavaScript 引擎做了如下四件事:
首先创建了一个空对象 tempObj; 接着调用 CreateObj.call 方法 ,并将 tempObj 作为 call 方法的参数,这样当 CreateObj 的执行上下文创建时 ,它的 this 就指向了 tempObj 对象; 然后执行 CreateObj 函数 ,此时的 CreateObj 函数执行上下文中的 this 指向了 tempObj 对象; 最后返回 tempObj 对象 。这样,我们就通过 new 关键字构建好了一个新对象 ,并且构造函数中的 this 其实就是新对象本身 。
this 的设计缺陷以及应对方案
就我个人而言 ,this 并不是一个很好的设计,因为它的很多使用方法都冲击人的直觉 ,在使用过程中存在着非常多的坑 。下面咱们就来一起看看那些 this 设计缺陷 。
1. 嵌套函数中的 this 不会从外层函数中继承
我认为这是一个严重的设计错误 ,并影响了很多开发者 。
至于如何解决?你可以在函数中声明一个变量 self 用来保存 this 。当然也可以使用 ES6 中的箭头函数来解决这个问题。
2. 普通函数中的 this 默认指向全局对象 window
上面我们已经介绍过了 ,在默认情况下调用一个函数 ,其执行上下文中的 this 是默认指向全局对象 window 的 。
不过这个设计也是一种缺陷 ,因为在实际工作中 ,我们并不希望函数执行上下文中的 this 默认指向全局对象 ,因为这样会打破数据的边界 ,造成一些误操作 。如果要让函数执行上下文中的 this 指向某个对象 ,最好的方式是通过 call 方法来显示调用。
这个问题可以通过设置 JavaScript 的“严格模式 ”来解决 。在严格模式下,默认执行一个函数 ,其函数的执行上下文中的 this 值是 undefined ,这就解决上面的问题了 。
总结
回顾下内容:
首先,在使用 this 时 ,为了避坑 ,你要谨记以下三点:
当函数作为对象的方法调用时,函数中的 this 就是该对象; 当函数被正常调用时 ,在严格模式下 ,this 值是 undefined ,非严格模式下 this 指向的是全局对象 window; 嵌套函数中的 this 不会继承外层函数的 this 值。最后 ,我们还提了一下箭头函数 ,因为箭头函数没有自己的执行上下文 ,所以箭头函数的 this 就是它外层函数的 this 。
以上就是深入理解函数执行上下文 this的详细内容 ,更多关于函数执行上下文 this的资料请关注本站其它相关文章!
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!