(74)Wangdao.com第十三天_Object 对象_属性描述对象

Object 对象

JavaScript 原生提供 Object 对象

JavaScript 的所有其他对象都继承自  Object 对象,即那些对象都是Object的实例

  • Object 对象的原生方法分成两类:
    • Object 对象本身的方法
      • 就是直接定义在 Object 对象的方法
      • 对象自身的方法(又称为”静态方法“)和实例方法两部分
    • Object 的实例方法
      • 定义在 Objec 原型对象 Object.prototype 上的方法,可以被 Object 实例直接使用
  • Object() 本身是一个函数
    • 将任意值转为对象。这个方法常用于保证某个值一定是对象
      • var obj = Object();
        
        // 等同于
        var obj = Object(undefined);
        var obj = Object(null);
        
        obj instanceof Object     // true
    • instanceof运算符用来验证,一个对象是否为指定的构造函数的实例
    • obj instanceof Object 返回 true,就表示 obj 对象是 Object 的实例
    • Object (原始类型的值)    Object 方法将其转为对应的包装对象的实例
    • Object (一个对象)    总是返回该对象,即不用转换
      • // 判断变量是否为对象的函数
        function isObject(value) {
          return value === Object(value);
        }
        
        isObject([]) // true
        isObject(true) // false
  • new Object()构造函数    的首要用途是 :  直接通过它来生成新对象。
    • // 通过var obj = new Object()的写法生成新对象,
      // 与字面量的写法var obj = {}是等价的。
      // 或者说,后者只是前者的一种简便写法
      // Object(value)new Object(value)两者的语义是不同的
      // Object(value)表示将value转成一个对象
      // new Object(value)则表示新生成一个对象,它的值是value
  • Object.keys() 接受一个对象作为参数,返回一个数组,该数组的成员都是该对象自身的(而不是继承的)所有属性名
  • Object.getOwnPropertyNames()   接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名
    • Object.keys()方法    只返回可枚举的属性(详见《对象属性的描述对象》一章)
    • Object.getOwnPropertyNames()方法    还返回不可枚举的属性名
    • var a = ['Hello', 'World'];
      
      Object.keys(a)     // ["0", "1"]
      Object.getOwnPropertyNames(a)     // ["0", "1", "length"]
      
      // 数组的length属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames() 方法的返回结果
    • 自定义计算对象属性个数的方法
      var obj = {
          p1: 123,
          p2: 456
      };
      
      Object.keys(obj).length;     // 2
      Object.getOwnPropertyNames(obj).length;     // 2
  • 其他静态方法
    • 对象属性模型的相关方法
      • Object.getOwnPropertyDescriptor():    获取某个属性的描述对象。
      • Object.defineProperty():    通过描述对象,定义某个属性。
      • Object.defineProperties():    通过描述对象,定义多个属性。
    • 控制对象状态的方法
      • Object.preventExtensions():    防止对象扩展。
      • Object.isExtensible():    判断对象是否可扩展。
      • Object.seal():    禁止对象配置。
      • Object.isSealed():    判断一个对象是否可配置。
      • Object.freeze():    冻结一个对象。
      • Object.isFrozen():    判断一个对象是否被冻结。
    • 原型链相关方法
      • Object.create():    该方法可以指定原型对象和属性,返回一个新的对象。
      • Object.getPrototypeOf():    获取对象的Prototype对象。

 

  • 定义在 Object.prototype 对象的 实例方法        所有Object的实例对象都继承了这些方法。
    • Object.prototype.valueOf()    返回当前对象对应的值。
      • 返回一个对象的“值”,默认情况下返回对象本身
        • var obj = new Object();
          obj.valueOf() === obj;      // true
      • 自动类型转换时会默认调用这个方法
      • 如果自定义valueOf方法,就可以得到想要的结果
        • var obj = new Object();
          obj.valueOf = function () {
              return 2;
          };
          
          1 + obj    // 3

          // 用自定义的obj.valueOf,覆盖Object.prototype.valueOf
    • Object.prototype.toString()    返回当前对象对应的字符串形式。
      • 返回一个对象的字符串形式,默认情况下返回类型字符串
        • var o1 = new Object();
          o1.toString()    // "[object Object]"
          
          var o2 = {a:1};
          o2.toString()    // "[object Object]"    该字符串说明对象的类型。
      • 通过自定义toString方法,可以让对象在自动类型转换时,得到想要的字符串形式。
        • var obj = new Object();
          
          obj.toString = function () {
              return 'hello';
          };
          
          obj + ' ' + 'world'     // "hello world"
      • 数组、字符串、函数、Date 对象调用toString方法,并不会返回[object Object],因为它们都自定义了toString方法,覆盖原始方法
        • [1, 2, 3].toString()    // "1,2,3"
          
          '123'.toString()    // "123"
          
          (function () {
              return 123;
          }).toString();
              // "function () {
              //   return 123;
              // }"
          
          (new Date()).toString();    // "Tue May 10 2016 09:11:31 GMT+0800 (C
      • 直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型
        • Object.prototype.toString.call(value);
      • 自定义一个比typeof运算符更准确的类型判断函数
        • var type = function (o){
              var s = Object.prototype.toString.call(o);
              return s.match(/\[object (.*?)\]/)[1].toLowerCase();
          };
          
          type({});    // "object"
          type([]);    // "array"
          type(5);    // "number"
          type(null);    // "null"
          type();    // "undefined"
          type(/abcd/);    // "regex"
          type(new Date());    // "date"
          
          // 在上面这个type函数的基础上,还可以加上专门判断某种类型数据的方法
          ['Null',
             'Undefined',
             'Object',
             'Array',
             'String',
             'Number',
             'Boolean',
             'Function',
             'RegExp'
          ].forEach(function (t) {
              type['is' + t] = function (o) {
                  return type(o) === t.toLowerCase();
              };
          });
          
          type.isObject({})    // true
          type.isNumber(NaN)    // true
          type.isRegExp(/abc/)    // true
    • Object.prototype.toLocaleString()    返回当前对象对应的本地字符串形式。
      • toString的返回结果相同,也是返回一个值的字符串形式
      • 主要作用是留出一个接口,让各种不同的对象实现自己版本的toLocaleString,用来返回针对某些地域的特定的值
      • 目前,主要有三个对象自定义了toLocaleString方法
        • Array.prototype.toLocaleString()
        • Number.prototype.toLocaleString()
        • Date.prototype.toLocaleString()   
          • // 日期 Date 的实例对象的toStringtoLocaleString返回值就不一样,而且toLocaleString的返回值跟用户设定的所在地域相关
    • Object.prototype.hasOwnProperty()    判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
    • Object.prototype.isPrototypeOf()    判断当前对象是否为另一个对象的原型。
    • Object.prototype.propertyIsEnumerable()    判断某个属性是否可枚举。

 

