浏览器工作原理与实践
李兵
前盛大创新院高级研究员
56402 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 46 讲
浏览器工作原理与实践
15
15
1.0x
00:00/00:00
登录|注册

11 | this:从JavaScript执行上下文的视角讲清楚this

修复存在问题的代码
箭头函数的this
使用this时的注意事项
普通函数中的this默认指向全局对象window
嵌套函数中的this不会从外层函数中继承
通过构造函数中设置
通过对象调用方法设置
通过函数的call方法设置
函数执行上下文中的this
全局执行上下文中的this
闭包
作用域链
词法作用域
思考时间
总结
this的设计缺陷以及应对方案
设置函数执行上下文中的this值
JavaScript中的this
作用域链和闭包
JavaScript执行上下文和this机制

该思维导图由 AI 生成,仅供参考

上篇文章中,我们讲了词法作用域、作用域链以及闭包,并在最后思考题中留了下面这样一段代码:
var bar = {
myName:"time.geekbang.com",
printName: function () {
console.log(myName)
}
}
function foo() {
let myName = "极客时间"
return bar.printName
}
let myName = "极客邦"
let _printName = foo()
_printName()
bar.printName()
相信你已经知道了,在 printName 函数里面使用的变量 myName 是属于全局作用域下面的,所以最终打印出来的值都是“极客邦”。这是因为 JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。
不过按照常理来说,调用bar.printName方法时,该方法内部的变量 myName 应该使用 bar 对象中的,因为它们是一个整体,大多数面向对象语言都是这样设计的,比如我用 C++ 改写了上面那段代码,如下所示:
#include <iostream>
using namespace std;
class Bar{
public:
char* myName;
Bar(){
myName = "time.geekbang.com";
}
void printName(){
cout<< myName <<endl;
}
} bar;
char* myName = "极客邦";
int main() {
bar.printName();
return 0;
}
在这段 C++ 代码中,我同样调用了 bar 对象中的 printName 方法,最后打印出来的值就是 bar 对象的内部变量 myName 值——“time.geekbang.com”,而并不是最外面定义变量 myName 的值——“极客邦”,所以在对象内部的方法中使用对象内部的属性是一个非常普遍的需求。但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 又搞出来另外一套 this 机制
所以,在 JavaScript 中可以使用 this 实现在 printName 函数中访问到 bar 对象的 myName 属性了。具体该怎么操作呢?你可以调整 printName 的代码,如下所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

