javascript-七个栗子深入理解this指向

    科技2022-07-31  94

    7个栗子深入理解this指向

    在了解this之前有必要了解一下-函数的生命周期 在调用一个函数的时候是先经历函数的创建然后再到执行的过程。而this的指向是再创建阶段就已经完成了。关于创建及执行的阶段做的事情看下面图图: 我曾写过这样一篇知识文章-点击传送

    例如person.getName();当执行到这行代码时就会经历函数的创建执行。

    执行上下文生命周期描述创建上下文分别创建变量对象,确认作用域链,以及确定this指向执行创建阶段之后,则执行代码,这个时候会完成变量赋值,函数引用,及其他代码

    在创建函数的时候就确定this及作用域这些操作【对应上js是一门词法分析这个知识点是相互呼应的】,意思就是this在创建阶段就已经确认了指向,而不受函数内部代码所影响。

    但也不是说函数在定义阶段就能够确定的喔。准备的说一个函数是经过定义 - 调用后再经历创建-执行的阶段。

    ---------------------------------人工分割线-------------------------------------------

    5个例子完美解析this解析

    栗子一:

    function a() { console.log('1') } a();

    先说一个知识点:谁调用this指向谁。

    那么a()是谁调用勒? 其实在非严格模式下这种调用方式会默认的加上window.a();而在严格模式下这种调用方式会报错。那么你该明白a内部如果使用this,其实就是相当于指向window对象

    栗子二:

    var a = 1; function fn() { var a = 2; console.log(a); // 2 console.log(this.a); // 1 } fn(); // 非严格模式下相当于 window.fn();

    再强调一遍:谁调用this就指向谁

    栗子三:

    var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //undefined console.log(this); //window } } } var j = o.b.fn; j();

    因为j()实际上是window.j();所以j()函数再执行的时候this指向window。不要被表面所迷惑,记住金句:谁调用this就指向谁

    栗子四:new关键字下的this

    function Person(){ this.name = "tony"; } var tony = new Person(); console.log(tony.name); //tony

    为什么tony为什么会有个name属性? 明白this.name = 'tony’这句代码为什么作用到了tony对象身上?

    new关键字的特殊性,因为new关键字实际上做了以下的操作:

    先创建空的、新的实例对象(New(func)返回的对象)将实例对象的原型指向构造函数的原型将构造函数的this改为指向实例对象 此时这个实例对象就是tony,并且this改成指向tony了最后返回该实例对象(特殊情况:如果构造函数本身有返回,并且返回的是对象或者函数,则直接根据构造函数的返回)

    至于怎么实际上是怎么修改了this指向这个问题稍后再谈!

    栗子五:

    箭头函数对this指向的特殊性

    引用阮一峰老师在ES6的书籍中对箭头函数做出的解释:

    函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

    这句话老实说,看完之后总是似懂非懂的感觉。但能够确定的是原来的:函数由谁调用this就指向谁。这个在这里不适用。 并且需要弄清楚的一点:怎么找到《函数定义所在对象》?

    // ES6 const obj = { getArrow() { return () => { console.log(this); }; } }

    当执行时发生了什么?

    let arrowFn = obj.getArrow(); // 也就是arrowFn此时就会绑定obj对象。 let arrowFn2 = obj.getArrow.call(window); // arrowFn此时就会绑定window对象

    一开始我出现过错误的看法:

    const obj = { getArrow() { return () => { console.log(this); }; } }

    我以为仅从这段代码种便能确认this的执行,但实际结果很显然证明我是错误的。 结合到:== “ 函数getArrow在定义时所在的对象,而不是使用时所在的对象 ” ==

    这句话得问题在于:

    函数在何时发生定义?函数在发生定义时的上下文this是什么? // 此时的this是window console.log(this); // this => window const obj = { getArrow() { return () => { console.log(this); }; } }

    混淆点1:箭头函数的定义难道不是在this=window的下面进行的? 答案:不是,此时箭头函数实际上还未定义。定义实际上发生在getArrow()方法在被调用时才开始定义也就是执行代码obj.getArrow()时,当执行到return ()=>{console.log(this)}此时才会去寻找this并绑定。所以说如果在执行:return ()=>{console.log(this)}时的this是谁,箭头函数指向的this就是谁。

    混淆点2:箭头函数的定义为什么不一定是obj对象? 答案:混淆点1的答案就能足够解释。这个例子理解的关键是箭头函数:return ()=>{console.log(this)}在什么时候发生定义?答案就是执行到这一句代码的时候,而不是写在obj里面最后绑定的就一定是obj。网上有很多有可能看着类似这样不太正确的解释。

    混淆点3:函数定义一定在执行的时候就发生吗? 答案:这…下面再举例来解答这个问题。

    栗子六:

    let fnObj = { getArrow : () => { console.log(this) } } fnObj.getArrow(); // 打印: window

    那么,getArrow方法在什么时候发生定义? 在执行到:

    let fnObj = { getArrow : () => { console.log(this) } }

    主代码执行这句代码的时候getArrow发生定义。此时的this是window,那么getArrow箭头函数则绑定window。(箭头函数本身没有this,那么他在定义是会寻找他在定义时的this来作为自己的this)

    栗子七:

    var id = 'window'; let foo = { id : 'foo', arrow: function() { setTimeout(() => { console.log(' arrow id:', this.id); }, 100); }, fn: function() { setTimeout(function() { console.log(' fn id:', this.id); }, 100); }, } foo.arrow(); // 思考打印 ?? foo.fn(); // 思考打印 ??

    先说foo.fn() 答案: 打印window 原因:setTimeout函数是由window调用执行,所以打印window很好理解。

    先说foo.arrow() 答案: 打印foo 原因:理解这个例子的关键在于理解:setTimeout中的匿名函数参数在什么时候定义?我相信换个写法就能够理解这个问题。

    fn: function() { setTimeout(() => { console.log(' arrow id:', this.id); }, 100); }, // ---》等价于下面写法 fn: function() { // arrowFn箭头函数的指向不懂的再回去看案例4 let arrowFn = () => { console.log(' arrow id:', this.id); } setTimeout(arrowFn, 100); },

    不要被表面迷惑了。将问题转心在:箭头函数在发生定义时所在的上下文this。

    ---------------------------------Ending-------------------------------------------

    有错误的非常感谢能够慷慨指出写博客挺累人,但对于自己梳理知识点确非常有用在写的过程中也找到了平常没留意的边边角角
    Processed: 0.010, SQL: 8