JavaScript中的this指向问题

目录

  • JavaScript中的this关键字
  • 代码中this的常见使用
  • this的默认绑定
  • this的隐式绑定
  • this的显示绑定
  • this的new绑定
  • 箭头函数中的this
  • 实际应用中,常见的this指向问题
  • this使用时建议遵循以下几点:

JavaScript中的this关键字

在JavaScript中,关键字 this 是一个特殊的对象,它在函数被调用时自动创建。通常用来指向当前执行的函数所属的对象。this 的值在函数的每次调用时可能会发生变化,具体取决于函数是如何被调用的。

this 的值可以是以下几种情况之一:

  1. 全局上下文中的 this:在全局作用域中(即在任何函数之外)使用 this,它将指向全局对象(在浏览器环境中是 window 对象)。
  2. 函数调用中的 this:当函数被作为一个方法调用时,this 将指向调用该方法的对象。
  3. 构造函数中的 this:当函数作为构造函数使用 new 关键字创建一个新的实例时,this 将指向新创建的对象。
  4. 显式绑定中的 this:通过使用 call()apply()bind()方法,可以显式地指定一个函数的 this 值。
  5. 箭头函数中的 this:箭头函数没有自己的 this 绑定,它会继承父级作用域的 this 值。
    不同情况下 this 的示例代码:
// 全局上下文中的 this
console.log(this); // 输出全局对象(在浏览器环境中是 window 对象)
// 函数调用中的 this
const obj = {
  name: "Alice",
  say: function() {
    console.log("Hello, " + this.name);
  }
};
obj.say(); // 输出 "Hello, Alice"

// 构造函数中的 this
function Person(name) {
  this.name = name;
}
const person = new Person("Alice");
console.log(person.name); // 输出 "Alice"

// 显式绑定中的 this
function sayName() {
  console.log(this.name);
}
const person1 = { name: "Alice" };
const person2 = { name: "John" };
sayName.call(person1); // 输出 "Alice"
sayName.call(person2); // 输出 "John"

// 箭头函数中的 this
const obj = {
  name: "Alice",
  greet: function() {
    setTimeout(() => {
      console.log("Hello, " + this.name);
    }, 1000);
  }
};
obj.greet(); // 输出 "Hello, Alice"

代码中this的常见使用

在代码中,this是一个关键字,代表当前执行代码的对象。它的重要性在于它允许我们在对象内部引用对象自身的属性和方法。
常见的使用this的情况有以下几种:

  1. 在对象方法中使用this:当我们在对象中定义方法时,可以使用this来引用该对象的其他属性和方法。例如:
const person = {
  name: 'John',
  age: 30,
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}. I am ${this.age} years old.`);
  }
};

person.sayHello(); // 输出:Hello, my name is John. I am 30 years old.

在上面的代码中,this.namethis.age引用了person对象的属性。
2. 构造函数中使用this:当我们使用构造函数创建对象时,可以使用this来引用新创建的对象的属性和方法。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = new Person('John', 30);
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30

在上面的扫描中,构造函数Person中的this.namethis.age引用了新创建的对象的属性。
3. 使用callapplybind改变函数中的this指向:有时候我们需要在函数执行时改变函数内部的this指向,可以使用callapplybind方法来实现。

const person1 = {
  name: 'John',
  age: 30,
};

const person2 = {
  name: 'Jane',
  age: 25,
};

function sayHello() {
  console.log(`Hello, my name is ${this.name}. I am ${this.age} years old.`);
}

sayHello.call(person1); // 输出:Hello, my name is John. I am 30 years old.
sayHello.apply(person2); // 输出:Hello, my name is Jane. I am 25 years old.

const sayHelloPerson1 = sayHello.bind(person1);
sayHelloPerson1(); // 输出:Hello, my name is John. I am 30 years old.

在上面的代码中,通过callapplybind方法,我们可以将sayHello函数中的this指向person1person2对象。

this的默认绑定

this的默认绑定是指在没有明确指定this的情况下,函数中的this将会绑定到全局对象(在浏览器环境中,全局对象是window对象)。这种默认绑定可以在全局作用域和独立函数调用中发生。

  1. 在全局作用域中,this的默认绑定指向全局对象。
function sayHello() {
  console.log("Hello, " + this.name);
}

var name = "John";
sayHello(); // 输出:Hello, John

在上面的代码中,当调用sayHello函数时,没有指定this的值,因此this的默认绑定将会指向全局对象,即window对象。由于全局对象中有一个name属性被赋值为"John",所以输出结果为"Hello, John"。
2. 在独立函数调用中,this的默认绑定也指向全局对象。

function sayAge() {
  console.log("I am " + this.age + " years old.");
}

var age = 18;

var obj = {
  age: 25,
  sayAge: sayAge
};

var func = obj.sayAge;
func(); // 输出:I am 18 years old.

在上面的代码中,sayAge函数被赋值给了变量func,并且在独立函数调用时没有指定this的值。因此,this的默认绑定将会指向全局对象。由于全局对象中有一个age变量被赋值为18,所以输出结果为"I am 18 years old."。

所以,在全局作用域和独立函数调用中,如果没有明确指定this的值,this将会默认绑定到全局对象。

this的隐式绑定

this的隐式绑定是指在函数作为对象的方法调用时,this会隐式地绑定到该对象上。这种绑定方式可以让我们在方法内部引用对象自身的属性和方法。

当一个函数作为对象的方法调用时,this会被隐式地绑定到该对象上,使得函数内部可以通过this来访问该对象的属性和方法。

const person = {
  name: 'John',
  age: 18,
  sayHello: function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

person.sayHello(); // 输出:Hello, my name is John and I am 18 years old.

在上面的代码中,我们定义了一个名为person的对象,它有两个属性name和age,以及一个方法sayHello。当我们调用person对象的sayHello方法时,this关键字在方法内部被隐式地绑定到person对象上。因此,this.name引用了person对象的name属性,this.age引用了person对象的age属性。

需要注意的是,隐式绑定只会在函数调用时发生,而不是在函数定义时。这意味着如果将一个方法赋值给一个变量,并在变量上调用该方法,那么this将不再被绑定到原来的对象上,而是绑定到全局对象上。

var person = {
  name: "John",
  sayHello: function() {
    console.log("Hello, " + this.name);
  }
};

var greetFunc = person.sayHello;
greetFunc(); // 输出:Hello, undefined

在上面的代码中,将person.sayHello方法赋值给了变量greetFunc,并在greetFunc上调用该方法。由于函数调用时没有指定this的值,因此this的默认绑定将会指向全局对象。由于全局对象中没有name属性,所以输出结果为"Hello, undefined"。

此外,需要注意的是,在箭头函数中,this的绑定方式与普通函数不同。箭头函数的this绑定是词法作用域,即继承自上级作用域,并且不受调用方式的影响。因此,在箭头函数中无法使用隐式绑定。

this的显示绑定

显式绑定是指在函数调用时明确指定函数内部的this值。显式绑定可以通过以下三种方法实现:

  1. 使用call方法:call()方法允许我们调用一个函数,并且显式地设置this的值。它接受一个参数列表,第一个参数是要绑定给this的对象,后面是传递给函数的参数。

  2. 使用apply方法:apply()方法与call()方法类似,只是它接受的参数是一个数组或类数组对象。第一个参数仍然是this的值,第二个参数是一个数组,其中包含函数的参数。

  3. 使用bind方法:bind()方法创建一个新的函数,将指定的对象作为this的值,并返回这个新函数。与call()和apply()不同,bind()方法不会立即执行函数,而是返回一个绑定了this的新函数。

上面三种方法的区别

  1. call方法和apply方法都可以立即调用函数并指定this值,它们的区别仅在于参数的传递方式。call方法使用参数列表,而apply方法使用参数数组。
  2. bind方法与call和apply方法不同,它不会立即调用函数,而是返回一个新的函数,需要在之后手动调用。bind方法常用于创建一个函数的新实例,并将其this值绑定到指定的对象。
// 1:使用call方法显式绑定this
function hello() {
  console.log(`Hello, ${this.name}!`);
}

const person = {
  name: 'John'
};

hello.call(person); // 输出:Hello, John!

// 2:使用apply方法显式绑定this
function sum(a, b) {
  console.log(a + b);
}

const numbers = [1, 2];

sum.apply(null, numbers); // 输出:3

// 3:使用bind方法显式绑定this
const calculator = {
  value: 10,
  add: function(num) {
    console.log(this.value + num);
  }
};

const addFive = calculator.add.bind(calculator, 5);
addFive(); // 输出:15

上面的代码展示了使用call、apply和bind方法显式绑定this的不同方式。第一个示例中,使用call方法将hello函数的this值绑定到person对象上。第二个示例中,使用apply方法将sum函数的this值绑定为null,并通过参数数组传递参数。第三个示例中,使用bind方法创建了一个新的函数addFive,它的this值永久地绑定到calculator对象上,并通过第二个参数传递了5。

this的new绑定

当使用new关键字创建对象时,会发生一种特殊的绑定,称为new绑定。这种绑定方式与显式绑定不同,它是根据构造函数创建新实例时自动发生的。

new绑定的过程如下:

  1. 创建一个新的空对象。
  2. 将这个新对象的原型指向构造函数的prototype属性。
  3. 将构造函数中的this绑定到新对象上,使构造函数内部的this引用这个新对象。
  4. 如果构造函数没有显式返回一个对象,则返回这个新对象。

通过这个过程,我们可以看到,当使用new关键字调用构造函数时,JavaScript会自动将构造函数中的this绑定到新创建的实例上。这使得我们可以在构造函数内部使用this来操作和修改新实例的属性和方法。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

var person1 = new Person("John", 18);
console.log(person1.name); // 输出 "John"
console.log(person1.age); // 输出 18

在上面的代码中,我们定义了一个Person构造函数,它接受两个参数name和age,并将它们赋值给新创建的实例的属性。当使用new关键字创建一个Person对象时,构造函数内部的this会自动绑定到新实例上,因此我们可以通过this来访问和设置新实例的属性。最后,我们可以通过访问person1对象的属性来验证new绑定的效果。

箭头函数中的this

  1. 箭头函数中的this是如何工作的:
    在箭头函数中,this的值是在函数定义时确定的,而不是在函数调用时确定的。箭头函数会捕获其所在上下文中的this值,并在函数体内部使用。换句话说,箭头函数的this是词法作用域上下文中的this,而不是动态绑定的。

  2. 箭头函数没有自己的this绑定,而是继承父级作用域的this:
    正常的函数在被调用时,this的值是由调用方式决定的,可以通过call、apply或bind方法来显式绑定this的值。但是箭头函数不同,它没有自己的this绑定,会自动继承父级作用域中的this值。这意味着箭头函数中的this与其所在的父级作用域中的this是一样的。

// 1:箭头函数中的this继承父级作用域的this
const obj = {
  name: 'John',
  sayHello: function() {
    setTimeout(() => {
      console.log(`Hello, ${this.name}!`);
    }, 1000);
  }
};

obj.sayHello(); // 输出:Hello, John!

// 2:使用箭头函数作为回调函数
const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log(this); // 输出:Window对象
});

在上面的第一段代码中,箭头函数作为setTimeout的回调函数,它继承了父级作用域中的this(即obj对象),所以在箭头函数中可以访问到this.name。

在第二段代码中,箭头函数作为addEventListener的回调函数,由于箭头函数没有自己的this绑定,它会继承父级作用域中的this(即全局作用域),所以在箭头函数中输出的this是Window对象。

需要注意的是,由于箭头函数没有自己的this绑定,所以在箭头函数中使用call、apply或bind方法来改变this的值是无效的,this仍然会继承父级作用域中的this。

实际应用中,常见的this指向问题

  1. 在嵌套函数中丢失this:当在一个函数内部定义另一个函数,并在内部函数中使用this时,this的指向会发生变化。可以使用箭头函数或通过在外部函数中将this赋值给一个变量来解决这一问题。

  2. 事件处理函数中的this:在事件处理函数中,this通常指向触发事件的元素。但是,如果事件处理函数是通过addEventListener()方法添加的,this将指向监听器函数所在的对象(通常是触发事件的元素)。可以使用箭头函数、bind()方法,或通过在外部函数中将this赋值给一个变量来解决这一问题。

  3. 回调函数中的this:当将一个函数作为参数传递给另一个函数,并在内部函数中使用this时,this的指向可能会变化。可以使用箭头函数、bind()方法,或通过在外部函数中将this赋值给一个变量来解决这一问题。

  4. 对象方法中的this:在对象方法中,this通常指向调用该方法的对象。但是,如果将该方法赋值给一个变量,并通过变量来调用方法,this将指向全局对象。可以使用bind()方法或箭头函数来解决这一问题。

this使用时建议遵循以下几点:

  1. 确定函数调用的方式,了解this的默认绑定规则。

  2. 使用显示绑定(call()、apply()、bind())或箭头函数来明确指定函数执行时的this值。

  3. 在嵌套函数中,注意this的指向可能会发生变化,可以通过将this赋值给一个变量来解决。

  4. 在事件处理函数、回调函数或对象方法中,使用箭头函数、bind()方法或将this赋值给一个变量来确保this指向正确。

你可能感兴趣的:(JavaScript,javascript,前端,开发语言,vue,echarts)