Kwin Huang

不会搞艺术的程序员不是好设计师.

this

2018-11-28

一. 全局执行

console.log(this); // Window

二. 函数中执行

  1. 非严格模式中
function func () {
  console.log(this);
}
func();
// Window
  1. 严格模式
"use strict";
function func() {
  console.log(this);
}
func();
// undefined

三. 作为对象的方法调用

当一个函数被当作一个对象的方法调用的时候,this 指向当前的对象 obj

var obj = {
  name: 'kk',
  func: function() {
      console.log(this.name);
  }
}
obj.func();
// kk

如果把对象的方法赋值给一个变量,调用该方法时,this 指向 Window:

var obj = {
  name: 'kk',
  func: function() {
    console.log(this);
  }
}
var test = obj.func;
test();
// Window

四. 作为构造函数使用

在 JS 中,为了实现类,我们需要定义一些构造函数,在调用一个构造函数的时候加上 new 这个关键字:

function Person(name) {
  this.name = name;
  console.log(this);
}
var p1 = new Person('hk');
// Person
// this 指向这个构造韩式调用的时候实例化出来的对象

如果构造函数当做普通函数来调用, this则指向Window

function Person(name) {
  this.name = name;
  console.log(this);
}
var p1 = Person('hk');
// Window

五. 在定时器中使用

setInterval(function() {
  console.log(this);
}, 1000)
// Window

如果没有特殊指向,setInterval 和setTimeout 的回调函数中 this 的指向都是 Window 。这是因为 JS 的定时器方法是定义在 Window 下的。

六. 箭头函数

全局环境下调用

let func = () => {
  console.log(this);
}
func();
// Window

作为对象的一个函数调用

var obj = {
  name: 'hh',
  func:  function () {
    console.log(this);
  }
}
obj.func();
// obj

var obj = {
    name: 'hh',
    func: () => {
       console.log(this);
    }
}
obj.func();
// Window

特殊情况:结合定时器调用

var obj = {
  name: 'hh',
  func: function() {
    setTimeout(function() {
      console.log(this);
    }, 0)
  }
}
obj.func();
// Window

var obj = {
    name: 'hh',
    func: function() {
        setTimeout(() => {
            console.log(this);
        }, 0)
    }
}
obj.func();
// obj

若在对象的函数中,普通函数作为定时器延时执行的函数调用,this 指向 Window;箭头函数作为定时器延时执行的函数调用, this 指向定义时所在的对象,也就是 func 中的 this,即 obj。

箭头函数中 this 的值取决于该函数外部非箭头函数的 this 的值,且不能通过 call() 、 apply() 和 bind() 方法来改变 this 的值。

七. call, apply, bind

call

fun.call(thisArg[, arg[, arg2[, ...]]]);

它会立即执行函数, 第一个函数是指执行函数中的上下文, 后面的参数是执行函数需要传入的参数

apply

fun.apply(thisArg, [argsArray]);

它也会立即执行函数,第一个参数是指定执行函数中 this 的上下文,第二个参数是一个数组,是传给执行函数的参数(与 call 的区别)

bind

var foo = fun.bind(thisArg[, arg[, arg2[, ...]]]);

它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 this 的上下文,后面的参数是执行函数需要传入的参数

例子:

function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(this);
}
var obj = {
  name: 'hk',
  age:18
};
Person.call(obj, 'mm', 10); // obj, {name: 'mm', age: 10}

Person.apply(obj, ['mm', 10]); // obj, {name: 'mm', age: 10}

var p1 = Person.bind(obj, 'mm', 10);
var p2 = new p1(); // Person {name: 'mm', age: 10}

示例中,call、apply 和 bind 的 this 都指向了 obj,都能正常运行;call、apply 会立即执行函数,call 和 apply 的区别就在于传递的参数,call 接收多个参数列表,apply 接收一个包含多个参数的数组;bind 不是立即执行函数,它返回一个函数,需要执行 p2 才能返回结果,bind 接收多个参数列表。

应用: 改变this的指向

使用es6的箭头函数

var name = 'hk';
var obj = {
  name: 'hh',
  func1: function() {
    console.log(this.name);
  },
  func2: function() {
    setTimeout(function() {
      this.func1();
    }, 1000);
  }
};
obj.func2(); 
// Uncaught TypeError: this.func1 is not a function
// 这时会报错, 因为setTimeout里面函数的this指向Window, 而Window对象上没有func1这个函数
var name = 'hk';
var obj = {
  name: 'hh',
  func1: function() {
    console.log(this.name);
  },
  func2: function() {
    setTimeout(() => {
      this.func1();
    }, 1000);
  }
};
obj.func2();
// hh
// 此时没有报错, 因为箭头函数里的this的值取决于该函数外部非箭头函数的this的值, 也就是func2的this的值, 即obj, obj.name 为hh

在函数内部使用_this = this 略

使用call/apply/bind

var name = 'hk';
var obj = {
  name: 'hh',
  func1: function() {
    console.log(this.name);
  },
  func2: function() {
    setTimeout(function() {
      this.func1();
    }.call(obj), 1000);
  },
  func3: function() {
    setTimeout(function() {
      this.func1();
    }.apply(obj), 1000);
  },
  func4: function() {
    setTimeout(function() {
      this.func1();
    }.bind(obj)(), 1000);
  }
};
obj.func2(); // hh
obj.func3(); // hh
obj.func4(); // hh

new 实例化一个对象