电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳解JavaScript的this指向和綁定

瀏覽:88日期:2023-10-15 09:21:28

注意: 本文屬于基礎篇,請大神繞路。如果你不夠了解,或者了解的還不完整,那么可以通過本文來復習一下。

this 指向的類型

剛開始學習 JavaScript 的時候,this 總是最能讓人迷惑,下面我們一起看一下在 JavaScript 中應該如何確定 this 的指向。

this 是在函數被調用時確定的,它的指向完全取決于函數調用的地方,而不是它被聲明的地方(除箭頭函數外)。當一個函數被調用時,會創建一個執行上下文,它包含函數在哪里被調用(調用棧)、函數的調用方式、傳入的參數等信息,this 就是這個記錄的一個屬性,它會在函數執行的過程中被用到。

this 在函數的指向有以下幾種場景:

作為構造函數被 new 調用 作為對象的方法使用 作為函數直接調用 被 call、apply、bind 調用 箭頭函數中的 this

下面我們分別來討論一下這些場景中 this 的指向。

1.new 綁定

函數如果作為構造函數使用 new 調用時, this 綁定的是新創建的構造函數的實例。

function Foo() { console.log(this)}var bar = new Foo() // 輸出: Foo 實例,this 就是 bar

實際上使用 new 調用構造函數時,會依次執行下面的操作:

創建一個新對象 構造函數的 prototype 被賦值給這個新對象的 __proto__ 將新對象賦給當前的 this 執行構造函數 如果函數沒有返回其他對象,那么 new 表達式中的函數調用會自動返回這個新對象,如果返回的不是對象將被忽略

2.顯式綁定

通過 call、apply、bind 我們可以修改函數綁定的 this,使其成為我們指定的對象。通過這些方法的第一個參數我們可以顯式地綁定 this。

