讲师中心 微信公众号
AI工具推荐 视频效率加速

JavaScript 中 this 的使用详细介绍

浅静姑娘_5132

浅静姑娘_5132

发布时间:2017-03-30 09:20:05

|

1470人浏览过

|

来源于php中文网

原创

tip

首先,我知道这篇文章很无聊,无非就是关于 js 中的 this,并且也已经有千千万万的文章写过这部分内容了;

但是,我还是想写一篇关于 js 中的 this 的文章,算是一个总结归纳吧;(大神们可以绕行看我的其他文章)

在 js 中,this 这个上下文总是变化莫测,很多时候出现 bug 总是一头雾水,其实,只要分清楚不同的情况下如何执行就 ok 了。

全局执行

首先,我们在全局环境中看看它的 this 是什么:

first. 浏览器:

console.log(this);

// Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
可以看到打印出了 

window

 对象;

second. node:

console.log(this);

// global
可以看到打印出了 

global

 对象;

总结:在全局作用域中它的 

this

 执行当前的全局对象(浏览器端是 

立即学习“Java免费学习笔记(深入)”;

window

,node 中是 

global

)。

函数中执行纯粹的函数调用

这是最普通的函数使用方法了:

function test() {
 console.log(this);
};

test();

// Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
我们可以看到,一个函数被直接调用的时候,属于全局调用,这时候它的 

this

 指向 全局对象;

严格模式 ‘use strict’;

如果在严格模式的情况下执行纯粹的函数调用,那么这里的的 

this

 并不会指向全局,而是 

undefined

,这样的做法是为了消除 js 中一些不严谨的行为:

'use strict';
function test() {
 console.log(this);
};

test();

// undefined
当然,把它放在一个立即执行函数中会更好,避免了污染全局:

(function (){
 "use strict";
 console.log(this);
})();

// undefined

作为对象的方法调用

当一个函数被当作一个对象的方法调用的时候:

var obj = {
 name: 'qiutc',
 foo: function() {
   console.log(this.name);
 }
}

obj.foo();

// 'qiutc'
这时候,

this

 指向当前的这个对象;

当然,我们还可以这么做:

function test() {
 console.log(this.name);
}
var obj = {
 name: 'qiutc',
 foo: test
}

obj.foo();

// 'qiutc'
同样不变,因为在 js 中一切都是对象,函数也是一个对象,对于 

test

 ,它只是一个函数名,函数的引用,它指向这个函数,当 

foo = test

,

foo

 同样也指向了这个函数。

如果把对象的方法赋值给一个变量,然后直接调用这个变量呢:

var obj = {
 name: 'qiutc',
 foo: function() {
   console.log(this);
 }
}

var test = obj.foo;
test();

// Window
可以看到,这时候 

this

 执行了全局,当我们把 

test = obj.foo

 ,

test

 直接指向了一个函数的引用,这时候,其实和 

obj

 这个对象没有关系了,所以,它是被当作一个普通函数来直接调用,因此,

this

 指向全局对象。

一些坑

我们经常在回调函数里面会遇到一些坑:

var obj = {
 name: 'qiutc',
 foo: function() {
   console.log(this);
 },
 foo2: function() {
   console.log(this);
   setTimeout(this.foo, 1000);
 }
}

obj.foo2();
执行这段代码我们会发现两次打印出来的 

this

 是不一样的:

第一次是 

foo2

 中直接打印 

this

,这里指向 

obj

 这个对象,我们毋庸置疑;

但是在 

setTimeout

 中执行的 

this.foo

 ,却指向了全局对象,这里不是把它当作函数的方法使用吗?这一点经常让很多初学者疑惑;

其实,

setTimeout

 也只是一个函数而已,函数必然有可能需要参数,我们把 

this.foo

 当作一个参数传给 

setTimeout

 这个函数,就像它需要一个 

fun

参数,在传入参数的时候,其实做了个这样的操作 

fun = this.foo

,看到没有,这里我们直接把 

fun

 指向 

this.foo

 的引用;执行的时候其实是执行了 

fun()

 所以已经和 

obj

 无关了,它是被当作普通函数直接调用的,因此 

this

 指向全局对象。

这个问题是很多异步回调函数中普遍会碰到的;

解决

为了解决这个问题,我们可以利用 闭包 的特性来处理:

var obj = {
 name: 'qiutc',
 foo: function() {
   console.log(this);
 },
 foo2: function() {
   console.log(this);
   var _this = this;
   setTimeout(function() {
     console.log(this);  // Window

     console.log(_this);  // Object {name: "qiutc"}
   }, 1000);
 }
}

obj.foo2();
可以看到直接用 

