当前位置: 首页 > news >正文

深入理解JavaScript设计模式之闭包与高阶函数

前言小序

一场失败面试

2023年的某一天,一场让我印象深刻的面试:
面试官:

“你了解闭包吗?请说一下你对闭包的理解。”

我自信满满地答道:

“闭包就是函数里面套函数,里面的函数可以访问外部函数的变量。”

面试官点点头,继续问:

“那你知道高阶函数吗?”

我依旧不慌不忙:

“高阶函数嘛,就是能接收函数作为参数,或者返回一个函数的函数,在函数里执行回调的那种。”

当时我心里还美滋滋地想:这不都是我看过的八股文嘛,稳了!走出面试室,我仿佛已经收到了 offer,决定先去吃顿大餐犒劳自己。吃完饭回到家,手机一响,是面试来电。
电话那头传来温柔而熟悉的声音:

“很抱歉,您未能通过本次面试……尽管这次未能成功,我们仍希望您能找到更适合您的工作机会……祝您未来职业道路顺利……吧啦吧啦吧啦……”

通话结束,我的心情也随着“嘟——”的一声沉入谷底,那一晚,我久久不能平静,回想起这场看似顺利的面试,却以失败告终,我突然意识到一个问题:我背得再标准,说得再流利,但那不是我真正理解的东西。于是,我下定决心:“一定要搞懂闭包到底是什么!”

面试后的觉醒

学习,启动!
我翻开了《JavaScript设计模式与开发实践》,一页页啃,一遍遍读,反复琢磨,不断练习。虽然学得不算特别深入,但也算是从“八股文选手”迈向了“理解者”的第一步。现在,我想用我自己的话,重新讲一遍我对闭包的理解:

“闭包是一个函数能够访问并记住它定义时所处的词法环境,即使这个函数在其作用域外执行。它不仅能延长变量的生命周期,还能封装私有状态,是JavaScript 中非常强大且常用的一种机制。”

至于高阶函数,我也有了更深的认识:

“高阶函数并不只是‘传函数进去然后调用’这么简单。它的核心在于抽象行为 —— 你可以把逻辑像数据一样传递,从而写出更灵活、可复用、富有表现力的代码。”

我希望下一次面试,我不再是那个只会背诵八股文的“伪高手”,而是能用自己的语言,准确表达出技术背后思想的那个“真正的我”。一雪前耻,就在下一场面试。加油!

闭包

在前端开发JavaScript这门语言中,JavaScript不仅是前端开发得力助手,还是一个多才多艺的小能手! 为什么这么说呢,因为javaScript不仅能干面向对象编程的活,还掌握了一些函数式编程的小技巧——比如闭包。
闭包这东西,听起来高深莫测,新手一听到闭包就懵,面试官一问闭包就巴拉巴拉开始背八股文:“函数里面套函数,访问外部变量…”,然后被pass掉😅!
经过反复学习研磨,我就对《JavaScript设计模式与开发实践》第一部分第三章进行个人理解与总结。如果觉得我的总结错误请"Coach Me !!" 哇哈哈哈哈。

变量作用域:谁的地盘听谁的

JavaScript编程中,变量也是有"势力范围"的,这个范围叫做作用域,比如你在函数里定义了一个变量,那它就是一个宅男,只能在这个函数内部活动,外面是看不到他的。

function sayHi() {var name = '小明';console.log(name); // 小明出来打招呼了
}
sayHi();
console.log(name); // Uncaught ReferenceError: name is not defined

看,小明只在函数里活跃,一出函数门,世界就忘了他。但 JavaScript 的函数有个特点:它像个玻璃罩子,从内往外能看到外面的东西,但从外往内是看不到里面的。这就是所谓的作用域链。再比如:

