1.this的使用场景

我们先把this的使用场景分为两大类:函数外和函数内:

函数外的this

就是在全局代码里,直接使用this:

"use strict";

let name = "window";
console.log(this);
console.log(this.name); // Window
// 'window'

从结果看,在函数外的this指向很简单,就直接指向的是全局变量Window对象,(浏览器环境,以下代码都是在浏览器环境)

而且严格模式或非严格模式都是。

函数内部的this

而在函数内部使用的时候,就可以分为以下几类:

  1. 函数独立调用
  2. 函数作为对象的方法被调用
  3. 函数作为构造函数调用
  4. 函数通过call,apply,bind调用

this指向确定的时间

在分析不同情况this的指向之前,我们确认一个很重要的问题,就是this的指向是什么时间确定的。

在说这个问题之前,需要简单说一下执行上下文,如果有看过: js的闭包、执行上下文、作用域链 这篇文章,我们就会知道执行上下文包含三个部分:

  • 变量对象
  • 作用域链
  • this指向

我们发现this其实执行上下文的一部分,当代码需要用到this的时候,也是从这里取的。所以执行上下文创建的时间,就是this确定的时间。

执行上下文的创建时间是:函数被调用,但是还没执行具体代码之前。

所以this的指向确定的时间也就明确了:当函数被调用的时候,才开始确定this指向。

2.分场景分析this指向

在了解了this被确定的时间后,我们现在来按上面所列出的场景,来具体分析在函数里面的this:

2.1 函数独立调用时

函数对立调用,其实就是我们最常见的以函数名直接调用函数:

// code-01-非严格模式
var name = "window";
function fun() {
var name = "function";
console.log(this);
console.log(this.name);
}
fun()
// >> Window
// >> 'window'

我们看到,当这样调用函数时,this指向的是全局对象Window,所以this.name就相当于Window.name:'window',而不是函数的内部变量name='function'

这里有一点需要说明的是,这是在非严格模式下,那如果是在严格模式下呢?我们看下面的例子:

// code-01-严格模式

"use strict"
var name = "window";
function fun() {
var name = "function";
console.log(this);
console.log(this.name);
}
fun()
// >> undefined
// >> 报错

从结果来看,在严格模式下,独立调用函数时,函数内部的this指向是 undefined

其实应该这么说:不管是严格模式还是非严格模式,独立调用函数时,函数内部的this指向都是 undefined,只不过在非严格模式下,js会自动把undefined的this默认指向全局对象:Window

2.2 函数作为对象的方法调用

函数作为一个对象的方法调用,我们举例来看:

//code-02 作为对象成员方法调用函数
var name = "window";
var obj = {
name: "obj",
fun: function () {
console.log(this.name);
},
child: {
name: "child",
fun: function () {
console.log(this.name);
},
},
};
// 作为成员方法调用
obj.fun();
// 'obj' // 多级调用
obj.child.fun();
// 'child' // 赋值后调用
let fun = obj.fun;
fun();
// 'window'

我们下面来分析下上面的代码结果:

  • obj.fun()

    首先我们从打印的结果来看,这里的this等于obj对象。

    所以当函数作为某个对象的方法来调用的时候,this指向这个方法所属的对象。

  • obj.child.fun();

    从打印的结果来看,这里this等于obj.child对象。

    所以不管是多少级的调用,this指向最近的所属对象。

  • var fun = obj.fun; fun();

    从打印的结果来看,这里this等于全局对象window。window.name = 'window'

    从代码看,这里先做了一个赋值操作,把函数obj.fun赋值给了变量fun, 上面我们有说到this的确定时间是在函数被调用的时候,这时候函数并没有被调用,只是做了赋值操作,所以这一步的时候,this并没有确定。

    当执行到fun()的时候,函数被调用,this在这个时候要确定指向,这时候就相当于是作为独立函数调用,应该指向的是undefined,但是在非严格模式下,undefined的this会默认指向全局变量window。

    所以this.name == window.name == 'window'。如果是严格模式,this.name == undefined.name,会报错。

2.3 函数作为构造函数调用

函数作为构造函数的情况,可以分为两种:

  1. 构造函数无返回
  2. 构造函数有返回值

    a. 返回一个对象

    b. 返回其他非对象的值

下面我们分别来看:

构造函数无返回

这是构造函数最常用的情况,直接来看代码:

//code-03 函数作为构造函数(无返回)

let _this;
function User(name, age) {
this.name = name;
this.age = age;
_this = this;
console.log(this);
// {name:"xiaoming",age:27}
} let xiaoming = new User("xiaoming", 27);
console.log(_this === xiaoming); // true