this

 仍然是 

window

;因为 

foo2

 中的 

this

 是指向 

obj

,我们可以先用一个变量 

_this

 来储存,然后在回调函数中使用 

_this

,就可以指向当前的这个对象了;

setTimeout 的另一个坑

之前啊说过,如果直接执行回调函数而没有绑定作用域,那么它的 

this

 是指向全局对象(

window

),在严格模式下会指向 

undefined

,然而在

setTimeout

 中的回调函数在严格模式下却表现出不同:

'use strict';

function foo() {
 console.log(this);
}

setTimeout(foo, 1);

// window
按理说我们加了严格模式,foo 调用也没有指定 

this

,应该是出来 

undefined

,但是这里仍然出现了全局对象,难道是严格模式失效了吗?

并不,即使在严格模式下,

setTimeout

 方法在调用传入函数的时候,如果这个函数没有指定了的 

this

,那么它会做一个隐式的操作—-自动地注入全局上下文,等同于调用 

foo.apply(window)

 而非 

foo()

;

当然,如果我们在传入函数的时候已经指定 

this

,那么就不会被注入全局对象,比如: 

setTimeout(foo.bind(obj), 1);

作为一个构造函数使用

在 js 中,为了实现类,我们需要定义一些构造函数,在调用一个构造函数的时候需要加上 

new

 这个关键字:

function Person(name) {
 this.name = name;
 console.log(this);
}

var p = new Person('qiutc');

// Person {name: "qiutc"}
我们可以看到当作构造函数调用时,

javascript-pro
javascript-pro

专注现代 ECMAScript、异步编程、性能优化和全栈的 JavaScript 专家,适用于现代开发

下载
this

 指向了这个构造函数调用时候实例化出来的对象;

当然,构造函数其实也是一个函数,如果我们把它当作一个普通函数执行,这个 

this

 仍然执行全局:

function Person(name) {
 this.name = name;
 console.log(this);
}

var p = Person('qiutc');

// Window
其区别在于,如何调用函数(

new

)。

箭头函数

在 ES6 的新规范中,加入了箭头函数,它和普通函数最不一样的一点就是 

this

 的指向了,还记得我们使用闭包来解决 

this

 的指向问题吗,如果用上了箭头函数就可以更完美的解决了:

var obj = {
 name: 'qiutc',
 foo: function() {
   console.log(this);
 },
 foo2: function() {
   console.log(this);
   setTimeout(() => {
     console.log(this);  // Object {name: "qiutc"}
   }, 1000);
 }
}

obj.foo2();
可以看到,在 

setTimeout

 执行的函数中,本应该打印出在 

window

,但是在这里 

this

 却指向了 

obj

,原因就在于,给 

setTimeout

 传入的函数(参数)是一个箭头函数:

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。


根据例子我们理解一下这句话:

在 

obj.foo2()

 执行的时候,当前的 

this

 指向 

obj

;在执行 

setTimeout

 时候,我们先是定义了一个匿名的箭头函数,关键点就在这,箭头函数内的

this

 执行定义时所在的对象,就是指向定义这个箭头函数时作用域内的 

this

,也就是 

obj.foo2

 中的 

this

,即 

obj

;所以在执行箭头函数的时候,它的 

this

 -> 

obj.foo2 中的 this

 -> 

obj

;

简单来说, 箭头函数中的 this 只和定义它时候的作用域的 this 有关,而与在哪里以及如何调用它无关,同时它的 this 指向是不可改变的。

call, apply, bind

在 js 中,函数也是对象,同样也有一些方法,这里我们介绍三个方法,他们可以更改函数中的 

this

 指向:

call
fun.call(thisArg[, arg1[, arg2[, ...]]])

它会立即执行函数,第一个参数是指定执行函数中 

this

 的上下文,后面的参数是执行函数需要传入的参数;

apply
fun.apply(thisArg[, [arg1, arg2, ...]])

它会立即执行函数,第一个参数是指定执行函数中 

this

 的上下文,第二个参数是一个数组,是传给执行函数的参数(与 

call

 的区别);

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

它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 

this

 的上下文,后面的参数是执行函数需要传入的参数;

这三个函数其实大同小异,总的目的就是去指定一个函数的上下文(this),我们以 

call

 函数为例;

为一个普通函数指定 this

var obj = {
 name: 'qiutc'
};

function foo() {
 console.log(this);
}

foo.call(obj);

// Object {name: "qiutc"}
可以看到,在执行 

foo.call(obj)

 的时候,函数内的 

this

 指向了 

obj

 这个对象,成功;

为对象中的方法指定一个 this

var obj = {
 name: 'qiutc',
 foo: function () {
   console.log(this);
 }
}

