🌞

再看 this

学完了整个 JavaScript 基础篇章之后,今天重新总结复习一下 this 的用法。

除了绕晕新手以外,this 还是有其存在的价值,如果没有 this,那么我们在写一个构造函数的时候,就不知道如何代指我们即将 new 出来的对象(因为事实上这个时候他并没有名字)。

1
2
3
4
const Person(name, age) {
  ??.name = name
  ??.age = age
}

但是实际上 this 却带来了很多副作用以及意想不到的用法,因此下面就来总结讨论一下 this 的值。


Key Points

  1. this 是为了解决 一个函数获取一个对象的引用 这个问题
  2. this 必须在函数体的内部才有意义,否则等于 window
  3. 可以对函数内的 this 进行 隐式指定显式指定
  4. 箭头函数 没有 this
  5. new 之后发生的 4 件事情

函数体外部的 this

this 必须在函数体的内部才有意义

如果 this 不在任何函数体内部的话,他的值是全局对象,当然在浏览器中就是 window,因此如果不在函数体内部的话,就无从讨论 this

1
2
3
4
5
6
this.a = 'Hi'
console.log(window.a) // 'Hi'
console.log(this.a) // 'Hi'
console.log(a) // 'Hi'
// window === this
// window.a === this.a === a

注意了!这里说的函数体内部要与函数的调用区分开来!最显然的是上面的 console.log(this.a) 的括号中的 this 并不是函数体内部的 this,所以他也是没有意义的。

函数体内部的 this

函数内部 this 的值取决于函数被调用的方式

我们有两种方式可以调用函数并指定 this 的值:隐式指定显式指定

举个栗子,我们有这样一个对象:

1
2
3
4
5
6
7
const harvey = {
  name: 'harvey',
  age: 22,
  greeting(){
    console.log(`Hello, I am ${this.name}`)
  }
}

然后想要调用其中的 greeting() 方法,我们可以 隐式指定 this 的值:

1
2
3
harvey.greeting()
// 'Hello, I am harvey'
// JS 自动指定 greeting 函数里面的 this 为 . 前面的 harvey 

我们也可以 显式指定 this 的值:

1
2
3
harvey.greeting.call(harvey)
// 'Hello, I am harvey'
// 通过 call 指定 greeting 中的 this 是 harvey

看起来好像没什么区别?不,区别大了。

让我们再定义一个对象,就能看出其中的端倪。

1
2
3
4
const kate = {
  name: 'kate',
  age: 22
}

我们现在可以这样调用 harveygreeting

1
2
3
harvey.greeting.call(kate)
// 'Hello, I am kate'
// 通过 call 指定 greeting 中的 this 是 kate

箭头函数的 this

箭头函数没有 this

如果我们把上面讨论的栗子中的函数改为箭头函数,那么他里面的 this 也就没有意义了,他的 this 具体的取值得向上一个函数找:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const harvey = {
  name: 'harvey',
  age: 22,
  greeting: () => {
    console.log(`Hello, I am ${this.name}`)
    console.log(this)
  },
  greeting2() {
    const greeting3 = () => {
      console.log(`Hello, I am ${this.name}`)
      console.log(this)
    }
    greeting3()
  }
}

harvey.greeting()
// Hello, I am 
// window
harvey.greeting2()
// Hello, I am harvey
// harvey

就算我们用 call 也无法指定 this

1
2
3
harvey.greeting.call(harvey)
// Hello, I am 
// window

new 运算符

在我们使用 new 去创建新对象的时候,系统自动帮我们做了 4 件事情

  • 自动创建空对象
  • 自动为空对象关联原型,原型的地址为 X.prototype
  • 自动将空对象作为 this 关键字运行构造函数
  • 自动 return this,也就是说可以接着写 new X().getName()

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    function Person(name, age){
    this.name = name
    this.age = age
    }
    Person.prototype.greeting = function () {
    console.log(`Hello, I am ${this.name}`)
    console.log(this)
    }
    
    const harvey = new Person('harvey', 22)
    // 这里自动将 harvey 作为 this 运行了构造函数
    
    
    harvey.greeting()
    

这里我自己尝试模拟了一下一个功能类似于 new 的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 自己实现一个类似 new 的函数
function X(a, b) {
    this.a = a + 1
    this.b = b + 1
}

const y = new X(1, 2)

function NEW(fun, arguments){
    let newObj = {}
    newObj.__proto__ = fun.prototype
    fun.apply(newObj, arguments)
    return newObj
}

const z = NEW(X, [3, 4])

好了,this 的讨论结束,理解的关键就在于我们最开始提到的 5 点 Key Points。


再看看网上总结的 this

留坑,以后再总结。

updatedupdated2020-02-052020-02-05