function foo(name, price) { this.name = name this.price = price}function Food(category, name, price) { foo.call(this, name, price) // call 方式調用 // foo.apply(this, [name, price]) // apply 方式調用 this.category = category}new Food(’食品’, ’漢堡’, ’5塊錢’)// 瀏覽器中輸出: {name: '漢堡', price: '5塊錢', category: '食品'}

call 和 apply 的區別是 call 方法接受的是參數列表,而 apply 方法接受的是一個參數數組。

func.call(thisArg, arg1, arg2, ...) // call 用法func.apply(thisArg, [arg1, arg2, ...]) // apply 用法

而 bind 方法是設置 this 為給定的值,并返回一個新的函數,且在調用新函數時,將給定參數列表作為原函數的參數序列的前若干項。

func.bind(thisArg[, arg1[, arg2[, ...]]]) // bind 用法

舉個例子:

var food = { name: ’漢堡’, price: ’5塊錢’, getPrice: function (place) { console.log(place + this.price) },}food.getPrice(’KFC ’) // 瀏覽器中輸出: 'KFC 5塊錢'var getPrice1 = food.getPrice.bind({ name: ’雞腿’, price: ’7塊錢’ }, ’肯打雞 ’)getPrice1() // 瀏覽器中輸出: '肯打雞 7塊錢'

關于 bind 的原理,我們可以使用 apply 方法自己實現一個 bind 看一下:

// ES5 方式Function.prototype.bind = Function.prototype.bind || function () { var self = this var rest1 = Array.prototype.slice.call(arguments) var context = rest1.shift() return function () { var rest2 = Array.prototype.slice.call(arguments) return self.apply(context, rest1.concat(rest2)) } }// ES6 方式Function.prototype.bind = Function.prototype.bind || function (...rest1) { const self = this const context = rest1.shift() return function (...rest2) { return self.apply(context, [...rest1, ...rest2]) } }

ES6 方式用了一些 ES6 的知識比如 rest 參數、數組解構。

注意: 如果你把 null 或 undefined 作為 this 的綁定對象傳入 call、apply、bind,這些值在調用時會被忽略,實際應用的是默認綁定規則。

var a = ’hello’function foo() { console.log(this.a)}foo.call(null) // 瀏覽器中輸出: 'hello'

3.隱式綁定

函數是否在某個上下文對象中調用,如果是的話 this 綁定的是那個上下文對象。

var a = ’hello’var obj = { a: ’world’, foo: function () { console.log(this.a) },}obj.foo() // 瀏覽器中輸出: 'world'

上面代碼中,foo 方法是作為對象的屬性調用的,那么此時 foo 方法執行時,this 指向 obj 對象。也就是說,此時 this 指向調用這個方法的對象,如果嵌套了多個對象,那么指向最后一個調用這個方法的對象:

var a = ’hello’var obj = { a: ’world’, b: { a: ’China’, foo: function () { console.log(this.a) }, },}obj.b.foo() // 瀏覽器中輸出: 'China'

最后一個對象是 obj 上的 b,那么此時 foo 方法執行時,其中的 this 指向的就是 b 對象。

4.默認綁定

函數獨立調用,直接使用不帶任何修飾的函數引用進行調用,也是上面幾種綁定途徑之外的方式。非嚴格模式下 this 綁定到全局對象(瀏覽器下是 winodw,node 環境是 global),嚴格模式下 this 綁定到 undefined (因為嚴格模式不允許 this 指向全局對象)。

var a = ’hello’function foo() { var a = ’world’ console.log(this.a) console.log(this)}foo() // 相當于執行 window.foo()// 瀏覽器中輸出: 'hello'// 瀏覽器中輸出: Window 對象

上面代碼中,變量 a 被聲明在全局作用域,成為全局對象 window 的一個同名屬性。函數 foo 被執行時,this 此時指向的是全局對象,因此打印出來的 a 是全局對象的屬性。

注意有一種情況:

var a = ’hello’var obj = { a: ’world’, foo: function () { console.log(this.a) },}var bar = obj.foobar() // 瀏覽器中輸出: 'hello'

此時 bar 函數,也就是 obj 上的 foo 方法為什么又指向了全局對象呢,是因為 bar 方法此時是作為函數獨立調用的,所以此時的場景屬于默認綁定,而不是隱式綁定。這種情況和把方法作為回調函數的場景類似:

var a = ’hello’var obj = { a: ’world’, foo: function () { console.log(this.a) },}function func(fn) { fn()}func(obj.foo) // 瀏覽器中輸出: 'hello'

參數傳遞實際上也是一種隱式的賦值,只不過這里 obj.foo 方法是被隱式賦值給了函數 func 的形參 fn,而之前的情景是自己賦值,兩種情景實際上類似。這種場景我們遇到的比較多的是 setTimeout 和 setInterval,如果回調函數不是箭頭函數,那么其中的 this 指向的就是全局對象.

其實我們可以把默認綁定當作是隱式綁定的特殊情況,比如上面的 bar(),我們可以當作是使用 window.bar() 的方式調用的,此時 bar 中的 this 根據隱式綁定的情景指向的就是 window。

this 綁定的優先級

this 存在多個使用場景,那么多個場景同時出現的時候,this 到底應該如何指向呢。這里存在一個優先級的概念,this 根據優先級來確定指向。優先級:new 綁定 > 顯示綁定 > 隱式綁定 > 默認綁定

所以 this 的判斷順序:

new 綁定: 函數是否在 new 中調用?如果是的話 this 綁定的是新創建的對象; 顯式綁定: 函數是否是通過 bind、call、apply 調用?如果是的話,this 綁定的是指定的對象; 隱式綁定: 函數是否在某個上下文對象中調用?如果是的話,this 綁定的是那個上下文對象; 如果都不是的話,使用默認綁定。如果在嚴格模式下,就綁定到 undefined,否則綁定到全局對象。

箭頭函數中的 this

箭頭函數 是根據其聲明的地方來決定 this 的,它是 ES6 的知識點。

箭頭函數的 this 綁定是無法通過 call、apply、bind 被修改的,且因為箭頭函數沒有構造函數 constructor,所以也不可以使用 new 調用,即不能作為構造函數,否則會報錯。

var a = ’hello’var obj = { a: ’world’, foo: () => { console.log(this.a) },}obj.foo() // 瀏覽器中輸出: 'hello'

我們可以看看 ECMAScript 標準中對箭頭函數的描述。

原文:

An Arrow Function does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

翻譯:

箭頭函數不為 arguments、super、this 或 new.target 定義本地綁定。箭頭函數中對 arguments、super、this 或 new.target 的任何引用都解析為當前所在詞法作用域中的綁定。通常,這是箭頭函數所在的函數作用域。

— ECMAScript Language Specification - Arrow Function | ECMA 標準 - 箭頭函數

一個 this 的小練習

用一個小練習來實戰一下:

var a = 20var obj = { a: 40, foo: () => { console.log(this.a) function func() { this.a = 60 console.log(this.a) } func.prototype.a = 50 return func },}var bar = obj.foo() // 瀏覽器中輸出: 20bar() // 瀏覽器中輸出: 60new bar() // 瀏覽器中輸出: 60

稍微解釋一下:

1)var a = 20 這句在全局變量 window 上創建了個屬性 a 并賦值為 20;

