说完了对象那些不常用的冷知识,是时候来看看JavaScript中对象属性有哪些有意思的东西了。

不出你所料,对象属性自然也有其相应的特征属性,但是这个话题有点复杂,让我们先从简单的说起,对象属性的分类。

面对一个复杂的事物,寻找其内在共性,妥善分类往往是快速认知该事物的捷径,这与程序员“将难以解决的大问题拆解为可以解决的小问题”的思维有异曲同工之妙。

那么,对象的属性根据不同的维度,可以如何分类呢?你或许想不到,竟然有如此多的分类方法,而不同的类别,有牵扯出特定的方法解决这一类别的某些问题。让我们看看吧:

按来源分类

  • 私有属性
  • 原型属性

JavaScript是一门基于原型链的语言,对象继承是节省内存空间,避免代码重复,逻辑混乱的好方法。而对象继承对于属性而言则带来一个问题,即我们需要区分某对象内的属性,究竟是对象自有的(私有属性),还是继承于其他对象(继承属性),无论是进行属性遍历还是对属性进行操作,我们都需要谨慎的思考这个问题。

让我们举例两个典型场景看看JavaScript是如何帮助我们解决这个问题的:

情景一: 属性查找

有时候,我们需要查找某个对象是否有某个属性,再进一步决定是否要执行下一步操作,JavaScript提供给我们的查找工具是in操作符,in操作符用以在给定对象中查找一个给定名称的属性,如果找到则返回true值。实际上,in操作符就是在哈希表中查找一个键是否存在(还记的我们的蓝色章鱼吗,in操作符只是检查章鱼是否有那只触角,并不关心触角上拿着的卡片上写了什么,更不会随着卡片的地址去读取值,这对于提升性能尤其重要),让我们看看代码示例:

  1. let obj = {
  2. x: 1,
  3. }
  4. console.log('x' in obj) // true
  5. console.log('y' in obj) // false

但是遗憾的是,in操作符会检查所有的私有属性和原型属性,因此你并不能通过in操作符知道该属性的真正来源。

但好在JavaScript还给我们提供了一个.hasOwnProperty()方法,每一个对象都有这样一个方法,专门用来判断某个属性是否是该对象的私有属性。

我们终于得到了我们想要的。太棒了。

小结:

  1. 查找属性(不区分属性来源):in操作符
  2. 查找私有属性:对象的.hasOwnProperty()方法

情景二: 属性枚举

有些时候,我们想要获得一个对象内所有属性的键或值(或者全部都要),这时我们就要枚举一个对象内的所有属性,通常,我们会使用for-in循环去实现这一点。

然而,很不巧的是,for-in循环会遍历所有可枚举的原型属性,注意这里有两点需要进一步说明:

  1. 可枚举:这牵扯到我们很快要谈到的属性特征属性(有点拗口是吧:))
  2. 会遍历原型属性:这样当一个对象的继承链很长而我们又只关心对象的私有属性时就会变得非常麻烦

当然,你可以在for-in循环中,再使用我们刚提到的.hasOwnProperty()方法,但是JavaScript给予了我们更好的选择:使用Object.keys()方法:

Object.keys()方法是ECMAScript5引入的方法,它可以获取可枚举属性的名字的数组,并且它只返回对象的自有属性。

因此,你可以基于是否需要一个数组,是否只需要对象自有属性来判断使用哪一种方法。

小结:

  1. 枚举属性(不区分属性来源):for-in循环
  2. 只枚举私有属性,且返回数组:Object.keys()方法

按作用分类

  • 数据属性
  • 访问器属性

你也许很少听说过这样的分类方式,因为我们几乎都在使用数据属性,让我来简要说明这两种类型的属性的区别:

数据属性包含了一个值,我们之前提到的对象的内部方法[[Put]]的默认行为就是创建数据属性:

  1. let obj = {
  2. x: 1, // x 是数据属性
  3. }

访问器属性不包含值,而是定义了一个当属性被读取时调用的函数(称为“getter”)和一个当属性被写入时调用的函数(称为“setter”):

  1. let obj = {
  2. x: 1,
  3. get y() {
  4. return 2
  5. }
  6. set y() {
  7. return 3
  8. }
  9. }
  10. console.log(obj.y) // 2

之所以访问器属性很少见到是因为我们很少需要在进行属性赋值或读取操作时触发一些行为,不过反过来说,如果这恰恰是你面临的场景,就大胆的使用吧。


对象属性的特征属性

绕了一大圈,终于可以回到正题,谈谈属性的特征属性了,相较于对象只有一个孤零零的[[Extensiable]]特征属性,对象属性要复杂的多:

