浅解函数柯里化及其相关知识点

堆栈

堆是堆内存的简称,栈(先进后出)是栈内存的简称。

堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放.

js数据类型

  • 基本数据类型
    Undefined、Null、Boolean、String、Number、Symbol都是直接按值直接存在栈中,每种类型的数据占用的内存空间大小都是固定的,并且由系统自动分配自动释放
  • 引用数据类型
    Object,Array,Function这样的数据存在堆内存中,但是数据指针是存放在栈内存中的,当我们访问引用数据时,先从栈内存中获取指针,通过指针在堆内存中找到数据

代码示例:

let arr_origin = [1,2,3,4,5];
let arr_copy = arr_origin;
let arr2 = arr_origin[2];

执行代码:

arr_copy[1] = 'change1';
arr2 = ‘change2’

我们发现arr_origin中的下标是1的值变成可change1,但是下标是2的值并未变化。原因是:arr_copy是arr_origin栈指针的引用,并未开辟新的内存空间,但是arr2是在栈内存中独立存在,所以会产生上述结果。熟悉此原理,则数据的深浅拷贝就不难理解了!

闭包

定义:能够访问另一个函数作用域的变量的函数
eg:

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
    return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}

为什么闭包函数能够访问其他函数的作用域 ?

var a = 1;
function fn(){
    var b = 2;
    function fn1(){
        console.log(b);
    }
    fn1();
}
fn();

1、在执行fn前,此时我们在全局执行环境(windows作用域),且环境里有个变量a
2、执行fn,此时栈内存就会push一个fn的执行环境,这个环境里有变量b和函数对象fn1,这里可以访问自身执行环境和全局执行环境所定义的变量
3、进入fn1,此时栈内存就会push 一个fn1的执行环境,这里面没有定义其他变量,但是我们可以访问到fn和全局执行环境里面的变量,因为程序在访问变量时,是向底层栈一个个找,如果找到全局执行环境里都没有对应变量,则程序抛出underfined的错误。
4、随着fn1()执行完毕,fn1的执行环境被杯销毁,接着执行完fn(),fn的执行环境也会被销毁,只剩全局的执行环境下,现在没有b变量,和fn1函数对象了,只有a 和 fn(函数声明作用域是window下)

当程序在调用某个函数时,做了一下的工作:准备执行环境,初始函数作用域链和arguments参数对象

由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多。

call和apply

作用:一般来说,this总是指向调用某个方法的对象,call和apply可以改变this指向

区别:

apply(thisObj,[arg1,arg2,…])
call(thisObj,arg1,arg2,…)

eg1:

function test () {
    console.log(123);        
}
test.call()//123

eg2:
定义一个构造函数

function Person (name, age) {
    this.name = name;
    this.age = age;
}

可以这样用

var person1 = new Person('洋哥', 20);

也可以这样用

var person2 = {};
Person.call(person2, '洋哥', 20) // 效果和上面的一模一样

eg3:

var myObject = {firstName:'my', lastName:'Object'};
function getMessage(sex,age){
    console.log(this.firstName + this.lastName + " 性别: " + sex + " age: " + age );
}
getMessage.call(myObject,"未知",22);

函数柯里化

定义

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术

就是只传递给函数某一部分参数来调用,返回一个新函数去处理剩下的参数(闭包

类数组转换为真正数组

function list() {
  return [].slice.call(arguments,1);;
}
console.log(list(1, 2, 3));//[2,3]

slice 在不接受任何参数的时候会返回 this 本身
arguments是类数组,具有长度属性,但不是数组
那就意味着 arguments.slice()行不通
call(arguments,1)改变this指向,将arguments把this给slice,从1开始切割所以返回[2,3]

简单的add函数

let add = function(x, y){
 return x + y
}

add函数柯里化

var addCurrying = function(x) {
  return function(y) {
    return x + y
  }
}
var increment = addCurrying(1)
var addTen = addCurrying(10)
increment(2)// 3
addTen(2)// 12

柯里化函数示例

eg1:

/**
  * @description 柯里化函数
  * @param {Function} fn 传入的需要运算的函数
  * @return {Function} 返回函数
  */
 let curry = function (fn) {//fn接收传入的第一个参数,即add函数
   let reuseArgs = Array.prototype.slice.call(arguments, 1);//除add函数之外的其他参数数组 [3,4,5]
   return function () {
     // selfArgs是自身参数
     let selfArgs = Array.prototype.slice.call(arguments);//接收到的curryAdd函数传入的值转化为数组 [6]
     // 合并自身参数和复用的参数
     let mergeArgs = reuseArgs.concat(selfArgs)
     // 计算返回结果
     return fn.apply(null, mergeArgs)//arguments为Object类型,所以此处把null的this指向fn
   }
 }
 /**
  * @description 求和函数
  * @return { Number } 和
  */
 let add = function () {
   let args = Array.prototype.slice.call(arguments);//类数组转数组
   let sum = 0
   for (let i = 0; i < args.length; i++) {
     sum += args[i]
   }
   return sum;//返回和
 }
 // 3, 4, 5是固定不变,可复用的参数
 let curryAdd = curry(add, 3, 4, 5)
 curryAdd(6) // 结果为18
 curryAdd(7, 8) // 结果为27

eg2:

// 支持多参数传递
function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);
    
        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }
    
        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    }
}

eg3(经典面试题):

// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回  
    //每个对象的toString和valueOf方法都可以被改写,每个对象执行完毕,如果被用以操作JavaScript解析器就会自动调用对象的toString或者valueOf方法
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;//注意此处不带(),表示执行对象,隐式执行reduce函数
}

add(1)(2)(3)                // 6
add(1, 2, 3)(4)             // 10
add(1)(2)(3)(4)(5)          // 15
add(2, 6)(1)                // 9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值