属性描述对象 attributes object

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等

  • {
        value: undefined,    // 该属性的属性值,默认为undefined
    
        writable: true,    // 表示属性值(value)是否可改变(即是否可写),默认为true
    
        enumerable: true,    // 表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for...in遍历对象对象原型中可以遍历的属性、Object.keys())跳过该属性
    
        configurable: true,    // 表示可配置性,默认为true。如果设为false,将阻止某些操作改写该属性,
    // 比如无法删除该属性,也不得改变该属性的属性描述对象(value属性除外)。也就是说,configurable属性控制了属性描述对象的可写性。
    get: undefined, // 表示该属性的取值函数(getter),默认为undefined 每次读取该属性,都会调用这个取值函数
    // 一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true set: undefined // 表示该属性的存值函数(setter),默认为undefined }

    // 方式1
    var obj = Object.defineProperties({}, {
    p1: { value: 1, enumerable: true },
    p2: { value: 2, enumerable: false }
    });

    // 方式2
    var obj = {name:"Tom", age:"22"};
    Object.defineProperty(obj, "name", {
    writable: false, // 属性不可写
    enumerable: false, // 属性不可遍历 如 for in 将跳过 name 属性
    configurable: false, // 属性不可配置 如 delete name
    });

    Object.defineProperty(obj, 'fullName', {
    get:function(){
    return this.name + "Michal";
    },
    set:function(newName){
    this.name = newName.split(" ")[0];
    }
    });
  • Object.getOwnPropertyDescriptor() 方法
    • 获取对象某个属性的属性描述对象
    • 第一个参数是目标对象。
    • 第二个参数是一个字符串,对应目标对象的某个属性名。
    • 只能用于对象自身的属性,不能用于继承的属性。。。对于继承的属性,返回 undefined
      • var obj = { p: 'a' };
        
        // 获取obj.p的属性描述对象
        Object.getOwnPropertyDescriptor(obj, 'p');
        
        // Object { 
        // value: "a",
        // writable: true, // enumerable: true, // configurable: true // }

         

  • Object.getOwnPropertyNames() 方法
    • 返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历

 

  • Object.defineProperty(object, propertyName, attributesObject) 方法
    • 通过属性描述对象,定义或修改一个属性,然后返回修改后的对象
    • object    属性所在的对象
    • propertyName    字符串,表示属性名
    • attributesObject    属性描述对象
      • var obj = Object.defineProperty({}, 'p', {
          value: 123,
          writable: false,    // 不可写    
          enumerable: true,
          configurable: false
        });
        
        obj.p // 123
        
        obj.p = 246;
        obj.p // 123

         

  • Object.defineProperties()
    • var obj = Object.defineProperties({}, {
          p1: { value: 123, enumerable: true },
          p2: { value: 'abc', enumerable: true },
          p3: { get: function () { return this.p1 + this.p2 },    // 每次读取该属性,都会调用这个取值函数
              enumerable:true,
              configurable:true
          }
      });
      
      obj.p1    // 123
      obj.p2    // "abc"
      obj.p3    // "123abc"

       

  • Object.prototype.propertyIsEnumerable()
    • 用于判断某个属性是否可遍历
    • 只能用于判断对象自身的属性,对于继承的属性一律返回false
      • var obj = {};
        obj.p = 123;
        
        obj.propertyIsEnumerable('p');      // true
        obj.propertyIsEnumerable('toString');     // false

         

元属性

属性描述对象的各个属性,即控制属性的属性

  • value 属性是目标属性的值
    • var obj = {};
      obj.p = 123;
      
      Object.getOwnPropertyDescriptor(obj, 'p').value;    // 123
      
      Object.defineProperty(obj, 'p', { value: 246 });
      
      obj.p    // 246

       

  • writable 属性,决定了目标属性的值(value)是否可以被改变
    • 正常模式下,对writablefalse的属性赋值不会报错,只会默默失败
    • 但是,严格模式下会报错,即使对 a 属性重新赋予一个同样的值
    • 'use strict';    // 采用严格模式
      
      var obj = {};
      
      Object.defineProperty(obj, 'a', {
          value: 37,
          writable: false
      });
      
      obj.a = 37;    // Uncaught TypeError: Cannot assign to read only property 'a' of object
    • __proto__是原型对象,它的foo属性不可写。obj对象继承proto,也不可以再自定义这个属性了。如果是严格模式,这样做还会抛出一个错误
      • var proto = Object.defineProperty({}, 'foo', {
            value: 'a',
            writable: false
        });
        
        var obj = Object.create(proto);
        
        obj.foo = 'b';
        obj.foo     // 'a'    如果是严格模式,此处将抛出一个错误

         

    • 有一个规避方法,就是通过覆盖属性描述对象,绕过这个限制。原因是这种情况下,原型链会被完全忽视
      • var obj = Object.create(proto);
        Object.defineProperty(obj, 'foo', {
            value: 'b'
        });
        
        obj.foo      // "b"

         

  • enumerable(可遍历性)返回一个布尔值,表示目标属性是否可遍历
    • 如果一个属性的enumerablefalse,下面三个操作不会取到该属性
      • for..in 循环
      • Object.keys() 方法
      • JSON.stringify() 方法
    • 因此,enumerable可以用来设置“秘密”属性
      • obj.x属性的enumerablefalse,所以一般的遍历操作都无法获取该属性,使得它有点像“秘密”属性,
      • 但不是真正的私有属性,还是可以直接获取它的值

 

  • configurable (可配置性)返回一个布尔值,决定了是否可以修改属性描述对象
    • configurablefalse时,valuewritableenumerableconfigurable都不能被修改了
    • obj.pconfigurable设置false。然后,改动valuewritableenumerableconfigurable,结果都报错
      • 注意,writable只有在false改为true会报错,true改为false是允许的
      • 至于value,只要writableconfigurable有一个为true,就允许改动
      • 直接目标属性赋值,不报错,但不会成功。如果是严格模式,还会报错。
      • 可配置性决定了目标属性是否可以被删除(delete)
  • 存取器属性还可以用存取器(accessor)定义
    • 存值函数称为setter,使用属性描述对象的set属性
    • 取值函数称为getter,使用属性描述对象的get属性
      • 一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数
      • 利用这个功能,可以实现许多高级特性,比如某个属性禁止赋值
      • var obj = Object.defineProperty({}, 'p', {
            get: function () {
                return 'getter';
            },
            set: function (value) {
                console.log('setter: ' + value);
            }
        });
        
        obj.p    // "getter"
        obj.p = 123    // "setter: 123"

         

    • JavaScript 还提供了存取器的另一种写法
      • var obj = {
            get p() {    // 取值函数get不能接受参数
                return 'getter';
            },
            set p(value) {    // 存值函数set只能接受一个参数(即属性的值)
                console.log('setter: ' + value);
            }
        };

        存取器往往用于,属性的值依赖对象内部数据的场合。

          • var obj ={
                $n : 5,
                get next() { return this.$n++ },
                set next(n) {
                    if (n >= this.$n){
                        this.$n = n;
                    }else{
                         throw new Error('新的值必须大于当前值');
                    }
                }
            };
            
            obj.next     // 5
            
            obj.next = 10;
            obj.next     // 10
            
            obj.next = 5;     // Uncaught Error: 新的值必须大于当前值

             

  • 拷贝对象的所有属性到另一个对象
    • var extend = function (to, from) {
          for (var property in from) {
              to[property] = from[property];
          }
      
          return to;
      }

       

    • 上面这个方法的问题在于,如果遇到存取器定义的属性,会只拷贝值
    • 为了解决这个问题,我们可以通过Object.defineProperty方法来拷贝属性
    • var extend = function (to, from) {
          for (var property in from) {
              if (!from.hasOwnProperty(property)){
      // hasOwnProperty 来过滤掉继承的属性,否则会报错,因为Object.getOwnPropertyDescriptor读不到继承属性的属性描述对象
      continue; } Object.defineProperty( to, property, Object.getOwnPropertyDescriptor(from, property) ); } return to; } extend({}, { get a(){ return 1 } }) // { get a(){ return 1 } })

       

控制对象状态

有时需要冻结对象的读写状态,防止对象被改变

JavaScript 提供了 强度依次递增的三种冻结方法是,

  • Object.preventExtensions()
    • 可以使得一个对象无法再添加新的属性
    • var obj = new Object();
      Object.preventExtensions(obj);
      
      Object.defineProperty(obj, 'p', {
            value: 'hello'
      });    // TypeError: Cannot define property:p, object is not extensible.
      
      obj.p = 1;
      obj.p     // undefined
    • Object.isExtensible() 方法
      • 用于检查一个对象是否使用了Object.preventExtensions方法。
      • 也就是说,检查是否可以为一个对象添加属性
        var obj = new Object();
        
        Object.isExtensible(obj);     // true
        Object.preventExtensions(obj);
        Object.isExtensible(obj);     // fal
  • var newObj = Object.seal(obj)       密封对象,可以读写,但是不能添加新属性,不能删除属性
    • 使得一个对象既无法添加新属性,也无法删除旧属性
    • Object.seal实质是把属性描述对象的configurable属性设为false,因此属性描述对象不再能改变
    • Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值
    • Object.isSealed方法用于检查一个对象是否使用了Object.seal方法
      • Object.isSealed() 为true时,Object.isExtensible方法返回false
  • var newObj  = Object.freeze(obj);
    • 可以使得一个对象无法添加新属性无法删除旧属性也无法改变属性的值——使得这个对象实际上变成了常量
    • 当进行修改属性、新增属性、删除属性时,这些操作并不报错,只是默默地失败。
    • 如果在严格模式下,则会报错
    • Object.isFrozen() 方法用于检查一个对象是否使用了Object.freeze方法
      • 使用Object.freeze方法以后,Object.isSealed将会返回trueObject.isExtensible返回false
      • 用途是,确认某个对象没有被冻结后,再对它的属性赋值, 就不会报错了
  • 上面的三个方法锁定对象的可写性有一个漏洞:可以通过改变原型对象,来为对象增加属性。
    • var obj = new Object();
      Object.preventExtensions(obj);
      
      var proto = Object.getPrototypeOf(obj);
      proto.t = 'hello';
      obj.t
      // hello
      
      
    • 解决方案是,把obj的原型也冻结住
      • var obj = new Object();
        Object.preventExtensions(obj);
        
        var proto = Object.getPrototypeOf(obj);
        Object.preventExtensions(proto);
        
        proto.t = 'hello';
        obj.t     // undefined
    • 如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容
      • var obj = {
            foo: 1,
            bar: ['a', 'b']
        };
        Object.freeze(obj);
        
        obj.bar.push('c');
        obj.bar   // ["a", "b", "c"]
  • 深度冻结对象成一个常量: 
  • function deepFreeze(obj){
        if(typeof obj !== "object"){
            return obj;
        };
    
        object.freeze(obj);
    
        for(var attr in obj){
            deepFreeze(obj[attr]);
        };
    };

 

posted @ 2018-10-12 17:37  耶梦加德  阅读(238)  评论(0编辑  收藏  举报