一、Object.freeze()方法来冻结对象,防止对象被修改
Object.freeze() 是JavaScript中的一个方法,用于冻结一个对象。被冻结的对象不能再被修改。具体来说,它做了两件事情:
防止添加新的属性:尝试添加新属性将失败,不会抛出错误,但新属性不会被添加到对象中。
防止删除属性:尝试删除对象的任何属性都将失败,不会抛出错误。
但请注意,它不会防止修改对象已有的属性值。也就是说,你仍然可以更改、替换或修改对象上现有的属性。
我们都知道const定义基本数据类型,这个值是不可以修改的。那么我们用const定义对象,可以修改对象吗?
const a = 5 // a = 10 // TypeError: Assignment to constant variable. const obj = { name: '张三' } obj.name = '李四' console.log(obj) // {name: "李四"}
答案是肯定的。那么如果我们想定义一个不可被修改的对象,应该怎么办呢!
那就要用到Object.freeze()了。
它的作用是冻结一个对象,被冻结的对象有以下几个特性:
- 不能添加新属性
- 不能删除已有属性
- 不能修改已有属性的值
- 不能修改原型
- 不能修改已有属性的可枚举性、可配置性、可写性
1、基本使用
var obj = { name: '张三', age: 18, address: '上海市' } obj.__proto__.habit = '运动' // 冻结对象 Object.freeze(obj) // 不能添加新属性 obj.sex = '男' console.log(obj) // {name: "张三", age: 18, address: "上海市"} // 不能删除原有属性 delete obj.age console.log(obj) // {name: "张三", age: 18, address: "上海市"} // 不能修改已有属性的值 obj.name = '李四' console.log(obj) // {name: "张三", age: 18, address: "上海市"} // 不能修改原型 obj.__proto__ = '随便什么值' console.log(obj.__proto__) // {habit: "运动", constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, …} // 不能修改已有属性的可枚举性、可配置性、可写性 Object.defineProperty(obj,'address',{ // TypeError: Cannot redefine property: address enumerable: false,// 表示是否可以枚举。直接在对象上定义的属性,基本默认true configurable: false,// 表示能否通过delete删除属性,能否修改属性的特性 writable: true// 表示能否修改属性的值。直接在对象上定义的属性,基本默认true })
这里要注意一点,Object.freeze()的返回值就是被冻结的对象,该对象完全等于传入的对象,所以我们一般不需要接收返回值。上面我们对对象进行了冻结,那么我们可以冻结数组吗?
2、冻结数组
var arr = [1,2,3,4,5] Object.freeze(arr) arr[0]='新值' console.log(arr) // [1, 2, 3, 4, 5]
其实很容易能想明白,数组本质也就是对象,只不过对象的key是下标,所以也可以冻结。如果我的对象里还有对象呢,那么冻结是否依然有效?
2.1、浅冻结
var obj = { name: '张三', info: { a: 1, b: 2 } } Object.freeze(obj) obj.name = '李四' console.log(obj) // {info: {a: 1, b: 2},name: "张三"} obj.info.a = 66 console.log(obj.info) // {a: 66, b: 2}
可以看到对象中如果还有对象的时候,Object.freeze()失效了。Object.freeze()只支持浅冻结,下面我们封装一个深冻结函数,日后可直接使用
2.1、深冻结
var obj = { name: '张三', info: { a: 1, b: 2 } } function deepFreeze(obj) { // 获取所有属性 var propNames = Object.getOwnPropertyNames(obj) // 遍历 propNames.forEach(item => { var prop = obj[item] // 如果某个属性的属性值是对象,则递归调用 if (prop instanceof Object && prop !== null) { deepFreeze(prop) } }) // 冻结自身 return Object.freeze(obj) } deepFreeze(obj) obj.name = '李四' console.log(obj) // {name: "张三", info: {…}} obj.info.a = 100 console.log(obj.info) // {a: 1, b: 2}
3、应用场景
Object.freeze()可以提高性能,如果你有一个对象,里面的内容特别特别多,而且都是一些静态数据,你确保不会修改它们,那你其实可以用Object.freeze()冻结起来,这样可以让性能大幅度提升,提升的效果随着数据量的递增而递增。一般什么时候用呢?对于纯展示的大数据,都可以使用Object.freeze提升性能。
4、Vue中使用Object.freeze
在vue项目中,data初始化 里面一般会有很多变量,后续如果不想使用它,可以使用Object.freeze()。这样可以避免vue初始化时候,做一些无用的操作,从而提高性能。
data(){ return{ list:Object.freeze({'我不需要改变'}) } }
二、Object.assign()方法或展开语法(…)来合并对象
在JavaScript中,你可以使用Object.assign()方法或者使用Spread Operator (…) 来合并对象。
1、Object.assign()
Object.assign() 静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
1.1、语法
Object.assign(target, ...sources)
1.2、参数
- target:需要应用源对象属性的目标对象,修改后将作为返回值。
- sources:一个或多个包含要应用的属性的源对象。
1.3、示例
let name = { name:'sea' },age = { age:15 },person= {} Object.assign(person,name,age) console.log(person) //{ name:'sea',age:15 }
在这个例子中,Object.assign()方法创建了一个新的对象,其属性是所有传入对象的属性的拷贝。
const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 }; const returnedTarget = Object.assign(target, source); console.log(target); // Expected output: Object { a: 1, b: 4, c: 5 } console.log(returnedTarget === target); // Expected output: true
如果目标对象与源对象具有相同的键(属性名),则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。
2、展开语法Spread Operator (…)
浅拷贝 (Shallow-cloning,不包含 prototype) 和对象合并,可以使用更简短的展开语法。而不必再使用 Object.assign() 方式。
Spread运算符也可以用来合并对象。它可以将一个数组或对象展开到一个函数或数组中。
let name = { name:'sea' },age = { age:15 } let person = { ...name,...age } console.log(person) //{ name:'sea',age:15 }
在这个例子中,Spread Operator (…) 用于扩展对象,所以它复制了所有对象的属性到新的对象中。
var obj1 = { foo: "bar", x: 42 }; var obj2 = { foo: "baz", y: 13 }; var clonedObj = { ...obj1 }; // 克隆后的对象:{ foo: "bar", x: 42 } var mergedObj = { ...obj1, ...obj2 }; // 合并后的对象:{ foo: "baz", x: 42, y: 13 }
3、区别
- Object.assign() 函数会触发 setters,而展开语法则不会。