「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」
今天来梳理一下与 Object 有关的内容。
在MDN上的介绍:Object - JavaScript | MDN (mozilla.org)
制作不可变对象
Object.freeze
我们都知道函数式编程中有一个概念叫纯函数,这是一个没有副作用的函数,对于相同的输入一定能得到相同的结果,不依赖外部变量,不能修改输入的值。
但是函数传参对于对象传的是引用地址,我们通过obj.a = 'xxx'
很容易就修改了对象obj
的实际内容。有没有一种方法让函数体内能够访问入参obj
的内容而又保证在函数体内没有人能修改它?
答案就是用 Object.freeze()
Object.freeze()可以冻结一个对象,冻结后该对象就再也不能被修改。
const a = { val: 233}
Object.freeze(a)
a.val = 250
console.log(a)//输出结果 {val: 233}
与之相似的还有 Object.seal(), Object.preventExtensions(),三者的区别如下:
freeze | seal | preventExtensions |
---|---|---|
全都不能修改(不能修改值、原型、不能增删属性,不可配置) | 不能添加新的属性且不可配置 | 不能添加新的属性 |
应用场景:如React的函数是组件的入参 props 是不可变的,在框架层上先将props用Object.freeze()包裹再传给组件函数,可以确保开发者一定无法在组件内修改prop的值。
获取原始类型
Object.prototype.toString.call
我们可以通过 typeof
鉴别基本类型,但是无法区分null和非null对象和数组。想要做到这一点,可以使用Object.prototype.toString.call
。
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(null))// [object Null]
console.log(Object.prototype.toString.call({})) // [object Object]
注意,不能少了prototype
属性,比如以下代码是不合法的:
Object.toString.call([])
这个会报TypeError
Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function
at Array.toString (<anonymous>)
原因是 Object和Boolen、Number、String等构造函数都是自身重写了 toString 方法,这个方法和 Object.prototype.toString
完全不一样。只有后者能够获取到原始类型。
以某对象为原型创建一个新对象
Object.create()
对象有这么一个特性,当你访问对象a的一个字段b,即访问a.b
的时候,如果 b 不是 a 自身的属性,js就会尝试沿着a的原型链查找最近的含有b属性的原型。而通过Object.create(obj)
方法可以用obj
作为原型创建一个空对象。
const a = Object.create({b: 1})
console.log(a)// {}
console.log(a.b)// 1
Object.create
的其中一个应用场景是模拟散列表。在es6的 Map、Set的标准数据结构出来以前,实现Map的方法一般如下所示:
const map = Object.create(null)
// 增或改
map.a = 1
// 删
delete map.a
// 查
'a' in map
为了模拟散列表,我们使用Object.create(null)
而不是直接使用{}
是因为后者原型上还有一些方法,名字可能会和用户设置的key重复,而前者将原型设置 null 则无此问题。
除此之外,另一个应用场景就是构造原型链。在软件工程的设计模式中有一个模式叫职责链模式,它的概念如下:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,沿着这条链传递该请求,直到有一个对象处理它为止。
原型链就是一个天生的职责链模式,我们可以将Object.create
加一层封装,设计成一个构建职责链节点的函数。
function createBuildNodeFn() {
let last;
return (options) => {
last = Object.create(last ?? null);
Object.assign(last, options);
return last;
};
}
const buildNode = createBuildNodeFn();
const node1 = buildNode({
handleXXX() {
// ...
}
});
const node2 = buildNode({
handleXXX() {
//...
}
});
node2.handleXXX()
对象属性描述符是个啥
Object.getOwnPropertyDescriptor
对象属性描述符用于描述对象一个属性的性质,它是一个含有以下字段的对象:
- value
- enumerable
- configurable
- writable
const c = { b: undefined};
console.log(Object.getOwnPropertyDescriptor(c, 'b'))
// {value: undefined, writable: true, enumerable: true, configurable: true}
默认情况下,除了 value 以外的3个字段取值都是true,但是Object.create
、Object.defineProperties
、Object.defineProperty
都有机会修改对象属性描述符,详情可自行查阅 MDN (mozilla.org)。
- configurable 设为 false 则无法再次修改属性描述符,也无法删除 delete 该属性,
- enumerable 设为 false 则无法被for循环遍历
- writable 设为 false 则无法被修改此值。