【js】this指向问题

发布于:2024-07-06 ⋅ 阅读:(15) ⋅ 点赞:(0)

1.首先先明确,this会出现在哪里。

this出现在全局作用域中,或函数作用域中(普通函数、箭头函数)。
对象是不产生作用域的,对象的{}和函数的{}不一样,this并不会直接出现在对象或类中,只会出现在对象/类的函数中。

2.this指向哪里

this在全局作用域下指向的是window。
this在函数中指向的比较复杂, 满足以下四点:
1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的;

3.this的绑定规则

以下指的规则都是普通函数中的,不包括箭头函数。

1.默认绑定
如果一个函数调用是直接调用,比如fn()这样,没有对象.fn(),就说它是默认绑定的,通常默认绑定时,函数中的this指向全局对象(window);
且要找到函数真正的调用位置,看它是否是独立函数直接调用。

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

foo();

2.隐式绑定
通过某个对象进行调用的。比如对象.fn()。通过某个对象发起的函数调用。这时候this会被绑定在该对象身上

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

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

obj.foo();

上面提到要找到函数真正的调用位置,看它是否是独立函数直接调用。
下面举个例子,这个实际的调用位置其实是直接调用的。

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

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

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

3.显式绑定
通过apply、call、bind进行绑定就是显式绑定

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

foo.call(window); // window
foo.call({name: "why"}); // {name: "why"}
foo.call(123); // Number对象,存放时123

4.new绑定
使用new,这个新对象会绑定到函数调用的this上。
绑定的this是这个类/构造函数的实例对象。

// 创建Person
function Person(name) {
  console.log(this); // Person {}
  this.name = name; // Person {name: "why"}
}

var p = new Person("why");
console.log(p);

4.特殊的this

1.setTimeout
setTimeout内部如果传的是普通函数,那这个this指向的是window。
这和setTimeout源码的内部调用有关,记住就好了。

setTimeout(function() {
  console.log(this); // window
}, 1000);

2.forEach
内部传普通函数,this指向的也是window。

var names = ["abc", "cba", "nba"];
names.forEach(function(item) {
  console.log(this); // 三次window
});

3.dom节点
this指向的是绑定事件的节点。

var box = document.querySelector(".box");
box.onclick = function() {
  console.log(this); // box对象
}

4.箭头函数
箭头函数没有this,也就是不绑定this,如果在箭头函数中使用this,则是根据外层作用域来决定this。
所以与上面的setTimeout、forEach特点结合,经常在setTimeout、forEach中使用箭头函数。

var obj = {
  data: [],
  getData: function() {
    setTimeout(() => {
      // 模拟获取到的数据
      var res = ["abc", "cba", "nba"];
      this.data.push(...res);
    }, 1000);
  }
}

obj.getData();

这里,setTimeout中通过箭头函数调用this,这个this与外层作用域相同,也就是和在getData函数中使用this的指向相同。

但如果getData也是个箭头函数,那么getData函数中使用this的指向 与 外层作用域相同,也是与全局作用域中的this一致,window。

var obj = {
  data: [],
  getData: () => {
    setTimeout(() => {
      console.log(this); // window
    }, 1000);
  }
}

obj.getData();

5.规则优先级

new绑定 > 显式绑定(bind)> 隐式绑定 > 默认绑定

1.new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
但new绑定优先级高于bind

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

var obj = {
  name: "obj"
}

// var foo = new foo.call(obj);
var bar = foo.bind(obj);
var foo = new bar(); // 打印foo, 说明使用的是new绑定

2.new绑定优先级高于隐式绑定

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

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

new obj.foo(); // foo对象, 说明new绑定优先级更高

3.显式绑定优先级高于隐式绑定

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

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

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

// 隐式绑定
obj1.foo(); // obj1
obj2.foo(); // obj2

// 隐式绑定和显式绑定同时存在
obj1.foo.call(obj2); // obj2, 说明显式绑定优先级更高

参考:前端面试之彻底搞懂this指向


网站公告

今日签到

点亮在社区的每一天
去签到