JavaScript對象詳解

對象的聲明有倆種:前端

  • 字面量
  • 經過new一個構造函數Object

二者惟一的區別就是,字面量形式,能夠一次賦值多個,經過new Object就得一個一個賦值es6

數據類型函數

  • JS種7種數據類型this

    • number
    • string
    • undefined
    • null
    • boolean
    • object
    • symbol(es6新增,也不算新了,es6都多久了...)
  • 簡單數據類型spa

    • number
    • string
    • undefined
    • null
    • boolean
  • 複雜基本類型(內置對象,object子類型)雙向綁定

    • Number
    • String
    • Boolean
    • Object
    • Function
    • Array
    • RegExp
    • Date
    • Error

補充:null爲何是基本類型可是依然會出現,typeof null 會返回 object?這是一個語言的bug,原理以下:原理是這樣的,不一樣的對象在底層都表示爲二進制,在 JavaScript 中二進制前三位都爲 0 的話會被判
斷爲 object 類型,null 的二進制表示是全 0,天然前三位也是 0,因此執行 typeof 時會返回「object」。code

深刻對比基本類型和複雜類型的區別,用string和String對比對象

var str = 'hello world';
var strObj = new String('hello String');

str.length                          // 11
strObj.length                    // 12

// typeof判斷屬於那種簡單數據類型
typeof str;                         // "string"
typeof strObj;                     //"object"

// instanceof判斷對象實例的鼻祖是哪一個複雜類型
str instanceof String         //  false
strObj instanceof String   // true

這段代碼能夠分析出,str經過字面量形式聲明,其數據類型爲基本數據類型,並非對象,strObj 爲複雜數據類型String的實例。咱們知道str, strObj的數據類型以後,就要拋出一個問題,爲何str並非對象,它怎麼就能調用length呢?這不是一個簡單數據類型該有的呀。js向你丟出一個結論,那就是簡單數據類型存在一個隱式轉換,它會從簡單數據類型轉換爲複雜數據類型。blog

對象

1. 對象的屬性

首先以對象字面量整一個對象ip

let obj = {
    name: 'hello',
    age: 12
}

訪問對象的屬性能夠經過 圓點運算符 相似於 obj.name, 還能夠經過屬性運算符 obj["name"]訪問。

2. 淺拷貝,深拷貝

ES6 定義了Object.assign()方法實現淺複製,該方法第一個參數是目標對象,以後還能夠跟一個或多個原對象,他會遍歷一個或多個原對象全部可枚舉的自有鍵並把他們複製(使用 = 操做符賦值,因此這種僵硬的賦值操做,不會將原對象的屬性的特性複製過來)到目標對象,最後返回目標對象。

3. 屬性描述符【升級打怪必備技能】

對象有本身的屬性和方法,對於咱們js對象的屬性來說,屬性還有本身的「屬性」,又稱爲屬性描述符。不要小看這個屬性描述符,可不得了,經過屬性描述符,咱們不只僅能夠配置屬性是讀寫權限,甚至經過setter,getter實現咱們的數據雙向綁定。該介紹一下這位調用屬性描述符的內置方法了:Object.defineProperty()。這個方法接受三個參數,第一個是指定的對象,第二個是指定的對象參數,第三個固然是要修改的屬性描述符了。當對象中不存在指定的屬性,Object.defineProperty()就建立這個屬性,當描述符中省略某些字段時,這些字段將使用它們的默認值,擁有布爾值的字段的默認值都是false

因此這也就是說,經過 Object.defineProperty()建立的屬性,描述符是不可配置的,由於configurable:false
// 直接對一個對象使用原點運算符賦值,屬性描述符都是true
var o = {};

o.a = 1;
// 等同於 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : true,
  configurable : true,
  enumerable : true
});


// 另外一方面,使用Object.defineProperty()卻將屬性修飾符關閉了
Object.defineProperty(o, "a", { value : 1 });
// 等同於 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : false,
  configurable : false,
  enumerable : false
});
var obj = {
    name: 'hello',
    age: 12
}

Object.defineProperty(obj, 'name', {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true
})

obj.name;       //2 not hello

接下來,咱們解釋一下屬性描述符的四大天王:value, writable, configurable, enumerable

  • configurable

爲啥先要介紹configurable呢,不得不說說他的重要性,這個描述符的值是true/false,它表示屬性描述符是否可配置,當值爲false時,剩下的描述符都是不可配置的,修改也不會生效,不包括value。簡單的說,configurable控制的其餘描述符的命運!後面咱們討論的時候,默認configurable爲true,否則,討論也沒有意義,根本不會改變嘛

var a = {
    name: 'tom',
    age: 22
}

Object.defineProperty(a, 'age', {
    enumerable: false,
    writable:false,
    configurable: false,
})
a.age = 12;   // a.age 爲12.即便writable 是false,由於configurable是false,因此writable其實仍是默認的true

我知道你確定是不信的,直接上圖。Object.getOwnPropertyDescriptor() 與Object.defineProperty()互爲一對兒,一個定義,一個查看定義