<html><body><script>// 全局变量 a:爷爷,在客厅里大声宣布:"我是1!"var a = 1;// func1 是爸爸,有自己的小秘密 b = 2var func1 = function () {var b = 2;console.log("爸爸说:我有秘密b =", b);// func2 是儿子,偷偷藏了一个宝贝 c = 3var func2 = function () {var c = 3;console.log("儿子说:我能看到爸爸的秘密b =", b); // 看得见爸爸console.log("儿子说:我也能看见爷爷a =", a); // 看得见爷爷};func2(); // 儿子执行完毕,把c藏回房间// 爸爸试图查看儿子的玩具c...console.log("爸爸说:我想看看儿子的c =", c); // ❌ 找不到!ReferenceError!};func1(); // 全家开始聚会</script></body>
</html>

输出结果
深入理解JavaScript设计模式之闭包与高阶函数
总结:JavaScript中,函数可以用来创造作用域,在函数里面可以看到外面的变量,在函数外面则无法看到函数里面的变量,搜索过程会随着代码执行环境创建的作用域链往外层逐层搜索,一直搜索到全局对象为止,变量的搜索是从内到外而不是从外到内的。

变量的生命周期:该走了,不该走的还在

变量不仅有“地盘”,还有“寿命”,全局变量活得久,除非手动删除,否则它一直活在,而函数里的局部变量呢?通常函数执行完,它们就“退休了”,但是有时候,局部变量就是“赖着不走”,比如:

 var func = function () {var a = 1;return function () {a++;console.log(a);};};var f = func();f(); // 输出:2f(); // 输出:3f(); // 输出:4f(); // 输出:5

上面代码中,a明明是在函数里声明的,按理说函数执完它就该"退休"了,但它偏偏没有走,为啥? 因为有一个函数(也就是返回的哪个匿名函数)还”爱着它“,它被闭包捕获了。于是这个变量不再只是一个普通的变量,而是变成了"有故事的变量”。局部变量的生命看起来被延续了!!!
利用闭包,可以完成很多有趣的工作,比如下面的经典案例:“假设页面上有5个div节点,我们通过循环来给每个div绑定onclick实践,按照索引顺序,点击第1个div时弹出0,点击第2个div弹出1,以此类推”:

<html><body><div>1</div><div>2</div><div>3</div><div>4</div><div>5</div><script>var nodes = document.getElementsByTagName("div");for (var i = 0, len = nodes.length; i < len; i++) {nodes[i].onclick = function () {alert(i);};}</script></body>
</html>

欻欻欻,这段简单的事件绑定就写好了,但是当你运行的时候,你就会发现吴伦点击哪个div,最后弹出的结果都是5,这是因为div节点的onclick事件被异步出发的,当事件触发的时候,for循环早就结束了,此时变量i的值已经是5,所以在div的onclick事件函数中顺着作用域链从内到外查找i的时候,查找到的值总是5。
深入理解JavaScript设计模式之闭包与高阶函数
上面的BUG可以通过闭包去解决,加入闭包,把每次循环的i值都封闭起来,当事件函数中顺着作用域链中从内到外查找i变量的时候,会先找到被封闭在闭包环境中的i,如果有5div,这里的i就分别是0,1,2,3,4

<html><body><div>1</div><div>2</div><div>3</div><div>4</div><div>5</div><script>var nodes = document.getElementsByTagName("div");for (var i = 0, len = nodes.length; i < len; i++) {(function (i) {nodes[i].onclick = function () {alert(i);return;};})(i);}</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数
根据上面了解的闭包机制,可以写一个通过闭包进行判断数据类型的案例

<html><body><script>var Type = {};for (var i = 0, type; (type = ["String", "Array", "Number"][i++]); ) {(function (type) {Type["is" + type] = function (obj) {return (Object.prototype.toString.call(obj) === "[object " + type + "]");};})(type);}console.log(Type.isArray([])); // 输出:trueconsole.log(Type.isString("str")); // 输出:true</script></body>
</html>

IIFE把当前的 type 值保存下来,这样每次循环都把当前的 type 传进一个新的函数作用域中,确保每个生成的方法都能正确记住自己的 type。运行完这段代码后,Type 对象变成了:

