暢談this的四種綁定方式

孟飯飯 2021-10-13 17:24:23
方式


前置知識

this 是什麼?

當一個函數被調用,會創建一個執行上下文,這個執行上下文會包含函數在哪裏被調用、函數的調用方式、傳入的參數等信息。this 就是一個執行上下文的一個屬性,會在函數執行的過程中用到。

this綁定出現的時機

this 是在運行時進行綁定的,並不是編寫時綁定的。也就是 this 是在函數被調用時發生的綁定。它的指向是什麼,完全取决於函數在哪裏被調用。this在任何情况下都不指向函數的詞法作用域

綁定規則

默認綁定

默認綁定時,使用嚴格模式,不能講全局對象用於默認綁定,this會綁定到undefined

function foo() {
console.log(this)
}
foo() // window
複制代碼

嚴格模式(strict mode):

function foo() {
 'use strict'
console.log(this)
}
foo() // undefined
複制代碼

隱式綁定

當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象。

本質上,判斷隱式綁定時,必須是一個對象內部包含一個指向函數的屬性,通過這個屬性間接的引用函數,從而把this間接(隱式)綁定到這個對象上。

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
foo: foo
}
obj.foo() // 1
複制代碼

對象屬性引用鏈中,只有上一層或者說最後一層的調用比特置起作用。

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
foo: foo
}
const obj1 = {
a: 2,
obj: obj
}
obj1.obj.foo() // 1
複制代碼

隱式丟失

  1. 引用賦值隱式丟失;

    function foo() {
    console.log(this.a)
    }
    const obj = {
    a: 1,
    foo: foo
    }
    const bar = obj.foo
    bar() // undefined
    複制代碼

    注意,bar 實際上引用的foo函數本身,所以應用了默認綁定。這時的this 指向window

  2. 參數傳遞隱式丟失;

    function foo() {
    console.log(this.a)
    }
    function doFoo(fn) {
    fn()
    }
    const obj = {
    a: 1,
    foo: foo
    }
    doFoo( obj.foo ) // undefined
    複制代碼

    參數傳遞,其實也是一種隱式賦值,邏輯和上一個案例一樣。

顯式綁定

調用函數時,强制將函數綁定到this。直接指定this的綁定對象,這稱為顯式綁定。可以通過callapplybind方法實現。

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
}
foo.call(obj) // 1
foo.apply(obj) // 1
const bindFoo = foo.bind(obj)
bindFoo() // 1
複制代碼

硬綁定

硬綁定等同於bind。重新定義了函數內部的this指向。

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
}
const bar = function(){
foo.call( obj )
}
bar() // 1
bar.call(window) // 1
複制代碼

以上代碼,同bind的效果一致。

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
}
const bar = foo.bind(obj)
bar() // 1
複制代碼

new綁定

function foo(a) {
this.a = a
}
const bar = new foo(2)
console.log(bar.a) // 2
複制代碼

使用new來調用foo(...)時,new內部的實現會構造一個新對象,並把它綁定到foo(...)調用的this上。我們稱為new綁定

綁定優先級

顯然,根據上面的案例,顯示綁定 > 隱式綁定 > 默認綁定。那麼new綁定和顯示綁定哪個高?這裏通過硬綁定來測試優先級。

function foo(value){
this.a = value
}
const obj = {
a: 1,
}
const bar = foo.bind(obj)
bar(2)
console.log(obj.a) // 2
const baz = new bar(3)
console.log(obj.a) // 2
console.log(baz.a) // 3
複制代碼

如此,那麼綁定優先級的順序為: new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定

我們可以通過下面四條規則,判斷this的綁定對象:

  1. 由new調用?
  2. 由call、apply、bind調用?
  3. 由上下文對象調用?
  4. 如上都不滿足,則是默認綁定,嚴格模式下綁定到undefined。

綁定意外

null或者undefined作為this的綁定對象的this忽略問題

null或者undefined作為this的綁定對象傳入call、apply、bind,這時,這些值在調用時會把忽略,實際應用的是默認綁定

如果函數並不關心this的話,這時,傳入null,作為一個占比特符,是個不錯的選擇。

