(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.valueOfObject.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 objectui

JavaScript 提供了一個內部數據結構,用來描述對象的屬性,控制它的行爲,好比該屬性是否可寫、可遍歷等等this

  • {
        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 每次讀取該屬性,都會調用這個取值函數
    // 一旦定義了取值函數(或存值函數),就不能將屬性設爲 set: undefined // 表示該屬性的存值函數(setter),默認爲undefined }

    // 方式1
    getsetwritabletruevar 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

         

元屬性spa

屬性描述對象的各個屬性,即控制屬性的屬性prototype

  • 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);
            }
        };

        存取器每每用於,屬性的值依賴對象內部數據的場合。code

          • 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)){
      // 來過濾掉繼承的屬性,不然會報錯,由於讀不到繼承屬性的屬性描述對象
      continue; } Object.defineProperty( to, property, Object.getOwnPropertyDescriptor(from, property) ); } return to; } extend({}, { get a(){ return 1 } }) // { get a(){ return 1 } })hasOwnPropertyObject.getOwnPropertyDescriptor

       

控制對象狀態對象

有時須要凍結對象的讀寫狀態,防止對象被改變
blog

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]);
        };
    };
相關文章
相關標籤/搜索