从结果来看,我们知道当函数作为构造函数的时候,该函数里面的this等于这个构造函数new的实例对象,就是这里的对象xiaoming。从【机制】JavaScript的原型、原型链、继承这篇可以知道操作符new实际上做了什么事情。

构造函数有返回

如果返回的是非对象,则返回值会被忽略,情况等同于无返回。

下面就只讨论返回值为一个对象的情况:

//code-03 函数作为构造函数(返回对象)

let _this;
function User(name, age) {
this.name = name;
this.age = age; _this = this;
console.log(this);
// {name:'xiaoming',age:27} let obj = {
name: "obj",
};
return obj;
}
let xiaoming = new User("xiaoming", 27); console.log(xiaoming);
// {name:'obj'} console.log(_this === xiaoming);
// false

从结果来看,当构造函数返回一个对象时,它new出来的实例就等于它返回的对象(xiaoming === obj),而构造函数的内部this并没有起到任何作用。

2.4 函数通过call,apply,bind调用

call,apply,bind都是可以指定this的值。

// code-04 指定this

function fun(name, age) {
console.log(name, age, this);
}
let obj = {
name: "obj",
}; fun.call(obj, "obj", 27);
fun.apply(obj, ["obj", 27]);
let funBind = fun.bind(obj, "obj", 27);
funBind(); // 结果返回都一样
// 'obj' 27 {name:obj}

call,apply,bind:

相同点:都可以指定函数内部的this值,参数的第一个即为this的值。

不同点:

  • call:fun参数(name,age),由call函数的第2,3..参数依次赋值。
  • apply:fun参数(name,age),由apply函数的第2个参数赋值,第二个参数是一个数组,所存的值依次赋值给fun参数。
  • bind:fun参数(name,age)赋值方式同call,但bind返回的是一个函数,而不是直接执行fun。

3.几种特殊情况

在说明了上面常用情景后,我们来分析几种特殊的情况:

数组成员

当函数作为数组的成员时:

// code-05 函数作为数组成员

function arrFun() {
console.log(this.length);
console.log(this === arr);
}
let arr = [1, 2, arrFun];
arr[2](); // 3
// true

从结果看,我们知道当函数作为数组的成员的时候,此函数内部的this指向的是当前数组。

可以这样理解:arr[2] == arr["2"], 类似于对象的成员方法。

事件绑定

函数作为绑定事件时:

// code-06 事件绑定

<button id="btn">点击</button>

document.getElementById("btn").addEventListener("click", function () {
console.log(this);
}); // <button id="btn">点击</button>

从结果看,我们知道当函数作为事件被绑定时,此函数内部的this指向的是绑定了该事件的dom元素。

异步函数:promise,setTimeout

异步执行函数的时候分为promise和setTimeout情况(关于异步机制可以参看 【机制】 JavaScript的事件循环机制总结 eventLoop):

// code-07 异步函数

"use strict";

setTimeout(function () {
console.log("setTimeout:", this);
}); new Promise(function (resolve) {
console.log("start");
resolve();
}).then(function () {
console.log("promise:", this);
}); // start
// promise: undefined
// setTimeout: Window

从结果来看,我们知道其实 setTimeout执行的函数下的this,相当于是在全局环境下的this:执行全局变量 Window对象,严格模式和非严格模式都一样。

promise下执行的函数其实相当于函数独立执行的情况:严格模式this等于undefined,非严格模式下会默认把undefined的this指向Window。

箭头函数

其实箭头函数本身没有this,它里面的this指向的是外部作用域中的this:

// code-08 箭头函数

"use strict";

let Obj = {
name: "obj",
fun_1: () => {
console.log(this);
},
fun_2() {
let fun = () => {
console.log(this);
};
fun();
},
};
Obj.fun_1();
// Window Obj.fun_2();
// Obj function foo() {
setTimeout(() => {
console.log(this);
});
} foo.call({ id: 42 });
// {id:42}

Obj.fun_1()

fun_1是箭头函数,本身没有this。它的外层作用域就是全局作用域,所以箭头函数的this指向的是全局作用域下的this:Window

Obj.fun_2()

fun_2函数内部的fun是箭头函数,本身没有this。它的外层作用域就是fun_2,而fun_2的this是调用它的对象Obj,所以箭头函数的this指向的也是Obj。

foo.call({ id: 42 })

foo函数用call调用,于是foo的this为{id:42}。本来setTimeout内部的函数this指向的是Widow,但是因为它是箭头本身没有this,箭头函数的this指向的是外部作用域的this,在这里就是foo的this:{id:42}。

-- 完 --

