1. 對象是什麼
對象就鍵/值對的集合,經過.屬性訪問或['...']鍵訪問,訪問實際是調用內部默認的[[Get]]操做,若是對象內沒有找到,還會查找原型鏈。
面試
2. 如何建立對象(語法)
定義對象的方法有兩種:算法
var obj = {
name: 'z'
}
複製代碼
var obj = new Object()
obj.name = 'z'
複製代碼
一般咱們都是使用聲明文字形式定義對象數組
3. 數據基本類型,對象的子類型
js中有6中主要類型:string、number、boolean、null、undefined、object
bash
前5中都是簡單基本類型,自己並非對象,這裏介紹一下null爲何不是對象函數
typeof null == object // true
複製代碼
不一樣對象在底層都表現爲二進制,js中二進制前3位都是0的話會被判斷爲object類型,null的二進制表示全都是0,,因此會有上面的bug。ui
咱們聽過一種說法,js中一切皆對象,那看下面的例子this
var str = 'not object'
console.log(str.length) // 10
複製代碼
既然string類型的值不是對象,爲何能夠直接在字符串字面量上訪問屬性和方法。 緣由是引擎自動把字面量轉化成String對象。spa
4. 內容
對象的內容是由一些存儲在特定命名位置的任意類型的組成的,稱之爲屬性
指針
對象的屬性名永遠是字符串,code
能夠在文字形式中使用[]包裹一個表達式當作屬性名
var prefix = 'attributes'
var obj = {
[prefix + 'my']: 1,
[prefix + 'you']: 2
}
obj.attribitusmy // 1
obj['attributesyou'] // 2
複製代碼
一般把屬於某個對象(類)的函數叫作「方法」,實際上函數和對象之間只是一種引用的間接關係。
function foo () {
console.log('foo')
}
var bar = foo // 對foo變量的引用
var obj = {
bar: foo
}
obj.bar // function foo() {...}
bar // function foo() {...}
複製代碼
obj.bar和bar都是對同一個函數的不一樣引用,不能說這個函數是特別地屬於某一個對象。最保險的說法是「函數」和「方法」在js中是能夠互換的。
數組也是對象,雖然每一個下標都是整數,但仍是能夠給數組增長屬性
var arr = [1,2,3]
arr.attr = '新屬性哦'
arr.length // 3
arr.attr // '新屬性哦'
複製代碼
雖然能夠給數組增長屬性,但這個屬性的屬性描述符enumerabe: false,是不可枚舉的
複製對象遠比咱們想象得複雜,由於咱們沒法選擇一個默認的複製算法。 這也是面試中常問的如何淺拷貝和深拷貝一個對象。
對於基本類型的值,它的值就是存儲在棧之中的,因此賦值操做就是直接把值複製過去,而對於引用類型的值來講,棧之中只是存儲了一個指針,這個指針指向堆中真正存放的值。
對於引用類型的值,淺拷貝就是隻須要拷貝值的指針,像賦值操做和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;
}
複製代碼
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"]
複製代碼
7. 遍歷
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}
複製代碼