JS 对象

文章将会对对象属性特性,原型链,对象拷贝进行说明。

一、JavaScript属性的可迭代、可修改和可配置特性

作者: Javier Márquez 

原题:JavaScript properties are enumerable, writable and configurable 

摘要:对属性的属性做了定义并通过示例说明了者几个属性的属性的应用场景, 比如不可枚举用于序列化、不可修改用于常量、不可配置用于完全不可变对象.

对象是JavaScript的重要部分. JavaScript 的对象语法十分精确并容易使用,所以我们经常将对象作为 HashMap 使用。

图1

你知道上面代码中的对象的所有属性都是可迭代、可修改和可配置的吗?

可迭代,意味着我能够通过 for..in 循环来访问该对象的所有属性. 还能通过 Object.keys() 方法获取该对象的所有属性名.

可修改,意味着我能修改该对象的所有属性的值,通过为这些属性赋予一个新值就能修改: ob.a = 1000;.

可配置,意味着我能修改属性的行为,让该对象的属性都是不可迭代的、不可修改的和不可配置的. 只有可配置的属性才能通过 delete 被删除.

我感打赌你知道Object属性的前两个特性(译者注:这里用特性描述属性的属性,而属性用来描述对象的属性.), 但是只有少数人知道通过调用 Object 的 defineProperty 方法能够创建对象的属性、修改对象的属性为不可迭代的和不可变的.

图二

我估计这种语法并不像平常的语法那么常见, 但是使用这种属性在解决某些问题时确实很方便. defineProperty 方法中定义对象属性的对象称为描述符(descriptor),可以通过 getOwnPropertyDescriptor 方法来查看任何属性的描述符.

有趣的是Object.defineProperty中属性描述符的默认选项值与上面的例子中的值正好相反:描述符默认是不可迭代、不可修改和不可配置的.

图三

此外,还可以在通过Object.create(prototype, properties)方法创建对象时指定对象属性的描述符.

图四

对象的不可枚举属性

前面说过, 可枚举属性可以通过for..in循环来访问, 而不可枚举属性不能. 基本上来说, 非枚举属性对于大多数将对象作为 HashMap 来使用的对象来说都是不能使用的.

不可枚举属性不能通过for..in迭代

Object.keys 函数不能返回

不能通过JSON.stringify序列化为JSON字符串

所以这类属性就像是某种秘密属性, 但是仍然能够访问.

图五

由于这类属性不能序列化, 我发现在处理数据模型对象时特别有用. 能够通过使用不可枚举属性来方便的添加信息到数据模型对象中.

图六

考虑一下,如果要创建一个ORM库的话,如果有该特性,那会有多方便.

如果要获取对象的所有属性, 包括可枚举的和不可枚举的, 可以通过Object.getOwnPropertyNames获取对象的所有属性名.

对象的不可修改属性

虽然到ES6的时候我们就有一直以来都期望的const语句, 但现在我们就能通过可修改属性模拟常量. 不可修改的属性的属性值是不可修改的.

图七

正如输出结果所示, 对 ob.B 的赋值并没有影响它的值. 这里需要需要,赋值语句总会返回被赋予的值, 无论赋值是否成功. 比如, 这里的不可修改属性就是赋值失败的. 而在严格模式(strict mode)下,尝试对不可修改属性赋值会引发TypeError异常.

图八

此外, 如果不可修改属性的值是一个对象的话, 那么对该对象的引用是不可修改的, 但是引用的对象的属性是可以修改的.

图九

如果想要让属性完全的不可修改, 可以用Object.freeze函数. 它让对对象的属性的添加、删除、修改都无效. 而且在严格模式下还会引发 TypeError 异常.

图十

对象的不可配置属性

如果上面的对象是定义为可配置的,那么你还对其进行修改. 还可以通过defineProperty来该变属性为可修改的或不可迭代的. 但是如果一旦定义属性为不可配置的,那么能做的事就只有一件了: 如果该属性是可修改的, 那么可以将其改为不可修改的. 其他任何类型的更新都将引发 TypeError 异常.

图十一

特别重要的一点, 可配置属性能能够通过 delete 操作符从对象中移除. 所以, 如果你创建了一个不可配置和不可修改的属性, 那该属性就是一个被冻结的属性.

图十二

结论

Object.defineProperty是从ES5引入的, 现在就可以开始使用了, 其被所有现代浏览器支持, 包括 IE9(甚至是 IE8, 但其仅支持 DOM 对象). 通过不同的方式来实现我们之前常常做的任务总是很有趣, 并且通过观察 JavaScript 的核心(即对象)是如何工作的, 我们能够轻松的学习新知识.

