如何理解js中this指向问题

398 阅读5分钟

this指向可以理解为把this跟他要指向的东西绑定起来,也就是说 this有四种绑定方式默认绑定,隐式绑定,显式绑定,new绑定.

默认绑定

其实就是函数调用,绑定全局对象window,如果是严格模式,这个时候绑定的为undefined.(打脸第一个不详细的)

new绑定

通过new调用的函数,那么this绑定的是new出来的新对象,前提是构造函数没有返回一个对象或者function,否则this绑定的是返回的对象或者函数

隐式绑定

函数被对象通过点语法调用的,一般是obj.fn(),这时候this指向的是这个对象

显式绑定

也可以叫做硬绑定,还记得,call,apply,bind; 硬绑定,就是硬生生的改过来!!!

补充一下: 这三个方法,又叫方法借用模式,简单讲,就是某个对象想用一个方法,但是自己没有,于是就找个人借一下. 举个例子

   var wsz = {
        0: "xh",
        1: "xb",
        2: "xl",
        length: 3
      };

//有上面这个对象,现在的需求是将lw这个元素添加到上面这个对象里,并且同时改变length的值
首先,这是一个伪数组,需求可以知道是让我们把"3:lw"加进去,传统的方法?

     wsz[3] = "lw";
     wsz.length = 4;
     console.log(wsz)

打印结果:

显然可以实现需求,那么如果需求是让我们加上七八十个呢?显然这样做就会累死得咯,想下班儿?加班儿还不是自己作出来的 思考:既然是一个伪数组,那么,伪数组和数组的区别?长的差不多,但是不能用数组的方法,可是好像数组有个push方法很合适这个需求,能否借来用用?

那么,我们可以尝试一下

    Array.prototype.push.call(wsz,"lw");     //push方法在哪儿就去哪儿借呗

运行结果

如果要依次加很多,就用apply方法,第二个参数传一个数组或者为数组就好了

    Array.prototype.push.apply(wsz,wsz);     //push方法在哪儿就去哪儿借呗

方法借用的思路: 1.想清楚借啥方法 2.想清楚找谁借 3.请call或者apply去帮你借


注意的是,我们用硬绑定的时候,简单说的是想给谁绑定就写谁,但是,如果传入的是null或者undefined,这些值在绑定的时候会被忽略,结果依然是默认绑定的,返回的是window或者undefined(严格模式下)

检验一下掌握结果吧:

var number = 5;
var obj = {
    number: 3,
    fn1: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1()
console.log(window.number);


//分析一下
//自执行函数在书写完成时就已经执行了  做的事情是:
//            1.给自己声明一个number对象
//            2.由于是自执行函数,this指向全局window,因此第二句代码将全局number变为10
//            3.给自己声明的number赋值为3
//            4.把那个函数返回出来
//        之后所有的调用全都是调用的返回值函数

//每一句的作用:
//第一次调用:
var fn1=obj.fn1;      //将返回值函数赋给fn1
fn1.call(null);       // call方法调用,看似是硬绑定,由于传参为null,因此相当于默认绑定,this指向window相当于window调用那个返回值函数 然后看函数内部代码
var num = this.number;      //声明一个num变量,将this(全局)的number赋给num,此时 num=10
this.number*=2        //将全局的number变为20
console.log(num)     //第一次打印为  10
number*=3            //按照变量搜索原则,找到自执行函数里的number并且变为了9
console.log(number)  //第二次打印自执行函数里的number   9

//第二次调用:
obj.fn1()            //对象调用,隐式绑定,返回值函数里的this指向obj,相当于obj再次调用自执行函数返回的那个函数 然后看函数内部
var num = this.number;      //重新声明一个num变量,将this(obj)的number赋给num,此时 num=3
this.number*=2         //注意this指向的是obj   因此将obj的number变成6
console.log(num)     //第三次打印   3
number*=3       //按照变量搜索原则,找到自执行函数里的number 此时经过第一次调用 number已经为9 因此将它变为27
console.log(number)  //第四次打印自执行函数的number   27

console.log(window.number);   //最后一次打印的是全局的number  20


所以最后结果为

需要注意的是

箭头函数的this指向的是外部的this,所以在使用定时器的时候,参数function常用箭头函数处理this指向问题.
参考 刘小夕 【面试篇】寒冬求职季之你必须要懂的原生JS(上) juejin.cn/post/684490…

更新一波面试题

分享刚遇到的一个面试题

var obj = {
  foo: 'bar',
  func: function () {
    var self = this
    console.log('outer func: self.foo = ' + self.foo)
    console.log('outer func: this.foo = ' + this.foo)
      (function () {
        console.log('inner func : self.foo = ' + self.foo)
        console.log('inner func : this.foo = ' + this.foo)
      }())
  }
}
obj.func()

单看调用方式,obj.func() ,显然这叫隐式绑定,此时函数内部的this指向obj,不管换不换名字,所以,self和this都指向obj,因此,前两个输出都是bar
之后为一个自调用函数,他的this指向的是window,但是self还是外部被对象调用的函数的self(this),因此第三个输出还是bar
第四个输出因为this已经指向全局window,因此打印的是undefined 可恶,居然紧张的这种题都能错,完辽.