IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    JavaScript对象

    Kyxy发表于 2017-03-01 01:44:33
    love 0

    对象


    对象是JavaScript的基本数据类型:属性的无序集合。每个属性key: value和属性描述符descripter组成。

    • 属性名key:字符串或合法的变量标识符;

    • 属性值value:可以是任意JavaScript值(number、string、boolean、null、undefined和数组、对象).
      value可以是getter和setter。

    • 属性描述符descripter:每个属性的三个相关特性writable、enumerable、configurable,其值都是布尔类型,默认都为true


    1 创建对象

    创建对象有三种方法:对象字面量{a: 1},new Constructor()、Object.create()

    1.1 对象字面量

    var book = {
        "main title": "JavaScript",                   //有空格或其他非标识符、关键字,必须使用双引号
        "sub-title": "The Definitive Guide",
        "for": "all audiences",
        author: {
            firstname: "David",
            lastname: "Flanagan"
        }
    }

    1.2 new操作符调用构造函数

    可以使用new操作符调用一个构造器函数Constructor()创建一个对象。

    var o = new String();
    var d = new Date();

    1.3 Object.create()方法

    原型:每个JS对象都有一个与之相关联的原型对象prototype,对象其原型对象上继承属性。(当前对象中的__proto__属性是其原型对象的链接)

    所有通过字面量创建的对象的原型都是Object.prototype,一个字面量对象{}相当于调用new Object()。

    Object.prototype的原型是null,所以null是原型的出口。

    Object.create()是一个静态函数(不是提供给对象调用的方法),用于创建对象:

    • 第一个参数:原型对象,创建的新对象以该参数为原型;

    • 第二个参数:可选,用于对对象属性的描述

    • 创建一个没有原型的对象:Object.create(null);

    • 创建一个普通空对象({}或new Object()):Object.create(Object.prototype);


    2 对象三个特殊属性

    每个对象都有与之相关的3个属性:原型prototype、类属性calss attribute、可扩展性extensible attribute

    2.1 原型

    每个JS对象都与另一个原型对象(prototype),利用原型可以实现继承。

    • 字面量的原型是:Object.prototype

    • new操作符创建的对象其原型:构造器函数的原型(prototype属性)

    • Object.create()创建的对象,其原型:第一个参数

    查询对象的原型

    • ES5中定义了Object.getPrototypeOf()函数,来查询对象的原型

    • 利用new创建的对象会继承constructor属性,指向创建该对的构造器函数,所有该对象的原型是:obj.constructor.prototype

    • 字面量{}的constructor属性指向Object()

    • 使用isPrototypeOf()方法查看一个对象是否为另一个对象的原型(或处于原型链中)

      Array.prototype.isPrototypeOf([]);   //  ==> true

    2.2 类属性

    对象的类属性class attribute是一个字符串,用来表示对象的信息。ES3与ES5均为提供设置类属性的API,只能通过继承自Object.prototype的toString()方法来简洁查询。

    {}.toString();    //  ==> "[object Object]"

    通过对返回字符串切片:第8位到倒数第2位即可获得对象的类型。但是许多对象重写了toString()方法,需要间接调用Function的call()方法

    function classof(o) {
        if(o === null) {return "Null";}
        if(o === undefined) {return "Undefined"}
        return Object.prototype.toString.call(o).slice(8, -1);
    }

    classof()方法可以返回传递给它的任意对象的类型,参数为number、string、boolean时,使用包装类型将其转化为对象,再进行操作。

        classof(null);  // ==> "Null"
        classof(2);  // ==> "Number"
        classof(false);  // ==> "Boolean"
        classof(new Date());  // ==> "Date"

    2.3 可扩展性extensible attribute

    对象的课扩展性用来描述对象是否可以添加新的属性。所有的内置对象和自定义对象都是可扩展的,除非将其转化为不可扩展

    • Object.esExtensible()函数判断传入对象是否可以扩展;

    • Object.preventExensible()函数将传入的对象设置为不可扩展,并且过程不可逆;只影响对象本身的可扩展性

    • Object.seal()函数将对象设置为不可扩展,属性描述符configurable设置为false(不能添加新属性,已有的属性不能删除或配置,但是对于writable: true时,可以修改属性的值);过程不可逆

    • Object.isSealed()函数判断一个对象是否封闭

    • Object.freeze()函数将对象设置为不可扩展,属性描述符configurable: false;,writable: false;只读。value为accessor property含有setter函数,不受影响


    3 组成对象的属性

    每个对象是无需的属性集合,属性名可以是合法的变量标识符或字符串;变量值可以是任意JavaScript值;属性由描述符descripter来控制特性;

    3.1 属性的查询与设置

    属性可以通过.和[]来访问:

    • 使用.访问时,其右侧必须是属性名称命名的简单标识符

    • 使用[]访问时,括号内必须是计算结果为字符串的表达式,字符串是属性的名字。变量名为关键字、含有空格或属性名是变量时,必须使用[]

      var author = book.author;
      var name = author.surname;
      var title = book["main title"];

    属性的设置:与访问相似,将其放在赋值表达式的左侧

        book.edtion = 6;
        book["main title"] = "ECMAScript";

    继承与错误

    在查询一个对象是否存在时,先查看自身属性,如果没有;通过原型链逐层向上查找,直到原型链顶端null为止。

    • 如果自身属性与原型链中均为找到,属性访问返回undefined,不报错

    • 如果查询不存在对象的属性,会报错

    • 为null和undefined设置属性会报错

    3.2 属性删除

    使用delete操作符,可以删除对象的属性(其描述符中configurable为true),并且只能删除自有属性,不能删除继承属性

    3.3 属性检测

    判断某个属性是否在某个对象中,JS有三种方法:in、hasOwnProperty()、propertyIsEnumerable()

    • in:如果对象自身属性或继承属性中包含该属性,返回true;

    • hasOwnProperty():只有对象自身属性包含该属性时,才返回true

    • propertyIsEnumerable():只有对象自身属性包含该属性,并且该属性是可以枚举(描述符中enumerable: true;)

      var o = {a : 1};
      "a" in o;  //  ==> true
      o.hasOwnProperty("a");  //  ==> true
      o.propertyIsEnumerable("a");  //  ==> true

    3.4 枚举属性

    使用for-in循环可以遍历对象中所有可枚举的属性(包括自身属性与继承属性),把属性名赋值给循环变量。ES5定义了Object.keys()和Object.getOwnPropertyNames()两个函数用来枚举属性名称。

    • Object.keys():返回对象中可枚举的自有属性名组成的数组

    • Object.getOwnPropertyNames():返回对象所有的自有属性名组成的数组,包括不可枚举的属性

    对象继承的内置方法都是不可枚举的,给对象中增加的属性都可以枚举(除非设置其enumerable: false)

    有许多使用工具库为Object.prototype增加新的属性,这些属性可以被所有对象继承使用,但是在ES5之前,不能将其设置为不可枚举,所以在for-in循环时会被枚举出来。

    //过滤继承的属性
    for(p in o) {
        if(!o.hasOwnProperty(p)) {continue;}
    }
    //跳过方法
    for(p in o) {
        if(typeof p === "function") {continue;}
    }

    操作对象属性的工具函数:

    /**
     * 枚举属性的工具函数
     * 将对象p中可枚举的属性复制到对象o中,返回对象o;
     * 如果p和o含有同名的属性,则覆盖o中的属性
     * 不处理getter和setter以及复制属性
     */
    function extend(o, p) {
      for(let prop in p) {
        o[prop] = p[prop];
      }
      return o;
    }
    
    /**
     * 将对象p中可枚举的属性复制到对象o中,返回对象o;
     * 如果o和p有同名属性,不影响o中的属性
     * 不处理getter和setter以及复制属性
     */
    function merge(o, p) {
      for(let prop in p) {
        if(o.hasOwnProperty(prop)) {
          continue;
        }
        o[prop] = p[prop];
      }
      return o;
    }
    
    /**
     * 如果o中的属性在p中没有同名属性,从o中删除这个属性,并返回o
     */
    function restrict(o, p) {
      for(let prop in o) {
        if(!(prop in p)) {
          delete o[prop];
        }
      }
      return o;
    }
    
    /**
     * 如果o中的属性在p中存在同名属性,从o中删除这个属性,并返回o
     */
    function substrict(o, p) {
      for(let prop in o) {
        if(prop in p) {
          delete o[prop];
        }
      }
      return o;
    }
    
    /**
     * 返回一个数组,包含o中可枚举的自有属性的名字
     */
    function keys(o) {
      //o必须是对象
      if(typeof o !== 'object') {
        throw TypeError();
      }
      var result = [];
      for(let prop in o) {
        if(o.hasOwnProperty(prop)) {
          result.push(prop);
        }
      }
      return result;
    }

    3.5 属性setter和getter

    属性的组成:key、value、descripter。在ES5中,value可以用一个或两个方法替代(getter和setter方法)

    • 所有JavaScript的value叫做数据属性data property

    • getter和setter定义的属性叫做存取器属性:accessor property

    不同于data property,accessor property的读写属性由getter和setter决定。

    • 如果属性同时有getter和setter,它是一个可读写的属性;

    • 如果属性只有getter,它是一个只读的属性;

    • 如果属性只有setter,它是一个只写的属性;读取只写属性总是返回undefined

    getter和setter定义

    var o = {
        data_prop: value;        //普通数据属性
        
        //存取器属性是成对定义的函数,函数名是该属性名,没有冒号分隔函数体以属性名
        get accessor_prop() {...},
        set accessor_prop(value) {...}
    }
    var p = {
      //x、y是普通可读写的数据属性
      x: 1.0,
      y: 1.0,
    
      //r是可读写的存取器属性,具有getter和setter
      //函数结束后要带上逗号
      get r() {return Math.sqrt(this.x * this.x + this.y * this.y)},
      set r(newvalue) {
        var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y);
        var ratio = newvalue / oldvalue;
        this.x *= ratio;
        this.y *= ratio;
      },
      //theta是只读存取器属性,只有getter方法
      get theta() {return Math.atan2(this.y, this.x)}
    };
    • 函数体内的this指向表示这个点的对象

    3.6 属性的描述符descripter

    属性除key、value,还有一组用于描述其特性的descripter,其中有writable、enumerable和configurable三个属性,其值都为布尔类型,默认为true

    假设将setter和getter看做descripter,同理将属性的value也看做descripter

    • data property的四个特性:value、writable、enumerable、configurable

    • accessory property的四个特性:getter、setter、enumerable、configurable

    ES5提供操作descripter的API:

    • 调用Object.getOwnPropertyDescripter()函数,获取某个对象中特定自有属性的descirpter

    • 调用Object.getPrototypeOf()函数可以获取继承属性的descirpter

    • 调用Object.defineProperty()函数可以设置属性的特性,或者新建属性,使其具有某种特性

      //返回{ value: 1, writable: true, enumerable: true, configurable: true }
      Object.getOwnPropertyDescriptor({a: 1}, "a");
      
      //继承属性和不存在的属性,返回undefined
      Object.getOwnPropertyDescriptor({});
      Object.getOwnPropertyDescriptor({}, "toString");
      var o = {};   //创建空对象
      //添加一个不可枚举的属性,其值为1
      Object.defineProperty(o, "x", {value: 1, writable: true, enumerable: false, configurable: true});
      
      o.x;  //属性存在,但不可枚举, ==>1
      Object.keys(o);     //  ==>  []
      
      //对x属性进行修改,使其变为只读
      Object.defineProperty(o, 'x', {writable: false});
      
      o.x = 2;     //操作失败,但不报错;在严格模式下抛出类型错误
      o.x;    //  ==> 1
      
      //属性的值可以配置
      Object.defineProperty(o, 'x', {value: 2});
      o.x;    //  ==> 2
      
      //将x从数据属性修改为存取器属性
      Object.defineProperty(o, 'x', {get function() {return 0;}});
      o.x;   //==> 0

    4 序列化对象

    序列化对象指将对象的状态转化为字符串,同时可以将JSON字符串转化为对象。ES5内置JSON对象的JSON.stringgify()和JSON.parse()可以完成序列化和解析。

    var o = {x: 1, y: {z: [false, null, ""]}};   //测试对象
    var s =JSON.stringify(o);         //  '{"x":1,"y":{"z":[false,null,""]}}'
    var p = JSON.parse(s);         //p是s的深拷贝

    JSON是JavaScript的子集,不能表示JavaScript中所有值。

    • 可以表示:object、Array、string、number、boolean、null,可以序列化与还原。infinity、NaN序列化结果是null,Date对象序列化结果是日期字符串

    • 不能表示:函数、RegExp、Error对象和undefined

    • JSON.stringify()只能序列化对象自有的可枚举属性。


    5 Object.prototype对象的方法

    • hasOwnProperty():检测该属性是否为对象自有属性

    • propertyIsEnumerable():检测该属性是否是对象自有,并且可枚举的属性

    • isPrototypeOf:检测某对象是否为另一个对象的原型

    • toString():无参数,返回调用该方法对象值的字符串,由于功能有限,某些对象重写了该方法:Array.toString()、Date.toString()、Function.toString()

    • toLoaclString():Object中的toLoaclString()只是调用toString(),只有Date和Number类对toLoaclString()定制,可以对数字、日期和时间进行本地化处理


    6 总结

    1. JS中对象是属性的无序集合,每个对象有三个相关特性:原型prototype、类属性class property、可扩展性extensible

    2. 对象的创建三种方法:对象字面量、new调用构造函数和Object.create()

    3. 每个属性由三个部分组成:key、value和descripter

      • key只能是字符串或者值为字符串的变量标识符

      • value可以是任意JavaScript值(数据属性data property);可以是一个或两个方法,getter和setter(控制区属性accessor property)

      • descripter:writable、enumerable、configurable,其值都是布尔类型,默认都为true。可以使用Object.defineProperty()操作描述符特性。

    4. writable指该属性是否可以写入数据;enumerable指该属性是否可以枚举,(使用for-in循环可以列出,ES5提供Object.keys()和Object.getOwnPropertyNames());configurable指该属性是否可以用delete操作符删除和配置。



沪ICP备19023445号-2号
友情链接