如何理解JavaScript中的this关键字
前言:王福朋老师的 javascript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 “大彻大悟” 的感觉,而看完之后却总是一脸懵逼。原型与闭包 可以说是 javascirpt 中理解起来最难的部分了,当然,我也只是了解到了一些皮毛,对于 javascript oop 更是缺乏经验。这里我想总结一下 javascript 中的 this
关键字,王福朋老师的在文章里也花了大量的篇幅来讲解 this
关键字的使用,可以说 this
关键字也是个值得注意的。
我也不知道博客园编译出来的markdown文档会这么丑,作为一个完美主义者,有点无法忍受,附上原文链接
我们都知道,每一个 ”代码段“ 都会执行在某一个 上下文环境 当中,而在每一个代码执行之前,都会做一项 “准备工作”,也就是生成相应的 上下文环境,所以每一个 上下文环境 都可能会不一样。
上下文环境 是什么?我们可以去看王福朋老师的文章(链接在文末),讲解的很清楚了,这里不赘述了。
”代码段“ 可以分为三种:
- 全局代码
- 函数体
-
eval
代码
与之对应的 上下文环境 就有:
- 全局上下文
- 函数上下文
(elav
就不讨论了,不推荐使用)
当然,这和 this
又有什么关系呢?this
的值就是在为代码段做 “准备工作” 时赋值的,可以说 this
就是 上下文环境 的一部分,而每一个不同的 上下文环境 可能会有不一样的 this
值。
每次在寻找一个问题的解决方案或总结一个问题的时候,我总会去尝试将这个问题进行合适的分类,而从不同的方面去思考问题。
所以,这里我大胆的将 this
关键字的使用分为两种情况:
全局上下文的
this
函数上下文的
this
(你也可以选择其他的方式分类。当然,这也不重要了)
全局上下文中的 this
在全局执行上下文中(在任何函数体外部),this
都指向全局对象:
// 在浏览器中, 全局对象是 window console.log(this === window) // true let 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(a) // 18 console.log(window.a) // 18 console.log(this.a) // 18 // 在 node 环境中,this 指向global console.log(this === global) // true
函数上下文中的 this
在函数内部,this
的值取决与函数被调用的方式。
this
的值在函数定义的时候是确定不了的,只有函数调用的时候才能确定 this
的指向。实际上 this
的最终指向的是那个调用它的对象。(也不一定正确)
1. 全局函数
对于全局的方法调用,this
指向 window
对象(node下为 global
):
let foo = function () { return this } // 在浏览器中 foo() === window // true // 在 node 中 foo() === global //true
但值得注意的是,以上代码是在 非严格模式 下。然而,在 严格模式 下,this
的值将保持它进入执行上下文的值:
let 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
调用全局变量:
let a = 'global this' let foo = function () { console.log(this.a) } foo() // 'global this'
let a = 'global this' let foo = function () { this.a = 'rename global this' // 修改全局变量 a console.log(this.a) } foo() // 'rename global this'
所以,对于全局的方法调用,this
指向的是全局对象 window
(或global
),即调用方法的对象。(注意严格模式的不同)
2. 作为对象的方法
当函数作为对象的方法调用时,它的 this
值是调用该函数的对象。也就是说,函数的 this
值是在函数被调用时确定的,在定义函数时确定不了(箭头函数除外)。
let 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的方法调用 let fn = obj.foo // 这里foo函数并没有执行 fn() // window {...} // undefined
this
的值同时也只受最靠近的成员引用的影响:
//接上面代码 let 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'
3. 作为构造函数
如果函数作为构造函数,那函数当中的 this
便是构造函数即将 new
出来的对象:
let foo = function () { this.name = 'zavier tang', this.age = 20, this.year = 1998, console.log(this) } let 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
的值就取传入对象的值:
let obj1 = { name: 'zavier1' } let obj2 = { name: 'zavier2' } let 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
的第一个参数,无论之后如何调用。
let foo = function () { console.log(this.name) } let obj1 = { name: 'zavier1' } let obj2 = { name: 'zavier2' } let g = foo.bind(obj1) g() // 'zavier1' let h = g.bind(ojb2) // bind只生效一次! h() // 'zavier1' let o = { name: 'zavier tang', f:f, g:g, h:h } o.f() // 'zavier tang' o.g() // 'zavier1' o.h() // 'zavier1'
5. 箭头函数
在箭头函数中,this
的值与创建箭头函数的上下文的 this
一致。
在全局代码中,this
的值为全局对象:
let foo = (() => this) //在浏览器中 foo() === window // true // 在node中 foo() === global // true
作为对象的方法:
let foo = (() => this) let 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
值。
上一篇: 【读书笔记】iOS-工作区的使用
下一篇: 前端算法面试题整理
推荐阅读
-
基于oracle中锁的深入理解
-
深入探讨:Oracle中如何查询正锁表的用户以及释放被锁的表的方法
-
asp.net页面中如何获取Excel表的内容
-
简介JavaScript中的setTime()方法的使用
-
在JavaScript中操作时间之setYear()方法的使用
-
JavaScript中的setUTCDate()方法使用详解
-
JavaScript中Date.toSource()方法的使用教程
-
JavaScript中setUTCFullYear()方法的使用简介
-
JavaScript中的toLocaleDateString()方法使用简介
-
详解JavaScript中Date.UTC()方法的使用