並且,將configurable修改成false,這是一個單向操做,你想再次修改它的值爲true以實現從新配置屬性描述符是不可能的,一旦設置false,永遠都是false了。據說你還不信,那我再上圖

var myObject = {
    a:2
};
myObject.a = 3;
myObject.a; // 3
Object.defineProperty( myObject, "a", {
    value: 4,
    writable: true,
    configurable: false, // 不可配置!
enumerable: true
} );

myObject.a; // 4
myObject.a = 5;
myObject.a; // 5

Object.defineProperty( myObject, "a", {
    value: 6,
    writable: true,
    configurable: true,
    enumerable: true
} ); // TypeError

  • writable

咱們介紹一下控制讀寫的小夥伴-writable,這個屬性描述符控制咱們是否能夠對屬性進行賦值,值爲false時,賦值是無論用的。嚴格模式下,還會拋出錯誤。至於writable怎麼處理屬性是否能被賦值,留一個疑問,稍後立刻揭曉。

// configurable爲true
var myObject = {};

Object.defineProperty( myObject, "a", {
    value: 2,
    writable: false, // 不可寫!
    configurable: true,
    enumerable: true
} );

myObject.a = 3;
myObject.a; // 2
  • Enumerable

這個屬性修飾符可能沒有那麼直觀,可是從字面來看,咱們應該是能瞭解到的,這是控制屬性是否能出如今對象可枚舉屬性中。若是將enumerable設置爲false,屬性就不會出如今可枚舉屬性中。enumerable定義了對象的屬性是否能夠在 for...in 循環和 Object.keys() 中被枚舉。

var obj = {
    name: 'tom',
    age: 12
}

for(prop in obj) {
    console.log(prop);     // name, age
}

Object.defineProperty(obj, 'name', {
    enumerable: false,
})

for(prop in obj) {
    console.log(prop);                //age
}

補充一些可枚舉相關的知識,什麼叫可枚舉屬性,就是指那些屬性描述符設置爲enumerable爲true的屬性。有不少方法是基於可枚舉性的。好比,Object.keys(), in, for in 。對於上面那個例子,就是使用了for in遍歷obj對象可枚舉屬性。

tips: 利用屬性修飾符拓展的功能
  • ES6 實現的常量const
    自己JS是沒有常量這個概念的,可是咱們可使用writable:false 和 configurable:false,就能夠建立一個一次賦值,不能修改的常量
  • 禁止拓展
    若是想禁止一個對象添加新屬性而且保留已有屬性,可使用Object.preventExtensions(),這樣顯式賦值就會靜默失敗,嚴格模式下,將會拋出TypeError異常
  • 密封,禁止拓展增強版,已有的屬性以及屬性修飾符都不能改
    Object.seal(..) 會建立一個「密封」的對象,這個方法實際上會在一個現有對象上調用
    Object.preventExtensions(..) 並把全部現有屬性標記爲 configurable:false。
    因此,密封以後不只不能添加新屬性,也不能從新配置或者刪除任何現有屬性(雖然能夠
    修改屬性的值)。
  • 凍結,更狠,Object.seal() 增強版,直接修改屬性修飾符writable:false,值都不能動了。
    Object.freeze(..) 會建立一個凍結對象,這個方法實際上會在一個現有對象上調用
    Object.seal(..) 並把全部「數據訪問」屬性標記爲 writable:false,這樣就沒法修改它們
    的值。

4. Getter/Setter

ES5可使用getter和setter部分改寫默認操做,可是隻能應用在單個屬性上,沒法應用在整個對象上,getter,setter都是隱藏函數,前者在獲取屬性值時自動調用,後者是在設置屬性值時自動調用。其實如今,咱們能夠總結一下前文所述。屬性描述符其實分爲兩種

  • 數據修飾符
  • 訪問修飾符

而咱們前文一直在講述數據修飾符,那麼什麼是訪問修飾符呢?
當咱們給一個屬性定義getter和setter或者二者都有時,這個屬性會被定義爲」訪問描述符「,對於訪問描述符而言,JavaScript會忽略它們的value和writable特性,取而代之的是關心set/get(還有configurable和enumerable)特性

實例演示:

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

一言以蔽之,setter/getter無非就是咱們在賦值或者讀取變量時,預先悄悄調用的一個隱藏方法。
還有一種經過對象文字語法建立的get,set(我也是從《你不知道的js》看到這種用法的,MDN這相關的文檔中我並無看到,學到了),這個不是經過Object.defineProperty()建立的,兩者都會在對象中建立一個不包含值的屬性,對於這個屬性的訪問會自動調用一個隱藏函數,它的返回值會被看成屬性訪問的返回值。

其實能夠更細緻的觀察到,經過對象文字語法建立的變量,他的修飾符按照規則確定是沒有value的,可是其餘配置信息是true,這一點很關鍵

大致上對象的內容就是這麼多,後續在作一個補充,MDN實在是前端寶典,看到這裏更推薦讀者閱讀MDN相關文檔:https://developer.mozilla.org...

相關文章
相關標籤/搜索