js中this的指向问题

在浏览器的全局环境中,严格和非严格模式的this都指向window;而在函数执行环境中,this的指向不是在函数定义的时候确定的,而是在函数执行的时候确定。

函数调用的几种方式:

  a、普通函数调用

  b、作为方法来调用

  c、作为构造函数来调用

  d、使用apply/call/bind方法来调用

  e、匿名函数调用

  f、es6中的箭头函数调用

总结一句话:在函数执行的时候,谁调用这个函数或者方法,那么this就指向谁。下面就这6种在浏览器环境下逐一分析,同时考虑严格模式和非严格模式的情况。

1、普通函数调用

  // "use strict";
  function say() {
    console.log(this);
  }
  say(); // 输出: Window 严格模式下:undefined  

这里的say()在非严格模式下等于:window.say();而在严格模式下,你可以想象成:undefined.say(),所以this指向undefined

2、作为方法来调用

// "use strict";
  let username = 'liutt';
  let person = {
    username: 'liuhw',
    say: function () {
      console.log(this.username);
    }
  };
  person.say(); // 严格和非严格模式都输出:liuhw
  let say = person.say;
  say(); // 输出:undefined,let属于块级作用域,所以window.username 未声明,严格模式下 Uncaught TypeError: Cannot read property 'username' of undefined

person作为一个对象实例,通过点语法调用say(),所以this指向person;因为做了变量赋值,所以最后一行say()就相当于作为普通函数调用,在严格模式下,因为this指向undefined,所以this.username就抛异常了

3、作为构造函数来调用

// "use strict";
  function Person(username) {
    this.username = username
  }
  let person = Person('liuhw');
  console.log(person.username); // 非严格和严格模式都抛异常
  console.log(window.username); // 非严格模式:liuhw,严格模式构造函数抛异常

非严格模式下Person('liuhw')等于window.Person('liuhw'),函数没有返回值,所以person为undefined,导致person.username抛异常

严格模式下Person('liuhw')等于undefined.Person('liuhw'),this指向undefined,导致构造函数中的this.username抛异常

// "use strict";
  function Person(username) {
    this.username = username
  }
  let person = new Person('liuhw');
  console.log(person.username); // 非严格和严格模式:liuhw
  console.log(window.username); // 非严格和严格模式:undefined

 4、使用apply/call/bind方法来调用

// "use strict";
  let username = 'liutt';
  let person = {
    username: 'liuhw',
    say: function () {
      console.log(this.username);
    }
  };
  person.say.apply(); // 输出:undefined 严格模式:抛异常
  person.say.call(); // 输出:undefined 严格模式:抛异常
  person.say.bind()(); // 输出:undefined 严格模式:抛异常
  person.say.apply(person); // 非严格和严格模式都输出:liuhw
  person.say.call(person); // 非严格和严格模式都输出:liuhw
  person.say.bind(person)(); // 非严格和严格模式都输出:liuhw

call、apply和bind可以改变this的指向,都指向它们的第一个参数,如果第一个参数为空,则默认为全局对象window

bind跟前两者有点区别,bind只是做个绑定,返回对应函数,而call和apply则立即调用函数,所以想要达到同样的目的,bind后面还得加上一对括号,表示立即调用返回的函数

5、匿名函数调用

// "use strict";
  var username = 'liutt';
  let person = {
    username: 'liuhw',
    printUsername: function() {
      console.log(this.username);
    },
    say: function () {
      (function (callback) {
        callback();
      })(this.printUsername);
    }
  };
  person.say(); // 非严格模式:liutt,严格模式:抛异常

在当前这个执行环境中匿名函数并没有绑定到任何一个对象上,所以this指向window,严格模式下this指向undefined

6、es6中的箭头函数调用

// "use strict";
  var username = 'liutt';
  setTimeout(() => {
    console.log(this.username) // 严格和非严格模式:liutt
  });
  function Person() {
    this.username = 'liuhw';
    setTimeout(() => {
      console.log(this.username);
    })
  }
  new Person(); // 严格和非严格模式:liuhw

箭头函数没有自己的this,它的this是继承自定义它的宿主对象,或者说是外部对象,如代码所示,第一个setTimeout中的箭头函数的宿主对象是window,第二个则是new Person(),所以经常遇到箭头函数多层嵌套,它们的this都是从最外层的宿主对象一层层继承下去。

posted @ 2019-04-03 15:02  liuhw1988  阅读(148)  评论(0编辑  收藏  举报