摘要:函數(shù)可計(jì)算某個(gè)字符串,并執(zhí)行其中的的代碼男男成功啦實(shí)現(xiàn)了函數(shù)參數(shù)的傳遞,那么函數(shù)返回值怎么處理呢。而且,如果傳入的對(duì)象是,又該如何處理所以還需要再做一些工作處理返回值返回返回值男男判斷傳入對(duì)象的類型,如果為就指向?qū)ο蟆?/p>
本文共 1320 字,讀完只需 5 分鐘概述
JS 函數(shù) call 和 apply 用來(lái)手動(dòng)改變 this 的指向,call 和 apply 唯一的區(qū)別就在于函數(shù)參數(shù)的傳遞方式不同,call 是以逗號(hào)的形式,apply 是以數(shù)組的形式:
let person1 = { name: "person1", say: function(age, sex) { console.log(this.name + " age: " + age + " sex: " + sex); } } let person2 = { name: "person" } person1.say.call(person2, 20, "男"); person1.say.apply(person2, [20, "男"]);
本文就嘗試用其他方式來(lái)模擬實(shí)現(xiàn) call 和 apply。
首先觀察 call 和 apply 有什么特點(diǎn)?
被函數(shù)調(diào)用(函數(shù)也是對(duì)象),相當(dāng)于 call 和 apply 是函數(shù)的屬性
如果沒(méi)有傳入需要 this 指向?qū)ο螅敲?this 指向全局對(duì)象
函數(shù)執(zhí)行了
最后都改變了 this 的指向
一、初步實(shí)現(xiàn)基于 call 函數(shù)是調(diào)用函數(shù)的屬性的特點(diǎn),call 的 this 指向調(diào)用函數(shù),我們可以嘗試把調(diào)用函數(shù)的作為傳入的新對(duì)象的一個(gè)屬性,執(zhí)行后,再刪除這個(gè)屬性就好了。
Function.prototype.newCall = function (context) { context.fn = this; // this 指的是 say 函數(shù) context.fn(); delete context.fn; } var person = { name: "jayChou" }; var say = function() { console.log(this.name); } say.newCall(person); // jayChou
是不是就初步模擬實(shí)現(xiàn)了 call 函數(shù)呢,由于 call 還涉及到傳參的問(wèn)題,所以我們進(jìn)入到下一環(huán)節(jié)。
二、eval 方式在給對(duì)象臨時(shí)一個(gè)函數(shù),并執(zhí)行時(shí),傳入的參數(shù)是除了 context 其余的參數(shù)。那么我們可以截取 arguments 參數(shù)數(shù)組的第一個(gè)后,將剩余的參數(shù)傳入臨時(shí)數(shù)組。
在前面我有講過(guò)函數(shù) arguments 類數(shù)組對(duì)象的特點(diǎn),arguments 是不支持?jǐn)?shù)組的大多數(shù)方法, 但是支持for 循環(huán)來(lái)遍歷數(shù)組。
Function.prototype.newCall = function (context) { context.fn = this; let args = []; for(let i=1; i< arguments.length; i++) { args.push("arguments[" + i + "]"); } // args => [arguments[1], arguments[2], arguments[3], ...] context.fn(args.join(",")); // ??? delete context.fn; } var person = { name: "jayChou" }; var say = function(age, sex) { console.log(`name: ${this.name},age: ${age}, sex: ${sex}`); } say.newCall(person);
上面?zhèn)鬟f參數(shù)的方式最后肯定是失敗的,我們可以嘗試 eval 的方式,將參數(shù)添加子函數(shù)的作用域中。
eval() 函數(shù)可計(jì)算某個(gè)字符串,并執(zhí)行其中的的 JavaScript 代碼
Function.prototype.newCall = function (context) { context.fn = this; let args = []; for(var i=1; i< arguments.length; i++) { args.push("arguments[" + i + "]"); } // args => [arguments[1], arguments[2], arguments[3], ...] eval("context.fn(" + args + ")"); delete context.fn; } var person = { name: "jayChou" }; function say(age, sex) { console.log(`name: ${this.name},age: ${age}, sex: ${sex}`); } say.newCall(person, 18, "男"); // name: jayChou,age: 18, sex: 男
成功啦!
實(shí)現(xiàn)了函數(shù)參數(shù)的傳遞,那么函數(shù)返回值怎么處理呢。而且,如果傳入的對(duì)象是 null,又該如何處理?所以還需要再做一些工作:
Function.prototype.newCall = function (context) { if (typeof context === "object") { context = context || window } else { context = Object.create(null); } context.fn = this; let args = []; for(var i=1; i< arguments.length; i++) { args.push("arguments[" + i + "]"); } // args => [arguments[1], arguments[2], arguments[3], ...] var result = eval("context.fn(" + args + ")"); // 處理返回值 delete context.fn; return result; // 返回返回值 } var person = { name: "jayChou" }; function say(age, sex) { console.log(`name: ${this.name},age: ${age}, sex: ${sex}`); return age + sex; } var check = say.newCall(person, 18, "男"); console.log(check); // 18男
判斷傳入對(duì)象的類型,如果為 null 就指向 window 對(duì)象。利用 eval 來(lái)執(zhí)行字符串代碼,并返回字符串代碼執(zhí)行的結(jié)果,就完成了模擬 call。
大功告成!
前面我們用的 eval 方式可以用 ES6 的解決還存在的一些問(wèn)題,有沒(méi)有注意到,這段代碼是有問(wèn)題的。
context.fn = this;
假如對(duì)象在被 call 調(diào)用前,已經(jīng)有 fn 屬性怎么辦?
ES6 中提供了一種新的基本數(shù)據(jù)類型,Symbol,表示獨(dú)一無(wú)二的值,另外,Symbol 作為屬性的時(shí)候,不能使用點(diǎn)運(yùn)算符。所以再加上 ES 的 rest 剩余參數(shù)替代 arguments 遍歷的工作就有:
Function.prototype.newCall = function (context,...params) { if (typeof context === "object") { context = context || window } else { context = Object.create(null); } let fn = Symbol(); context[fn] = this var result = context[fn](...params); delete context.fn; return result; } var person = { name: "jayChou" }; function say(age, sex) { console.log(`name: ${this.name},age: ${age}, sex: ${sex}`); return age + sex; } var check = say.newCall(person, 18, "男"); console.log(check); // 18男四、apply
apply 和 call 的實(shí)現(xiàn)原理,基本類似,區(qū)別在于 apply 的參數(shù)是以數(shù)組的形式傳入。
Function.prototype.newApply = function (context, arr) { if (typeof context === "object") { context = context || window } else { context = Object.create(null); } context.fn = this; var result; if (!arr) { // 判斷函數(shù)參數(shù)是否為空 result = context.fn(); } else { var args = []; for (var i = 0; i < arr.length; i++) { args.push("arr[" + i + "]"); } result = eval("context.fn(" + args + ")"); } delete context.fn; return result; }
es6 實(shí)現(xiàn)
Function.prototype.newApply = function(context, parameter) { if (typeof context === "object") { context = context || window } else { context = Object.create(null) } let fn = Symbol() context[fn] = this; var result = context[fn](...parameter); delete context[fn]; return result; }總結(jié)
本文通過(guò)原生 JS 的 ES5 的方法和 ES 6 的方法模擬實(shí)現(xiàn)了 call 和 apply 的原理,旨在深入了解這兩個(gè)方法的用法和區(qū)別,希望你能有所收獲。
歡迎關(guān)注我的個(gè)人公眾號(hào)“謝南波”,專注分享原創(chuàng)文章。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98832.html
摘要:但是三作為構(gòu)造函數(shù)時(shí)函數(shù)其實(shí)還有一個(gè)非常重要的特點(diǎn)返回的函數(shù)如果作為構(gòu)造函數(shù),搭配關(guān)鍵字出現(xiàn)的話,我們的綁定就需要被忽略。其次,當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)時(shí),之前綁定的會(huì)失效。 本文共 1100 字,讀完只需 4 分鐘 概述 前一篇文章我們嘗試模擬實(shí)現(xiàn)了 call 和 apply 方法,其實(shí) bind 函數(shù)也可以用來(lái)改變 this 的指向。bind 和 call和 apply 兩者的區(qū)別...
摘要:模擬實(shí)現(xiàn)操作符構(gòu)造函數(shù)返回結(jié)果創(chuàng)建一個(gè)空對(duì)象取傳入的第一個(gè)參數(shù),即構(gòu)造函數(shù),并刪除第一個(gè)參數(shù)。二處理返回值構(gòu)造函數(shù)也是函數(shù),有不同類型返回值。有時(shí)候構(gòu)造函數(shù)會(huì)返回指定的對(duì)象內(nèi)容,所以要對(duì)這部分進(jìn)行處理。 本文共 1230 字,讀完只需 5 分鐘 寫在前面 最近工作太忙,快接近兩周沒(méi)更新博客,總感覺(jué)有一些事情等著自己去做,雖然工作內(nèi)容對(duì)自己提升挺大,但我總覺(jué)得,一直埋著頭走路,偶爾也...
摘要:寫在前面深入系列共計(jì)篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順底層知識(shí)的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫在前面 JavaScript 深入系列共計(jì) 15 篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順 JavaScript 底層知識(shí)的系列。重點(diǎn)講解了如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、...
摘要:將元素作為對(duì)象的鍵,默認(rèn)鍵對(duì)應(yīng)的值為如果對(duì)象中沒(méi)有這個(gè)鍵,則將這個(gè)元素放入結(jié)果數(shù)組中去。 前言 數(shù)組去重在日常開發(fā)中的使用頻率還是較高的,也是網(wǎng)上隨便一抓一大把的話題,所以,我寫這篇文章目的在于歸納和總結(jié),既然很多人都在提的數(shù)組去重,自己到底了解多少呢。又或者是如果自己在開發(fā)中遇到了去重的需求,自己能想到更好的解決方案嗎。 這次我們來(lái)理一理怎么做數(shù)組去重才能做得最合適,既要考慮兼容性,...
摘要:專題系列第十八篇,講解遞歸和尾遞歸定義程序調(diào)用自身的編程技巧稱為遞歸。然而非尾調(diào)用函數(shù),就會(huì)創(chuàng)建多個(gè)執(zhí)行上下文壓入執(zhí)行上下文棧。所以我們只用把階乘函數(shù)改造成一個(gè)尾遞歸形式,就可以避免創(chuàng)建那么多的執(zhí)行上下文。 JavaScript 專題系列第十八篇,講解遞歸和尾遞歸 定義 程序調(diào)用自身的編程技巧稱為遞歸(recursion)。 階乘 以階乘為例: function factorial(n...
閱讀 718·2021-11-16 11:44
閱讀 3551·2019-08-26 12:13
閱讀 3246·2019-08-26 10:46
閱讀 2361·2019-08-23 12:37
閱讀 1192·2019-08-22 18:30
閱讀 2536·2019-08-22 17:30
閱讀 1843·2019-08-22 17:26
閱讀 2295·2019-08-22 16:20