从EC/VO/AO堆栈内存和作用域角度分析compose函数 | 跟着redux大佬学编程思想

274 阅读3分钟

0 / 热热身

function fun(n, o) {
    console.log(o);
    return {
        fun: function (m) {
            return fun(m, n);
        }
    };
}
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

△ 答案是?

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

△ 图3.7_图解

1 / redux 中的compose函数

function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg
    }

    if (funcs.length === 1) {
        return funcs[0]
    }

    return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

△ redux中的compose函数

我们普通写调用函数,但是可读性太差

const add1 = x => x + 1;
const mul3 = x => x * 3;
const div2 = x => x / 2;
div2(mul3(add1(add1(0)))); //=>3

△ 普通调用函数

那么,需要写一个可读性较高的组合函数:

const operate = compose(div2, mul3, add1, add1);
operate(0);

△ 调用compose函数

compose(div2, mul3, add1, add1)传参的顺序与div2(mul3(add1(add1(0)))) 调用函数的顺序关系

那么,我们上次写的reduceRight从里面往外调用函数实现组合函数的调用

然而,redux使用的是reduce按照输入参数的顺序调用的

咱就从EC/VO/AO 堆栈内存作用域的角度,一步一步分析就好

2 / 逐步分析

咱们就根据:执行上下文、作用域、作用域链、VO、AO这些一步步分析即可

// funcs=[div2,mul3, add1, add1] 函数集合
//  return funcs.reduce((a, b) => (...args) => a(b(...args)))
return funcs.reduce((a, b) => {
    let fn = (x) => {
        return a(b(x));
    };
    return fn;
});

△ 分析reduce的写法

(1)compose()函数调用

【0】EC(C001) 假设这是compse() 执行形成的执行上下文

operate 是一个函数

那么 ,compose return 出去的是一个函数

let result = funcs.reduce((a,b)=>{
    return function anonymous(x){
        return a(b(x));
    };
});

△ result 得到的是一个函数

result 接收到的结果是一个函数

此时,需要通过reduce每次调用callback形成 函数私有上下文

在每次的函数的私有上下文中,都会创建一个匿名函数

每个匿名函数所处的作用域是不同的

代码执行到:funcs.reduce(callback)

① reduce第一轮遍历

**【1】 第一轮遍历 EC(CB1)**私有执行上下文

AO(CB1) 变量对象

​ a=div2

​ b=mul3

​ anonymous=0xA001

作用域链:<EC(CB1), EC(C001)>

形参赋值:a=div2; b=mul3

变量提升:anonymous=0xA001

代码执行:

return function anonymous(x){
    a(b(x));
}; //=====> 【return 0xA001】;

△ 第一轮循环返回的值

② reduce第一轮遍历

【2】第二轮遍历 EC(CB2) 私有执行上下文

AO(CB2) 变量对象

​ a=0xA001

​ b=add1

​ anonymous=0xA002

作用域链:<EC(CB2), EC(C001)>

形参赋值:a=0xA001; b=add1

变量提升:anonymous=0xA002

代码执行:

return function anonymous(x){
    a(b(x));
}; //=> 【return 0xA002】;

△ 第二轮循环返回的值

③ reduce第三轮遍历

**【3】第三轮遍历 EC(CB3)**私有执行上下文

AO(CB3) 变量对象

​ a=0xA002

​ b=add1

​ anonymous=0xA003

作用域链:<EC(CB3), EC(C001)>

形参赋值:a=0xA003; b=add1

变量提升:anonymous=0xA003

代码执行:

return function anonymous(x){
    a(b(x));
}; //=> 【return 0xA003】;

△ 第三轮循环返回的值

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

(4)reduce遍历结束后,赋值

三轮遍历结束后,把0xA003 赋值给operate

operate(0) 执行

③ 0xA003(0) 执行

【3】EC(0xA003)

AO(0xA003)

​ x=0

作用域链:<EC(0xA003),EC(CB3) >

形参赋值:x=0

代码执行:

a(b(x));

​ => x 是自己的:x=0; a和b都是上级上下文的

​ => a=0xA002

​ => b=add1

​ ==> 0xA002(add1(0))

​ => add1(0) => x+1=1

​ => add1(0) 就当它是最后结果了,为了最后看到的效果是一样的,就不写计算了

​ => 0xA002() 调用

② 0xA002() 调用

【2】EC(0xA002)

AO(0xA002)

​ x = add1(0)

作用域链:<EC(0xA002),EC(CB2) >

形参赋值:x=add1(0)

代码执行:

a(b(x));

​ => x 是自己的:x=add1(0);a和b都是上级上下文的

​ => a=0xA001

​ => b=add1

​ ==> 0xA001(add1(add1(0)))

​ => add1(add1(0))就当是计算后的结果了

​ => 0xA001() 调用

① 0xA001() 调用

【1】EC(0xA001)

AO(0xA001)

​ x = add1(add1(0))

作用域链:<EC(0xA001),EC(CB1) >

形参赋值:x = add1(add1(0))

代码执行:

a(b(x));

​ => x 是自己的:x=add1(add1(0)); a和b都是上级上下文的

​ => a=div2

​ => b=mul3

​ ==> div2(mul3(add1(add1(0))))

即:div2(mul3(add1(add1(0))))

公众号:朝霞的光影笔记 ID:zhaoxiajingjing

- end -


把你的心 我的心串一串

串一株幸运草 串一个同心圆