this指向问题【JavaScript基础面试题】

前言

this关键字是前端面试题中最常见的考点,本文章将由浅入深,从最基本的例子开始介绍this的使用方法,以及每道面试题的原理。

this指向什么

在全局作用域下,this指向的是window

console.log(this); // window

var name = "codereasy";
console.log(this.name); // codereasy
console.log(window.name); // codereasy

然而,在实际项目中,我们一般不会在全局作用域下使用this关键字。接下来,我们看看在函数中this关键字指向的是什么。

// 定义一个函数
function foo() {
  console.log(this);
}

// 1.调用方式一: 直接调用
foo(); // window

// 2.调用方式二: 将foo放到一个对象中,再调用
var obj = {
  name: "codereasy",
  foo: foo
}
obj.foo() // obj对象

// 3.调用方式三: 通过call或者apply调用
foo.call("ctgu"); // String {"ctgu"}对象

通过以上例子,我们可以总结出以下几点:

  • this和函数定义的位置没有关系,只和调用者有关系。
  • this是在运行时被绑定的。

那么,this是根据什么规则被绑定的呢?

this的绑定规则

默认绑定

普通调用

如果我们不给函数绑定调用者,那么在默认情况下,this指向的是window

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

foo();

链式调用

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

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

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

函数作为参数,传给另一个函数

function foo(func) {
  func()
}

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

foo(bar);

可以发现,不论函数在哪调用,只要没有给函数绑定对象,this永远指向window

隐式绑定

通过对象调用函数

在下面例子中,obj调用了foo()方法。因此,this会隐式的被绑定到obj对象上。

function foo() {
  console.log(this); // obj对象
}

var obj = {
  name: "codereasy",
  foo: foo
}

obj.foo();

示例一的变体

初学者可能会疑惑,下例中,this指向的是obj1还是obj2呢?答案是obj1。永远记住,谁直接调用foo()(换而言之,谁离foo()更近),那么foo()中的this就指向谁。

function foo() {
  console.log(this); // obj对象
}

var obj1 = {
  name: "obj1",
  foo: foo
}

var obj2 = {
  name: "obj2",
  obj1: obj1
}

obj2.obj1.foo();

隐式绑定失效

以下例子最终输出的是window。为什么呢?按照上文的思路,找到调用foo()的位置。我们发现最终是通过bar()来执行函数的,在执行bar()的时候,没有给它绑定任何对象,因此根据默认绑定规则,this指向window

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

var obj = {
  name: "obj",
  foo: foo
}

// 讲obj的foo赋值给bar
var bar = obj.foo;
bar();

显示绑定

根据上文的例子,大家可以发现,隐式绑定的都是通过如下方式实现的:

obj.fun()

因此,隐式绑定有一个前提条件,obj对象上有fun方法,才能像上述代码那样绑定。

如果obj对象上没有fun方法,obj能否调用fun()函数呢?答案是可以的。通过显示绑定来实现。

call函数
function foo() {
  console.log(this);
}

foo.call(window); // window
foo.call({name: "codereasy"}); // {name: ""codereasy"}
foo.call(666); // Number对象
bind函数

foo()是原函数,bar是新函数(通过bind给bar绑定了调用者)。因此,今后执行bar的时候,函数中this永远指向obj。

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

var obj = {
  name: "codereasy"
}

var bar = foo.bind(obj);

bar(); // obj对象

new绑定

在JavaScript中,我们可以使用new来创建一个新对象。

当我们使用new关键字调用函数的时候,会执行如下操作:
1.创建一个空对象。
2.空对象的__proto__属性指向构造函数的Prototype属性。
3.执行构造函数,如果构造函数中有this,则此this指向刚刚创建的空对象
4.返回刚刚创建的对象

function Student(name) {
  console.log(this); // Student {}
  this.name = name; // Student {name: "codereasy"}
}

var p = new Student("codereasy");
console.log(p);

不同绑定方式的优先级

显示绑定优先于隐式绑定

从下面例子中可以看出:obj1.foo()属于隐式绑定,foo.call(obj2)属于显式绑定。当他们同时存在时,隐式绑定失效了,显式绑定生效了。最终,this指向的是obj2

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

var obj1 = {
  name: "obj1",
  foo: foo
}

var obj2 = {
  name: "obj2",
  foo: foo
}

// 隐式绑定和显示绑定同时存在
obj1.foo.call(obj2); // obj2
new绑定优先级高于显示绑定

从下面例子中可以看出,foo.bind(obj)是显示绑定,new foo(...)是new 绑定。
最终console.log()输出值是foo,说明new绑定生效了

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

var obj = {
  name: "obj"
}

var bar = foo.bind(obj);
var foo = new bar(); // foo
优先级顺序总结

new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

箭头函数

箭头函数中的this不受以上规则约束,它的this完全取决于外层作用域

示例1:

我们发现,下述代码中,greet()中打印的this,和 outer() 中打印的 this 是一样的。这说明箭头函数的 this 和外层作用域中的 this 是一样的。

function outer() {
  console.log(this); // 输出:{ name: 'Alice', greet: [Function: greet] }
  const greet = () => {
    console.log(this); // 输出:{ name: 'Alice', greet: [Function: greet] }
  };
  greet();
}

const person = {
  name: "Alice",
  greet: outer,
};

person.greet();

示例2:

箭头函数的 this定义的时候决定,而非调用的时候决定。

从下面代码可以看出,arrow()outer()内部被调用,但是 arrowthis 指向的是 window,和 outerthis 不同。这说明,在定义arrow的时候,它的 this 就已经固定了,是 window。因此,不管在哪里调用 arrow 函数,它内部的 this 始终指向的是 window。

const arrow = ()=>{
  //this是什么? window
  console.log("this是什么",this);
}

function outer() {
  //我是外层函数 {name: 'coderEasy', outer: ƒ}
  console.log("我是外层函数",this);
  arrow();
}

obj = {
  name:"coderEasy",
  outer : outer,
}

obj.outer();

其他规则

显示绑定失效

通过上文可知,通过call、apply、bind可以给函数显示绑定对象(调用者)。但是,如果我们绑定的对象是null、undefined,那么这个显示绑定就会被忽略,函数中的this依然指向window,而不是null、undefined

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codereasy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值