Type = {isString: function(obj) {return Object.prototype.toString.call(obj) === '[object String]';},isArray: function(obj) {return Object.prototype.toString.call(obj) === '[object Array]';},isNumber: function(obj) {return Object.prototype.toString.call(obj) === '[object Number]';}
};

输出:
深入理解JavaScript设计模式之闭包与高阶函数

闭包的更多作用:不只是谈恋爱,还能干活!

1、封装私有变量:你的变量我来守护

如果你想让某些变量不被外界随意修改,闭包就能帮你做到这一点,闭包可以把一些不需要暴露在全局的变量封装成"私有变量"。假设有一个计算乘积的简单函数:

 var mult = function () {var a = 1;for (var i = 0, l = arguments.length; i < l; i++) {a = a * arguments[i];}return a;};console.log(mult(2, 3, 4, 5));

深入理解JavaScript设计模式之闭包与高阶函数
上面实现了Mult的乘积函数,你会发现,每一次都会清空a,重新计算,每一次计算都是浪费,但是我们可以借用闭包的机制实现一个通过闭包实现的缓存机机制提高这个函数的性能:

<html><body><script>var mult = (function () {var cache = {};return function () {var args = Array.prototype.join.call(arguments, ",");if (args in cache) {return cache[args];}console.log("我被执行了");var a = 1;for (var i = 0, l = arguments.length; i < l; i++) {a = a * arguments[i];}return (cache[args] = a);};})();console.log(mult(1, 2, 3, 4, 5));console.log(mult(1, 2, 3, 4));console.log(mult(1, 2, 3, 4, 5));console.log(mult(1, 2, 3, 4, 6));console.log(mult(1, 2, 3, 4, 6));console.log(mult(1, 2, 3, 4, 6));</script></body>
</html>

效果:
深入理解JavaScript设计模式之闭包与高阶函数
不难发现,我调用了六次mult,但是乘积逻辑只运行了3次,将每次的乘积结果都缓存到cache中,每次先判断是否有,如果有运行返回结果,如果没有运行乘积逻辑。上面的乘积代码还可以进行优化,将乘积逻辑独立出来。

