JavaScript 迷之 this 总结

阅读 723
收藏 46
2017-04-18
原文链接:www.imooc.com

JavaScript的this迷之指向是不是弄晕了你,我是被转的晕头转向,所以来总结一下this的指向问题。

this的指向总体可以分为四种情况:

  1. 直接调用
  2. 作为对象方法调用
  3. 构造函数调用
  4. call()跟apply()方法调用

在说this的指向之前,我们先要知道this是在函数调用的时候确定的,这使得我们可以非常灵活的使用this,所以这才叫迷之this。

  1. 直接调用

废话不多说,直接上码

    var name = "xiaoming";
    function getName() {
      var name = "xioahong";
      console.log(this.name); // xiaoming
      console.log(this === window); // true
    }
    getName();

上面的例子可以说明函数独立调用的使用this指向全局window,但是如果在严格模式会报错。

顺便一提,ES6已经默认使用严格模式。

看了上面的例子,不要以为只有全局作用域独立调用函数,this才指向全局window,其实在任何作用域像 函数名() 这样的独立调用函数的this都指向全局window

  function getName() {
    function personName() {
      console.log(this === window);    // true
    }
    return personName;
  }
  getName()();

或许有人看不懂getName()();这个的意思,其实就是在调用getName函数再处理它的返回值,就相当于下面的例子。

  function getName() {
    function personName() {
      console.log(this === window);    // true
    }
    personName();
  }
  getName();

而在接触this后,你是否想改变this的指向,可是this的指向是不可以改变的,我们先来看下this指向不可改变。

  var obj = {
    name: "xiaoming"
  }

  function getName() {
    this = obj;
    console.log(this.name);
  }
  getName();  // 运行报错
2.作为对象的方法的调用

先上两个例子

  var name = "xiaohong";
  var obj = {
    name: "xiaoming",
    getName: function() {
      console.log(this.name); // xiaoming
      console.log(this === obj);  // true
    }
  }
  obj.getName();
  (obj.getName)();    // 结果与上面调用方式相同
   // 这只是在引用一个函数,所以this指向保持不变
  var name = "xiaohong";
  var obj = {
    name: "xiaoming",
    getName: function() {
      console.log(this.name); // xiaohong
      console.log(this === obj);    // false
    }
  }
  var fn = obj.getName;
  fn();

乍一看上面两个例子没有什么区别,但是区别很大:
例1调用obj对象里面的getName()方法,所以this指向obj;
例2将obj.getName的引用赋值给变量fn,之后又独立调用,说以this指向了全局Window

在此总结:
如果调用函数被一个对象所拥有,则该函数内部的this指向该对象。
如果函数单独调用,则this指向全局window,在严格模式下报错。
(调用匿名函数在我的理解中也相当于函数独立调用)

来个简单的自执行函数来证明一下

  (function() {
    console.log(this === window);    // true
  })();

我们来看下下面这个例子

var name = "global";
var obj = {
    name:  "local",
    getName: function() {
        return function() {
            // 该匿名函数为闭包
            return this.name;    // 返回的是?
        };
    }
};

console.log(obj.getName());

这个答案当然是 "global" 。因为我们知道每个函数在调用的时候会获取两个对象:this和arguments。而内部函数在搜索这个两个对象时只会搜索到其活动对象为止。而匿名函数的执行环境具有全局性,所以这个this就指向了全局window

但我们要访问匿名函数的外部函数变量的话要怎么做?看这里。

var name = "global";
var obj = {
    name:  "local",
    getName: function() {
        var that = this;
        return function() {
            return that.name;    // local
        };
    }
};

console.log(obj.getName());
我们不可以改变this的值,但我们可以把this用变量存储起来,这样this的指向就不会变了。
3.构造函数调用
  function Person(name, age) {
    this.name = name;    // 此处this的指向是?
    this.age = age;
  }
  var person = new Person("xiaoming", 22);
  console.log("name:" + person.name + ",age:" + person.age);   

参考JavaScript高级程序设计,我们可以知道new操作符做了四件事:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给了新对象(因此this指向了该对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

所以上面的打印结果是: name:xiaoming,age:22

4.使用call()跟apply()方法绑定this

先来看下定义

call方法:
语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj
指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

apply方法:
语法:apply([thisObj[,argArray]])
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
说明:
如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。
如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数

我们来看下怎么绑定this

  var obj = {
    name: "xiaoming"
  }

  function getName() {
    console.log(this.name); // xiaoming
  }

  getName.call(obj);

其实在我的理解中call跟apply是没有什么区别,只是传递参数的形式不一样。
call():一个一个的传递参数;
apply():以数组的形式传递。

  var obj = {
    num1: 1
  }

  function count(num2, num3) {
    console.log(this.num1 + num2 + num3);
  }

  count.call(obj, 1, 1);    // 3
  count.apply(obj, [1, 1]);    // 3

使用call()跟apply()可以借用其他对象的方法。
例如:借用Math()对象的最大值方法找出数组中的最大值

  var 
    arr = [2,34,54,23,233,44],
    maxNum = Math.max.apply(Math, arr);

  console.log(maxNum);    // 233

本文对你有帮助?欢迎扫码加入前端学习小组微信群:

评论