如何理解JavaScript中的this关键字
前言
王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 "大彻大悟" 的感觉,而看完之后却总是一脸懵逼。原型与闭包 可以说是 JavaScirpt 中理解起来最难的部分了,当然,我也只是了解到了一些皮毛,对于 JavaScript OOP 更是缺乏经验。这里我想总结一下 Javascript 中的 this
关键字,王福朋老师的在文章里也花了大量的篇幅来讲解 this
关键字的使用,可以说 this
关键字也是值得重视的。
我们都知道,每一个 "代码段" 都会执行在某一个 上下文环境 当中,而在每一个代码执行之前,都会做一项 "准备工作",也就是生成相应的 上下文环境,所以每一个 上下文环境 都可能会不一样。
上下文环境 是什么?我们可以去看王福朋老师的文章(链接在文末),讲解的很清楚了,这里不赘述了。
"代码段" 可以分为三种:
- 全局代码
- 函数体
eval
代码
与之对应的 上下文环境 就有:
- 全局上下文
- 函数上下文
(elav
就不讨论了,不推荐使用)
当然,这和 this
又有什么关系呢?this
的值就是在为代码段做 "准备工作" 时赋值的,可以说 this
就是 上下文环境 的一部分,而每一个不同的 上下文环境 可能会有不一样的 this
值。
每次在寻找一个问题的解决方案或总结一个问题的时候,我总会去尝试将这个问题进行合适的分类,而从不同的方面去思考问题。
所以,这里我大胆的将 this
关键字的使用分为两种情况:
全局上下文的
this
函数上下文的
this
(你也可以选择其他的方式分类。当然,这也不重要了)
全局上下文中的 this
在全局执行上下文中(在任何函数体外部),this
都指向全局对象:
// 在浏览器中, 全局对象是 window
console.log(this === window) // true
var a = 'Zavier Tang'
console.log(a) // 'Zavier Tang'
console.log(window.a) // 'Zavier Tang'
console.log(this.a) // 'Zavier Tang'
this.b = 18
console.log(b) // 18
console.log(window.b) // 18
console.log(this.b) // 18
// 在 node 环境中,this 指向global
console.log(this === global) // true
函数上下文中的 this
在函数内部,this
的值取决与函数被调用的方式。
this
的值在函数定义的时候是确定不了的,只有函数调用的时候才能确定 this
的指向。实际上 this
的最终指向的是那个调用它的对象。(也不一定正确)
1. 全局函数
对于全局的方法调用,this
指向 window
对象(node下为 global
):
var foo = function () {
return this
}
// 在浏览器中
foo() === window // true
// 在 node 中
foo() === global //true
但值得注意的是,以上代码是在 非严格模式 下。然而,在 严格模式 下,this
的值将保持它进入执行上下文的值:
var foo = function () {
"use strict"
return this
}
f2() // undefined
即在严格模式下,如果 this
没有被执行上下文定义,那它为 undefined
。
在生成 上下文环境 时:
- 若方法被
window
(或global
)对象调用,即执行window.foo()
,那this
将会被定义为window
(或global
);- 若被普通对象调用,即执行
obj.foo()
,那this
将会被定义为obj
对象;(在后面会讨论)- 但若未被对象调用,即直接执行
foo()
,在非严格模式下,this
的值默认指向全局对象window
(或global
),在严格模式下,this
将保持为undefined
。
通过 this
调用全局变量:
var a = 'global this'
var foo = function () {
console.log(this.a)
}
foo() // 'global this'
var a = 'global this'
var foo = function () {
this.a = 'rename global this' // 修改全局变量 a
console.log(this.a)
}
foo() // 'rename global this'
所以,对于全局的方法调用,this
指向的是全局对象 window
(或global
),即调用方法的对象。(注意严格模式的不同)
2. 作为对象的方法
当函数作为对象的方法调用时,它的 this
值是调用该函数的对象。也就是说,函数的 this
值是在函数被调用时确定的,在定义函数时确定不了(箭头函数除外)。
var obj = {
name: 'Zavier Tang',
foo: function () {
console.log(this)
console.log(this.name)
}
}
obj.foo() // Object {name: 'Zavier Tang', foo: function} // 'Zavier Tang'
//foo函数不是作为obj的方法调用
var fn = obj.foo // 这里foo函数并没有执行
fn() // Window {...} // undefined
this
的值同时也只受最靠近的成员引用的影响:
//接上面代码
var o = {
name: 'Zavier Tang in object o',
fn: fn,
obj: obj
}
o.fn() // Object {name: 'Zavier Tang in object o', fn: fn, obj: obj} // 'Zavier Tang in object o'
o.obj.foo() // Object {name: 'Zavier Tang', foo: function} // 'Zavier Tang'
在原型链中,this
的值为当前对象:
var Foo = function () {
this.name = 'Zavier Tang'
this.age = 20
}
Foo.prototype.getInfo = function () {
console.log(this.name)
console.log(this.age)
}
var tang = new Foo()
tang.getInfo() // "Zavier Tang" // 20
虽然这里调用的是一个继承方法,但 this
所指向的依然是 tang
对象。
参考:《Object-Oriented JavaScript》(Second Edition)
3. 作为构造函数
如果函数作为构造函数,那函数当中的 this
便是构造函数即将 new
出来的对象:
var Foo = function () {
this.name = 'Zavier Tang',
this.age = 20,
this.year = 1998,
console.log(this)
}
var tang = new Foo()
console.log(tang.name) // 'Zavier Tang'
console.log(tang.age) // 20
console.log(tang.year) // 1998
当 Foo
不作为构造函数调用时,this
的指向便是前面讨论的,指向全局变量:
// 接上面代码
Foo() // window {...}
4. 函数调用 apply
、call
、 bind
时
当一个函数在其主体中使用 this
关键字时,可以通过使用函数继承自Function.prototype
的 call
或 apply
方法将 this
值绑定到调用中的特定对象。即 this
的值就取传入对象的值:
var obj1 = {
name: 'Zavier1'
}
var obj2 = {
name: 'Zavier2'
}
var foo = function () {
console.log(this)
console.log(this.name)
}
foo.apply(obj1) // Ojbect {name: 'Zavier1'} //'Zavier1'
foo.call(obj1) // Ojbect {name: 'Zavier1'} //'Zavier1'
foo.apply(obj2) // Ojbect {name: 'Zavier2'} //'Zavier2'
foo.call(obj2) // Ojbect {name: 'Zavier2'} //'Zavier2'
与 apply
、call
不同,使用 bind
会创建一个与 foo
具有相同函数体和作用域的函数。但是,特别要注意的是,在这个新函数中,this
将永久地被绑定到了 bind
的第一个参数,无论之后如何调用。
var foo = function () {
console.log(this.name)
}
var obj1 = {
name: 'Zavier1'
}
var obj2 = {
name: 'Zavier2'
}
var g = foo.bind(obj1)
g() // 'Zavier1'
var h = g.bind(ojb2) // bind只生效一次!
h() // 'Zavier1'
var o = {
name: 'Zavier Tang',
f:f,
g:g,
h:h
}
o.f() // 'Zavier Tang'
o.g() // 'Zavier1'
o.h() // 'Zavier1'
5. 箭头函数
箭头函数是 ES6 语法的新特性,在箭头函数中,this
的值与创建箭头函数的上下文的 this
一致。
在全局代码中,this
的值为全局对象:
var foo = (() => this)
//在浏览器中
foo() === window // true
// 在node中
foo() === global // true
其实箭头函数并没有自己的 this
。所以,调用 this
时便和调用普通变量一样在作用域链中查找,获取到的即是创建此箭头函数的上下文中的 this
。
当箭头函数在创建其的上下文外部被调用时,箭头函数便是一个闭包,this
的值同样与原上下文环境中的 this
的值一致。由于箭头函数本身是不存在 this
,通过 call
、 apply
或 bind
修改 this
的指向是无法实现的。
作为对象的方法:
var foo = (() => this)
var obj = {
foo: foo
}
// 作为对象的方法调用
obj.foo() === window // true
// 用apply来设置this
foo.apply(obj) === window // true
// 用bind来设置this
foo = foo.bind(obj)
foo() === window // true
箭头函数 foo
的 this
被设置为创建时的上下文(在上面代码中,也就是全局对象)的 this
值,而且无法通过其他调用方式设定 foo
的 this
值。
与普通函数对比,箭头函数的 this
值是在函数创建创建确定的,而且无法通过调用方式重新设置 this
值。普通函数中的 this
值是在调用的时候确定的,可通过不同的调用方式设定 this
值。
总结
this
关键字的值取决于其所处的位置(上下文环境):
在全局环境中,
this
的值指向全局对象( window 或 global )。在函数内部,
this
的取值取决于其所在函数的调用方式,也就是说this
的值是在函数被调用的时候确定的,在创建函数时无法确定。当然,箭头函数是个例外,箭头函数本身不存在this
,而在箭头函数中使用this
获取到的便是创建其的上下文中的this
。同时,使用函数的继承方法call
、apply
和bind
会修改this
的指向。但值得注意的是,使用bind
方法会使this
的值永久的绑定到给定的对象,无法再通过调用call
和apply
方法修改this
的值,箭头函数调用call
、apply
或bind
方法无法修改this
。
参考:
- 深入理解javascript原型和闭包(完结) - 王福朋 - 博客园
- this - JavaScript | MDN
- javascript中的this作用域详解 - coder_Jenny - 博客园
如何理解JavaScript中的this关键字的更多相关文章
- 转载 深入理解JavaScript中的this关键字
转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一 ...
- 浅显易懂的理解JavaScript中的this关键字
在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 1. 一般用处 对 ...
- 正确理解JavaScript中的this关键字
JavaScript有this关键字,this跟JavaScript的执行上下文密切相关,很多前端开发工程师至今对this关键字还是模棱两可,本文将结合代码讲解下JavaScript的this关键字. ...
- 用自然语言的角度理解JavaScript中的this关键字
转自:http://blog.leapoahead.com/2015/08/31/understanding-js-this-keyword/ 在编写JavaScript应用的时候,我们经常会使用th ...
- 深入理解JavaScript中的this关键字
1. 一般用处 2. this.x 与 apply().call() 3. 无意义(诡异)的this用处 4. 事件监听函数中的this 5. 总结 在JavaScript中this变量是一个令人难以 ...
- 理解JavaScript中的this关键字
JavaScript中this关键字理解 在爬虫的过程中遇到了前端的js代码,对于this关键字理解的不是很清楚,所以写下这篇笔记,不足之处,希望得以改之. this的指向在函数定义的时候无法确定,只 ...
- 理解javascript中的with关键字
说起js中的with关键字,很多小伙伴们的第一印象可能就是with关键字的作用在于改变作用域,然后最关键的一点是不推荐使用with关键字.听到不推荐with关键字后,我们很多人都会忽略掉with关键字 ...
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
随机推荐
- APP携参安装技术怎样帮助APP推广
APP 如何自动实现携带参数安装?这是许多开发者感兴趣的问题,毕竟在 APP 开发的许多逻辑上常常不可避免的需要判断安装来源,比如:广告投放.用户邀请.用户行为.社交分享等 APP 推广环节,国内的 ...
- React中方法的this绑定
第一种 在组件(类)的constructor中绑定this class Demo extends Component { constructor(this) { super(this) this.st ...
- JS高级学习历程-8
2 构造函数和普通函数的区别 两者本身没有实质区别,具体看使用 new 函数(); -------->构造函数 函数(); ---------> 普通函数 <!D ...
- Django-Rest-Framework的视图和路由
Django-Rest-Framework的视图和路由 restful framework Django-Rest-Framework的视图 APIView django中写CBV的时候继承的是V ...
- 查询索引range失效
在某一个时间字段加索引,短的时间范围内查询,索引生效,为range.长时间范围,索引失效,查全表. 当索引查的数据量超过全表30%的数据,索引失效,会查全表.
- ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”
ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面” DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求 ...
- 课程增加功能(java web)
1.设计思想 先写类DBUtil用来连接数据库.在UserDaoImpl2类中写在数据库中添加课程表信息的方法.然后定义类Calss2来写保存超级课表数据:课程名称,任课教师,上课地点的属性及其get ...
- Canada Cup 2016 D. Contest Balloons 好题。优先队列 + 简单贪心
http://codeforces.com/contest/725/problem/D 这题一看就是贪心的了,w - t最小的那个,肯定是优先打死. 但是一直都不会写,为什么呢,因为这个太像二分答案了 ...
- 译:Java 中的正则表达式性能概述
原文链接:https://www.baeldung.com/java-regex-performance 作者: baeldung 译者:Darren Luo 1. 概述 在本快速教程中,我们将展示模 ...
- Spring 基础知识 - 依赖注入
所谓的依赖注入是指容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖. 依赖注入主要目的是为了解耦,体现了一种“组合”的理念. 无论是xml配置.注解配置还是Ja ...