前言

本文将从如下几点展开:

this在不同调用方式下的指向?

改变函数this的指向的apply、call如何使用?

new关键字?

箭头函数this指向?

如何确定this的指向

this的指向是在运行时确定的,js中没有动态作用域,因为this的存在,让js有了动态作用域的表现。
  • 根据函数的调用对象判断

    this指向最后调用的那个对象,this和在定义的位置没有关系,只 和调用的对象有关。如果没有调用者默认this指向window。

  • this指向new出来的对象

    为什么this指向new出来的对象呢?new的原理是什么呢?

  • this指向apply、call的第一参数
案例练习:

1、this指向函数的最后的调用者

例子1:

var innerHtml = 'windowstring'
function fn() {
    console.log(this.innerHtml)
}

var obj = {
    innerHtml: 'objstring',
    fn: fn
}
obj.fn = fn
obj.fn() //'objstring'
fn()//'windowstring'

例子2:

var x = 10; 
var obj = { 
x: 20,
f: function(){ 
console.log(this.x); 
} }; 
obj.f(); // obj.f.call(obj) // ==> 20 
var fOut = obj.f;
 fOut(); // fOut.call(window) //==> 10 
var obj2 = { x: 30, f: obj.f } obj2.f(); // obj2.f.call(obj2) //==> 30 

例子3:
this是在运行时确定的。
以下是访问了this对象中不存在的变量和方法的2个例子:

var name = "windowsName";
 var a = {
  // name: "Cherry",
  fn : function () {
   console.log(this.name);  // undefined
  }
 }
 window.a.fn();

函数执行没有明确的调用者时,函数this都指向window:

var name = "windowsName";
 var a = {
  name : "Cherry",
  func1: function () {
   console.log(this.name)  
  },
  func2: function () {
   setTimeout( function () {
    this.func1()
   },100);
  }
 };
 a.func2()  // this.func1 is not a function

2、this指向new出来的对象

3、this指向call、apply的第一个参数

设置this指向特定对象的方法

1.箭头函数

2.apply、call、bind

3.函数内部保存this

使用箭头函数:

箭头函数,不会创建自己的this,它从其作用域链的上一层继承。

function Person(){
  this.age = 0;
   (() => {
   this.age = 30;
  setInterval(() => {
    this.age++; // |this| 正确地指向 p 实例
    console.log(this.age)
  }, 1000);
  })()
}

var p = new Person();

箭头函数与Es5函数的不同:

1、严格模式下,箭头函数this指向全局对象,普通函数this指向undefined。

2、箭头函数没有arguments。

3、箭头函数没有prototype属性。

4、箭头函数的this没有普通函数this的规则:

  • 不能使用new关键词,不能作为构造函数。
  • this不会指向函数的调用者。
  • apply/call的第一个参数会被忽略。

这也是为什么vue中methods中函数不能用箭头函数的原因,箭头函数中会拿不到vue的实例,vue中函数绑定vue实例是通过函数的apply方法。

(注意:箭头函数也可以创建闭包。)

案例实践:

例子1:

var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2

例子2:

var name = 'bob'

var obj = {
    name: 'alice',
    showName: () => {
        console.log(this.name)
    }
}

obj.showName()

例子3:

function getValue(value) {
  this.name = '4444'
    var b = bb=> {
      var f = v => this.name + v;
      var obj = {
          name: 'youyi',
       };
      return f.call(obj, value);
      }
  return  b()
}
console.log(getValue( 'is fine')) //4444is fine

例子4:

var adder = {
  base : 1,
    
  add : function(a) {
     var b = vv => {
        var f = v => v + this.base;
        return f(a);
    }
    return b()
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3)

例子5:

var adder = {
  base : 1,
    
  add : function(a) {
     function b() {
        var f = v => v + this.base;
        return f(a);
    }
    return b()
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 NaN
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3)

this经典题目

例子1:

var a=10;
var foo={
  a:20,
  bar:function(){
      var a=30;
      console.log(this)
      return this.a;
    }
};
foo.bar()
(foo.bar)()
(foo.bar=foo.bar)()
(foo.bar,foo.bar)()

例子2:

function t(){
this.x=2;
}
t();
console.log(window.x);

例子3:

var obj = {
x: 1,
y: 2,
t: function() {
console.log(this.x)
}
}
obj.t();

var dog={x:11};
dog.t=obj.t;
dog.t();


show=function(){
console.log('show'+this.x);

}

dog.t=show;
dog.t();

例子4:

name = 'this is window';
var obj1 = {
name: 'php',
t: function() {
console.log(this.name)
}
};
var dog1 = {
name: 'huzi'
};

obj1.t();

dog1.t = obj1.t;

var tmp = dog1.t;
tmp(); //this本来指向window

(dog1.t = obj1.t)(); //'huzi' 对比1题中的(foo.bar=foo.bar)()
dog1.t.call(obj1);

例子5:

var number=2;
var obj={
number:4,
/*匿名函数自调*/
fn1:(function(){
var number;
this.number*=2;//4 8

number=number*2;//NaN
number=3;
return function(){
var num=this.number;
this.number*=2;// 8 8
console.log('0000000...',num); //4 4
number*=3;//9
console.log('222222...',number);//9 27
}
})(),

db2:function(){
this.number*=2;
}
}

var fn1=obj.fn1;

console.log('11111111:',number);//4

fn1();

obj.fn1();

console.log('window:',window.number);//8

console.log('obj:',obj.number);//8 易错点

参考资料:

 

this经典面试题

this和闭包

学会JS的this这一篇就够了,根本不用记

详解JS中的this、apply、call、bind(经典面试题)


贝er
58 声望6 粉丝

不仅仅是程序员