因为所有对象属性都具有:

  1. [[Enumerable]]特征属性:决定一个属性是否可以被遍历;
  2. [[Configurable]]特征属性:决定一个属性是否可以被配置

而只有数据属性有以下两个属性:

  1. [[Value]]特征属性:即属性的值;
  2. [[Writable]]特征属性:值为布朗类型,决定该属性值是否可以写入;

而只有访问器属性有以下两个属性:

  1. [[Get]]特征属性:即为getter函数内容;
  2. [[Set]]特征属性:即为setter函数内容;

让我们先来看看这些特征属性的意义,再来谈谈如何配置这些特征属性:

[[Enumerable]]

并不是所有的属性都是可枚举的,实际上,对象的大部分原生方法的[[Enumerable]]特征属性的值都被设置为false(所以使用for-in循环时,不会遍历出一大堆你不需要的内容),那我们该如何判断一个属性是否是可枚举的呢?

JavaScript为我们提供了.propertyIsEnumerable()方法去检查一个属性是否可枚举,像.hasOwnProperty()方法一样,每个对象都拥有这个方法:

  1. let obj = {
  2. x: 1,
  3. }
  4. console.log(obj.propertyIsEnumerable('x')) // true

[[Configurable]]

可配置是指:

  1. 删除操作;
  2. 属性类型变更操作(从数据属性变为访问器属性,或者相反):
  3. 使用Object.defineProperty()方法配置属性(别着急,我们之后会着重讲解这个方法);

因此,当你设置某个属性的[[Configurable]]特征属性为false时,以上三种操作就都不能正确执行。


配置特征属性

是时候讲解JavaScript为我们提供的配置属性特征属性的方法了:Object.defineProperty()

该方法接收三个参数:

  1. 拥有该属性的对象
  2. 属性名(字符串)
  3. 包含需要设置的特征的属性描述对象(属性描述对象具有和特征属性额同名的属性,但是名字中不包含中括号)

让我们看看该方法的实际用法:

  1. let obj = {
  2. x: 1,
  3. }
  4. Object.defineProperty(obj, 'x', {
  5. enumerable: false,
  6. })
  7. console.log('x' in obj) // true
  8. console.log(obj.propertyIsEnumerable('x')) // false

我们通过Object.defineProperty()方法使obj对象的x属性为不可遍历的,在之后的检测中,我们看到控制台输入属性存在,但不可遍历。

让我们再看看数据属性配置特征属性的示例:

  1. let obj = {
  2. x: 1,
  3. }
  4. Object.defineProperty(obj, 'x', {
  5. value: 2,
  6. enumerable: true,
  7. configurable: true,
  8. writable: true,
  9. })
  10. console.log(obj.x) // 2
  11. // 注意我们所做的实际上完全等同于以下这段代码
  12. let obj = {
  13. x: 2,
  14. }

下面是访问器属性配置特征属性的示例:

  1. let obj = {
  2. x: 1,
  3. }
  4. Object.defineProperty(obj, 'x', {
  5. get: function() {
  6. console.log('reading...')
  7. return this.x
  8. },
  9. set: function(value) {
  10. console.log('setting...')
  11. this.x = value
  12. },
  13. enumerable: true,
  14. configurable: true,
  15. })

使用访问器属性特征属性比使用对象字面形式定义访问器属性的优势在于,你可以为已有的对象定义这些属性。如果你想要用对象字面形式,你只能在创建对象时定义访问器属性。

需要注意的是,一旦你决定使用Object.defineProperty()方法配置属性的特征属性,你需要完整在配置对象中列出enumerable属性与configurable属性,因为在默认情况下,这些属性的值皆为false,这可能不是你想要的。


定义多重属性

当你需要配置一个对象的多个属性时,你需要使用Object.defineProperties()方法,其用法如下:

  1. let obj = {
  2. x: 1,
  3. }
  4. Object.defineProperties(obj, {
  5. x: {
  6. value: 2,
  7. enumerable: true,
  8. configurable: true,
  9. writable: true,
  10. },
  11. y: {
  12. get: function() {
  13. console.log('reading...')
  14. return this.x
  15. },
  16. set: function(value) {
  17. console.log('setting...')
  18. this.x = value
  19. },
  20. },
  21. })

获取属性特征属性

目前为止,我们提到了属性的所有特征属性,以及如何设置,最后,让我们看看JavaScript为我们提供的查看属性特征属性的方法:Object.getOwnPropertyDescriptor()。其用法如下:

  1. let obj = {
  2. x: 1,
  3. }
  4. const descriptor = Object.getOwnPropertyDescriptor(obj, 'x')
  5. console.log(descriptor.enumerable) // true
  6. console.log(descriptor.configurable) //true
  7. console.log(descriptor.writable) // true
  8. console.log(descriptor.value) // 1