【机制】js中的this指向的更多相关文章

  1. 理解js中this的指向

         学习自原文  http://www.cnblogs.com/pssp/p/5216085.html后的一点小结(原文作者总结的很棒^_^)! 关于js中this的指向,在函数定义的时候还无法 ...

  2. js中this的指向

    在js中this的指向对于新手来说一定是个难题,但是如果你真正理解了的话,也就没什么问题啦,下面就来讲讲this吧. JS中,this的值取决于调用的模式(调用对象),而JS中共有4种调用模式: 1. ...

  3. JS中的this 指向问题

    我发现在对JS的学习中有很多朋友对this的指向问题还是有很大的误区或者说只是大致了解,但是一旦遇到复杂的情况就会因为this指向问题而引发各种bug. 对于之前学习过c或者是Java的朋友来说可能这 ...

  4. 轻松了解JS中this的指向

    JS中的this指向一直是个让人头疼的问题,想当初我学的是天昏地暗,查了好多资料,看的头都大了,跟他大战了那么多回合,终于把它搞定个七八分,其实往往都是我们复杂化了,现在就让大家轻松看懂this的指向 ...

  5. js中改变this指向的call、apply、bind 方法使用

    前言: 由于js 中this的指向受函数运行环境的影响,指向经常改变,使得开发变得困难和模糊,所以在封装sdk,写一些复杂函数的时候经常会用到this 指向绑定,以避免出现不必要的问题,call.ap ...

  6. js中 this 的指向

    js中 this的指向一共存在3种地方: 1.全局的this; 2.构造函数的this; 3.call/apply; 一.全局的this: function test(){ this.d = 3;// ...

  7. 彻底理解js中this的指向,不必硬背。

    首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然 ...

  8. 了解学习JS中this的指向

    [转] 首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问 ...

  9. JS中this的指向问题

    JS中this的定义:this对象是在运行时基于函数的执行环境绑定的(通俗点来说就是:this代表当前函数属于哪个对象). this一般情况下都代表的是global对象,在浏览器中就是window对象 ...

  10. Js中的this指向问题

    函数中的this指向和当前函数在哪定义的或者在哪执行的都没有任何的关系分析this指向的规律如下: [非严格模式]1.自执行函数中的this永远是window [案例1] var obj={ fn:( ...

随机推荐

  1. C - Door Man(欧拉回路_格式控制)

    现在你是一个豪宅的管家,因为你有个粗心的主人,所以需要你来帮忙管理,输入会告诉你现在一共有多少个房间,然后会告诉你从哪个房间出发,你的任务就是从出发的房间通过各个房间之间的通道,来把所有的门都关上,然 ...

  2. A - 最长回文(马拉车算法//manacher)

    给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.回文就是正反读都是一样的字符串,如aba, abba等 Input输入有多组case,不超过120组,每组输入为一 ...

  3. P3355 骑士共存问题 (最小割)

    题意:nxn的棋盘 有m个坏点 求能在棋盘上放多少个马不会互相攻击 题解:这个题仔细想想居然和方格取数是一样的!!! 每个马他能攻击到的地方的坐标 (x+y)奇偶性不一样 于是就黑白染色 s-> ...

  4. HDU - 3281 dp

    题意: 给你b个球,m个楼层,你需要找到一个楼层数k,使得从小于k这个楼层上面扔下去球,而球不会碎.求在最糟糕的情况下你最多要尝试多少次 题解: dp[i][j]表示你有b个球,楼层总数为m,你找到那 ...

  5. PowerShell随笔6---ISE

    简单的命令可以通过控制台窗口输入执行,但是我的脚本逻辑复杂,需要保存.总不能在命令行工具中执行吧. 关了窗口,啥都没了.有没有一个IDE,有. 在PowerShell命令行窗口中输入:ISE,就会打开 ...

  6. ElasticSearch入门到筋痛

    1. 什么是ES ES中文网:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html ES:是一款基于Lucene ...

  7. Ansible 自动化部署

    参考 BLOG: Ansible 系列模块 Ansible 部署与使用 Ansible Book Ansible Ansible 是一个自动化统一配置管理工具,自动化主要体现在 Ansible 集成了 ...

  8. Leetcode(22)-括号生成

    给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", "(()())& ...

  9. Python import本地模块

    无法识别本地模块 在pycharm中选择文件夹Mark Directory as -> Source root. 或者使用sys.path.append()添加文件夹路径 还是报错ImportE ...

  10. CNN可视化技术总结(四)--可视化工具与项目

    CNN可视化技术总结(一)-特征图可视化 CNN可视化技术总结(二)--卷积核可视化 CNN可视化技术总结(三)--类可视化 导言: 前面介绍了可视化的三种方法--特征图可视化,卷积核可视化,类可视化 ...