【笔记】Object 原型方法整理

Object 原型方法整理
这些方法主要在 ES5 及后续版本中加入,浏览器支持情况因方法而异。
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 常用于原型链式继承和对象拷贝。
原型链式继承及Object.create
在没有create()方法的时候,我们将如下封装一个object函数:
1 | function object(o) { |
并实现继承,如:
1 | var person = { |
Object的实现同上object()方法,
语法:
1 | Object.create(proto, [propertiesObject]) |
参数:
- proto:新创建对象的原型对象。
- propertiesObject:可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
返回值:
- 一个新对象,带着指定的原型对象和属性。
注意:如果propertiesObject参数是 null 或非原始包装对象,则抛出一个 TypeError 异常。
继承实现
1 | // Shape - 父类(superclass) |
对象拷贝
由于Object的特性,我们可以借助以实现对象的拷贝工作,注意:拷贝的是原型上的属性。
如
1 | let obj1 = { |
实现“干净”的字典对象
早期部分for...in的遍历中,会将对象的构造器、原型属性/方法也列入循环之中,为了避免这种情况,大多都利用obj.hasOwnProperty()方法进行过滤,如
1 | var obj = { |
但有了Object.create()方法后,我们可以通过Object.create(null)创建“干净”的对象,如
1 | var obj = Object.create(null, { |
后面的Object.setPrototypeOf()也可实现类似的功能
Object.assign()
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
早期实现
在没有Object.assign()方法的时候,其功能通常由一个for...in循环创建对象属性实现,可参考jQuery/Zepto的$.extend()方法。如
1 | function extend(target, source, deep) { |
介绍
语法:
1 | Object.assign(target, ...sources) |
参数:
- target:目标对象。
- sources:源对象。
返回值:
- 目标对象。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
执行异常会打断后续拷贝任务。
复制对象
利用Object.assign()可复制对象(拷贝可枚举属性),如
1 | const obj = { a: 1 }; |
注意:针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
1 | let obj1 = { a: 0 , b: { c: 0}}; |
合并对象
利用Object.assign()可以实现对象的合并,如
1 | const o1 = { a: 1 }; |
原始类型会被包装为对象
如:
1 | const v1 = "abc"; |
DOM设置属性
如下:
1 | let $dom = document.querySelector('#test'); |
Object.entries()
Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
基本
语法:
1 | Object.entries(obj) |
参数:
- obj:可以返回其可枚举属性的键值对的对象。
返回值:
- 给定对象自身可枚举属性的键值对数组。
如:
1 | const obj = { foo: 'bar', baz: 42 }; |
将Object转为Map
因为new Map() 构造函数接受一个可迭代的entries。借助Object.entries方法你可以很容易的将Object转换为Map,如:
1 | var obj = { foo: "bar", baz: 42 }; |
而Map转为Object则通过Object.fromEntries()方法,如
1 | const entries = new Map([ |
Object.freeze()和Object.isFrozen()
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性(enumerable)、可配置性(configurable)、可写性(writable),以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
因此通过Object.preventExtensions()和Object.freeze()处理后的对象在此通常判断为true。
基本
语法:
1 | Object.freeze(obj) |
参数:
- obj:要被冻结的对象。
返回值:
- 被冻结的对象。
被冻结对象自身的所有属性都不可能以任何方式被修改。数据属性的值不可更改,访问器属性(有getter和setter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。
Object.isFrozen()
该方法判断一个对象是否被冻结。一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的。
VueJS中冻结数据以提高性能
VueJS在绑定数据中,会对数据的getter/setter进行转换处理,通过Object.freeze()可避免此操作从而大幅提高性能。不过要注意前提是保证数据不会修改,因此通常仅用作于接口数据的处理。如
1 | new Vue({ |
Object.preventExtensions()
Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
基本
语法:
1 | Object.preventExtensions(obj) |
参数:
- obj:将要变得不可扩展的对象。
返回值:
- 已经不可扩展的对象。
注意,一般来说,不可扩展对象的属性可能仍然可被删除。尝试将新属性添加到不可扩展对象将静默失败或抛出TypeError
Object.preventExtensions()仅阻止添加自身的属性。但属性仍然可以添加到对象原型。
1 | // Object.preventExtensions将原对象变的不可扩展,并且返回原对象. |
Object.isExtensible()
Object.isExtensible()方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。
Object.seal()
该方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。
基本
语法:
1 | Object.seal(obj) |
参数:
- obj:将要被密封的对象。
返回值:
- 被密封的对象。
1 | var obj = { |
Object.isSealed()
该方法判断一个对象是否被密封。密封对象是指那些不可 扩展 的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象。
Object.seal()和Object.freeze()
Object.seal做了如下几件事情,
- (1)给对象设置,
Object.preventExtensions(obj1),
禁止更改原型,禁止添加属性, - (2)为对象的每一个属性设置,
writable:false,
禁止更改属性值, - (3)为对象的每一个属性设置,
configurable:false。
禁止删除属性,禁止更改writable为true,禁止更改enumerable为false,禁止更改configuable为true
而Object.freeze做了两件事情,
- (1)给对象设置,
Object.preventExtensions(obj1),
禁止更改原型,禁止添加属性, - (2)为对象的每一个属性设置,
configurable:false,
禁止更改属性值,
与Object.freeze不同的是,Object.seal后的对象是可写的writable:true。
Object.setPrototypeOf()
该方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null。
警告: 由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于 obj.proto = … 语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何[[Prototype]]已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]。相反,你应该使用 Object.create()来创建带有你想要的[[Prototype]]的新对象。
基本
语法:
1 | Object.setPrototypeOf(obj, prototype) |
参数:
- obj:要设置其原型的对象。
- prototype:该对象的新原型(一个对象 或 null)。
如果对象的[[Prototype]]被修改成不可扩展(通过 Object.isExtensible()查看),就会抛出 TypeError异常。如果prototype参数不是一个对象或者null(例如,数字,字符串,boolean,或者 undefined),则什么都不做。否则,该方法将obj的[[Prototype]]修改为新的值。
实现“干净”的字典对象
类似于Object.create()对其的实现原理,
如:
1 | var obj = { a: 1 }; |
Object.is()
该方法判断两个值是否是相同的值。
基本
语法:
1 | Object.is(value1, value2); |
参数:
- value1:第一个需要比较的值。
- value2:第二个需要比较的值。
返回值:
- 表示两个参数是否相同的布尔值 。
Object.is() 判断两个值是否相同。如果下列任何一项成立,则两个值相同:
- 两个值都是 undefined
- 两个值都是 null
- 两个值都是 true 或者都是 false
- 两个值是由相同个数的字符按照相同的顺序组成的字符串
- 两个值指向同一个对象
- 两个值都是数字并且
- 都是正零 +0
- 都是负零 -0
- 都是 NaN
- 都是除零和 NaN 外的其它同一个数字
这种相等性判断逻辑和传统的 == 运算不同,== 运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),然后才进行相等性比较,(所以才会有类似 “” == false 等于 true 的现象),但 Object.is 不会做这种类型转换。
这与 === 运算符的判定方式也不一样。=== 运算符(和== 运算符)将数字值 -0 和 +0 视为相等,并认为 Number.NaN 不等于 NaN。
1 | Object.is('foo', 'foo'); // true |
解决了坑爹的NaN != NaN
大家都知道,NaN不等于任何值,包括它本身,因此之前判断NaN的方式都是通过isNaN()方法。现在有了Object.is()方法后,我们可以更好得判断,如
1 | let val = NaN; |
相关链接
- [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create]{https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create}
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com