可以看到,该方法接收两个参数,一个目标对象以及想要获取特征属性的属性名,该函数会返回一个特征属性描述对象,包含属性特征属性的所有信息。


难以置信,我们终于讲完了属性的所有特征属性。看到这里的你也值得为自己鼓掌

关于JavaScript对象,你所不知道的事(二)- 再说属性的更多相关文章

  1. JavaScript中你所不知道的Object(二)--Function篇

    上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.Str ...

  2. overflow:hidden 你所不知道的事

    overflow:hidden 你所不知道的事 overflow:hidden这个CSS样式是大家常用到的CSS样式,但是大多数人对这个样式的理解仅仅局限于隐藏溢出,而对于清除浮动这个含义不是很了解. ...

  3. 你所不知道的linq(二)

    上一篇说了from in select的本质,具体参见你所不知道的linq.本篇说下from...in... from... in... select 首先上一段代码,猜猜结果是什么? class P ...

  4. 关于JavaScript对象,你所不知道的事(一)- 先谈对象

    这篇博文的主要目的是为了填坑,很久之前我发表了一篇名为关于JavaScript对象中的一切(一) - 对象属性的文章,想要谈一谈JavaScript对象,可那时只是贴了一张关于这个主题的思维导图,今天 ...

  5. JavaScript中你所不知道的Object(一)

    Object实在是JavaScript中很基础的东西了,在工作中,它只有那么贫瘠的几个用法,让人感觉不过尔尔,但是我们真的了解它吗? 1. 当我们习惯用 var a = { name: 'tarol' ...

  6. 关于Javascript你可能不知道的事

    NaN表示一个不能产生正常结果的运算结果.它不等于任何值,包括它自己.可以用isNaN(number)来检测. 同Java中的字符串一样,JS中的字符串是不可变的.也就是说一旦字符串被创建,就无法改变 ...

  7. Java单例你所不知道的事,与Volatile关键字有染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 如果问一个码农最先接触到的设计模式是什么,单例设计模式一定最差也是“之一”. 单例,Singleton,保证内存中只有一份实例对象存在. 问:为什 ...

  8. 微信5.4你所不知道的事 X5浏览引擎提速50%-80%

    微信5.4新增包括搜索公众号.识别图中二维码.面对面收钱等功能,但是你可知道新版微信X5浏览引擎提速了,提升50%-80%的网络传输速度及相同比例流量节省? 从X5浏览引擎开发人员得知,X5浏览技术基 ...

  9. 闭包----你所不知道的JavaScript系列(4)

    一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...

随机推荐

  1. Python--上传文件和下载文件

    #!/usr/bin/env python# -*- coding: utf-8 -*-# @Time : 2017/12/28 13:56# @Author : Aries# @Site : # @ ...

  2. Python全栈day10(Pycharm的安装和使用)

    Python开发IDE 一,下载Pycharm专业版 二,安装Pycharm 三,新建项目 四,设置字体大小

  3. Hadoop伪分布安装详解(五)

    目录: 1.修改主机名和用户名 2.配置静态IP地址 3.配置SSH无密码连接 4.安装JDK1.7 5.配置Hadoop 6.安装Mysql 7.安装Hive 8.安装Hbase 9.安装Sqoop ...

  4. 剑指Offer——平衡二叉树

    题目描述: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 平衡二叉树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质 ...

  5. LeetCode_Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  6. 使用paramiko的SFTP get或put整个目录

    在<使用paramiko执行远程linux主机命令>中举例说明了执行远程linux主机命令的方法,其实paramiko还支持SFTP传输文件. 由于get或put方法每次只能传输一个文件, ...

  7. wpa安装方法

    1.openssl 2.lib 1.1.2 3.wpa lua 编译错误 http://www.blogjava.net/xiaomage234/archive/2013/09/13/404037.h ...

  8. Linux:Centos7升级内核(转)

    更新前,内核版本为: uname -r 3.10.0-327.10.1.el7.x86_64 升级的方法: 1.导入key rpm --import https://www.elrepo.org/RP ...

  9. std::decay

    参考资料 • cplusplus.com:http://www.cplusplus.com/reference/type_traits/decay/ • cppreference.com:http:/ ...

  10. memento模式

    参考资料 • 维基百科:https://en.wikipedia.org/wiki/Memento_pattern • 百度百科:http://baike.baidu.com/link?url=ZQZ ...