<html><body><script>var mult = (function () {var cache = {};var calculate = function () {console.log("我被执行了");// 封闭 calculate 函数var a = 1;for (var i = 0, l = arguments.length; i < l; i++) {a = a * arguments[i];}return a;};return function () {var args = Array.prototype.join.call(arguments, ",");if (args in cache) {return cache[args];}return (cache[args] = calculate.apply(null, arguments));};})();console.log(mult(1, 2, 3, 4, 5));console.log(mult(1, 2, 3, 4));console.log(mult(1, 2, 3, 4, 5));console.log(mult(1, 2, 3, 4, 6));console.log(mult(1, 2, 3, 4, 6));console.log(mult(1, 2, 3, 4, 6));</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数

2、延长变量的寿命:别着急销毁,我还想用

在数据上报业务中,如果你以为直接在函数里面创建了一个img对象就完事了,浏览器可能还没有把请求发送出去就把这个变量回收了,这个时候,我们可以用闭包吧img给锁住,确保请求一定能发出去。有关前端埋点上报内容直通车>>>>

var report = function( src ){ var img = new Image(); img.src = src; 
}; 
report( 'http://xxx.com/getUserInfo' );

上面代码中实现了原始图片上报的功能,但是在低版本的浏览器下使用report函数进行数据上报会数据丢失,也就是说,report函数并不是每次都发起了http请求,丢失数据原因是img是report函数中的局部变量,当report函数调用结束后,img局部变量就会被立即销毁,而此时还没来得及发出http请求就会导致数据丢失,接下来通过闭包将img变量封闭起来,就能解决了数据上报丢失的问题了:

var report = (function () {var imgs = [];return function (src) {var img = new Image();imgs.push(img);img.src = src;};
})();
report( 'http://xxx.com/getUserInfo' );

闭包就像是一个“拖延症患者”,不让变量轻易被销毁。

闭包的面向对象设计:换汤不换药

你说闭包厉害吧,其实它也能实现面向对象的一些功能,比如下面写一个计数器:

<html><body><script>function createCounter() {var count = 0;return {increment: function () {count++;},get: function () {return count;},};}var counter = createCounter();counter.increment();console.log(counter.get()); // 1</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数
是不是很像一个简单的类?只不过它是用闭包实现的,看到这里是不是觉得非常眼熟? 你想想,仔细想想哪里出现过这种结构的使用!我想你已经想到了,vuedata就是使用了类似于闭包的概念来封装和保护组件内的数据,但是区别在于vuedata更多的结合了闭包,响应式系统以及其他高级特性的综合方案。

用闭包实现命令模式:命令也能谈恋爱

我虽然学到第一部分,还没学到命令模式,但是经过大量的网页搜索得出:是一种行为型设计模式,主要用于将请求封装成对象,从而实现调用者和执行者之间的解耦,方便扩展和修改。命令模式的核心思想是将请求封装成一个对象,使命令的发起者和执行者分离。
命令模式本来是面向对象中的一种经典设计模式,在js中可以通过闭包来实现他,比如书中封装了一个打开电视关闭电视的命令模式:

<html><body><script>var tv = {open: function () {console.log("打开电视机");},close: function () {console.log("关上电视机");},};function createCommand(receiver) {return {execute: function () {receiver.open();},undo: function () {receiver.close();},};}var command = createCommand(tv);command.execute(); // 打开电视机command.undo(); // 关上电视机</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数
闭包在这里的作用就是吧接收者tv悄悄地保存起来,等需要的时候在进行调用。

闭包与内存管理:不是泄露,是你太执着

每次面试,问道闭包必会与“内存泄漏”拿出来一块说事,一听到闭包就会想:“哎呀,会内存泄漏啊!” 其实太冤枉闭包了,闭包之所以让变量“不死”,是因为你还需要他,这不是内存泄漏,而是你主动选择了保留变量。
但是如果你使用不恰当,比如你在DOM元素上面绑定了一个闭包,而这个闭包又引起了这个DOM元素本身,那这就形成了循环调用,这时候才可能造成内存泄漏的问题。
如果想解决内存泄漏问题其实非常的简单:只需要即使断开引用,让垃圾回收机制正常工作【myObject = null;】
一句话总结:闭包不是洪水猛兽,它只是一个有感情,有记忆,有责任的函数。

高阶函数

什么是高阶函数:你以为的函数 VS 真相

在 JavaScript 的世界里,函数不仅仅是个执行器,它是超级英雄。为什么?因为它可以:
上得了参数舞台(函数当参数),下得了返回结果(函数当返回值)这类能文能武、能攻能守的函数,被称为——高阶函数。

高阶函数作为参数传递:属实打工人

我们可以把函数当作参数传递,抽离出一部分容易变化的业务逻辑,把这部分业务逻辑放在函数参数中,这样一来分离业务代码种变化与不变的部分。其中非常常见和重要的应用场景就是常见的回调函数。“函数也能做回调?这不是打工人吗!”

回调函数

在以往Ajax请求的使用中,回调非常非常频繁,大名鼎鼎的“回调地狱”就处至ajax应用出现的问题,当我们想在ajax请求返回之后做一些事情,但是又不知道请求返回的时间,最常见的方案就是把callBack函数当作参数传入发起ajax请求的方法中,等待请求完成后执行callBack的函数

 var getUserInfo = function (userId, callback) {$.ajax("http://xxx.com/getUserInfo?" + userId, function (data) {if (typeof callback === "function") {callback(data);}});};getUserInfo(13157, function (data) {alert(data.userName);});

看着像正常 AJAX,其实背地里 Callback 像极了“我写好了函数,结果还得把逻辑交给别人决定”,属实打工人无疑了。

典型应用:创建 100div,然后把这些div设置成隐藏,正常想法是将创建div与隐藏div的代码硬编码到appendDiv的创建函数中,这显然不合理更不够灵活,更更难以服用,于是可以将隐藏代码抽离出来,用回调的形式传入appendDiv方法:"哦~ 雅,实在太雅!!真的不要太雅!!!"

<html><body><script>var appendDiv = function (callback) {for (var i = 0; i < 100; i++) {var div = document.createElement("div");div.innerHTML = i;document.body.appendChild(div);if (typeof callback === "function") {callback(div);}}};appendDiv(function (node) {console.log("回调隐藏");node.style.display = "none";});</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数

Array.prototype.sort

Array.prototype.sort接收一个函数当作参数,这个函数里面封装了数组元素的排序规则,从Array.prototype.sort这个API可以看淡,这个API不变的是对数组进行排序,而需要变是使用什么规则排序是可变的,把可变的部分封装在函数里面,动态传入,使Array.prototype.sort方法成为了一个非常灵活好用的方法。

//从小到大排列
[ 1, 4, 3 ].sort( function( a, b ){ return a - b; 
}); // 输出: [ 1, 3, 4 ] //从大到小排列
[ 1, 4, 3 ].sort( function( a, b ){ return b - a; 
}); // 输出: [ 4, 3, 1 ]

函数作为返回值输出:什么函数还能生孩子?

啥玩意? 函数还能生孩子? 哦哦哦,看错了,原来是返回函数呀!相比把函数当作参数传递,函数当作返回值输出的应用场景那可更多了,让函数继续生孩子传宗接代,哦,不对,是让函数继续返回一个可执行函数,那不就代表着运算过程可以一直延续了嘛!!!

判断数据类型

小白写法:

<html><body><script>var isString = function (obj) {return Object.prototype.toString.call(obj) === "[object String]";};var isArray = function (obj) {return Object.prototype.toString.call(obj) === "[object Array]";};var isNumber = function (obj) {return Object.prototype.toString.call(obj) === "[object Number]";};console.log(isArray([])); // 输出:trueconsole.log(isString("str")); // 输出:trueconsole.log(isNumber(1)); // 输出:true</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数
这种虽然也可以进行类型的判断,但是不难发现,这些函数大部分实现都是相同的,不同的只是 Object.prototype.toString.call( obj )返回的字符串。为了避免多余的代码,我们尝试把这些字符串作为参数提前值入 isType,通过循环批量注册isType函数,大佬写法:

<html><body><script>var Type = {};for (var i = 0, type; (type = ["String", "Array", "Number"][i++]); ) {(function (type) {Type["is" + type] = function (obj) {return (Object.prototype.toString.call(obj) === "[object " + type + "]");};})(type);}Type.isArray([]); // 输出:trueType.isString("str"); // 输出:true</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数

单例模式getSingle

javaScript中的单例模式大概意思是一种确保类只有一个实例,并提供全局访问点的方式。由于 JavaScript 的语言特性(如对象字面量、模块系统等),实现单例有多种方式,接下来通过高阶函数实现一个getSingle的单例模式:

<html><body><script>var getSingle = function (fn) {var ret;return function () {return ret || (ret = fn.apply(this, arguments));};};var getScript = getSingle(function () {return document.createElement("script");});var script1 = getScript();var script2 = getScript();console.log(script1 === script2); // 输出:true</script></body>
</html>

上面代码是通过闭包+高阶函数实现了一个惰性单例模式,确保某个程序运行过程中最多执行一次,并且始终返回相同的实例。妈的,真特娘优雅!!!
深入理解JavaScript设计模式之闭包与高阶函数

高阶函数AOP(面向切面编程):前后偷摸加戏AOP

所谓的AOP就是面向切面编程,就是原本安安静静写个函数,突然前后各插一脚:

<html><body><script>Function.prototype.before = function (beforefn) {var __self = this; // 保存原函数的引用return function () {// 返回包含了原函数和新函数的"代理"函数beforefn.apply(this, arguments); // 执行新函数,修正 thisreturn __self.apply(this, arguments); // 执行原函数};};Function.prototype.after = function (afterfn) {var __self = this; // 保存原函数的引用return function () {var ret = __self.apply(this, arguments);afterfn.apply(this, arguments);return ret;};};var func = function () {console.log(2);};func = func.before(function () {console.log(1);}).after(function () {console.log(3);});func();</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数
上面函数把一个函数“动态织入”到另外一个函数中。

高阶函数的其他应用

函数柯里化:我先不执行,等等再说

面试中,如雷贯耳的curring【函数柯里化】又称部分求值,一个curring的函数首先会接收一些参数,接收这些参数之后,该函数并不会立即执行,而是继续返回另外一个函数,刚传入的参数在函数形成的闭包中被保存起来,等到函数被真正需要求值得时候,之前传入的所有参数都会被一次性求值。
案例一、编写一个计算每月开销得函数,在每天结束之前,都要记录今天花了多少钱;
没接触过函数柯里化之前:

 var monthlyCost = 0;var cost = function (money) {monthlyCost += money;};cost(100); // 第 1 天开销cost(200); // 第 2 天开销cost(300); // 第 3 天开销//cost( 700 ); // 第 30 天开销console.log(monthlyCost); // 输出:600

深入理解JavaScript设计模式之闭包与高阶函数
虽然通过上面代码也能实现,但是我不想关心每天花多少钱,我只想在月底统一计算一次,如果每月的前29天,我们都只是保存好当天的开销,直到30天才会进行求值计算,这样就达到了要求,函数柯里化写法如下:

<html><body><script>var cost = (function () {var args = [];return function () {if (arguments.length === 0) {var money = 0;for (var i = 0, l = args.length; i < l; i++) {money += args[i];}return money;} else {[].push.apply(args, arguments);}};})();cost(100); // 未真正求值cost(200); // 未真正求值cost(300); // 未真正求值cost(300); // 未真正求值console.log(cost()); // 求值并输出:600</script></body>
</html>

上面还是不是一个标准的柯里化函数,接下来我们实现一个任意参数链式调用的标砖柯里化函数,这个函数将传入的函数 fn 转换为一个“延迟执行”的柯里化版本。它会收集所有传入的参数,直到你调用时不带参数,才真正执行原函数。
解析:当调用cost()的时候,如果明确带上了参数,表示此时并不是真的在计算结果,而是将参数保存起来,此时让const函数返回另外一个函数,只有当不带参数的形式执行cost()时,才会计算结果,通过调用return arguments.callee;,每调用一次 cost(...),它都会返回自己(即 arguments.callee),所以可以继续链式调用【cost(300, 300)(300)()】

<html><body><script>var currying = function (fn) {console.log("我被执行了");var args = [];return function () {if (arguments.length === 0) {return fn.apply(this, args);} else {[].push.apply(args, arguments);return arguments.callee;}};};var cost = (function () {var money = 0;return function () {console.log("我cost执行了", arguments);for (var i = 0, l = arguments.length; i < l; i++) {money += arguments[i];}return money;};})();var cost = currying(cost); // 转化成 currying 函数cost(100); // 未真正求值cost(200); // 未真正求值cost(300)(50); // 未真正求值console.log(cost(300, 300)(300)()); // 未真正求值</script></body>
</html>

深入理解JavaScript设计模式之闭包与高阶函数

非函数柯里化:函数世界的开放平台

uncurring不像curring那样拆分参数,而是相反把一个原来只能为某个对象服务的方法变成都能使用的通用函数,比如数组对象得push函数,其实push不止只能为数组服务,也可以为其他对象服务只要你有和数组一样的特性即可使用,你可以理解为“去标签化”,js本来就是鸭子语言,只要你会“嘎嘎叫”,你就是只鸭子,所以,不止数组才能调用push,如:

(function() {Array.prototype.push.call(arguments, 4);console.log(arguments); // [1, 2, 3, 4]
})(1, 2, 3);

上面代码中就是一个类数组对象,调用了专属于push得方法,但是上面每次都要写.call(obj,...)实在是太麻烦,就像你每次吃火锅都要带上锅和调料,于是uncurring就诞生了,类似于函数世界里的开放平台一样。
uncurring把一个方法从它的原生对象中解放出来,让他为所有人都能使用,比如Array.prototype.push(obj,value),你会发现每次让 obj 借用 Array.prototype.push 方法的功能都比较麻烦,但是你使用uncurring后会变得非常清爽,直接push(obj,value)即可,原先push为个人服务,经过uncurring的解放后,push可以为人民服务

其实实现这个`uncurring也不复杂,看代码:

Function.prototype.uncurrying = function () {var self = this;return function () {var obj = Array.prototype.shift.call(arguments); // 拿第一个参数当 thisreturn self.apply(obj, arguments); // 然后执行原方法};
};

在所有函数原型后面添加一个uncurring方法,先记录下该方法的this,然后返回一个匿名函数,拿到第一个参数当作this,然后执行原方法,即可解放数组中所有的方法。

<html><body><script>Function.prototype.uncurrying = function () {var self = this;return function () {var obj = Array.prototype.shift.call(arguments); // 拿第一个参数当 thisreturn self.apply(obj, arguments); // 然后执行原方法};};var push = Array.prototype.push.uncurrying();var obj = { length: 0 };push(obj, "hello");push(obj, "hello1");push(obj, "hello2");push(obj, "hello3");push(obj, "hello4");console.log(obj); // { '0': 'hello', length: 1 }</script></body>
</html>

你想用我的方法?没问题,请把你要代表的对象第一个传进来,我来假装它是 this,如下运行结果
深入理解JavaScript设计模式之闭包与高阶函数

致敬—— 《JavaScript设计模式》· 曾探

完~

http://www.xdnf.cn/news/884287.html

相关文章:

  • 【JVM】三色标记法原理
  • VisDrone无人机视觉挑战赛观察解析2025.6.5
  • 无人机避障与视觉跟踪技术分析!
  • 装备制造项目管理具备什么特征?如何选择适配的项目管理软件系统进行项目管控?
  • Spring Boot + Elasticsearch + HBase 构建海量数据搜索系统
  • 【数据分析】基于adonis2与pairwise.adonis2的群组差异分析教程
  • vue-router路由问题:可以通过$router.push()跳转,但刷新后又变成空白页面
  • Uniapp 二维码生成与解析完整教程
  • Spring IoC 详解:原理、实现与实战
  • 【Go语言基础【3】】变量、常量、值类型与引用类型
  • Excel处理控件Aspose.Cells教程:使用 C# 从 Excel 进行邮件合并
  • [Git] 文件删除
  • 五、查询处理和查询优化
  • 自动驾驶TPM技术杂谈 ———— 车辆安全设计思考维度
  • 中阳视角下的资产配置趋势分析与算法支持
  • 使用ArcPy进行栅格数据分析(2)
  • MPLAB X IDE ​软件安装与卸载
  • ocrapi服务docker镜像使用
  • 嵌入式学习笔记DAY33(网络编程——TCP)
  • 三格电子SG-UHF-80系列:工业自动化的超高频RFID革新力量
  • 软考 系统架构设计师系列知识点之杂项集萃(82)
  • 【Netty4核心原理⑧】【揭开Bootstrap的神秘面纱 - 服务端Bootstrap❶】
  • 计算机网络自顶向下期末复习:第一章
  • 3D模型格式转换工具HOOPS Exchange赋能大型资产建模平台:多源CAD数据访问与转换!
  • XDMA pcie环路测试
  • SQL SERVER中获取外部数据的两种方法!
  • 企业数据一致性难题的根源探究
  • 【Java工程师面试全攻略】Day5:MySQL数据库面试精要
  • Hive的TextFile格式优化方法
  • 【Go语言基础【四】】局部变量、全局变量、形式参数