JavaScript || 對象

對象


對象是JavaScript的基本數據類型:屬性的無序集合。每一個屬性key: value和屬性描述符descripter組成。數組

  • 屬性名key:字符串或合法的變量標識符;ide

  • 屬性值value:能夠是任意JavaScript值(numberstringbooleannullundefined和數組、對象).
    value能夠是gettersetter函數

  • 屬性描述符descripter:每一個屬性的三個相關特性writableenumerableconfigurable,其值都是布爾類型,默認都爲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()建立一個對象。ui

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

1.3 Object.create()方法

原型:每一個JS對象都有一個與之相關聯的原型對象prototype,對象其原型對象上繼承屬性。(當前對象中的__proto__屬性是其原型對象的連接)this

全部經過字面量建立的對象的原型都是Object.prototype,一個字面量對象{}至關於調用new Object()prototype

Object.prototype的原型是null,因此null是原型的出口。rest

Object.create()是一個靜態函數(不是提供給對象調用的方法),用於建立對象:code

  • 第一個參數:原型對象,建立的新對象以該參數爲原型;

  • 第二個參數:可選,用於對對象屬性的描述

  • 建立一個沒有原型的對象: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.prototypetoString()方法來簡潔查詢。

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

經過對返回字符串切片:第8位到倒數第2位便可得到對象的類型。可是許多對象重寫了toString()方法,須要間接調用Functioncall()方法

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

classof()方法能夠返回傳遞給它的任意對象的類型,參數爲numberstringboolean時,使用包裝類型將其轉化爲對象,再進行操做。

classof(null);  // ==> "Null"
    classof(2);  // ==> "Number"
    classof(false);  // ==> "Boolean"
    classof(new Date());  // ==> "Date"
    classof("");  // ==> "String"
    classof({});  // ==> "Object"
    classof([]);  // ==> "Array"
    classof(/./);  // ==> "RegExp"
    classof(window);  // ==> "Window",宿主對象
    function F() {};    //自定義一個構造器函數
    classof(new F());  // ==> "Object"

2.3 可擴展性extensible attribute

對象的課擴展性用來描述對象是否能夠添加新的屬性。全部的內置對象和自定義對象都是可擴展的,除非將其轉化爲不可擴展

  • Object.esExtensible()函數判斷傳入對象是否能夠擴展;

  • Object.preventExensible()函數將傳入的對象設置爲不可擴展,而且過程不可逆;隻影響對象自己的可擴展性

  • Object.seal()函數將對象設置爲不可擴展,屬性描述符configurable設置爲false(不能添加新屬性,已有的屬性不能刪除或配置,可是對於writable: true時,能夠修改屬性的值);過程不可逆

  • Object.isSealed()函數判斷一個對象是否封閉

  • Object.freeze()函數將對象設置爲不可擴展,屬性描述符configurable: false;writable: false;只讀。valueaccessor 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,不報錯

  • 若是查詢不存在對象的屬性,會報錯

  • nullundefined設置屬性會報錯

3.2 屬性刪除

使用delete操做符,能夠刪除對象的屬性(其描述符中configurabletrue),而且只能刪除自有屬性,不能刪除繼承屬性

3.3 屬性檢測

判斷某個屬性是否在某個對象中,JS有三種方法:inhasOwnProperty()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 屬性settergetter

屬性的組成:keyvaluedescripter。在ES5中,value能夠用一個或兩個方法替代(gettersetter方法)

  • 全部JavaScript的value叫作數據屬性data property

  • gettersetter定義的屬性叫作存取器屬性:accessor property

不一樣於data propertyaccessor property的讀寫屬性由gettersetter決定。

  • 若是屬性同時有gettersetter,它是一個可讀寫的屬性;

  • 若是屬性只有getter,它是一個只讀的屬性;

  • 若是屬性只有setter,它是一個只寫的屬性;讀取只寫屬性老是返回undefined

gettersetter定義

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

屬性除keyvalue,還有一組用於描述其特性的descripter,其中有writableenumerableconfigurable三個屬性,其值都爲布爾類型,默認爲true

假設將settergetter看作descripter,同理將屬性的value也看作descripter

  • data property的四個特性:valuewritableenumerableconfigurable

  • accessory property的四個特性:gettersetterenumerableconfigurable

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中全部值。

  • 能夠表示:objectArraystringnumberbooleannull,能夠序列化與還原。infinityNaN序列化結果是nullDate對象序列化結果是日期字符串

  • 不能表示:函數、RegExpError對象和undefined

  • JSON.stringify()只能序列化對象自有的可枚舉屬性。


5 Object.prototype對象的方法

  • hasOwnProperty():檢測該屬性是否爲對象自有屬性

  • propertyIsEnumerable():檢測該屬性是不是對象自有,而且可枚舉的屬性

  • isPrototypeOf:檢測某對象是否爲另外一個對象的原型

  • toString():無參數,返回調用該方法對象值的字符串,因爲功能有限,某些對象重寫了該方法:Array.toString()Date.toString()Function.toString()

  • toLoaclString()Object中的toLoaclString()只是調用toString(),只有DateNumber類對toLoaclString()定製,能夠對數字、日期和時間進行本地化處理


6 總結

  1. JS中對象是屬性的無序集合,每一個對象有三個相關特性:原型prototype、類屬性class property、可擴展性extensible

  2. 對象的建立三種方法:對象字面量、new調用構造函數和Object.create()

  3. 每一個屬性由三個部分組成:keyvaluedescripter

    • key只能是字符串或者值爲字符串的變量標識符

    • value能夠是任意JavaScript值(數據屬性data property);能夠是一個或兩個方法,gettersetter(控制區屬性accessor property

    • descripterwritableenumerableconfigurable,其值都是布爾類型,默認都爲true。可使用Object.defineProperty()操做描述符特性。

  4. writable指該屬性是否能夠寫入數據;enumerable指該屬性是否能夠枚舉,(使用for-in循環能夠列出,ES5提供Object.keys()Object.getOwnPropertyNames());configurable指該屬性是否能夠用delete操做符刪除和配置。

相關文章
相關標籤/搜索