这篇文章实际是 ES6 let 和 const深入分析 这篇文章的扩展,建议阅读后再看本篇文章。

在上篇文章中讲过,js中,使用const定义基本数据类型,值存储在栈内存当中,const保证的是栈内存中的值不被改变

如果使用const定义引用数据类型,栈内存中保存的仅仅是一个地址,而定义的数组、对象的具体值,实际上保存在堆内存中,就算使用const定义过,我们也可以二次修改数组、对象等引用数据类型的值。

实际上单纯使用const,似乎并不能达到我们所认为的完全只读,在某些业务场景下,我们想要的,是一旦定义,就不可被更改,达到真正意义上的只读!

Object.freeze()方法点击查看MDN官方说明

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

具体例子:

1、const直接定义一个对象

const _personObj = {
    name: "JiaJia"
};

_personObj.name = 'Tom';

console.log(_personObj);

定义一个对象,其中的name值被修改,看看打印出什么

name最终被修改为'Tom';

2、const定义时,结合Object.freeze()方法

const _personObj = Object.freeze({
    name: "JiaJia"
});

_personObj.name = 'Tom';

console.log(_personObj);

对象果然被冻结了,中途就算修改了name的值,最终还是保持定义时的值'JiaJia';


仅仅使用Object.freeze()方法,这么简单就结束了吗?不,继续看一个例子:

const _personObj = Object.freeze({
    someOne_1: "JiaJia",

    someOne_2: {
        name: "Jack"
    }
});

_personObj.someOne_1 = 'Tom';

_personObj.someOne_2.name = 'Rose';

console.log(_personObj);

可以看到,someOne_1的值,保持定义时的'JiaJia',中途无法修改;

但 someOne_2中name的值,从最初的'Jack'被改变为'Rose',中途可以被修改;

使用Object.freeze()方法不是已经冻结了对象么,为什么对象中的对象,值依然能被修改?

其实目前做的,只能算作 “浅冻结”,对象中的对象,其实真正的值,又放在堆内存当中了;

浅冻结只能冻结当前对象、数组等内部第一层,如果第一层中,还嵌套着对象、数组等,那么这种更深的层级,浅冻结是无法干预到的;

这时候就要请出“深冻结”,不论内部有多少层,每层不管嵌套着多少对象、数组,通通冻结:

/**
 * @description: 深度冻结
 * @param {Object} obj 需要深度冻结的对象
 * @return {Object} 经过深度冻结后的对象 
 */
const deepFreeze = (obj) => {
    // 取回定义在obj上的属性名
    const propNames = Object.getOwnPropertyNames(obj);

    // 在冻结自身之前冻结属性
    propNames.map((value, index, array) => {
        const prop = obj[value];

        //如果prop是个对象,冻结它
        if (typeof prop == 'object' && prop !== null){
            deepFreeze(prop);
        }
    });

    // 冻结自身(no-op if already frozen)
    return Object.freeze(obj);
};

const _personObj = deepFreeze({
    someOne_1: "JiaJia",

    someOne_2: {
        name: "Jack"
    }
});

_personObj.someOne_1 = 'Tom';

_personObj.someOne_2.name = 'Rose';

console.log(_personObj);

这里定义了一个函数deepFreeze,为了使对象不可变,需要递归冻结每个类型为对象的属性(深冻结),这样不论对象或数组其中嵌套了多少层,都可以通过递归一一冻结,看看结果:

经过深度冻结,中途不论怎么修改值,值都不受影响,仍然保持最初时的值,达到了真正意义上的“只读”。