JS笔记:函数闭包及柯里化

本文探讨了JavaScript中的闭包特性,包括其在保存函数执行状态、封装和性能优化方面的作用。同时,介绍了函数作为一等公民的特性,如作为参数和返回值的使用,特别是`Function.prototype.bind`方法的应用。接着,文章详细阐述了函数柯里化(Currying)的概念,展示了如何将多参数函数转换为接受单一参数并返回新函数的技巧,以实现参数的分步传递和逻辑的分解,提高代码可读性和复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闭包:

闭包的功能
1、保存函数执行状态

将字符串中的一些特定字符按顺序用数组中的元素替换,例如:
 * var arr = ['c','f','h','o'];
 * var str = 'ab4de8g4ijklmn7';
 * 替换后 str == 'abcdefghijklmno';
 * replace的用法请参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace
**/
// var arr = ['c','f','h','o'];
// var str = 'ab4de8g4ijklmn1';
// console.log(str);

// var func = (function(){
//   // count变量会保存在闭包作用域内,表示func被调用次数(即正在替换第几个字符)
//   var count = 0; 
//   return function(){
//     return arr[count++]; 
//   }
// })();

// str = str.replace(/\d/g, func)
// console.log(str);

2、封装

闭包使用举例2 -- 封装
1.暴露type类型和start, stop, getStatus方法
2.隐藏status,light对象状态
**/
var Car = function(type){
  var status = "stop",
      light = "off";
  return {
    type: type,
    start: function(){
      status = "driving";
      light = "on";
    },
    stop: function(){
      status = "stop";
      light = "off";
    },
    getStatus: function(){
      console.log(type + " is " + status + " with light " + light);
    }
    }
}

var audi = new Car("audi");
audi.start();
audi.getStatus();
audi.stop();
audi.getStatus();

3、性能优化

//不使用闭包
function sum(i, j) {
  var add = function(i, j){
    return i+j;
  }
  return add(i, j)
}
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
  sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);

// 使用闭包.不需要保存状态的函数使用闭包
var sum = (function() {
  var add = function(i, j){
    return i+j;
  }
  return function(i,j) {
    add(i, j);
  }
})()
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
  sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);

First-class function
函数可以当做普通变量使用
函数作为参数
异步回调函数
函数作为返回值

Function.prototype.bind function原型对象上的一个方法,所有函数都可以调用
用法同apply
区别:bind不会立即调用 。bind保存的函数的引用,可以接受参数。

move函数实现移动平面图上一个点位置功能
var move = function(x,y){
    this.x += x;
    this.y += y;
}
// // 定一个点p
var p = {x:1, y:1}; 
// bind方法收受一个参数,并返回一个接受余下参数的函数过程。此时p点并没有移动。
var pmove0 = move.bind(p); 
console.log(p);
pmove0(1,2); 
console.log(p);// 这时p移动到了(2,3)位置。

// 当然你也可以这样调用
var pmove1 = move.bind(p,1);//只传递一个参数
console.log(p);
pmove1(2);//这里再传一个参数
console.log(p);

或者这样调用
var pmove2 = move.bind(p,1,2);
console.log(p);
pmove2();
console.log(p);

函数的柯里化 curry
1. 函数柯里化通常是指把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的并且返回一个接受余下的参数而且返回结果的新函数的技术。*/

1、最简单的柯里化
sum函数接受三个参数,并返回求和结果

var sum = function(a,b,c) {
    return a+b+c;
}
// 最简单柯里化的sum函数
var sum_curry = function(a){
    return function(b,c){
        return a+b+c;
    }
}
  1. 更泛化的定义是指给函数分步传递参数,每次函数接受部分参数后应用这些参数,并返回一个函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数的函数,直至返回最后结果。归纳一下就是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。
// 2. 泛化的柯里化
// currying实现将一个函数转变为柯里化函数
var currying = function (fn) {
   var _args = [];
   return function () {
    if (arguments.length === 0) {
      // 实现最终的计算
      return fn.apply(this, _args); 
    }
    // 这里只是简单的将参数缓存起来(用于解释柯里化概念,并非实际应用场景)
    Array.prototype.push.apply(_args, [].slice.call(arguments)); 
    return arguments.callee;
   }
};
// sum函数接受任意参数,并返回求和结果
var sum=function () {
   var total = 0;
   for (var i = 0, c; c = arguments[i++];) {
       total += c;
   }
   return total;
};
// 或得一个泛化柯里化的sum函数
var sum_curry = currying(sum); 
sum_curry(1)(2,3);
sum_curry(4);
console.log(sum_curry());
  1. 从更上层的角度去理解,柯里化允许和鼓励你将一个复杂过程分割成一个个更小的更容易分析的过程(这些小的逻辑单元将更容易被理解和测试),最后这样一个难于理解复杂的过程将变成一个个小的逻辑简单的过程的组合。
### JavaScript 中的柯里化概念 柯里化是一种将接受多个参数的函数转换成一系列只接受单个参数的函数的技术。通过这种方式,可以轻松创建自定义函数,只需部分应用现有函数即可[^1]。 #### 实现方式 以下是基于引用内容的一个简单实现: ```javascript Function.prototype.curry = function(...args) { const fn = this; return function curried(...moreArgs) { if (moreArgs.length === 0) { return fn.apply(null, args); } return curried.bind(null, ...args.concat(moreArgs)); }; }; // 使用示例 var add = function(a, b) { return a + b; }; var addTen = add.curry(10); // 创建一个新函数,固定第一个参数为10 console.log(addTen(20)); // 输出 30 ``` 在这个例子中,`curry` 方法被挂载到 `Function.prototype` 上,允许任何函数都可以调用 `.curry()` 来生成一个新的柯里化的版本。每次调用都会累积参数直到满足原函数的要求为止[^1]。 另一种更通用的方式如下所示: ```javascript const curry = (fn) => { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function (...nextArgs) { return curried.apply(this, args.concat(nextArgs)); }; } }; }; // 测试案例 let multiplyThreeNumbers = (a, b, c) => a * b * c; let curriedMultiply = curry(multiplyThreeNumbers); console.log(curriedMultiply(2)(3)(4)); // 结果应该是 24 ``` 此版本利用闭包特性逐步收集所需参数直至达到目标函数签名所指定的数量后才最终求值[^5]。 ### 注意事项 尽管 JavaScript 并不像某些其他编程语言那样严格依赖接口来定义行为契约[^4],但在实际开发过程中合理运用诸如柯里化这样的高阶技术能够显著提升代码可读性复用率。然而也要注意过度抽象可能导致不必要的复杂度增加以及性能开销等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值