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