Object.defineProperty还给了我们能够自定义getter和setter的自由, 不过今天的文章没有涉及到. 如果想要了解更多, 那就去看看一定会令人有意外收获的Mozilla’ Documentation吧.


二、JS原型

JS原型可以说是JS对于OOP的一种转变和兼容,其可以理解为JS对于继承的一种实现。关键字是prototype。这一关键字是我们JS的函数对象之中所特有的。而有些浏览器在控制台打印出对象的时候会有_proto_属性,实际上这一属性不是标准属性,只是部分浏览器实现了而已。

图十三

紧接着直接上第二张图说明

图十四

由此可见其实prototype和_proto_是不太相同的两个属性。最后来看一下什么是原型链

图十五

上一张国外大神给的图片:

图十六

弄清楚了这张图实际上原型差不多就基本弄通了,但是有几个特别的方面我们是需要注意一下的。

1.当我们在设置了原型的时候生成了对象,但是这是如果原型修改过了,之前生成的对象的原型是无法改变的。这点要特别注意。

2.原型对象之中会有一个constructor属性,这一属性在我们定义原型的时候会自动的指向我们的构造函数,但是某些情况下我们修改原型之后这一属性并不会自动指向,这是我们就需要进行修改,以确保原型链的正确性。


三:对象拷贝

对象拷贝分为深拷贝和浅拷贝。

1.浅拷贝:我们使用的对象的赋值运算实际上就算是浅拷贝的一种了,其拷贝的实际上是对象的引用。总结可以为:浅拷贝指两个不同的变量存的是同一个对象的地址,即两个变量指向同一块内存区域.

2.深拷贝:深拷贝则不是引用的拷贝了,而是开阔一块新的区域对对象之中的可枚举属性进行全面复制,这是候两个变量指向的区域是完全不相同的。

JS之中提供了一个方法为Object.assign(target, obj1, obj2),在MDN上面描述为将所有可枚举属性的值从一个或多个源对象复制到目标对象。String类型和 Symbol 类型的属性都会被拷贝, 并且该方法会忽略值为 或者 undefined 的源对象。所以可以说这个方法是一个介于浅拷贝和深拷贝之间的内容。

-- 当我们将target值设置成为{}的时候。并且其被拷贝对象之中没有引用类型的时候,则其实际上就是一次深拷贝。

-- 当target为{}的时候,且被拷贝对象之中有引用类型的时候,则新的对象指向的同一块内存区域,即对引用类型进行了浅拷贝。

有上面两点我们可以看出assign方法实际上是对属性进行了拷贝。而且,如果背靠背对象之中有属性相同的话,则之后的参数会将前面参数的拷贝获取的值给覆盖。

图十七

Object.assign 拷贝的是对象的可枚举属性,该方法使用源对象的 [[Get]] 和目标对象的 [[Set]],所以它会调用相关 getter 和 setter。因此,不如说它是分配属性,而不仅仅是复制或定义新的属性。如果合并源包含 getter,这可能使其不适合将新属性合并到原型中,将属性定义(包括其可枚举性)复制到原型应使用Object.getOwnPropertyDescriptor() 和 Object.defineProperty() ,因此 Object.assign 不能拷贝对象的继承属性

图十八

ECMAScript 有 5 种原始类型(primitive type): Undefined、Null、Boolean、Number 和 String。当Object.assign的源对象是原始类型时,源对象会被包装成“对象”,对应的键是它在源对象中的索引值:

图十九

在出现错误的情况下,例如,如果属性不可写,会引发TypeError,异常会打断后续的拷贝任务。如果在引发错误之前添加了任何属性,则可以更改target对象。

图二十
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • js对象的属性有很多种,总结这一篇文章是为将来理解对象复制和对象继承打基础。 数据描述符 1. 可枚举性(Enum...
    csRyan阅读 443评论 0 2
  • 对象: 1. 对象中包含一系列的属性,这些属性是无序的。 2. 每个属性都有一个字符串key和对应的value;(...
    一树青枫阅读 340评论 0 1
  • 概述 在Javascript编程时,经常需要遍历对象的键、值,ES5提供了for...in用来遍历对象,然而其涉及...
    邱鹏城阅读 11,946评论 2 5
  • 对象的概述 什么是对象? 对象是一个具体的事物,包含一系列的属性,这些属性是无序的,每一属性都有一个字符串key和...
    李华炎阅读 342评论 0 1
  • 最近看了不少随笔翻译,文学作品的作用就是让我们浮躁的心沉淀下来,有空反思下自己的最近,不至于走得太快,忘记了自己为...
    遇见金凤阅读 234评论 0 0