[this的指向] js中this的指向

92 阅读4分钟

this是什么

this 是一个关键字 用于指向函数在“运行时 (runtime)”所在的“执行上下文 (execution context)“

简单来说,this 就是“调用我的人”或“我所属的对象”。

this指向的四大法则(优先级从高到低)

法则一:new 绑定 (优先级最高)

当一个函数被用作构造函数,通过 new 关键字调用时,this 会被绑定到新创建的那个对象实例上。

function Person(name) {
  // 在 new 调用时,JS 引擎会先创建一个空对象 {}
  // 然后把 this 指向这个空对象
  this.name = name;
  console.log(this); // this -> 新创建的 Person 实例
}

const alice = new Person('Alice'); // { name: 'Alice' }
console.log(alice.name); // 输出: Alice
  • 场景:这是 this 指向最明确、最没有争议的一种情况。
法则二:显式绑定 (call, apply, bind)

当一个函数通过 call(), apply(), 或 bind() 方法调用时,this 会被强制绑定到这些方法指定的第一个参数上。

这就像是在说:“嘿,函数,不管你本来是谁的,现在我命令你,你的 this 必须是‘我’!”

function sayHello() {
  console.log("Hello, " + this.name);
}

const personA = { name: 'Alice' };
const personB = { name: 'Bob' };

// 使用 call,立即执行函数,并将 this 绑定到 personA
sayHello.call(personA); // 输出: Hello, Alice

// 使用 apply,与 call 类似,只是参数以数组形式传递
sayHello.apply(personB); // 输出: Hello, Bob

// 使用 bind,不会立即执行,而是返回一个 this 被永久绑定的新函数
const sayHelloToAlice = sayHello.bind(personA);
sayHelloToAlice(); // 输出: Hello, Alice
  • 场景:当你需要在一个特定的上下文中执行一个函数时,这种方式非常有用。
法-则三:隐式绑定 (上下文对象调用)

当一个函数作为某个对象的方法被调用时(即通过 object.method() 的形式),this 会被绑定到那个直接调用它的对象上。

这就像是在说:“谁在点我,我 (this) 就是谁。”

const person = {
  name: 'Alice',
  sayHello: function() {
    console.log("Hello, " + this.name); // this -> person 对象
  }
};

person.sayHello(); // 输出: Hello, Alice
  • 场景:这是面向对象编程中最常见的 this 指向。

  • 隐式绑定的“陷阱”——丢失 this
    如果将对象的方法赋值给一个变量,然后再调用这个变量,this 的指向就会改变!

    codeJavaScript

    const person = {
      name: 'Alice',
      sayHello: function() {
        console.log("Hello, " + this.name);
      }
    };
    
    const greet = person.sayHello; // 只是把函数本身赋给了 greet
    
    greet(); // 输出: Hello, undefined (在非严格模式下) 或 TypeError (在严格模式下)
    
    • 为什么?  因为 greet() 在被调用时,它不再是通过 person. 来调用的,它是一个独立的函数调用。此时,它会应用下面的法则四
法则四:默认绑定 (优先级最低)

当一个函数不满足以上任何一种绑定规则,被直接、独立地调用时(如 myFunction()),this 就会被绑定到全局对象上。

  • 浏览器非严格模式下,全局对象是 window。
  • 严格模式 ("use strict")  下,this 会是 undefined。
// 非严格模式
function sayName() {
  console.log(this.name);
}

var name = "Global Name"; // 在浏览器中,var 声明的全局变量会挂载到 window 上

sayName(); // 输出: Global Name (因为 this 指向了 window)

// 严格模式
"use strict";
function sayNameStrict() {
  console.log(this); // 输出: undefined
  console.log(this.name); // TypeError: Cannot read properties of undefined
}

sayNameStrict();

特例 箭头函数

箭头函数是 ES6 引入的,它完全不遵循上述四条黄金法则。

箭头函数的 this 指向规则只有一个:

箭头函数没有自己的 this 绑定。它会捕获其“出生地”(定义时)所在的、最近一层非箭头函数的 this 值,并将其永久地作为自己的 this

const obj = {
  name: 'RenRan',
  normalFn: function() {
    const p = ()=>{
      console.log(this.name);
    }
    p()
  },
  arrowFn: () => {
    console.log('arrowFn:', this.name)
  }
}

obj.normalFn() // ✅ normalFn: RenRan
obj.arrowFn()  // ❌ arrowFn: undefined(或空)

分析:p是箭头函数,没有的自己的this,找自己上层的第一个非箭头函数的this,即匿名函数function,它的this指向obj

arrowFn箭头函数的this为啥指向window,关键 对象字面量不是作用域(关键!)

在 JavaScript 里:

const obj = {
  name: 'RenRan',
  arrowFn: () => {
    console.log(this)
  }
}

这里的 { ... }对象字面量(object literal)
它只是一个值(object) ,不是作用域(scope)

箭头函数和普通函数的区别

一、this的区别

普通函数的this是在函数被调用的时候决定的(遵循四大法则,new绑定,显示绑定,隐式绑定,默认绑定)

箭头函数没有自己的this,它会捕获其“出生地”(定义时)所在的、上面最近一层非箭头函数的 this 值,并将其永久地作为自己的 this

二、 构造函数 (new)的区别

普通函数可以作为构造函数,箭头函数不能作为构造函数

三、箭头函数没有 arguments

argumentsJavaScript 函数内部自动创建的一个类数组对象,它包含了调用函数时传入的所有参数

剩余参数允许你把函数中的 不确定数量的参数收集到一个数组中

四、 核心区别四:没有 prototype 属性

  • 普通函数

    • 每个普通函数(除了内置函数)在创建时,都会自动获得一个 prototype 属性,它是一个对象。
    • 这个 prototype 对象是实现 JavaScript 原型继承的核心。
  • 箭头函数

    • 没有 prototype 属性
    • 这与它不能作为构造函数是一脉相承的。既然不能创建实例,自然也就不需要原型对象来让实例继承。