var obj2 = {
 name: 'tcqiu222222'
};

obj.foo.call(obj2);

// Object {name: "tcqiu222222"}
可以看到,执行函数的时候这里的 

this

 指向了 

obj2

,成功;

为构造函数指定 this

function Person(name) {
 this.name = name;
 console.log(this);
}

var obj = {
 name: 'qiutc2222222'
};

var p = new Person.call(obj, 'qiutc');

// Uncaught TypeError: Person.call is not a constructor(…)
这里报了个错,原因是我们去 

new

 了 

Person.call

 函数,而非 

Person

 ,这里的函数不是一个构造函数;

换成 

bind

 试试:

function Person(name) {
 this.name = name;
 console.log(this);
}

var obj = {
 name: 'qiutc2222222'
};

var Person2 = Person.bind(obj);

var p = new Person2('qiutc');

// Person {name: "qiutc"}

console.log(obj);

// Object {name: "qiutc2222222"}
打印出来的是 

Person

 实例化出来的对象,而和 

obj

 没有关系,而 

obj

 也没有发生变化,说明,我们给 

Person

 指定 

this

 上下文并没有生效;

因此可以得出: 使用 bind 给一个构造函数指定 

this

,在 

new

 这个构造函数的时候,

bind

 函数所指定的 

this

 并不会生效;

当然 

bind

 不仅可以指定 

this

 ,还能传入参数,我们来试试这个操作:

function Person(name) {
 this.name = name;
 console.log(this);
}

var obj = {
 name: 'qiutc2222222'
};

var Person2 = Person.bind(obj, 'qiutc111111');

var p = new Person2('qiutc');

// Person {name: "qiutc111111"}
可以看到,虽然指定 

this

 不起作用,但是传入参数还是起作用了;

为箭头函数指定 this

我们来定义一个全局下的箭头函数,因此这个箭头函数中的 

this

 必然会指向全局对象,如果用 

call

 方法改变 

this

 呢:

var afoo = (a) => {
 console.log(a);
 console.log(this);
}

afoo(1);

// 1
// Window

var obj = {
 name: 'qiutc'
};

afoo.call(obj, 2);

// 2
// Window
可以看到,这里的 

call

 指向 

this

 的操作并没有成功,所以可以得出: 箭头函数中的 this 在定义它的时候已经决定了(执行定义它的作用域中的 this),与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this。

只要记住箭头函数大法好,不变的 

this

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

相关专题

更多
OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

20

2026.10.16

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

0

2026.10.16

OpenJDK怎么部署Java应用
OpenJDK怎么部署Java应用

Java项目开发完成后,怎样部署到目标环境并正常运行?本专题介绍OpenJDK应用部署流程,涵盖运行环境准备、版本兼容检查、应用打包、启动参数配置和日志查看,帮助你理清部署前后的操作顺序,减少环境差异造成的运行问题。

0

2026.10.16

Revel框架模板渲染与表单验证
Revel框架模板渲染与表单验证

本专题围绕Revel框架视图层开发,介绍模板语法、变量输出、布局复用、表单提交、参数绑定与验证规则,帮助开发者完成安全规范的页面交互。

0

2026.10.16

Revel框架MVC路由与控制器
Revel框架MVC路由与控制器

本专题聚焦Revel框架MVC核心机制,讲解路由定义、控制器Action、参数绑定、请求响应和反向路由,并结合常见场景梳理接口请求处理流程。

0

2026.10.16

Revel框架入门与安装配置
Revel框架入门与安装配置

本专题从Revel框架环境搭建讲起,系统介绍命令行工具安装、项目创建、目录结构、配置文件及热代码重载,帮助开发者快速完成首个Go Web应用。

20

2026.10.16

Linux 新手先学哪些命令
Linux 新手先学哪些命令

Linux新手命令专题汇集终端基础、文件目录操作、文本查看、软件安装和权限管理等入门文章,帮助按实际任务练习常用命令并建立命令行操作习惯。

20

2026.10.15

MySQL 忘记密码怎么办
MySQL 忘记密码怎么办

MySQL忘记密码专题汇集不同版本与系统下的root密码重置方法,覆盖MySQL 5.7、8.0、Mac、Linux及本地集成环境,并提示恢复权限与重新登录的检查步骤。

20

2026.10.15

Vue.js 中 ref 和 reactive 的区别
Vue.js 中 ref 和 reactive 的区别

Vue.js中ref和reactive的区别专题汇集两种响应式API的定义、访问方式、对象解构、侦听与性能取舍等文章,帮助结合实际状态类型选择合适写法。

20

2026.10.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn