深入理解 JavaScript 中的 this 指向

112 阅读3分钟

this指向基本原则

全局作用域

在全局作用域中(非严格模式下),this 指向全局对象——在浏览器中为 window。

在严格模式下,全局作用域中的 this 是 undefined。

console.log(this); // 在浏览器中,输出 `window`

对象方法中的this

当一个函数作为对象的方法调用时,this 指向调用该方法的对象。

const obj = {
   name: 'Alice',
   getName: function() {
     console.log(this.name);
   }
 };

 obj.getName(); // `this` 指向 `obj`,输出 'Alice'

构造函数中的this

函数作为构造函数使用时(通过 new 操作符),this 指向新创建的实例对象

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

const person = new Person('Bob');
console.log(person.name); // `this` 指向新创建的 `person` 对象,输出 'Bob'

普通函数中的this

在非严格模式下,普通函数中的 this 默认指向全局对象

在严格模式下,this 为 undefined

function show() {
  console.log(this);
}

show(); // 在非严格模式下,输出 `window`

箭头函数中的this

箭头函数不绑定自己的 this,它会继承定义时外层作用域中的 this

无论箭头函数如何被调用,this 的指向都不会改变

const obj = {
  name: 'Charlie',
  greet: function() {
    const arrowFunc = () => {
      console.log(this.name);
    };
    arrowFunc();
  }
};

obj.greet(); // `this` 继承自 `greet` 方法,指向 `obj`,输出 'Charlie'

常见的 this 指向误区与困惑

普通函数中的this指向全局对象

const person = {
  name: 'Alice',
  getName: function() {
    function innerFunction() {
      console.log(this.name);
    }
    innerFunction(); // `this` 指向全局对象,输出 `undefined`
  }
};

person.getName();

解决方案:可以使用 bind、call、apply 方法显式绑定 this,或者使用箭头函数。

const person = {
  name: 'Alice',
  getName: function() {
    const innerFunction = () => {
      console.log(this.name);
    };
    innerFunction(); // `this` 指向 `person` 对象,输出 'Alice'
  }
};

person.getName();

箭头函数的this与bind

箭头函数的 this 是在定义时绑定的,不能通过 bind、call 或 apply 方法改变其 this 指向

const obj = {
  count: 1,
  increment: function() {
    return () => {
      this.count++;
      return this.count;
    };
  }
};

const counter = obj.increment();
console.log(counter()); // 输出 2

const newCounter = counter.bind({ count: 10 });
console.log(newCounter()); // 输出 3,因为 `this` 仍然指向 `obj`

回调函数中的this

const button = {
  text: 'Click me',
  click: function() {
    setTimeout(function() {
      console.log(this.text);
    }, 1000);
  }
};

button.click(); // `this` 指向全局对象,输出 `undefined`

解决方案:可以使用箭头函数来继承外层作用域的 this,确保 this 指向正确

const button = {
  text: 'Click me',
  click: function() {
    setTimeout(() => {
      console.log(this.text);
    }, 1000);
  }
};

button.click(); // `this` 指向 `button` 对象,输出 'Click me'

练一练

试题1:

const obj = {
  count: 1,
  increment: function() {
    return () => {
      this.count++;
      return this.count;
    };
  }
};

const counter = obj.increment();
console.log(counter());

const newCounter = counter.bind({ count: 10 });
console.log(newCounter());

试题2:

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person1 = { name: 'Alice' };
const person2 = { name: 'Bob' };

greet.call(person1, 'Hello', '!');
greet.apply(person2, ['Hi', '?']);

const greetPerson1 = greet.bind(person1, 'Welcome');
greetPerson1('!!!');

试题3:

const calculator = {
  value: 0,
  add: function(a, b) {
    this.value = a + b;
    return this.value;
  },
  calculate: function(fn) {
    fn();
    console.log(this.value);
  }
};

const newCalc = {
  value: 100
};

calculator.calculate(function() {
  calculator.add(5, 10);
});

newCalc.calculate = calculator.calculate;
newCalc.calculate(function() {
  calculator.add(15, 20);
});

可以评论写出你的答案以及理解,查缺补漏,更进一步

总结与实践

理解 this 的指向需要对 JavaScript 的执行上下文和调用方式有深刻的理解。通过上面的分析和示例,希望你能更好地掌握 this 的行为,并在实际开发中有效避免常见的陷阱。

始终记住:this 的指向由函数的调用方式决定,而不是函数的定义位置。

善用箭头函数:箭头函数可以帮助你避免 this 指向的问题,但也要注意它不能被重新绑定。

使用工具函数:bind、call 和 apply 是改变 this 指向的有效工具,使用时要明确 this 指向的目标。


编程的世界总是充满未知,期待与你在技术的海洋中再相遇
I just hope my code makes more sense than my life.