2)首先執行的是 obj.foo(),這是一個箭頭函數,箭頭函數不創建新的函數作用域直接沿用語句外部的作用域,因此 obj.foo() 執行時箭頭函數中 this 是全局 window,首先打印出 window 上的屬性 a 的值 20,箭頭函數返回了一個原型上有個值為 50 的屬性 a 的函數對象 func 給 bar;

3)繼續執行的是 bar(),這里執行的是剛剛箭頭函數返回的閉包 func,其內部的 this 指向 window,因此 this.a 修改了 window.a 的值為 60 并打印出來;

4)然后執行的是 new bar(),根據之前的表述,new 操作符會在 func 函數中創建一個繼承了 func 原型的實例對象并用 this 指向它,隨后 this.a = 60 又在實例對象上創建了一個屬性 a,在之后的打印中已經在實例上找到了屬性 a,因此就不繼續往對象原型上查找了,所以打印出第三個 60。

如果把上面例子的箭頭函數換成普通函數呢,結果會是什么樣?

var a = 20var obj = { a: 40, foo: function () { console.log(this.a) function func() { this.a = 60 console.log(this.a) } func.prototype.a = 50 return func },}var bar = obj.foo() // 瀏覽器中輸出: 40bar() // 瀏覽器中輸出: 60new bar() // 瀏覽器中輸出: 60

這個例子就不詳細講解了。

如果把上面兩個例子弄懂原理,基本上 this 的指向就掌握的差不多啦~

