likes
comments
collection
share

函数在哪里调用,this就指向哪里,真的对吗?

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

JavaScript 中的 this 关键字

JavaScript 中的 this 关键字是一个非常重要的概念,它用来指代当前执行代码的上下文,通常是一个对象。了解 this 的绑定规则对于理解 JavaScript 函数和面向对象编程非常关键。但 this 的指向是动态的,它取决于代码执行的方式和上下文。这可能导致在实际使用中分不清this实际指向的内容,从而非常困扰我们,不过不用担心,接下来我们将详细剖析这个关键词的指向规则。

this 的绑定规则

1. 默认绑定

默认绑定就是函数在哪个词法作用域里生效,this就指向哪里,诶?难道说函数在哪里调用,this就指向哪里,真的对吗?我们要注意,仅限于默认绑定时,我们的函数在哪个词法作用域里生效而不是调用在哪就指向哪,所以说这句话不全对,存在很多瑕疵,让我们来用一个例子看看函数调用位置是否是this指向:

function foo(){
  function bar(){
    console.log(this)
  }
  bar()
}
foo()

让我们运行一下试试,我们的bar()foo函数中调用,那我们来看看this会指向foo函数吗?我们在浏览器中执行了一下结果是window对象,我们并没有绑定上foo函数,这就是默认绑定

我们再来看看在哪个词法作用域里生效,this就指向哪里是怎样的,其实还是这个例子,我们会发现我们的bar调用在foo函数中,而foo函数是没有词法作用域的,函数只有作用域,那么我们就需要找foo的词法作用域,没错,是全局,如果一个函数没有任何上下文对象,它就会绑定到全局对象(在浏览器中通常是 window 对象)。

2. 隐式绑定

隐式绑定发生在函数被一个对象所拥有并调用的情况下。在这种情况下,this 指向这个拥有它的对象。

const person = {
  name: "Bob",
  sayName: function() {
    console.log(`Hello, ${this.name}`);
    console.log(this); // this 指向 person 对象
  }
};

person.sayName();

这个就比较简单了,我们的sayName函数person对象所拥有的函数,那么this就指向了person对象,所以我们在外面调用这个函数,函数中的this.name会打印出Bob也就是person对象中的name。这种绑定对象的情况我们称之为隐式绑定。

3. 隐式丢失

隐式丢失通常发生在函数被多个对象链式调用的情况下。这时,this 指向引用函数的对象,而不是最初的对象。我们也可以理解为就近原则。看这个例子:

const person1 = {
  name: "Alice",
  sayName: function() {
    console.log(`Hello, ${this.name}`);
  }
};

const person2 = {
  name: "Bob"
};

person2.sayName = person1.sayName; // 借用 person1 的 sayName 函数

person1.sayName(); // Hello, Alice
person2.sayName(); // Hello, Bob

这个例子比较直观可以看到,谁调用就是指谁,假如我们再来一个例子:

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

var obj = {
  a: 2,
  foo: foo
}

var obj2 = {
  a : 3,
  obj: obj
}
obj2.obj.foo()

我们使用对象obj2来调用对象obj2里的对象obj,从而调用对象obj中的foo函数,绕晕了没?到底是对象obj2调用的foo函数还是对象obj调用的foo函数呢?我们运行一下,它输出的是2!没错,这就是就近原则,如果像这样链式调用一个对象里的函数,他指向最近的对象obj

在这样的调用下我们丢失了一个对象obj2的引用,指向了对象obj,这就是隐式丢失

4. 显式绑定

JavaScript 提供了 callapplybind 方法,可以用来显式绑定函数的 this。这些方法允许你在函数被调用时明确指定 this 指向的对象。

function sayName() {
  console.log(`Hello, ${this.name}`);
}

const person = {
  name: "Charlie"
};

sayName.call(person); // 使用 call 显式指定 this,输出 "Hello, Charlie"

5. new 绑定

当一个函数用 new 关键字调用时,它会创建一个新的对象,并将该对象绑定到函数的 this

function Person(name) {
  this.name = name;
}

const person = new Person("Alice");
console.log(person.name); // 输出 "Alice"

在上面的例子中,Person函数被用new关键字调用,它在内部创建了一个新的对象(person),并将this绑定到这个新对象。这允许我们将属性name赋值给新对象,最终创建了一个名为"Alice"的person对象。

new绑定是一种常见的方式来创建自定义对象的构造函数,这些对象可以拥有自己的属性和方法。这种方式非常有用,因为它允许你创建多个相似的对象,同时保持代码的可维护性。

需要注意的是,如果在构造函数内部没有显式返回其他对象,new操作符会返回新创建的对象,如上面的person对象。但如果构造函数内部显式返回了一个对象,那么new操作符会返回该对象,而不是新创建的对象。这可以用来实现一些高级的模式,例如单例模式。

总之,new绑定是JavaScript中用于创建对象实例的一种重要机制,它允许你构造自定义对象并初始化其属性。

this 不能引用词法作用域中的内容

首先要明确的是,this 并不是指向词法作用域(函数内部)中的变量或内容。而是根据函数被调用的方式来确定其指向。我们已经知道了this关键字的几种绑定规则,但我们要记住我们通过this关键字无法引用任何函数词法作用域中的内容:

让我们来看几个例子:

var b = 1

function foo(){
  console.log(this.b)
}

foo()

这个例子很简单,我们的this在这种情况下就是默认绑定,绑定为全局对象(在浏览器中通常是 window 对象),那么我们在浏览器中执行,我们肯定能输出1对吧,但是假如我们直接用node在vs code里运行,我们会得到undefined,因为在vs code里node并没有创建全局window对象,所以我们无法引用。

那如果是在函数词法作用域呢?

function foo(){
  var b = 1
  function bar(){
    console.log(this.b)
  }
  bar.call(foo)
}
foo()

这个例子里很明显我们使用显示绑定规则将this指针指向了foo,那么我们如果打印this我们会得到foo函数,但是我们这个例子里,我们能输出foo函数中的b吗?

跑一下试试,结果是undefined,失败了,我们并不能输出foo函数中的b,这也就是this 不能引用词法作用域中的内容,我们只能通过指向对象,从而获取对象中的内容。

箭头函数

最后,要注意箭头函数 (=>) 在 this 方面的特殊行为。箭头函数没有自己的 this 绑定,而是继承自外层函数的 this

const obj = {
  value: 42,
  getValue: function() {
    return () => {
      console.log(this.value); // 这里的 this 指向外层函数的 this
    };
  }
};

const innerFunction = obj.getValue();
innerFunction(); // 输出 42

所以我们无法将指针指向箭头函数,因为箭头函数根本没有this这个概念,哪怕是箭头套箭头套箭头也是一直逐层往外找。

总之,理解 this 的绑定规则对于正确处理 JavaScript 中的上下文非常重要。不同的绑定规则可以在不同的情况下用于确保 this 指向正确的对象。我们如果想要灵活使用this指针一定要理解和掌握它的绑定规则。

那么我们这篇文章到这里就结束啦~

如果你想了解更多这类文章,点赞关注作者更新更多后续~