📕 重学JavaScript: this中的隐式绑定场景

简介: 隐式绑定就是根据函数调用的方式来确定函数内部的this指向,这种方式比较隐晦,你需要注意一些规则和特例。今天我们就专门来聊聊关于隐式绑定的几个场景。

📕 重学JavaScript: this中的隐式绑定场景

嗨,大家好!这里是道长王jj~ 🎩🧙‍♂️

我相信大家了解了JavaScript的this关键字代表什么含义,这里我就不过多的阐述。

我这里就简要地概括一下:

  1. this是一个关键字,它表示当前执行上下文中的一个对象,也就是说,它指向谁取决于函数是怎么调用的。
  2. this有两种绑定方式,一种是显示绑定,一种是隐式绑定。

显示绑定就是用call/apply/bind这些方法来指定函数内部的this指向,这种方式比较直接,你可以自由地把this绑定到任何对象上。

隐式绑定就是根据函数调用的方式来确定函数内部的this指向,这种方式比较隐晦,你需要注意一些规则和特例。

今天我们就专门来聊聊关于隐式绑定的几个场景。

1️⃣ 全局上下文

当你在全局环境中使用this时,它指向全局对象(浏览器中是window,Node.js中是global)。

这是默认的绑定方式,也是最不安全的方式,因为你可能不小心修改了全局对象的属性或方法。

比如,你在全局环境中定义了一个变量a和一个函数fn,然后用this来访问它们,就像这样:

var a = 1;
function fn() {
   
  console.log(this.a);
}
console.log(this.a); // 1
fn(); // 1

这里的this都指向全局对象,所以可以访问到a和fn。但是如果你不小心把this.a赋值为其他值,就会影响全局变量a,就像这样:

var a = 1;
function fn() {
   
  this.a = 100;
  console.log(this.a);
}
console.log(this.a); // 1
fn(); // 100
console.log(this.a); // 100

这里的fn函数内部的this也指向全局对象,所以它把this.a赋值为100,相当于把全局变量a赋值为100。这样就会导致全局变量被污染,可能会引发一些难以发现的bug。

2️⃣ 直接调用函数

当你直接调用一个函数时,它内部的this也指向全局对象。

这和全局上下文类似,但是有一个例外,就是当你在严格模式下调用函数时,它内部的this会指向undefined。

这是为了防止你误操作全局对象而设置的安全措施。

比如,你定义了一个函数fn,在非严格模式下直接调用它,就像这样:

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

这里的fn函数内部的this指向全局对象window。但是如果你在严格模式下直接调用它,就像这样:

"use strict";
function fn() {
   
  console.log(this);
}
fn(); // undefined

这里的fn函数内部的this指向undefined。

这是因为在严格模式下,JS不允许你隐式地使用全局对象作为this,而是要求你显示地指定this的值。

如果你没有指定this的值,那么就会默认为undefined。这样可以避免你不小心修改了全局对象的属性或方法。

3️⃣ obj.fn()的形式调用

当你用一个对象来调用它的方法时,它内部的this指向这个对象。

这是最常见的绑定方式,也是最符合直觉的方式,因为你可以把这个对象看作是方法的拥有者或接收者。

比如,你有一个对象叫obj,它里面有一个属性a和一个方法fn,然后用obj来调用它的方法fn,就像这样:

var obj = {
   
  a: 1,
  fn: function() {
   
    console.log(this.a);
  }
};
obj.fn(); // 1

这里的obj.fn()内部的this指向obj对象,所以可以访问到obj对象的属性a。但是如果你把obj.fn赋值给另一个变量,并用这个变量来调用函数,就像这样:

var obj = {
   
  a: 1,
  fn: function() {
   
    console.log(this.a);
  }
};
var anotherFn = obj.fn;
anotherFn(); // undefined

这里的anotherFn()内部的this不再指向obj对象,而是指向全局对象,所以无法访问到obj对象的属性a。

这是因为anotherFn只是一个普通的函数,它不是obj对象的方法,所以它不会继承obj对象的this。

4️⃣ DOM事件绑定(特殊)

当你给一个DOM元素绑定一个事件处理函数时,它内部的this指向这个DOM元素。

这是一个特殊的情况,因为这里涉及到了浏览器的事件机制,它会把事件处理函数作为DOM元素的方法来调用。

比如,你有一个按钮元素叫btn,它里面有一个文本内容叫click me,然后你给它绑定了一个点击事件处理函数fn,就像这样:

<button id="btn">click me</button>
<script>
  var btn = document.getElementById("btn");
  function fn() {
    
    console.log(this); // <button id="btn">click me</button>
    console.log(this.innerHTML); // click me
  }
  btn.addEventListener("click", fn);
</script>

