如何理解JavaScript的this关键字

前言

王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 “大彻大悟” 的感觉,而看完之后却总是一脸懵逼。原型与闭包 可以说是 JavaScirpt 中理解起来最难的部分了,当然,我也只是了解到了一些皮毛,对于 JavaScript OOP 更是缺乏经验。这里我想总结一下 Javascript 中的 this 关键字,王福朋老师的在文章里也花了大量的篇幅来讲解 this 关键字的使用,可以说 this 关键字也是值得重视的。

我们都知道,每一个 “代码段” 都会执行在某一个 上下文环境 当中,而在每一个代码执行之前,都会做一项 “准备工作”,也就是生成相应的 上下文环境,所以每一个 上下文环境 都可能会不一样。

上下文环境 是什么?我们可以去看王福朋老师的文章(链接在文末),讲解的很清楚了,这里不赘述了。

"代码段" 可以分为三种:

  • 全局代码
  • 函数体
  • eval 代码

与之对应的 上下文环境 就有:

  • 全局上下文
  • 函数上下文

elav 就不讨论了,不推荐使用)

当然,这和 this 又有什么关系呢?this 的值就是在为代码段做 “准备工作” 时赋值的,可以说 this 就是 上下文环境 的一部分,而每一个不同的 上下文环境 可能会有不一样的 this值。

每次在寻找一个问题的解决方案或总结一个问题的时候,我总会去尝试将这个问题进行合适的分类,而从不同的方面去思考问题。

所以,这里我大胆的将 this 关键字的使用分为两种情况:

  1. 全局上下文的 this

  2. 函数上下文的 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. 函数调用 applycallbind

当一个函数在其主体中使用 this 关键字时,可以通过使用函数继承自Function.prototypecallapply 方法将 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'

applycall 不同,使用 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,通过 callapplybind 修改 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

箭头函数 foothis 被设置为创建时的上下文(在上面代码中,也就是全局对象)的 this 值,而且无法通过其他调用方式设定 foothis 值。

与普通函数对比,箭头函数的 this 值是在函数创建创建确定的,而且无法通过调用方式重新设置 this 值。普通函数中的 this 值是在调用的时候确定的,可通过不同的调用方式设定 this 值。

总结

this 关键字的值取决于其所处的位置(上下文环境):

  1. 在全局环境中,this 的值指向全局对象( window 或 global )。

  2. 在函数内部,this 的取值取决于其所在函数的调用方式,也就是说 this 的值是在函数被调用的时候确定的,在创建函数时无法确定。当然,箭头函数是个例外,箭头函数本身不存在 this,而在箭头函数中使用 this 获取到的便是创建其的上下文中的 this。同时,使用函数的继承方法 callapplybind 会修改 this 的指向。但值得注意的是,使用 bind 方法会使 this 的值永久的绑定到给定的对象,无法再通过调用 callapply 方法修改 this 的值,箭头函数调用 callapplybind 方法无法修改 this


参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "this"关键字JavaScript中表示当前上下文中的对象。在函数中,"this"指向调用该函数的对象,而在全局作用域中,"this"指向全局对象(通常是window对象)。使用"this"关键字可以访问对象的属性和方法。 ### 回答2: JavaScript中的`this`关键字是一个非常重要的概念,它代表着当前函数的执行上下文(execution context)。它表示了当前代码正在执行的情境,在不同的情境下,`this`所代表的对象是不同的。 在全局作用域下,`this`指向的是全局对象,在浏览器中为`window`对象,在Node.js中为`global`对象。如下所示: ```javascript console.log(this === window); // true,在浏览器中输出 true ``` 当在一个函数中使用`this`关键字时,`this`所代表的对象会根据函数的调用方式而变化。常见的函数调用方式有四种,分别是函数调用、方法调用、构造函数调用和apply/call/bind方法调用。 1. 函数调用时,`this`指向全局对象或undefined(严格模式下)。 ```javascript function test() { console.log(this); } test(); // 全局作用域下 this 指向 window,输出 window ``` 2. 方法调用时,`this`指向调用该方法的对象。 ```javascript var person = { name: 'Tom', sayHello: function() { console.log('Hello, ' + this.name); } } person.sayHello(); // 方法调用,此时的 this 指向 person 对象,输出 Hello, Tom ``` 3. 构造函数调用时,`this`指向新创建的对象。 ```javascript function Person(name) { this.name = name; } var tom = new Person('Tom'); // 构造函数调用,此时的 this 指向新创建的对象 tom console.log(tom.name); // 输出 Tom ``` 4. apply/call/bind方法调用时,`this`指向指定的对象。 ```javascript var person1 = { name: 'Tom' }; var person2 = { name: 'Jerry' }; function sayName() { console.log('My name is ' + this.name); } sayName(); // 输出 My name is undefined sayName.apply(person1); // apply方法调用,此时的 this 指向 person1 对象,输出 My name is Tom var sayName1 = sayName.bind(person2); sayName1(); // bind方法调用,此时的 this 指向 person2 对象,输出 My name is Jerry ``` 总之,`this`关键字的作用非常重要,它可以让我们更加灵活地编写代码,但同时也需要注意使用它的场景和调用方式。 ### 回答3: JavaScript中的this关键字是一个非常重要的概念,它用于访问当前对象的上下文。它代表执行当前方法的对象。当我们在JavaScript中编写代码时,this关键字的上下文可以是全局对象,当前对象、任意对象或函数的作用域。 当我们在全局范围内使用this时,它将指向浏览器窗口或全局对象。而在对象里,this关键字则代表该对象。例如,如果我们在一个名为person的对象中使用this,它将返回person对象: ``` const person = { name: 'Tom', age: 30, greet() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } }; person.greet(); // 输出'Hello, my name is Tom and I am 30 years old.' ``` 当我们在函数内部使用this时,它可能会与预期的结果不同。在函数内部,this关键字通常为undefined。这是因为在JavaScript中,函数是一等对象,可以作为参数传递,并且可以按照不同的方式调用。因此,this关键字的上下文取决于如何调用该函数。 一种解决这个问题的方法是使用箭头函数。箭头函数的this关键字始终指向定义该函数的作用域: ``` const person = { name: 'Tom', age: 30, greet() { const sayHello = () => { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } sayHello(); } }; person.greet(); // 输出'Hello, my name is Tom and I am 30 years old.' ``` 在这个例子中,箭头函数中的this关键字始终指向person对象。通过使用箭头函数,我们可以避免错误的this关键字上下文,并确保我们能够正确地访问对象的属性和方法。 总之,JavaScript中的this关键字非常重要,可以帮助我们访问当前对象的上下文。在函数内部使用时需要谨慎,可以考虑使用箭头函数来避免错误的上下文。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值