摘要:深入系列第十篇,通過(guò)和的模擬實(shí)現(xiàn),帶你揭開(kāi)和改變的真相一句話介紹方法在使用一個(gè)指定的值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。
callJavaScript深入系列第十篇,通過(guò)call和apply的模擬實(shí)現(xiàn),帶你揭開(kāi)call和apply改變this的真相
一句話介紹 call:
call() 方法在使用一個(gè)指定的 this 值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法。
舉個(gè)例子:
var foo = { value: 1 }; function bar() { console.log(this.value); } bar.call(foo); // 1
注意兩點(diǎn):
call 改變了 this 的指向,指向到 foo
bar 函數(shù)執(zhí)行了
模擬實(shí)現(xiàn)第一步那么我們?cè)撛趺茨M實(shí)現(xiàn)這兩個(gè)效果呢?
試想當(dāng)調(diào)用 call 的時(shí)候,把 foo 對(duì)象改造成如下:
var foo = { value: 1, bar: function() { console.log(this.value) } }; foo.bar(); // 1
這個(gè)時(shí)候 this 就指向了 foo,是不是很簡(jiǎn)單呢?
但是這樣卻給 foo 對(duì)象本身添加了一個(gè)屬性,這可不行吶!
不過(guò)也不用擔(dān)心,我們用 delete 再刪除它不就好了~
所以我們模擬的步驟可以分為:
將函數(shù)設(shè)為對(duì)象的屬性
執(zhí)行該函數(shù)
刪除該函數(shù)
以上個(gè)例子為例,就是:
// 第一步 foo.fn = bar // 第二步 foo.fn() // 第三步 delete foo.fn
fn 是對(duì)象的屬性名,反正最后也要?jiǎng)h除它,所以起成什么都無(wú)所謂。
根據(jù)這個(gè)思路,我們可以嘗試著去寫(xiě)第一版的 call2 函數(shù):
// 第一版 Function.prototype.call2 = function(context) { // 首先要獲取調(diào)用call的函數(shù),用this可以獲取 context.fn = this; context.fn(); delete context.fn; } // 測(cè)試一下 var foo = { value: 1 }; function bar() { console.log(this.value); } bar.call2(foo); // 1
正好可以打印 1 哎!是不是很開(kāi)心!(~ ̄▽ ̄)~
模擬實(shí)現(xiàn)第二步最一開(kāi)始也講了,call 函數(shù)還能給定參數(shù)執(zhí)行函數(shù)。舉個(gè)例子:
var foo = { value: 1 }; function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.call(foo, "kevin", 18); // kevin // 18 // 1
注意:傳入的參數(shù)并不確定,這可咋辦?
不急,我們可以從 Arguments 對(duì)象中取值,取出第二個(gè)到最后一個(gè)參數(shù),然后放到一個(gè)數(shù)組里。
比如這樣:
// 以上個(gè)例子為例,此時(shí)的arguments為: // arguments = { // 0: foo, // 1: "kevin", // 2: 18, // length: 3 // } // 因?yàn)閍rguments是類數(shù)組對(duì)象,所以可以用for循環(huán) var args = []; for(var i = 1, len = arguments.length; i < len; i++) { args.push("arguments[" + i + "]"); } // 執(zhí)行后 args為 [foo, "kevin", 18]
不定長(zhǎng)的參數(shù)問(wèn)題解決了,我們接著要把這個(gè)參數(shù)數(shù)組放到要執(zhí)行的函數(shù)的參數(shù)里面去。
// 將數(shù)組里的元素作為多個(gè)參數(shù)放進(jìn)函數(shù)的形參里 context.fn(args.join(",")) // (O_o)?? // 這個(gè)方法肯定是不行的啦?。?!
也許有人想到用 ES6 的方法,不過(guò) call 是 ES3 的方法,我們?yōu)榱四M實(shí)現(xiàn)一個(gè) ES3 的方法,要用到ES6的方法,好像……,嗯,也可以啦。但是我們這次用 eval 方法拼成一個(gè)函數(shù),類似于這樣:
eval("context.fn(" + args +")")
這里 args 會(huì)自動(dòng)調(diào)用 Array.toString() 這個(gè)方法。
所以我們的第二版克服了兩個(gè)大問(wèn)題,代碼如下:
// 第二版 Function.prototype.call2 = function(context) { context.fn = this; var args = []; for(var i = 1, len = arguments.length; i < len; i++) { args.push("arguments[" + i + "]"); } eval("context.fn(" + args +")"); delete context.fn; } // 測(cè)試一下 var foo = { value: 1 }; function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.call2(foo, "kevin", 18); // kevin // 18 // 1
(??????)??
模擬實(shí)現(xiàn)第三步模擬代碼已經(jīng)完成 80%,還有兩個(gè)小點(diǎn)要注意:
1.this 參數(shù)可以傳 null,當(dāng)為 null 的時(shí)候,視為指向 window
舉個(gè)例子:
var value = 1; function bar() { console.log(this.value); } bar.call(null); // 1
雖然這個(gè)例子本身不使用 call,結(jié)果依然一樣。
2.函數(shù)是可以有返回值的!
舉個(gè)例子:
var obj = { value: 1 } function bar(name, age) { return { value: this.value, name: name, age: age } } console.log(bar.call(obj, "kevin", 18)); // Object { // value: 1, // name: "kevin", // age: 18 // }
不過(guò)都很好解決,讓我們直接看第三版也就是最后一版的代碼:
// 第三版 Function.prototype.call2 = function (context) { var context = context || window; context.fn = this; var args = []; for(var i = 1, len = arguments.length; i < len; i++) { args.push("arguments[" + i + "]"); } var result = eval("context.fn(" + args +")"); delete context.fn return result; } // 測(cè)試一下 var value = 2; var obj = { value: 1 } function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age } } bar.call(null); // 2 console.log(bar.call2(obj, "kevin", 18)); // 1 // Object { // value: 1, // name: "kevin", // age: 18 // }
到此,我們完成了 call 的模擬實(shí)現(xiàn),給自己一個(gè)贊 b( ̄▽ ̄)d
apply的模擬實(shí)現(xiàn)apply 的實(shí)現(xiàn)跟 call 類似,在這里直接給代碼,代碼來(lái)自于知乎 @鄭航的實(shí)現(xiàn):
Function.prototype.apply = function (context, arr) { var context = Object(context) || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push("arr[" + i + "]"); } result = eval("context.fn(" + args + ")") } delete context.fn return result; }下一篇文章
JavaScript深入之bind的模擬實(shí)現(xiàn)
重要參考知乎問(wèn)題 不能使用call、apply、bind,如何用 js 實(shí)現(xiàn) call 或者 apply 的功能?
深入系列JavaScript深入系列目錄地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列預(yù)計(jì)寫(xiě)十五篇左右,旨在幫大家捋順JavaScript底層知識(shí),重點(diǎn)講解如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點(diǎn)概念。
如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎star,對(duì)作者也是一種鼓勵(lì)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91675.html
摘要:也就是說(shuō)當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,時(shí)指定的值會(huì)失效,但傳入的參數(shù)依然生效。構(gòu)造函數(shù)效果的優(yōu)化實(shí)現(xiàn)但是在這個(gè)寫(xiě)法中,我們直接將,我們直接修改的時(shí)候,也會(huì)直接修改函數(shù)的。 JavaScript深入系列第十一篇,通過(guò)bind函數(shù)的模擬實(shí)現(xiàn),帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)...
摘要:深入系列第十二篇,通過(guò)的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用獲得構(gòu)造函數(shù)實(shí)例的真相一句話介紹運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象類型之一也許有點(diǎn)難懂,我們?cè)谀M之前,先看看實(shí)現(xiàn)了哪些功能。 JavaScript深入系列第十二篇,通過(guò)new的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用new獲得構(gòu)造函數(shù)實(shí)例的真相 new 一句話介紹 new: new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具...
摘要:第一版首先要獲取調(diào)用的函數(shù),用可以獲取的指向?yàn)?,因?yàn)槭堑膶?shí)例相當(dāng)于把掛載到上,所以可以取到測(cè)試一下但是第一版不可以傳遞多個(gè)參數(shù)第二版這里會(huì)自動(dòng)調(diào)用這個(gè)方法。 // 第一版 Function.prototype.call2 = function(context) { // 首先要獲取調(diào)用call的函數(shù),用this可以獲取 // this的指向?yàn)閎ar,因?yàn)閎ar是Func...
摘要:寫(xiě)在前面深入系列共計(jì)篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順底層知識(shí)的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 深入系列共計(jì) 15 篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順 JavaScript 底層知識(shí)的系列。重點(diǎn)講解了如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、...
摘要:之前文章詳細(xì)介紹了的使用,不了解的查看進(jìn)階期。不同的引擎有不同的限制,核心限制在,有些引擎會(huì)拋出異常,有些不拋出異常但丟失多余參數(shù)。存儲(chǔ)的對(duì)象能動(dòng)態(tài)增多和減少,并且可以存儲(chǔ)任何值。這邊采用方法來(lái)實(shí)現(xiàn),拼成一個(gè)函數(shù)。 之前文章詳細(xì)介紹了 this 的使用,不了解的查看【進(jìn)階3-1期】。 call() 和 apply() call() 方法調(diào)用一個(gè)函數(shù), 其具有一個(gè)指定的 this 值和分...
閱讀 1996·2021-09-07 10:24
閱讀 2095·2019-08-30 15:55
閱讀 2049·2019-08-30 15:43
閱讀 674·2019-08-29 15:25
閱讀 1063·2019-08-29 12:19
閱讀 1948·2019-08-23 18:32
閱讀 1523·2019-08-23 17:59
閱讀 954·2019-08-23 12:22