Javascript基础——this指向

前几天发布的Javavscript基础——原型和原型链 收藏转化率还挺高,看来你们对于JS基础知识仍是很看重的,因为JS语言设计的关系,不少语言特性不是那么清晰。好比经典的this在哪的问题。 本文研究一下Javascript的this指向。javascript

Javascript的this指向问题,有些人可能以为很简单,有些人却以为扑朔迷离,看完本文以后相应会对this的掌握有一个直观的判断,而不是"开局全靠猜"。html

敲黑板

  1. function函数this指向由调用方式肯定,跟定义环境无关。java

  2. 箭头函数this指向由定义环境决定,与调用方式无关,也不能够bind(this)面试

严格模式

  1. 非严格模式下,全局做用域下的this指向windowapp

  2. 严格模式下,全局做用域下的this指向undefined函数

如下讨论均为非严格模式,这个不影响今天的讨论。ui

实践

说结论每每是让人难以理解的,下面经过不一样的调用场景对this作一个说明。this

1. 直接调用

function test() {
  console.log(this);
}
test(); // 输出undefined
复制代码

直接调用是最简单的, 大部分人在这里都能回答的很好。spa

总结

直接调用时this指向全局做用域prototype

  • 非严格模式this指向window
  • 严格模式this指向undefined

2. 对象调用

use strict
var n = 1;
var a = {
  n: 2,
  b: function() {
  	console.log(this.n);
  }
};

a.b(); // 输出2
var b = a.b;
b(); // 输出1
复制代码

面试题:请问上述例子输出什么?

非严格模式下,输出2和1,严格模式下输出2和一个报错(this指向undefined,访问undefined的n属性确定报错)

那若是你这么回答,满分!

分析

知其然还要知其因此然,咱们分析一下:

为何输出2?

由于a.b()是对象调用方式,因此b()中的this指向a

为何输出1?

这个很是有意思,并且也颇有迷惑性,面试的时候常常问到,也常常有人被问倒。

var b = a.b
复制代码

a.b赋值给变量b,b就是一个函数,请注意: 这里只是赋值,没有调用,因此b中的this指向还不肯定

b();
复制代码

调用函数b,这是什么调用方式? 普通调用,因此this指向全局做用域。

总结

对象调用方式下this指向调用对象。

是否GET? 若是没有GET,请关注公众号NodeJs之路,我在线给你答疑。

开胃菜已经吃了,下面来点"有难度的(实际上也没啥难度)"。

3. 嵌套对象调用

var n = 1;
var a = {
  n: 2,
  b: {
    n: 3,
    c: function() {
      console.log(this.n)
    }
  }
};
复制代码

面试题:请问上述例子中function中的this指向哪里?

正确答案:未肯定调用环境的状况下,this的指向不肯定

错误答案:指向a.b对象,Too young too simple!

var n = 1;
var a = {
  n: 2,
  b: {
    n: 3,
    c: function() {
      console.log(this.n)
    }
  }
};

a.b.c(); // 输出3

a.c = a.b.c;
a.c(); // 输出2

var c = a.b.c; 
c(); // 输出1
复制代码

这道题跟以前那道对象调用很像。

为何输出3?

对象调用方式下指向调用对象,a.b.c()中c()是经过a.b对象调用,指向对象a.b

为何输出?

a.c = a.b.c 给a对象定义一个函数c,注意,此时没有调用!this指向不肯定

a.c() 经过a对象来调用c(),因此this指向对象a

为何输出1?

var c = a.b.c 函数赋值给普通变量,注意,此时没有调用!

c(); 普通方式调用,指向window

总结

嵌套对象调用方式下,this指向最终调用函数的对象。

a.b.c.d.e.f.g.h() h函数中的this指向a.b.c.d.e.f.g

4. 构造函数方式调用

var name = 1;
function Person() {
  this.name = 2;
}

var p1= Person(); // p1为undefined
console.log(p1.name); // 报错

var p2 = new Person();
console.log(p2.name); // 输出2

复制代码

p1为何是undefined?

这道题比较坑,跟调用方式和this指向无关,由于Person函数没有返回值,js中,默认会返回undefined.

p2.name为何是2?

使用new操做符时,构造函数的返回值默认指向对象实例,因此p2.name就是Person()中的this.name

若是在Person()函数中加上return this的话,Person()返回值仍是this,由于这是普通调用。

5. 构造函数中指明返回值

原则上构造函数不该该有返回值,可是若是真的写了会怎么样?咱们来探讨一下。

返回复杂对象

function Person() {
  this.name = 2;
  return {};
}
var p1 = new Person();
console.log(p1.name)// 输出undefined
复制代码

返回简单对象

虽然Js只有对象,可是有一些如string,number这种通常叫作简单对象,date,regex,array,object等等叫复杂对象。

function Person() {
  this.name = 2;
  return 1;
}
var p1 = new Person();
console.log(p1.name)// 输出2
复制代码

返回null

使用typeof null返回的是[object],证实null是个对象,不过我们来看看构造函数返回null的表现。

function Person() {
  this.name = 2;
  return null;
}
var p1 = new Person();
console.log(p1.name)// 输出2
复制代码

总结

构造函数中this指向对象实例自己,若是构造函数指明了返回值,那么表现以下:

  • 返回普通值,this指向不变,仍是对象实例自己
  • 返回复杂对象,this指向新对象,也就是你new Person()返回的是那个新对象

6. bind

var a = {
    n: 1
  };
  var b = {
    n: 2
  }
  function f() {
    console.log(this.n);
  }
  var fa = f.bind(a);
  var fb = fa.bind(b);

  fa(); // 输出1
  fb(); // 输出1
复制代码

第1个输出1应该不难理解,bind能够更改function内部的this指向。屡次bind已经bind过的函数,this指向不变。

bind的实现原理有点复杂,将在下一篇文章进行详细解读。

总结

bind能够手动绑定function的this,this指向第1次bind时的this。

7. apply/call

这两个函数在this指向上表现一致,放到一块儿讲

var a = {
    n: 1
  };
  var b = {
    n: 2
  }
  function f() {
    console.log(this.n);
  }
  f.call(a); // 输出1
  f.apply(b); // 输出2

复制代码

call和apply的第1个参数为function执行时的this,这个this是肯定的,对未使用过bind的函数进行屡次apply/call,this指向都会改变。

8. 箭头函数

var n = 1;
var b = {
  n: 2,
  a: () => {
    console.log(this.n);
  }
}
b.a(); // 输出1
b.a.call({n:3}); // 输出1

复制代码

b.a定义时的this和n,b所在的this一致,默认状况下为全局做用域

箭头函数的this指向定义时所在的this,这个是明确的,可是若是定义时所在的是1个function,那么this指向同上面7点。

说下我以前学JS遇到过的问题: ES5下function才会有做用域隔离, {}这种玩意不会隔离做用域。

结尾

  1. 直接调用this指向全局做用域window,严格模式指向undefined
  2. 对象调用方式指向调用对象
  3. 嵌套对象调用方式指向最终调用对象(离function最近的那个)
  4. 构造函数方式调用指向对象实例
    1. 构造函数返回String/Number等简单类型时this指向不变,返回null指向也不变
    2. 构造函数返回Object/Array等复杂对象时,new Person()的返回值为return的对象
  5. bind能够更改function的this,一经绑定,永不改变。可是并不执行函数
  6. apply/call能够更改没有被bind过的this
  7. 箭头函数的this指向为定义箭头函数的this
相关文章
相关标签/搜索