JavaScript 中常见的 this 的用法你真的了解清楚了吗?

158 阅读4分钟

引言

this作为最常见并且在面试者常考的关键字之一他到底在代码中起到什么作用呢?适用范围是怎么样的?并且有哪几种绑定规则呢?在使用中需要注意什么?接下来让我们来一一了解

this的作用和常见使用范围

作用:

this 提供了一种更优雅的方式来隐式的传递一个对象引用,可以让代码更加简洁易于复用

使用范围:
  1. 函数作用域
  2. 全局作用域(this 指向 window)

(注:this 是一个代词,代指一个对象)

绑定规则

  • 默认绑定 --- 当函数被独立调用时,函数的this指向window (注意:node 环境下没有 window,只会输出 undefined,而浏览器环境下就有 window 会打印它所指对象在 window 中的值,并且 this 指向谁不代表他是谁的)

假设存在一个函数 foo(){}和函数 bar(){foo()}

foo()  // 独立调用
bar.foo()  // 非独立调用

image.png

输出:undefined

image.png

注意:这里的 obj.foo 是属于引用对象 obj 中的 foo 这个元素,然后这个元素引发函数 foo 的执行,所以这里实际时通过对 obj 的引用实现 foo 函数的调用,并且此调用为单独调用,所以返回:undefined。

  • 隐式绑定 --- 当函数引用上下文对象 且被该对象调用时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象(函数 foo 在对象 obj 中,并且被 obj 调用了)

image.png

输出:2

  • 隐式丢失(隐式绑定的一种特殊情况) --- 当一个函数被多层对象调用时,函数的this 指向最近的那一层对象

image.png

  • 显示绑定
    1. fn.call(obj, x, x, ...)显示的将 fn 里面的 this 绑定到 obj 上, call负责帮 fn 接收参数

image.png

这里单独调用,指向的是 window

image.png

foo 里的 this 指向 obj

function foo(x, y) {
    console.log(this.a, x + y);
    
}

var obj = {
    a: 1
};

foo()
foo.call(obj,1,2)

image.png

注意:foo.call() 里面第一个一定要是对象,如果是其他字符的话就默认为 null ,也就是undefined。

  1. fn.apply(obj, [x, x,...])显示的将 fn 里面的 this 绑定到 obj 上, apply负责帮 fn 接收参数,参数必须以数据组盛放

fn.apply(obj, [x, x,...]) 的只有一个区别,那就是后面接收的参数必须用中括号来装。

  1. fn.bind(obj, x, x,...)显示的将 fn 里面的 this 绑定到 obj 上,bind会返回一个i新的函数, bind 和新函数都可以负责帮 fn 接收参数,参数的零散的传入

foo.bind() 不会直接帮你执行 foo 函数,他会返回一个新的函数:

image.png

必须要有一个变量(这里为 bar)来承接执行结果。

这里 bind()里面第一个是对象,后面接受参数 也可以用 bar() 来接收参数

image.png

  • new 绑定 -- new 的原理会导致函数的 this 指向实例对象

image.png

this 指向实例对象 p

这是 new 一个对象自发做的操作

  1. 创建一个对象 obj
  2. 让函数指到 obj 上
  3. 指向函数体
  4. 让obj.proto = Person()
  5. 返回 obj

image.png

image.png

打印结果

image.png

注意

在我们不确定 this 指向谁的时候,我们通常可以用console.log(this)来进行判断。(在非箭头函数的情况下, this 的指向符合:“谁调用,指向谁”)

  • this 不是在函数定义时绑定,而是在函数被调用时动态确定:
const obj = {
  value: "A",
  getValue() { return this.value }
};

const getValue = obj.getValue;
console.log(obj.getValue()); // "A" (隐式绑定)
console.log(getValue());     // undefined (默认绑定,this=全局对象)
  • 箭头函数没有自己的 this,它继承定义时的外层作用域:
const obj = {
  value: "A",
  getValue: () => this.value, // this 指向外层(通常是全局)
};
console.log(obj.getValue()); // undefined (非预期结果!)
  • 回调函数(如 setTimeout、事件监听)会丢失原始绑定:
const button = {
  text: "Click me",
  click() {
    setTimeout(function() {
      console.log(this.text); // undefined (this 指向全局)
    }, 100);
  }
};
button.click();

解决方案:

// 方法1:使用箭头函数
setTimeout(() => console.log(this.text), 100);

// 方法2:使用 bind
setTimeout(function() {
  console.log(this.text);
}.bind(this), 100);

// 方法3:保存 this 引用
const self = this;
setTimeout(function() {
  console.log(self.text);
}, 100);
  • 严格模式下,默认绑定的 this 为 undefined
"use strict";
function test() {
  console.log(this); // undefined
}
test();
  • 类方法中的 this 默认绑定到实例
  • 但单独提取方法会丢失绑定:
class Counter {
  count = 0;
  increment() { this.count++ }
}

const c = new Counter();
const increment = c.increment;
increment(); // TypeError: Cannot read property 'count' of undefined

解决方案:

class Counter {
  constructor() {
    this.increment = this.increment.bind(this);
    // 或使用箭头函数:increment = () => { this.count++ }
  }
}
  • 在 DOM 事件处理函数中,this 默认指向触发事件的元素
button.addEventListener('click', function() {
  console.log(this); // <button> 元素
});

需注意的是:当使用箭头函数,this 会继承外层值(通常不是元素)

button.addEventListener('click', () => {
  console.log(this); // 可能是 window 或 undefined
});
  • 函数内部的嵌套函数(非箭头函数)会丢失外部函数的 this
const obj = {
  value: "A",
  outer() {
    function inner() {
      console.log(this.value); // undefined (this=全局)
    }
    inner();
  }
};
obj.outer();
  • 当设计接收回调的 API 时,允许调用方指定 this
// 好的 API 设计示例 (类似 Array.prototype.map)
function customMap(callback, thisArg) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    // 使用 call 绑定 this
    result.push(callback.call(thisArg, this[i], i, this));
  }
  return result;
}