但是使用null來忽略this綁定可能會有一些副作用。如果某個函數確實使用了this(比如第三方庫的一個函數),那默認綁定規則,就會把這個this綁定到全局對象,這將會導致不可預計的後果(比如修改全局對象)。

如此,我們應該用一種更為安全的方法,來作為占比特符。

我們可以創建一個空的非委托對象Object.create(null)。忽略this綁定時總是傳入一個空的非委托對象,就不會對全局對象產生任何影響。

function foo(value){
console.log(value)
}
var ø = Object.create(null)
foo.call(ø, 2) // 2
複制代碼

間接引用

創建一個函數的“間接引用”,這種情况下,調用這個函數會應用默認綁定規則。這種情况容易在賦值是發生:

function foo() {
console.log(this.a)
}
const obj = {
a: 1,
foo: foo
}
obj.foo() // 1
const bar = obj.foo // 賦值
bar() // undefined
複制代碼

軟綁定

硬綁定可以將函數的this强制綁定到指定的對象上。但是硬綁定存在一個問題,就是會降低函數的靈活性,並且在硬綁定之後無法再使用隱式綁定或者顯式綁定來修改 this 的指向。

而軟綁定就可以解决如上的問題,可以給默認綁定設置一個全局對象undefinednull之外的值,這樣就可以實現和硬綁定同樣的效果,同時保留了隱式綁定或者顯式綁定來修改 this 的能力。

可以看看這篇關於軟綁定的文章。實現代碼如下(來源:你不知道的JavaScript》上卷):

if(!Function.prototype.softBind){
Function.prototype.softBind=function(obj){
var fn=this;
var args=Array.prototype.slice.call(arguments, 1);
var bound=function(){
return fn.apply(
// 判定當前this,如果綁定到了全局對象或undefined,null,則修改this為傳入的obj,否則什麼也不做
(!this || this === (window || global)) ? obj : this,
args.concat.apply(args,arguments)
);
};
bound.prototype=Object.create(fn.prototype);
return bound;
};
}
複制代碼

看效果:

var name = 'global'
const obj = {
name: 'obj',
foo: function () {
console.log(this.name)
}
}
const obj1 = {
name: 'obj1'
}
obj.foo() // => obj // 隱式綁定
setTimeout(obj.foo, 0) // 默認綁定 this丟失,global
// 現在我們使用軟綁定
const softFoo = obj.foo.softBind(obj)
setTimeout(softFoo, 0) // obj 軟綁定,this指向默認設置的obj
softFoo.call(obj1) // obj1,可以使用call顯式改變this
obj1.foo = softFoo
obj1.foo() // obj1,軟綁定的this指向成功被隱式綁定修改了,綁定到了obj1
setTimeout(obj1.foo, 0) // obj 回調函數相當於一個隱式的傳參,本來會有默認綁定將this綁定全局,但是有軟綁定,這裏this還是指向obj
複制代碼

箭頭函數

語法:

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
複制代碼

注意事項:

  • 箭頭函數不使用this 的四種標准規則,而是根據外層(函數或者全局)作用域來决定this
  • 箭頭函數不能作為構造函數,new 箭頭函數() 會程序報錯;
  • 同理,由於箭頭函數不能作為構造函數,內部也就沒有自身的上下文對象,也就不能使用arguments對象;

小結

好了,以上就是關於 this 的一些介紹,講解了 this 是什麼,this 綁定出現的時機,關於 this 的四種綁定,以及綁定優先級等。完結**ヽ(°▽°)ノ*~

