深刻理解JS中的對象

1. 對象是什麼

對象就鍵/值對的集合,經過.屬性訪問或['...']鍵訪問,訪問實際是調用內部默認的[[Get]]操做,若是對象內沒有找到,還會查找原型鏈。面試

2. 如何建立對象(語法)

定義對象的方法有兩種:算法

  • 聲明文字形式
var obj = {
        name: 'z'
    }
複製代碼
  • 構造形式
var obj = new Object()
    obj.name = 'z'
複製代碼

一般咱們都是使用聲明文字形式定義對象數組

3. 數據基本類型,對象的子類型

js中有6中主要類型:string、number、boolean、null、undefined、objectbash

前5中都是簡單基本類型,自己並非對象,這裏介紹一下null爲何不是對象函數

typeof null == object // true
複製代碼

不一樣對象在底層都表現爲二進制,js中二進制前3位都是0的話會被判斷爲object類型,null的二進制表示全都是0,,因此會有上面的bug。ui

js中的內置對象

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

咱們聽過一種說法,js中一切皆對象,那看下面的例子this

var str = 'not object'
console.log(str.length) // 10
複製代碼

既然string類型的值不是對象,爲何能夠直接在字符串字面量上訪問屬性和方法。 緣由是引擎自動把字面量轉化成String對象。spa

4. 內容

對象的內容是由一些存儲在特定命名位置的任意類型的組成的,稱之爲屬性指針

對象的屬性名永遠是字符串,code

  1. 可計算屬性名

   能夠在文字形式中使用[]包裹一個表達式當作屬性名

var prefix = 'attributes'

var obj = {
    [prefix + 'my']: 1,
    [prefix + 'you']: 2
}

obj.attribitusmy        // 1
obj['attributesyou']    // 2
複製代碼
  1. 屬性與方法

   一般把屬於某個對象(類)的函數叫作「方法」,實際上函數和對象之間只是一種引用的間接關係。

function foo () {
    console.log('foo')
}

var bar = foo   // 對foo變量的引用

var obj = {
    bar: foo
}

obj.bar    // function foo() {...}
bar    // function foo() {...}
複製代碼

obj.bar和bar都是對同一個函數的不一樣引用,不能說這個函數是特別地屬於某一個對象。最保險的說法是「函數」和「方法」在js中是能夠互換的。

  1. 數組

數組也是對象,雖然每一個下標都是整數,但仍是能夠給數組增長屬性

var arr = [1,2,3]
arr.attr = '新屬性哦'
arr.length  // 3
arr.attr    // '新屬性哦'
複製代碼

雖然能夠給數組增長屬性,但這個屬性的屬性描述符enumerabe: false,是不可枚舉的

  1. 複製對象

複製對象遠比咱們想象得複雜,由於咱們沒法選擇一個默認的複製算法。 這也是面試中常問的如何淺拷貝和深拷貝一個對象。

對於基本類型的值,它的值就是存儲在棧之中的,因此賦值操做就是直接把值複製過去,而對於引用類型的值來講,棧之中只是存儲了一個指針,這個指針指向堆中真正存放的值。

對於引用類型的值,淺拷貝就是隻須要拷貝值的指針,像賦值操做和ES6的Object.assign(target, source, source, ...)

而深拷貝就是須要拷貝出一個和原值一毛同樣的值,但倒是兩個互不影響的值。

由於JSON.stringify 在序列化的時候會丟失屬性 Map, Set, RegExp, Date, ArrayBuffer 和其餘內置類型,當須要拷貝的對象中沒有這些類型的值時,咱們能夠

var obj = {...}
var copy = JSON.parse(JSON.stringify(obj))
複製代碼

若是有這些類型的值的話,就須要用到遞歸了

//使用遞歸的方式實現數組、對象的深拷貝
    function deepClone(obj) {
        //判斷要進行深拷貝的是數組仍是對象
        var objClone = Array.isArray(obj) ? [] : {};
        //進行深拷貝的不能爲空,而且是對象或者是數組
        if (obj && typeof obj === "object") {
            for (key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (obj[key] && typeof obj[key] === "object") {
                        // 遞歸進行拷貝
                        objClone[key] = deepClone(obj[key]);
                    } else {
                        objClone[key] = obj[key];
                    }
                }
            }
        }
        return objClone;
    }
複製代碼
  1. 屬性描述符
var obj = {}

Object.defineProperty(obj, "a", {
    value: 1,           // 屬性值
    writable: true,     // 是否能夠修改屬性值
    configurable: true, // 屬性是否可配置
    enumerable: true    // 是否可枚舉
})
// Object.getOwnPropertyDescriptor() 方法返回指定對象上一個自有屬性對應的屬性描述符
Object.getOwnPropertyDescriptor(obj, "a")
//{
//    value: 1,           // 屬性值
//    writable: true,     // 是否能夠修改屬性值
//    configurable: true, // 屬性是否可配置
//    enumerable: true    // 是否可枚舉
//}
複製代碼

注意:當configurable: false時,仍是能夠單向的將writable由true改成false

5. [[Get]]、[[Put]]

   對象默認的[[Get]]和[[Put]]操做,分別能夠控制屬性值的獲取和設置。

   在ES5中可使用getter和setter改寫單個屬性的默認操做,getter和setter都是隱藏函數,getter在獲取值時調用,setter在設置值時使用。

   當給一個屬性定義getter和setter時,這個屬性被定義爲「訪問描述符」

var obj = {
    // 給a屬性定義一個getter
    get a() {
        return this._a_
    },
    // 給a定義一個setter
    set a(val) {
        return this._a_ = val * 2
    }
}

Object.defineProperty(obj, "b", {
    get: function() {
        return this.a * 2
    },
    enumerable: true
})

obj.a = 1
obj.a   // 2
obj.b   // 4
複製代碼

6. 存在性

var obj = {
    a: 2
}

Object.defineProperty(obj, "c", {
    value: 'c',
    enumerable: false,
    configurable: true,
    writable: true
})

("a" in obj)    // true
("b" in obj)    // false

obj.hasOwnProperty("a") // true
obj.hasOwnProperty("b") // false
Object.keys(obj)    // ["a"]
Object.getOwnPropertyNames(obj) // ["a", "c"]
複製代碼
  • in操做符會檢查屬性是否在對象及其[[Prototype]]鏈中
  • obj.hasOwnProperty()只會檢查屬性是否在當前對象中,不會檢查原型鏈
  • Object.keys(obj)返回一個數組,包含全部可枚舉的屬性
  • Object.getOwnPropertyNames(obj)返回一個數組,只查找對象直接包含的屬性,包括不可枚舉的屬性

7. 遍歷

  • for.. in 循環能夠用來遍歷對象的可枚舉屬性,包括[[Prototype]]鏈
  • forEach(..)會遍歷數組中全部值並忽略回調函數的返回值
  • every(..)會運行到回調函數返回false
  • some(..)會一直運行到回調函數返回true
  • for..of循環首先會向被訪問對象請求一個迭代器對象,經過調用迭代器對象的next()方法遍歷全部返回值
var arr = [1,2,3]
var it = arr[Symbol.iterator]()
it.next();  // {value: 1, done: false}
it.next();  // {value: 2, done: false}
it.next();  // {value: 3, done: false}
it.next();  // {value: undefined, done: true}
複製代碼
相關文章
相關標籤/搜索