首页 > 代码库 > JS中六种数据类型(六)——Object(转)

JS中六种数据类型(六)——Object(转)

ECMAScript中的对象是可变的键控集合(即一组数据和功能的集合)。它将很多值聚合在一起,可通过名字访问这些值。对象也可看做属性的容器,每个属性都是一个名/值对。属性的名字可以是包括空字符串在内的任意字符串。属性值可以是除undefined值之外的任何值。对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)他的属性。
 
    一、属性类型
    ECMA-262第5版在定义只有内部采用的特性时,描述了属性的各种特征。为了表示特性时内部值,该规范把它们放在了两对方括号中,例如:[[Enumerable]]。ECMAScript对象中有两种属性:数据属性和访问器属性。
    1.数据属性
    数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。
     a.[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。这个特性默认值为true。
     b.[[Enumerable]]:表示能否通过for-in循环返回属性。这个特性默认值为true。
     c.[[Writable]]:表示能否修改属性的值。这个特性默认值为true。
     d.[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined。
    2.访问器属性:
     a.[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true.
     b.[[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true。
     c.[[Get]]:在读取属性时调用的函数。默认值为undefined。
     d.[[Set]]:在写入属性时调用的函数。默认值为undefined。
 
    二、创建对象
    1.通过new创建对象
    new 运算符创建并初始化一个新对象。关键字new后跟随一个函数调用。这里的函数称做构造函数,构造函数用以初始化一个新创建的对象。JavaScript语言核心中的原始类型都包含内置构造函数。例如:

        技术分享
 
除了这些内置构造函数,用自定义构造函数来初始化新对象也是非常常见的。
 
    2.对象字面量
    创建对象最简单的方式就是在JavaScript代码中使用对象字面量。对象字面量是由若干名/值对组成的映射表,名/值对中间用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来,结构为:{属性名1:属性值1,属性名2:属性值2,……}。属性名可以是JavaScript标识符也可以是字符串字面量(包括空字符串)。属性的值可以是任意类型的JavaScript表达式,表达式的值(可以是原始值也可以是对象值)就是这个属性的值。请看示例:

        技术分享
 
 
    3.原型
    我们创建的每个对象都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中,请看示例:

         技术分享
 
    在此,我们将sayName()方法和所有属性直接添加到了Person的prototype属性中,构造函数变成了空函数。即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但与构造函数不同的是,新对象的这些属性和方法是由所有实例共享的。换句话说,person1和person2访问的都是同一组属性和同一个sayName()函数。由于对象的原型牵扯到的知识点非常多且复杂,在此就不向大家阐述,我将会在以后的文章中为大家详细讲述原型(prototype)这一概念。除此之外,JavaScript还有一种Object.create()方法来创建对象。
 
    三、基于对象属性的各种操作
 
    1.对象属性的获取
    访问对象属性可以使用点表示法和方括号表示法。对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号来说([]),方括号内必须是一个计算结果为字符串的表达式,这个字符串就是属性的名字。请看示例:

        技术分享
 
    当使用方括号时,我们说方括号内的表达式必须返回字符串。其实更严谨的说,表达式必须返回字符串或返回一个可以转换为字符串的值(这个特点可以用于处理很多情况)。由此我们可以联想到数组,这和数组获取元素的方法极为相似,其实数组也是对象,只是其属性名为数组下标而已。
 
    2.属性访问错误
    属性访问并不总是返回或设置一个值。查询一个不存在的属性并不会报错,如果在对象o自身的属性或继承的属性中均未找到属性x,属性访问表达式o.x返回undefined。回想一下我们person对象有属性"name",而没有属性"subname":
        技术分享
    但是如果对象不存在,那么试图查询这个不存在的对象的属性就会报错。null和undefined值都没有属性,因此查询这些值的属性会报错,请看示例:
        技术分享
除非确定person和person.subname都是(或在行为上)对象,否则不能写这样的表达式person.subname.length,因为这样会报错,下面提供了两种避免出错的方法:
        技术分享
        技术分享


    第二种示例使用了 "&&"的一种复合语言习惯的用法,因此只有在person为真值(不能是null或者undefined)的情况下才会计算之后的值,&&的这一行为称作“短路”。
    当然,给null和undefined设置属性也会报类型错误。给其他值设置属性也不总是成功,有一些属性时只读的,不能重新复制,有一些对象不允许新增属性,但让人颇感意外的是,这些设置属性的失败操作不会报错:

        技术分享

    这是一个历史遗留问题,这个bug在ECMAScript5的严格模式中已经修复。在严格模式中,任何失败的属性设置操作都会抛出一个类型错误异常。
 
 
    3.删除属性
    delete运算符可以删除对象的属性。它的操作数应当是一个属性访问表达式。让人感到意外的是,delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。
 
        技术分享

    delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)
    当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true:
        技术分享
    delete不能删除那些可配置性为false的属性(尽管可以删除不可扩展的可配置属性)。某些内置对象的属性是不可配置的,比如通过变量声明和函数声明创建的全局对象的属性。在严格模式中,删除一个不可配置属性会报一个类型错误。在非严格模式中,这些情况下的delete操作会返回false:
        技术分享
 
 
在非严格模式中删除全局对象的可配置属性时,可以省略对全局对象的引用,直接在delete操作符后跟随要删除的属性名即可:
        技术分享
 
然后再严格模式中,delete后跟随一个非法的操作数(比如x),则会报一个语法错误,因此必须显式指定对象及其属性:
        技术分享
 
    4.枚举属性
    for-in语句可用来遍历一个对象中的所有属性名。该枚举过程将会列出所有的属性——包括函数和你可能不关心的原型中的属性——所以有必要过滤掉那些你不想要的值。最为常用的过滤器是hasOwnProperty方法,以及使用typeof来排除函数:
        技术分享
    属性名出现得顺序是不确定的,因此要对任何可能出现的顺序有所准备。如果你想要确保属性以特定的顺序出现,最好的办法就是完全避免使用for-in语句,而是创建一个数组,在其中以正确的顺序包含属性名:
        技术分享
    通过使用for而不是for-in ,可以得到我们想要的属性,而不用担心可能发掘出原型链中的属性,并且我们按正确的顺序获得了它们的值。






    浏览更多文章请关注微信公众号:wcs290130——JavaScript那些事儿。JavaScript技术交流群:344292264  。

JS中六种数据类型(六)——Object(转)