JS中this值的问题

this 值

this 的值取决于它出现的上下文:函数、类、全局

一、函数上下文

在函数内部,this 的值取决于函数如何被调用。可以将 this 看作是函数的一个隐藏参数(就像函数定义中声明的参数一样),this 是语言在函数体被执行时为你创建的绑定。

1.普通函数

对于典型的函数,this 的值是函数被访问的对象。换句话说,如果函数调用的形式是 obj.f(),那么 this 就指向 obj。

function getThis() {
  return this;
}

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

obj1.getThis = getThis;
obj2.getThis = getThis;

console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }

在这里,getThis() 函数被调用两次,每次调用时,this 的值都是调用它的对象。

不过我们不能认为是谁拥有这个getThis()函数,而应该是谁调用了这个函数,this 的值是调用者。

const obj3 = {
  __proto__: obj1,
  name: "obj3",
};

console.log(obj3.getThis()); // { name: 'obj3' }

向上面的代码添加这样一段,我们发现虽然obj1拥有getThis()函数,但是obj3调用了getThis()函数,所以this的值是obj3。
因为this的值是调用者,而不是拥有者。

2.回调函数

当一个函数作为回调函数传递时,this 的值取决于如何调用回调,这由 API 的实现者决定。回调函数通常以 undefined 作为 this 的值被调用(直接调用,而不附加到任何对象上),这意味着如果函数是在非严格模式,this 的值会是全局对象(globalThis)。这在迭代数组方法、Promise() 构造函数等例子中都是适用的。

function logThis() {
  "use strict";
  console.log(this);
}

[1, 2, 3].forEach(logThis); // undefined、undefined、undefined

3.箭头函数

箭头函数中的this

箭头函数没有自己的this绑定。当箭头函数内的this关键字被解析时,它实际上引用的是最近一层非箭头函数的作用域中的this值。这种行为称为词法作用域的this。

这意味着:

箭头函数不会创建自己的this上下文,而是继承了外部函数的this值。
如果箭头函数是在全局作用域中定义的,那么它的this将指向全局对象(在浏览器中是window,在Node.js环境中是global)。

在类的方法中定义的箭头函数会捕获类实例的this,这使得它们非常适合用作事件处理器或回调函数,因为不需要手动绑定this。

const obj = {
    name: 'Alice',
    traditionalMethod: function() {
        console.log(this.name); // 输出 "Alice"
    },
    arrowMethod: () => {
        console.log(this.name); // 输出 undefined (如果在浏览器中运行) 或 global.name (在Node.js中)
    }
};

obj.traditionalMethod(); // 调用传统方法
obj.arrowMethod();       // 调用箭头函数方法

js中作用域分为全局作用域、函数作用域和块作用域。

obj对象本身并不是一个作用域。对象是一个数据结构,它可以包含属性和方法,但它不提供一个新的作用域。只有函数才能创建新的作用域。

arrowMethod 是一个箭头函数。

关键点在于 arrowMethod 的定义位置。虽然 arrowMethod 是 obj 的一个属性,但它的定义是在全局作用域中进行的。

因此,arrowMethod 捕获的是全局作用域中的 this,而不是 obj 的 this

如果你想让 arrowMethod 的 this 指向 obj,可以在 obj 的一个方法内部定义箭头函数,这样箭头函数会捕获该方法的 this:

const obj = {
    name: 'Alice',
    traditionalMethod: function() {
        console.log(this.name); // 输出 "Alice"
    },
    arrowMethod: function() {
        const innerArrow = () => {
            console.log(this.name); // 输出 "Alice"
        };
        return innerArrow;
    }
};

obj.traditionalMethod(); // 调用传统方法
obj.arrowMethod()();     // 调用箭头函数方法

在箭头函数外部加了一个普通函数,产生了一个作用域,使得箭头函数的 this 指向了这个普通函数中的 this。

总结

1、对象本身不创建新的作用域。

2、箭头函数的 this 绑定是在定义时确定的,而不是在调用时确定的

4.构造函数

当一个函数被用作构造函数(使用 new 关键字)时,无论构造函数是在哪个对象上被访问的,其 this 都会被绑定到正在构造的新对象上。除非构造函数返回另一个非原始值,不然 this 的值会成为 new 表达式的值。

function C() {
  this.a = 37;
}

let o = new C();
console.log(o.a); // 37

function C2() {
  this.a = 37;
  return { a: 38 };
}

o = new C2();
console.log(o.a); // 38

在第二个例子(C2)中,因为在构造过程中返回了一个对象,this 被绑定的新对象被丢弃。(这基本上使得语句 this.a = 37; 成为了死代码。它并不完全是死代码,因为它被执行了,但是它可以被消除而不产生任何外部效果。)

bind() 方法

调用 f.bind(someObject) 会创建一个新函数,这个新函数具有与 f 相同的函数体和作用域,但 this 的值永久绑定到 bind 的第一个参数,无论函数如何被调用。

也就是说,会创建一个和f一模一样的函数体和作用域,只有this被更改为了我们指定的值。

function f() {
  return this.a;
}

const g = f.bind({ a: "azerty" });
console.log(g()); // azerty

const h = g.bind({ a: "yoo" }); // bind 只能生效一次!
console.log(h()); // azerty

const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 azerty azerty

call()和apply()

使用 call() 和 apply(),你可以对 this 进行传值,就像它是一个显式参数。

function add(c, d) {
  return this.a + this.b + c + d;
}

const o = { a: 1, b: 3 };

// 第一个参数被绑定到隐式的 'this' 参数;
// 剩余的参数被绑定到命名参数。
add.call(o, 5, 7); // 16

// 第一个参数被绑定到隐式的 'this' 参数;
// 第二个参数是一个数组,其成员被绑定到命名参数。
add.apply(o, [10, 20]); // 34
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值