版权声明
本文为[孟飯飯]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/10/20211013172422490Y.html

  1. Error in spring source code compilation: the monoprocessor in reactor.core.publisher is outdated
  2. Buctf [geek Challenge 2019] http
  3. 20 个值得学习的 Vue 开源项目
  4. 20 projets open source à apprendre
  5. Scène et application de la manette des gaz anti - bavardage
  6. Qu'est - ce que j'ai gagné en abandonnant vue pour les six mois de React?
  7. À partir de [Bytecode cache] et de [http cache], intervieweur: « est - ce si mince? »
  8. [niveau intermédiaire et avancé] obligatoire, 30 + questions manuscrites à haute fréquence et réponses détaillées (dix mille caractères longs), voyez comment "vous" ne pouvez pas m'abattre
  9. Mise en œuvre d'un outil d'échafaudage universel pour l'ingénierie Web de 0 à 1
  10. 【中高级前端】必备,30+高频手写题及详细答案(万字长文),看“你”怎么难倒我
  11. In less than two days, the box office exceeded 400 million, and Changjin Lake broke out, breaking seven records in Chinese film history
  12. Tong Liya Jin Chen bumps her hair, Xie Na Zhao Liying bumps her shirt, and she sees EQ from the reaction
  13. react之组件生命周期
  14. L'équipe de vue dévoile un nouvel outil d'échafaudage rapide comme la foudre Create View, qui remplacera la vue CLI à l'avenir, avec seulement 300 lignes de code, apprenez - le!
  15. 20 dessins illustrant le fonctionnement du moteur de rendu du Navigateur
  16. In less than two days, the box office exceeded 400 million, and Changjin Lake broke out, breaking seven records in Chinese film history
  17. 千锋重庆web前端学习之理解CSS位置属性
  18. 什么是语义HTML标记以及如何使用它们?
  19. Si vous vous représentez avec un tableau, c'est le champ de Van Gogh.
  20. 前端面试手写代码
  21. 前端开发框架Vue中Vuex的使用原理分享
  22. vue-echarts初次体验
  23. 分享一些web前端开发好用的网站
  24. 每天读一点webpack-003
  25. react之组件生命周期
  26. Alibaba collection version of mybatis handwritten documents, Java front-end interview questions
  27. SpringBoot Java后端实现okhttp3超时设置
  28. react之組件生命周期
  29. Cycle de vie des composants de React
  30. 使用Reactor将阻塞调用变为异步非阻塞
  31. Baked cake wife sun photos, plain face on camera, beautiful appearance is still a beauty, watching children during the festival is a little helpless
  32. 亚洲知名插画师荒川(arakawa) 仅8件独版NFT作品系列《Can't Out》正式上架Element综合市场
  33. Taiyuan: singing, welcoming the national day, gathering to praise blessings
  34. Arakawa, un illustrateur Asiatique bien connu, n'a mis sur le marché que huit pièces de la collection NFT "can't out" en une seule édition.
  35. Résumé des questions d'entrevue Hadoop (II) - - hdfs
  36. 如何解决“Serverless”系统的冷启动问题
  37. BootstrapBlazor 模板安装
  38. BootstrapBlazor 模板安装
  39. Tong Liya Jin Chen bumps her hair, Xie Na Zhao Liying bumps her shirt, and she sees EQ from the reaction
  40. 使用ESLint+Prettier来统一前端代码风格
  41. 为什么说 Node.js 是实时应用程序开发的绝佳选择
  42. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  43. 使用elementui在完成项目中遇到的未知知识点2
  44. On the mechanism of webpack loader
  45. 云原生体系下 Serverless 弹性探索与实践
  46. vue开发技巧
  47. Une fleur merveilleuse de l'histoire de l'industrie des nouveaux véhicules énergétiques, Zhongtai Jiangnan T11, une voiture vintage que vous n'avez jamais vue
  48. 致敬!再见了!LayUI !
  49. Vue安装和卸载
  50. Implement a flipped character with the transform attribute of CSS
  51. 你的第一个 Docker + React + Express 全栈应用
  52. [apprentissage de l'algorithme] 1486. Fonctionnement exclusif du tableau (Java / C / C + + / python / go / Rust)
  53. Zhang Daxian sends a blessing video on xYG relay, showing positive energy in details
  54. 前端技巧-JS元编程ES6 symbol公开符号
  55. Article de 37 ans seul à l'hôpital!Il boitait, soupçonnait d'être blessé, souriait avec douleur
  56. 前端推荐!10分钟带你了解Konva运行原理
  57. npm ERR! iview-project@3.0.0 init: `webpack --progress --config webpack.dev.config.js
  58. 零基础学习Web前端需要注意什么呢?
  59. The Youth League promotes Yiyang Qianxi new film, and the relationship between the two generation and the generation is good. Li Fei is blessed.
  60. Qu'est - ce qu'il faut remarquer à l'avant - plan Web de l'apprentissage de base zéro?