this指向

90 阅读4分钟

JavaScript this 指向规则详解

�� 核心规则总结

JavaScript中this的指向遵循以下5个核心规则,按优先级从高到低排列:

  1. new绑定 - 最高优先级
  2. 显式绑定 - call/apply/bind
  3. 隐式绑定 - 对象方法调用
  4. 默认绑定 - 独立函数调用
  5. 箭头函数 - 特殊规则,不受上述规则影响

1. new绑定(New Binding)

规则说明

使用new关键字调用构造函数时,this指向新创建的实例对象。

案例演示

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHello = function() {
    console.log(`我是${this.name},今年${this.age}岁`);
  };
}

const person1 = new Person('张三', 25);
const person2 = new Person('李四', 30);

person1.sayHello(); // "我是张三,今年25岁"
person2.sayHello(); // "我是李四,今年30岁"

console.log(person1.name); // "张三"
console.log(person2.name); // "李四"

特殊情况:构造函数返回对象

function Test(name) {
  this.name = name;
  this.method = function() {
    console.log('普通方法:', this.name);
  };
  
  return {
    name: "返回对象",
    method: () => {
      console.log('箭头方法:', this.name);
    }
  };
}

const t = new Test("构造参数");
console.log(t.name);        // "返回对象"
t.method();                 // "箭头方法: 构造参数"

解析:

  • 构造函数返回对象,所以t指向返回的对象
  • t.name是返回对象的name属性
  • 箭头函数继承外层作用域的this,即构造函数内的this,所以是"构造参数"

2. 显式绑定(Explicit Binding)

规则说明

使用callapplybind方法显式指定this的指向。

案例演示

function greet() {
  console.log(`你好,我是${this.name}`);
}

const person = { name: '王五' };
const student = { name: '赵六' };

// call方法
greet.call(person);  // "你好,我是王五"
greet.call(student); // "你好,我是赵六"

// apply方法
greet.apply(person);  // "你好,我是王五"
greet.apply(student); // "你好,我是赵六"

// bind方法
const boundGreet = greet.bind(person);
boundGreet(); // "你好,我是王五"

// 带参数的例子
function introduce(age, city) {
  console.log(`我是${this.name}${age}岁,来自${city}`);
}

introduce.call(person, 28, '北京');    // "我是王五,28岁,来自北京"
introduce.apply(student, [22, '上海']); // "我是赵六,22岁,来自上海"

3. 隐式绑定(Implicit Binding)

规则说明

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

案例演示

const user = {
  name: '小明',
  age: 25,
  profile: {
    hobby: '编程',
    showHobby: function() {
      console.log(`${this.name}的爱好是${this.hobby}`);
    }
  },
  sayHello: function() {
    console.log(`你好,我是${this.name}`);
  },
  getAge: function() {
    return this.age;
  }
};

user.sayHello(); // "你好,我是小明"
console.log(user.getAge()); // 25

// 嵌套对象中的this
user.profile.showHobby(); // "undefined的爱好是编程" (this指向profile对象)

隐式绑定丢失的情况

函数的赋值只是把函数本事赋值给新变量,不会保留原来调用的上下文。this指向在调用的时候根据调用的方式确定

const obj = {
  name: '对象',
  method: function() {
    console.log(this.name);
  }
};

// 正常调用
obj.method(); // "对象"

// 方法赋值给变量,丢失隐式绑定
const fn = obj.method;
fn(); // undefined (默认绑定)

// 作为回调函数,丢失隐式绑定
setTimeout(obj.method, 1000); // undefined

// 解决方案1:使用箭头函数包装
setTimeout(() => obj.method(), 1000); // "对象"

// 解决方案2:使用bind
setTimeout(obj.method.bind(obj), 1000); // "对象"

4. 默认绑定(Default Binding)

规则说明

独立函数调用时,this指向全局对象(浏览器中是window,Node.js中是global)。 独立调用 = 函数调用时没有明确的调用者(没有.操作符)

案例演示


// 非严格模式
function globalTest() {
  console.log(this); // window (浏览器) 或 global (Node.js)
  console.log(this === window); // true (浏览器中)
}

globalTest(); // 独立函数调用

// 严格模式
'use strict';
function strictTest() {
  console.log(this); // undefined
}

strictTest(); // undefined

// 混合模式
function mixedTest() {
  console.log(this); // window
}

function strictWrapper() {
  'use strict';
  mixedTest(); // window (mixedTest不是严格模式)
}

5. 箭头函数(Arrow Functions)

规则说明

箭头函数没有自己的this,它会继承外层作用域的this

案例演示

const obj = {
  name: '对象',
  
  // 普通方法
  regularMethod: function() {
    console.log('普通方法:', this.name); // "对象"
    
    // 内部普通函数
    function innerFunction() {
      console.log('内部普通函数:', this.name); // undefined (默认绑定)
    }
    innerFunction();
    
    // 内部箭头函数
    const innerArrow = () => {
      console.log('内部箭头函数:', this.name); // "对象" (继承外层this)
    };
    innerArrow();
  },
  
  // 箭头函数方法
  arrowMethod: () => {
    console.log('箭头函数方法:', this.name); // undefined (继承全局作用域)
  }
};

obj.regularMethod();
obj.arrowMethod();

// 事件处理中的this
const button = document.createElement('button');
button.textContent = '点击我';

// 普通函数
button.addEventListener('click', function() {
  console.log('普通函数:', this); // button元素
});

// 箭头函数
button.addEventListener('click', () => {
  console.log('箭头函数:', this); // window (继承外层作用域)
});

�� 综合案例分析

案例1:多层嵌套调用

const obj = {
  name: '外层对象',
  inner: {
    name: '内层对象',
    method: function() {
      console.log(this.name); // "内层对象"
      
      function nested() {
        console.log(this.name); // undefined (默认绑定)
      }
      nested();
    }
  }
};

obj.inner.method(); // 输出: "内层对象" 和 undefined

案例2:类中的this

class MyClass {
  constructor(name) {
    this.name = name;
  }
  
  // 普通方法
  regularMethod() {
    console.log('普通方法:', this.name);
  }
  
  // 箭头函数方法
  arrowMethod = () => {
    console.log('箭头方法:', this.name);
  }
  
  // 包含回调的方法
  methodWithCallback() {
    setTimeout(function() {
      console.log('回调普通函数:', this.name); // undefined
    }, 100);
    
    setTimeout(() => {
      console.log('回调箭头函数:', this.name); // 类实例的name
    }, 200);
  }
}

const instance = new MyClass('类实例');
instance.regularMethod(); // "普通方法: 类实例"
instance.arrowMethod();   // "箭头方法: 类实例"
instance.methodWithCallback(); // 输出: undefined 和 "回调箭头函数: 类实例"

�� 快速判断技巧

  1. 看调用方式

    • obj.method() → 隐式绑定
    • new Function() → new绑定
    • func.call(obj) → 显式绑定
    • func() → 默认绑定
  2. 看函数类型

    • 箭头函数 → 继承外层作用域
    • 普通函数 → 按上述规则判断
  3. 看上下文

    • 严格模式 → 默认绑定为undefined
    • 非严格模式 → 默认绑定为全局对象
  4. 特殊情况

    • 构造函数返回对象 → 覆盖this
    • 方法赋值给变量 → 丢失隐式绑定
    • 回调函数 → 通常丢失隐式绑定

📝 记忆口诀

"新显隐默箭" - 按优先级记忆:

  • :new绑定
  • :显式绑定
  • :隐式绑定
  • :默认绑定
  • :箭头函数(特殊规则)

掌握了这些规则和案例,你就能准确判断任何JavaScript代码中this的指向了!