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

JavaScript 中的 this 关键字

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

this 的绑定规则

1. 默认绑定

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

js 复制代码
function foo(){
  function bar(){
    console.log(this)
  }
  bar()
}
foo()

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

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

2. 隐式绑定

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

js 复制代码
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 指向引用函数的对象,而不是最初的对象。我们也可以理解为就近原则。看这个例子:

js 复制代码
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

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

js 复制代码
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 指向的对象。

javascript 复制代码
function sayName() {
  console.log(`Hello, ${this.name}`);
}

const person = {
  name: "Charlie"
};

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

5. new 绑定

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

javascript 复制代码
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关键字无法引用任何函数词法作用域中的内容:

让我们来看几个例子:

js 复制代码
var b = 1

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

foo()

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

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

js 复制代码
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

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

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

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

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

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

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

相关推荐
追逐时光者2 分钟前
精选 10 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
造糖主义4 分钟前
vue-el-upload上传组件自定义删除-预览按钮遮罩层,不受原有的上传打开文件夹
前端·javascript·vue.js
要开心吖ZSH1 小时前
Spring Boot 自动配置:从 spring.factories 到 AutoConfiguration.imports 的演变
spring boot·后端·spring
brzhang1 小时前
我十几个项目都是这套Flutter 快速开发框架,今天开源了,从此你只用关心业务了
前端·后端·架构
qziovv1 小时前
控制Vue对话框显示隐藏
javascript·vue.js·elementui
神仙别闹2 小时前
基于ASP.NET+SQL Server的网站登录注册功能设计与实现
后端·asp.net
threejs源码翻译官2 小时前
显微镜图像处理【部署】- pytorch模型转onnx使用GPU进行推理
后端
stark张宇2 小时前
Linux 用户、用户组、文件权限、文件查找
后端
小宁爱Python2 小时前
TypeScript 泛型详解:从基础到实战应用
前端·javascript·typescript
wjpwjpwjp08312 小时前
[MySQL基础3] 数据控制语言DCL和MySQL中的常用函数
数据库·笔记·后端·学习·mysql