JavaScript中的this机制是一个常见的难点,本文通过深入浅出的方式介绍了JavaScript中this的机制及其应用。文章首先从作用域链和闭包的概念入手,通过代码示例展示了JavaScript中的作用域链和C++中的区别。然后详细介绍了全局执行上下文中的this和函数执行上下文中的this,并指出了this的设计缺陷,并提出了相应的应对方案。文章还提到了箭头函数的特性,以及如何解决嵌套函数中this指向的问题。总结时强调了在使用this时需要注意的三点,并提出了对箭头函数的应用。整体而言,本文通过具体的代码示例和对比,帮助读者快速了解JavaScript中this的机制及其应用。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《浏览器工作原理与实践》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(96)

  • 最新
  • 精选
  • ytd
    // 修改方法一:箭头函数最方便 let userInfo = { name:"jack.ma", age:13, sex:'male', updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(() => { this.name = "pony.ma" this.age = 39 this.sex = 'female' },100) } } userInfo.updateInfo() setTimeout(() => { console.log(userInfo) },200) // 修改方法二:缓存外部的this let userInfo = { name:"jack.ma", age:13, sex:'male', updateInfo:function(){ let me = this; // 模拟 xmlhttprequest 请求延时 setTimeout(function() { me.name = "pony.ma" me.age = 39 me.sex = 'female' },100) } } userInfo.updateInfo() setTimeout(() => { console.log(userInfo); },200) // 修改方法三,其实和方法二的思路是相同的 let userInfo = { name:"jack.ma", age:13, sex:'male', updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 void function(me) { setTimeout(function() { me.name = "pony.ma" me.age = 39 me.sex = 'female' },100) }(this); } } userInfo.updateInfo() setTimeout(() => { console.log(userInfo) },200) let update = function() { this.name = "pony.ma" this.age = 39 this.sex = 'female' } 方法四: 利用call或apply修改函数被调用时的this值(不知掉这么描述正不正确) let userInfo = { name:"jack.ma", age:13, sex:'male', updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(function() { update.call(userInfo); // update.apply(userInfo) }, 100) } } userInfo.updateInfo() setTimeout(() => { console.log(userInfo) },200) // 方法五: 利用bind返回一个新函数,新函数被调用时的this指定为userInfo let userInfo = { name:"jack.ma", age:13, sex:'male', update: function() { this.name = "pony.ma" this.age = 39 this.sex = 'female' }, updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(this.update.bind(this), 100) } }

    作者回复: 很赞,总结的很全,这个可以做参考答案

    2019-08-29
    9
    201
  • William
    setTimeOut() 函数内部的回调函数,this指向全局函数。修复:在外部绑this或者使用箭头函数。 ``` let userInfo = { name:"jack.ma", age:13, sex: "male", updateInfo:function(){ let that = this; // 模拟 xmlhttprequest 请求延时 setTimeout(()=>{ that.name = "pony.ma" that.age = 39 that.sex = "female" },100) } } userInfo.updateInfo() ``` ``` let userInfo = { name:"jack.ma", age:13, sex: "male", updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(()=>{ this.name = "pony.ma" this.age = 39 this.sex = "female" },100) } } userInfo.updateInfo() ```

    作者回复: 非常好! 补充下解释: 如果被setTimeout推迟执行的回调函数是某个对象的方法,那么该方法中的this关键字将指向全局环境,而不是定义时所在的那个对象。 如果是严格模式,那么this会被设置为undefined。 这一点很容易让人混淆!!!

    2019-08-29
    7
    26
  • 悬炫
    关于箭头函数,文章中说其没有自己的执行上下文,难道箭头函数就像let定义的变量一样是哥块级作用域吗?其内部定义的变量都是存储在词法环境中是吗?

    作者回复: 箭头函数在执行时比块级作用域的内容多,比函数执行上下文的内容少,砍掉了很多函数执行上下文中的组件。 不过在箭头函数在执行时也是有变量环境的,因为还要支持变量提升!所以变量环境的模块还是砍不掉的

    2019-08-29
    8
    24
  • 风一样的浪漫
    老师请问下outer的位置是在变量对象内还是外,第10节描述是在内部的,可是11节的图outer放在变量对象外面了

    作者回复: 是在里面的,11为了图简单点,调整到外面了

    2019-09-05
    2
    14
  • 李懂
    文章只是简单讲了下this的几种场景,不像前面变量申明,可以很清晰的知道在执行上下文的位置,也没有画图,看完还是不知道不能深入理解,更多的是一种记忆,这种是指向window,那种是指向对象。能不能深入到是如何实现this,才能知道缺陷的原因,这里一直是没理解的难点!

    作者回复: this的缺陷并不是浏览器实现机制导致的,而是浏览器按照标准来实现的。 其实浏览器说我可以实现得更好,但是标准摆在这儿,大家都只认标准!

    2019-08-29
    5
    6
  • pyhhou
    思考题,有两种方法 1. 将 setTimeout 里面的函数变成箭头函数 2. 在 setTimeout 外将 this 赋值给其他的变量,setTimeout 里面的函数通过作用域链去改变 userInfo 的属性 很不错的文章,受益匪浅,感谢老师。这里有一个疑问就是,关于箭头函数,文章中说其没有自己的执行上下文,这里指的是箭头函数并不会创建自己的执行上下文变量并压栈,其只是被看作是一个块区域吗?那么在实际的开发中如何在普通函数和箭头函数之间做选择?关于这一点,老师有没有相关推荐的文章呢?谢谢老师

    作者回复: 箭头要展开了得话一节来讲,关于箭头函数的最佳实践网上应该有不少资料,可以查查!

    2019-08-29
    6
  • 朱维娜🍍
    之前看到一种说法:this指向的永远是调用它的对象。按照这种说法,嵌套函数的调用者是window,与文中所述的“showThis调用内部函数不能继承this”有所出入,想请老师解答一下这种说法是否正确?

    作者回复: 调用者是对象,函数内部是调用的地方,不能说是调用者。 obj.showThis() 这里的obj是调用者,通过点操作符来实现的

    2019-08-31
    3
    4
  • 潘启宝
    let userInfo = { name:"jack.ma", age:13, sex:'male', updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(function(){ this.name = "pony.ma" this.age = 39 this.sex = 'female' }.bind(this),100) } } userInfo.updateInfo()

    作者回复: 使用bind没有问题

    2019-08-29
    6
    2
  • 子曰
    let userInfo = { name:"jack.ma", age:13, sex:"male", updateInfo:function(){ // 模拟 xmlhttprequest 请求延时 setTimeout(()=>{ this.name = "pony.ma" this.age = 39 this.sex = "female" },100) } } userInfo.updateInfo()

    作者回复: 箭头函数没问题

    2019-08-29
    1
  • mfist
    延时函数更新此时的this对象指向了window全局对象。 解决方法就是文章老师提到的两种方法。 1 this保存给self变量,通过变量作用域机制传递给嵌套函数。 2箭头函数去锁定函数定义时候的this对象,箭头函数没有上下文,它会继承函数初始化对应上下文。 思考: 1 能否通过bind和apply改变箭头函数this指向? 回头试一下,然后好好理理这几节内容

    作者回复: 建议看看其他老铁的留言

    2019-08-29
    1
收起评论
显示
设置
留言
96
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部