摘要:眾所周知,這三個函數都是改變執行上下文的,那么我們來捋一捋,這些函數內部到底做了什么。
前言
稍微翻了一下call,apply, bind 的各種論壇上的文章, 發現講的都太淺了,大部分都只講了個用法, 對于實現的原理卻都沒有提,因此,在這里,我寫下這篇文章, 希望能讓大家認識到原理所在。
眾所周知, 這三個函數都是改變執行上下文的 , 那么我們來捋一捋,這些函數內部到底做了什么。
callFunction是函數對象的構造方法,call,apply,bind 都是函數原型上的方法 作為實例 他自身也有這三個方法
圈中的為原型方法, 方塊的為實例方法,另外length屬性就是argument的長度,我們通常調用一個函數
function a(){ console.log(this,"a")}; function b(b){console.log(b)} a.call(b,"我是B的參數")
執行a, 并把context指向b, 這里大家都沒有疑問, 那么問題來了
function a(){ console.log(this,"a")}; function b(){console.log(this,"b")} a.call.call.call(b,"b") // 這個結果是什么呢? 答案是
傻眼了吧 ? 怎么執行了B 并且this指向了這個 b字符串
我們來分析一下 call是原型上的方法 那么a.call 他本身也是一個函數 所以a.call.call.call 不就是a.call.call的原型上的call方法么?
所以不就是執行call.call 并改變 call.call的上下文
我們來擼一遍call的源碼,
第一個參數是上下文, 當我們call(null),this指向了window 當我們傳入字符串 會把字符串包裝成對象
a.call 執行 this是指向a的(誰調用this 指向誰) 然后又執行了a方法,所以內部是
Function.prototype.call = function(context){ context = context ? Object(context):window this() // 因為調用的都是函數 所以this是一個函數 也就是a }
那這樣并未改變this指向啊,咋辦? 上句話不是說((誰調用this 指向誰)),所以我們要在內部改變掉this,做如下修改
Function.prototype.call = function(context){ context = context ? Object(context):window context.fn = this context.fn() // 通過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this }
那么參數怎么傳呢,我們首先要拿到call的里的參數
Function.prototype.call = function(context,...args){ context = context ? Object(context):window context.fn = this let r = context.fn(...args) // 通過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this delete context.fn //刪除屬性 return r // 返回執行的結果 }
現在我們回頭分析一下a.call.call.call(b,"b")
a.call.call是個函數,它調用call方法 執行它 我們進入函數里面看看
首先把context 也就是b轉為字符串對象 它的屬性上賦予fn 也就是a.call.call ,然后執行context.fn(...args), 也就是a.call.call("b") 接著刪除fn 返回執行結果 宏觀來看 是a.call.call這個函數去執行并傳入("b") a.call.call 也就是Function.prototype.call 所以就是call("b") 所以啊, 結果才是this是指向string的b 并且參數是undefined
Function.prototype.call = function(context,...args){ context = context ? Object(context):window context.fn = this //a.call.call("b") var r = context.fn(...args) // 通過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this delete context.fn return r } function a(){ console.log(this,"a")}; function b(args){console.log("我是this:" + this,"我是b的參數args:" + args)} a.call.call.call(b,"我到底是參數呢還是this")
結論!
一個函數call2次或者2次以上 執行的永遠是b(b需要是一個函數), 并且call的第二個參數成為當前context
apply 就是參數不同 直接上代碼
Function.prototype.apply = function(context,...args){ context = context ? Object(context):window context.fn = this; var r; if(args.length){ r = context.fn(...args) delete context.fn }else{ r = context.fn() } return r } function a(args){ console.log(this,args)}; function b(){console.log("我是this:" + this)} a.apply(b,[1,2,3,4])
bind 明天寫 累了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101838.html
摘要:也就是說當返回的函數作為構造函數的時候,時指定的值會失效,但傳入的參數依然生效。構造函數效果的優化實現但是在這個寫法中,我們直接將,我們直接修改的時候,也會直接修改函數的。 JavaScript深入系列第十一篇,通過bind函數的模擬實現,帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數...
摘要:和概覽我們要將歸為一類,單獨歸為一類三者的共同點是都可以指定和都是綁定在的原型上的,所以的實例都可以調用這三個方法至于為什么,看完這篇文章你就懂了如果你不懂什么是實例的話,請移步深入淺出面向對象和原型概念篇深入淺出面向對象和原型概念篇第一個 1.call/apply和bind概覽 我們要將call/apply歸為一類,bind單獨歸為一類 三者的共同點是都可以指定this call/...
摘要:三個方法的作用,都是改變的指向,只是用法稍微有些區別什么是既不指向函數自身,也不指函數的詞法作用域。它在函數定義的時候是確定不了的在函數被調用時才發生的綁定,也就是說具體指向什么,取決于你是怎么調用的函數。 1.排序法 思路:給數組先排序(由大到小排序),第一項就是最大值 let arr = [1,5,6,7,9,20,40,2,3]; let max1 = arr.sort(func...
摘要:文章盡量使用大量實例進行講解,它們的使用場景。在嚴格模式下,函數被調用后,里面的默認是后面通過調用函數上的和方法,該變指向,函數里面的指向。利用,可以傳入外層的上下文。同樣適用的還有,里面的對象,它也是一種類數組對象。 call,apply and bind in JavaScript 在ECMAScript中,每個函數都包含兩個繼承而來的方法:apply() 和 call(),這兩個...
摘要:深入系列第十篇,通過和的模擬實現,帶你揭開和改變的真相一句話介紹方法在使用一個指定的值和若干個指定的參數值的前提下調用某個函數或方法。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。 JavaScript深入系列第十篇,通過call和apply的模擬實現,帶你揭開call和apply改變this的真相 call 一句話介紹 call: call() 方法在使用一個指定的 this...
閱讀 1948·2021-11-24 10:45
閱讀 1452·2021-11-18 13:15
閱讀 4524·2021-09-22 15:47
閱讀 3902·2021-09-09 11:36
閱讀 2006·2019-08-30 15:44
閱讀 3081·2019-08-29 13:05
閱讀 2495·2019-08-29 12:54
閱讀 1986·2019-08-26 13:47