| 调用方式 | 示例 | 函数中this的指向 |
| 通过new调用 | new method() | 新对象 |
| 直接调用 | method() | 全局对象 |
| 通过对象调用 | obj.method() | 前面的对象 |
| call、apply、bind | method.call(ctx) | 第一个参数 |
我们说的this指向是一个函数里边的this指向,如果这个this不在函数里边,那this指向取决于环境,如果是浏览器环境,那指向window,如果是node环境,指向空对象。
函数中的this指向谁,取决于如何去调用这个函数
创建执行上下文的时候就确定了这一次函数调用的this指向谁,执行上下文什么时候创建的,是执行的时候创建的,执行就是调用,所以this的指向就是函数调用的时候确定的
- const shape = {
- radius: 10,
- diameter() {
- return this.radius * 2
- },
- perimeter: () => 2 * Math.PI * this.radius
- }
-
- shape.diameter()
- shape.perimeter()
20 and 62.8318530717958620 and NaN20 and 63NaN and 63注意 diameter 的值是一个常规函数,但是 perimeter 的值是一个箭头函数。
对于箭头函数,this 关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用 perimeter 时,this 不是指向 shape 对象,而是它的周围作用域(在例子中是 window)。
在 window 中没有 radius 这个属性,因此返回 undefined。
- function Person(firstName, lastName) {
- this.firstName = firstName
- this.lastName = lastName
- }
-
- const lydia = new Person('Lydia', 'Hallie')
- const sarah = Person('Sarah', 'Smith')
-
- console.log(lydia)
- console.log(sarah)
Person {firstName: "Lydia", lastName: "Hallie"} and undefinedPerson {firstName: "Lydia", lastName: "Hallie"} and Person {firstName: "Sarah", lastName: "Smith"}Person {firstName: "Lydia", lastName: "Hallie"} and {}Person {firstName: "Lydia", lastName: "Hallie"} and ReferenceError对于 sarah,我们没有使用 new 关键字。当使用 new 时,this 引用我们创建的空对象。当未使用 new 时,this 引用的是全局对象(global object)。
我们说 this.firstName 等于 "Sarah",并且 this.lastName 等于 "Smith"。实际上我们做的是,定义了 global.firstName = 'Sarah' 和 global.lastName = 'Smith'。而 sarah 本身是 undefined。
- const person = { name: 'Lydia' }
-
- function sayHi(age) {
- console.log(`${this.name} is ${age}`)
- }
-
- sayHi.call(person, 21)
- sayHi.bind(person, 21)
undefined is 21 Lydia is 21function functionLydia is 21 Lydia is 21Lydia is 21 function使用这两种方法,我们都可以传递我们希望 this 关键字引用的对象。但是,.call 是立即执行的。
.bind 返回函数的副本,但带有绑定上下文!它不是立即执行的。
- function Car() {
- this.make = "Lamborghini";
- return { make: "Maserati" };
- }
-
- const myCar = new Car();
- console.log(myCar.make);
"Lamborghini""Maserati"ReferenceErrorTypeError返回属性的时候,属性的值等于 返回的 值,而不是构造函数中设定的值。我们返回了字符串 "Maserati",所以 myCar.make等于"Maserati".
- class Dog {
- constructor(name) {
- this.name = name;
- }
- }
-
- Dog.prototype.bark = function() {
- console.log(`Woof I am ${this.name}`);
- };
-
- const pet = new Dog("Mara");
-
- pet.bark();
-
- delete Dog.prototype.bark;
-
- pet.bark();
"Woof I am Mara", TypeError"Woof I am Mara","Woof I am Mara""Woof I am Mara", undefinedTypeError, TypeError我们可以用delete关键字删除对象的属性,对原型也是适用的。删除了原型的属性后,该属性在原型链上就不可用了。在本例中,函数bark在执行了delete Dog.prototype.bark后不可用, 然而后面的代码还在调用它。
当我们尝试调用一个不存在的函数时TypeError异常会被抛出。在本例中就是 TypeError: pet.bark is not a function,因为pet.bark是undefined.
- var status = "😎"
-
- setTimeout(() => {
- const status = "😍"
-
- const data = {
- status: "🥑",
- getStatus() {
- return this.status
- }
- }
-
- console.log(data.getStatus())
- console.log(data.getStatus.call(this))
- }, 0)
"🥑" and "😍""🥑" and "😎""😍" and "😎""😎" and "😎"this关键字的指向取决于使用它的位置。 在函数中,比如getStatus,this指向的是调用它的对象,上述例子中data对象调用了getStatus,因此this指向的就是data对象。 当我们打印this.status时,data对象的status属性被打印,即"🥑"。
使用call方法,可以更改this指向的对象。data.getStatus.call(this)是将this的指向由data对象更改为全局对象。在全局对象上,有一个名为status的变量,其值为”😎“。 因此打印this.status时,会打印“😎”。
- class Person {
- constructor() {
- this.name = "Lydia"
- }
- }
-
- Person = class AnotherPerson {
- constructor() {
- this.name = "Sarah"
- }
- }
-
- const member = new Person()
- console.log(member.name)
"Lydia""Sarah"Error: cannot redeclare PersonSyntaxError我们可以将类设置为等于其他类/函数构造函数。 在这种情况下,我们将Person设置为AnotherPerson。 这个构造函数的名字是Sarah,所以新的Person实例member上的name属性是Sarah。
- class Counter {
- constructor() {
- this.count = 0;
- }
-
- increment() {
- this.count++;
- }
- }
-
- const counterOne = new Counter();
- counterOne.increment();
- counterOne.increment();
-
- const counterTwo = counterOne;
- counterTwo.increment();
-
- console.log(counterOne.count);
0123答案: D
counterOne 是类 Counter 的一个实例。类 Counter 包含一个count 属性在它的构造函数里, 和一个 increment 方法。首先,我们通过 counterOne.increment() 调用方法 increment 两次。现在, counterOne.count 为 2.
然后,我们创建一个新的变量 counterTwo 并将 counterOne 的引用地址赋值给它。因为对象受引用地址的影响,我们刚刚创建了一个新的对象,其引用地址和 counterOne 的等价。因此它们指向同一块内存地址,任何对其的副作用都会影响 counterTwo。现在 counterTwo.count 为 2。
我们调用 counterTwo.increment() 将 count 的值设为 3。然后,我们打印 counterOne 里的count,结果为 3。
- const user = {
- email: "my@email.com",
- updateEmail: email => {
- this.email = email
- }
- }
-
- user.updateEmail("new@email.com")
- console.log(user.email)
my@email.comnew@email.comundefinedReferenceErrorupdateEmail 函数是一个箭头函数,它没有和 user 对象绑定。这就意味着 this 关键字不会引用到 user 对象,但是会引用到全局对象。 user 对象内部的 email 的值不会更新。当打印 user.email 的时候, 原始值 my@email.com 被返回。