以上就是詳解JavaScript的this指向和綁定的詳細內容,更多關于JavaScript的this指向和綁定的資料請關注好吧啦網其它相關文章!

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 钣金加工厂家-钣金加工-佛山钣金厂-月汇好| China plate rolling machine manufacturer,cone rolling machine-Saint Fighter | 称重传感器,测力传感器,拉压力传感器,压力变送器,扭矩传感器,南京凯基特电气有限公司 | 广西教师资格网-广西教师资格证考试网 | 众品家具网-家具品牌招商_家具代理加盟_家具门户的首选网络媒体。 | 车间除尘设备,VOCs废气处理,工业涂装流水线,伸缩式喷漆房,自动喷砂房,沸石转轮浓缩吸附,机器人喷粉线-山东创杰智慧 | 称重传感器,测力传感器,拉压力传感器,压力变送器,扭矩传感器,南京凯基特电气有限公司 | 黑田精工电磁阀-CAMMOZI气缸-ROSS电磁-上海茂硕机械设备有限公司 | 新材料分散-高速均质搅拌机-超声波分散混合-上海化烁智能设备有限公司 | 刹车盘机床-刹车盘生产线-龙口亨嘉智能装备 | 3D全息投影_地面互动投影_360度立体投影_水幕灯光秀 | 广东银虎 蜂窝块状沸石分子筛-吸附脱硫分子筛-萍乡市捷龙环保科技有限公司 | 安徽合肥格力空调专卖店_格力中央空调_格力空调总经销公司代理-皖格制冷设备 | 仓储笼_金属箱租赁_循环包装_铁网箱_蝴蝶笼租赁_酷龙仓储笼租赁 测试治具|过炉治具|过锡炉治具|工装夹具|测试夹具|允睿自动化设备 | 合肥废气治理设备_安徽除尘设备_工业废气处理设备厂家-盈凯环保 合肥防火门窗/隔断_合肥防火卷帘门厂家_安徽耐火窗_良万消防设备有限公司 | SRRC认证|CCC认证|CTA申请_IMEI|MAC地址注册-英利检测 | 炒货机-炒菜机-炒酱机-炒米机@霍氏机械 | 红酒招商加盟-葡萄酒加盟-进口红酒代理-青岛枞木酒业有限公司 | 继电器模组-IO端子台-plc连接线-省配线模组厂家-世麦德 | CPSE安博会| 广州各区危化证办理_危险化学品经营许可证代办| 气动隔膜泵-电动隔膜泵-循环热水泵-液下排污/螺杆/管道/化工泵「厂家」浙江绿邦 | 天坛家具官网| 深圳高新投三江工业消防解决方案提供厂家_服务商_园区智慧消防_储能消防解决方案服务商_高新投三江 | 软装设计-提供软装装饰和软装配饰及软装陈设的软装设计公司 | 安徽千住锡膏_安徽阿尔法锡膏锡条_安徽唯特偶锡膏_卡夫特胶水-芜湖荣亮电子科技有限公司 | 超高频感应加热设备_高频感应电源厂家_CCD视觉检测设备_振动盘视觉检测设备_深圳雨滴科技-深圳市雨滴科技有限公司 | 智能电表|预付费ic卡水电表|nb智能无线远传载波电表-福建百悦信息科技有限公司 | 博博会2021_中国博物馆及相关产品与技术博览会【博博会】 | led太阳能路灯厂家价格_风光互补庭院灯_农村市政工程路灯-中山华可路灯品牌 | 物流之家新闻网-最新物流新闻|物流资讯|物流政策|物流网-匡匡奈斯物流科技 | 液氮罐_液氮容器_自增压液氮罐_杜瓦瓶_班德液氮罐厂家 | 动库网动库商城-体育用品专卖店:羽毛球,乒乓球拍,网球,户外装备,运动鞋,运动包,运动服饰专卖店-正品运动品网上商城动库商城网 - 动库商城 | 测试治具|过炉治具|过锡炉治具|工装夹具|测试夹具|允睿自动化设备 | 千淘酒店差旅平台-中国第一家针对TMC行业的酒店资源供应平台 | 本安接线盒-本安电路用接线盒-本安分线盒-矿用电话接线盒-JHH生产厂家-宁波龙亿电子科技有限公司 | LED灯杆屏_LED广告机_户外LED广告机_智慧灯杆_智慧路灯-太龙智显科技(深圳)有限公司 | 临海涌泉蜜桔官网|涌泉蜜桔微商批发代理|涌泉蜜桔供应链|涌泉蜜桔一件代发 | NBA直播_NBA直播免费观看直播在线_NBA直播免费高清无插件在线观看-24直播网 | 西安中国国际旅行社(西安国旅)| 建筑消防设施检测系统检测箱-电梯**检测仪器箱-北京宇成伟业科技有限责任公司 | 光伏支架成型设备-光伏钢边框设备-光伏设备厂家 |