likes
comments
collection
share

深入探究:一篇文章搞懂JS this指向

作者站长头像
站长
· 阅读数 24

引言

大家好,欢迎来到本篇博客!今天,我们将一起探讨JavaScript中一个常常让人感到困惑的话题——this指向。无论你是初学者还是有一定经验的开发者,对于this的概念理解都至关重要。让我们一起来揭开this的神秘面纱吧!

为什么要有this?

  1. 为了让对象中的函数有能力访问对象自己的属性。

以以下简单代码为例:

深入探究:一篇文章搞懂JS this指向

如果我们不使用this对象中的函数将无法访问对象自己的属性。加上this则可以访问。

深入探究:一篇文章搞懂JS this指向
  1. this可以显著的提升代码质量,减少上下文参数的传递。

第一部分代码是不使用this的传参模式,而第二部分使用this大大减少了参数的传递,提高了函数的灵活性和可复用性,使得这些函数在不同上下文中都能正常运行。如果第二部分代码暂时看不懂也没关系,等你看完这篇文章就可以一秒get。

function identify(context) {
    return context.myname.toUpperCase();
}

function speak(context) {
    var greeting = "Hello I am " + identify(context);
    console.log(greeting);
}

var me = {
    myname: 'xx'
}

speak(me);
function identify() {
    return this.myname.toUpperCase();
}

function speak() {
    var greeting = "Hello I am " + identify.call(this);
    console.log(greeting);
}

var me = {
    myname: 'xx'
}

speak.call(me);

this的指向规则

this绑定优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

默认绑定

当一个函数独立调用,不带任何修饰符的时候,this一定指向window

深入探究:一篇文章搞懂JS this指向

在浏览器中通常是window对象,在Node.js环境中是global空对象

再来一个简单的例子:

function foo() {
  console.log(this);  // window
}

function bar() {
  foo()
}

function baz() {
  bar()
}

baz()

因为this在foo中,而foo在调用时不带任何修饰符,所以this指向window

隐式绑定

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

const person = { 
    name: 'Alice', 
    greet: function() { 
    console.log(this.name);
    } 
};
person.greet(); // Alice 

再来一个例子:

深入探究:一篇文章搞懂JS this指向

看上去像是obj调用了b函数,但是实际上this在foo内独立调用,所以仍然使用默认绑定规则,指向window

隐式丢失

隐式丢失是指函数在使用隐式绑定规则时,丢失了原本的预期的上下文,导致this指向不再符合预期的情况。

这种情况通常发生在以下情形下:

  1. 将函数从原始对象中分离出来:
const obj = { 
    name: 'Alice', 
    greet() { 
        console.log(this); // Window
    } 
}; 
const greet = obj.greet; // 隐式丢失 
greet(); // 在这里,隐式绑定丢失,this不再指向obj 
  1. 将函数作为参数传递并在其他上下文中调用:
const obj1 = { 
    name: 'Alice', 
    greet() { 
        console.log(`Hello, ${this.name}`);
    } 
}; 
const obj2 = { 
    name: 'Bob' 
}; 
obj1.greet(); // Hello, Alice 
setTimeout(obj1.greet, 1000); 
// 在这里,隐式丢失

当你传递一个函数给 setTimeout 时,它在指定的时间间隔后执行该函数。这个函数会在全局上下文中执行,而不是在定义时的上下文(例如对象中),因此会导致 this 的指向改变。

在这些情况下,this的上下文丢失了,可能会导致意外的行为或错误。为避免隐式丢失,可以使用箭头函数、bind方法、callapply来明确指定this的上下文,或者在传递函数时保持上下文一致。

显示绑定

通过callapplybind方法,我们可以显式地指定函数内部this的指向。

1. bind 方法:

  • bind 方法创建一个新的函数,该函数在调用时将指定的 this 值绑定到传递给 bind 的值,但并不立即调用该函数。
  • 语法:function.bind(thisArg, arg1, arg2, ...)
  • bind 可以用于固定函数的 this 值,并传递初始参数。
function greet(name) { 
    console.log(`Hello, ${name}! I'm ${this.job}.`); 
} 
let person = { 
    job: 'engineer' 
}; 
let greetPerson = greet.bind(person, 'Alice'); 
greetPerson(); // 输出:Hello, Alice! I'm engineer.

// bind返回函数调用时也可以传参,优先识别bind中的参数
//greetPerson('Tom');
  • bind 多次指向 只识别第一次指向
var obj = {
    a: 1
}
var obj1 = {
    a: 2
}
function foo() {
    console.log(this.a); // 输出 1 
}
let res = foo.bind(obj).bind(obj1);
res()
  • bind 优先级高于 call 和 apply。通过bind指定的this,call和apply无法再次更改。
var obj = {
    a: 1
}

var obj1 = {
    a: 2
}

function foo() {
    console.log(this.a);
}
let res = foo.bind(obj); // 输出 1 
res.apply(obj1);

2. call 方法:

  • call 方法允许你在调用函数时指定函数内部的 this 值,以及以参数列表的形式传递参数。
  • call 方法立即调用相应的函数。
  • 语法:function.call(thisArg, arg1, arg2, ...)
function greet(name) { 
    console.log(`Hello, ${name}! I'm ${this.job}.`); 
} 
let person = { 
    job: 'teacher' 
}; 
greet.call(person, 'Bob'); // 输出:Hello, Bob! I'm teacher. 

3. apply 方法:

  • apply 方法与 call 类似,不同之处在于它接收一个数组或类数组对象作为参数。
  • 语法:function.apply(thisArg, [argArray])
function greet(name) { 
    console.log(`Hello, ${name}! I'm ${this.job}.`); 
} 
let person = { 
    job: 'doctor' 
}; 
greet.apply(person, ['Charlie']); // 输出:Hello, Charlie! I'm doctor. 

4. 关键区别:

  • bind 返回一个新函数,不会立即调用,允许延迟执行;而 callapply 立即调用相应的函数。
  • callapply 都允许你显式设置函数内部的 this 值和传递参数,但 apply 接收的参数是数组形式。
  • bind 适用于创建具有永久 this 绑定的新函数,而 callapply 适用于在特定上下文中立即调用函数。

new绑定

当使用new关键字创建实例时,this会绑定到它创建的实例对象上。

function Person(name) { 
    this.name = name; 
} 
const john = new Person('John'); 
console.log(john.name); // John 

箭头函数中的this

  • 箭头函数不存在this机制,所以箭头函数里的this会沿用外层最近一层非箭头函数的this。
const obj = { 
    func1: function() { 
        console.log(this); // obj 
        const func2 = () => { 
            console.log(this); // obj 
        }; 
        func2(); 
    } 
}; 
obj.func1(); 

总结

经过这篇文章的探讨,相信大家对JavaScript中this的指向有了更清晰的认识。记住这些规则并在实际开发中多加练习,相信你很快就能够游刃有余地处理this的使用了! 希望这篇文章能够帮助到你,如果有任何疑问或想法,欢迎在评论区和我交流讨论。今天的内容就到这里了。如果你觉得这篇文章有帮助或有所启发,别忘了给我一个鼓励的哦!🚀🔍

转载自:https://juejin.cn/post/7366826090835853351
评论
请登录