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

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-------------------------------------------

  • 有错误的非常感谢能够慷慨指出
  • 写博客挺累人,但对于自己梳理知识点确非常有用
  • 在写的过程中也找到了平常没留意的边边角角
posted @ 2020-10-10 04:24  bigname22  阅读(140)  评论(0编辑  收藏  举报