调用方式 | 示例 | 函数中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.83185307179586
20
and NaN
20
and 63
NaN
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 undefined
Person {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 21
function
function
Lydia is 21
Lydia is 21
Lydia is 21
function
使用这两种方法,我们都可以传递我们希望 this
关键字引用的对象。但是,.call
是立即执行的。
.bind
返回函数的副本,但带有绑定上下文!它不是立即执行的。
- function Car() {
- this.make = "Lamborghini";
- return { make: "Maserati" };
- }
-
- const myCar = new Car();
- console.log(myCar.make);
"Lamborghini"
"Maserati"
ReferenceError
TypeError
返回属性的时候,属性的值等于 返回的 值,而不是构造函数中设定的值。我们返回了字符串 "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"
, undefined
TypeError
, 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 Person
SyntaxError
我们可以将类设置为等于其他类/函数构造函数。 在这种情况下,我们将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);
0
1
2
3
答案: 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.com
new@email.com
undefined
ReferenceError
updateEmail
函数是一个箭头函数,它没有和 user
对象绑定。这就意味着 this
关键字不会引用到 user
对象,但是会引用到全局对象。 user
对象内部的 email
的值不会更新。当打印 user.email
的时候, 原始值 my@email.com
被返回。