js闭包与状态管理

308 阅读2分钟

  闭包在前端领域内算是老生常谈的一个话题了,基于我去解读zustand源码,我特定将闭包这个重新进行学习整理,已做到知其所以,知其所以然;

1、闭包概念

  闭包可以这样说:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行;总结为一下两点

  • 1、函数可以记住当前所在的词法作用域;
  • 2、函数脱离当前词法作用域,也可以记住之前的词法作用域;

下面我们判断一个例子:

function foo(){
  var a = 12
  function bar(){
    console.log(a)
  }
  bar()
}
foo()

  我们判断一下他是否形成闭包了呢?答案是没有,这块只不过是一个正常的的foo函数调用,bar函数通过作用域查找方式进行了执行,严格的说并不算闭包,只是有一个闭包的雏形;

我们在看看下面一段代码:

function foo(){
  var a = 12
  function bar(){
    console.log(a)
  }
  return bar
}
const fn1 = foo()
fn1() // 2

  当foo被执行后,理论上,foo在后续的代码找中没有引用,垃圾回收机制就会自动回收,然而bar中引用了foo函数作用的变量,导致无法进行垃圾回收,当然我们执行fn1就会再次访问到bar的作用域,因此我们可以通过console.log(a)可以拿到foo值;

2、闭包与模块模式

  我们通过下面方式来去说明代码结构

function todo() {
    let states = [];

    const getState = (i) => {
        console.log(states[i], 'getState');
        return states[i] || null;
    };

    const pushState = (data) => {
        states.push(data);
        return true;
    }

    const removeState = (i) => {
        return states.splice(i, 1) || null;
    }

    return { getState, pushState, removeState };
}


const myTodo = todo();

myTodo.pushState('just todo something');

myTodo.getState(0);

  todo这个函数的方式,在javascript中称之为模块模式,我们通过调用todo的方式,让其内部形成闭包,达到缓存数据的办法;

3、关于状态管理工具的原理

  最近读完了zustand源码,他的状态管理办法就是主题代码就是用到闭包(参考上面的模块模式)及发布订阅者的方式实现的,我大概写了toy版本的状态管理核心代码,具体如下:

function createStore(createState) {
    let state;
    let listeners = new Set();
    // 获取store内容
    const getState = () => state;
   // 设置store内容值
    const setState = (partial, replace) => {

        const nextState = typeof partial === 'function' ? partial(state) : partial;

        if (nextState !== state) {
            const prevState = state;
            state = replace ? nextState : Object.assign({}, state, nextState);
            listeners.forEach(listener => listener(state, prevState));
        }
    }
    // 添加订阅信息
    const subscribe = (listener) => {
        listeners.add(listener);
        return () => listeners.delete(listener);
    }
    // 清除所有的listener
    const destroy = () => listeners.clear();
    
    const api = {getState, setState, destroy, subscribe};
    // 创建初始的state
    state = createState(setState, getState, api);

    return api;
}

const store = createStore((setState) => ({
    count: 0,
    increatment: () => setState(state => ({count: state.count + 1})),
}));

store.getState().increatment();

console.log(store.getState().count);