这是因为浏览器会把fn函数作为btn元素的方法来调用,就像这样:

btn.fn();

所以fn函数内部的this就指向了btn元素。

5️⃣ new构造函数绑定

当你用new关键字来调用一个构造函数时,它内部的this指向一个新创建的对象。

这是一种特殊的绑定方式,因为这里涉及到了JS的原型链机制,它会把构造函数作为新对象的原型来调用。

比如,你定义了一个构造函数叫Person,它里面有一个属性name和一个方法sayName,然后用new关键字来创建一个实例叫p,就像这样:

function Person(name) {
   
  this.name = name;
  this.sayName = function() {
   
    console.log(this.name); // Tom
  };
}
var p = new Person("Tom");

这是因为JS会把Person函数作为p对象的原型来调用,就像这样:

p.__proto__ = Person.prototype;
Person.call(p, "Tom");

所以Person函数内部的this就指向了p对象。😁

6️⃣ 箭头函数

当你使用箭头函数时,它内部的this不会根据调用方式改变,而是继承自它所在的词法作用域(lexical scope)。

也就是说,箭头函数没有自己的this,它会沿着作用域链向上寻找最近的一个普通函数,然后把它内部的this作为自己的this。

比如,你定义了一个普通函数fn,在它里面定义了一个箭头函数arrowFn,并用this来访问a属性,就像这样:

var a = 1;
function fn() {
   
  var a = 2;
  var arrowFn = () => {
   
    console.log(this.a);
  };
  arrowFn();
}
fn(); // 1

这里的arrowFn函数内部的this不是指向fn函数或者全局对象,而是指向fn函数外面的那个普通函数(在这里就是全局环境)。

这是因为箭头函数没有自己的this,它会沿着作用域链向上寻找最近的一个普通函数(在这里就是全局环境),

然后把它内部的this(在这里就是全局对象)作为自己的this。

也就是说,箭头函数内部的this和它外面的普通函数内部的this是一样的。

💥 出现多种隐式绑定时,优先级谁最高?

我先说答案:new > call、apply、bind > obj.fn() > 直接调用。

当一个函数有多种绑定方式时,它会按照这个优先级来确定内部的this指向。😁

比如,你有一个构造函数叫Person,它里面有一个属性name和一个方法sayName,然后你用new关键字来创建一个实例叫p,并用call方法来指定this指向obj对象,就像这样:

function Person(name) {
   
  this.name = name;
  this.sayName = function() {
   
    console.log(this.name);
  };
}
var obj = {
   
  name: "Tom"
};
var p = new Person("Jerry").sayName.call(obj);

p.sayName() //Jerry

这里的p.sayName()内部的this指向谁呢?

根据优先级,我们可以知道,new绑定的优先级高于call绑定,所以p.sayName()内部的this指向p对象,而不是obj对象。

所以,当你调用p.sayName()时,就会打印出Jerry

那箭头函数呢?

其实箭头函数不属于这几个场景的优先级,因为箭头函数没有自己的this,它会继承自它所在的词法作用域(lexical scope)的this。

也就是说,箭头函数内部的this和它外面的普通函数内部的this是一样的。

所以,箭头函数内部的this的优先级取决于它外面的普通函数的优先级。


🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨

目录
相关文章
|
1月前
|
设计模式 JavaScript 前端开发
深入理解 JavaScript 中的绑定机制(下)
深入理解 JavaScript 中的绑定机制(下)
|
1月前
|
JavaScript 前端开发
深入理解 JavaScript 中的绑定机制(上)
深入理解 JavaScript 中的绑定机制(上)
|
1月前
|
JavaScript 前端开发
javascript中的this
javascript中的this
|
3月前
|
JavaScript 前端开发
错综复杂的this:理清你的JavaScript代码中的指向问题
错综复杂的this:理清你的JavaScript代码中的指向问题
|
1月前
|
JavaScript
JS中改变this指向的六种方法
JS中改变this指向的六种方法
|
2天前
|
JavaScript 前端开发
js开发:请解释this关键字在JavaScript中的用法。
【4月更文挑战第23天】JavaScript的this关键字根据执行环境指向不同对象:全局中指向全局对象(如window),普通函数中默认指向全局对象,作为方法调用时指向调用对象;构造函数中指向新实例,箭头函数继承所在上下文的this。可通过call、apply、bind方法显式改变this指向。
7 1
|
1月前
|
JavaScript
JS中call()、apply()、bind()改变this指向的原理
JS中call()、apply()、bind()改变this指向的原理
|
2月前
|
JavaScript 前端开发
JavaScript中this的指向问题
JavaScript中this的指向问题
|
3月前
|
前端开发 JavaScript
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(下)
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(下)
|
3月前
|
前端开发 JavaScript
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(上)
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(上)