本文是 重溫基礎 系列文章的第十二篇。
今日感受:需要總結下2018。
這幾天,重重的感冒發燒,在家休息好幾天,傷···。
系列目錄:
- 【複習資料】ES6/ES7/ES8/ES9資料整理(個人整理)
- 【重溫基礎】1.語法和資料型別
- 【重溫基礎】2.流程控制和錯誤處理
- 【重溫基礎】3.迴圈和迭代
- 【重溫基礎】4.函式
- 【重溫基礎】5.表示式和運算子
- 【重溫基礎】6.數字
- 【重溫基礎】7.時間物件
- 【重溫基礎】8.字串
- 【重溫基礎】9.正規表示式
- 【重溫基礎】10.陣列
- 【重溫基礎】11.Map和Set物件
本章節複習的是JS中物件的使用,這是重點。
前置知識:
JavaScrip的設計是一個簡單的基於物件的正規化。
物件是一系列屬性的集合,一個屬性包含一個鍵和一個值,也叫鍵值對。
若一個屬性的值,是一個函式,則稱這個屬性為方法。
一個物件,可以有很多方法,就像一個杯子,可以有顏色,重量,形狀等屬性。
注意:
物件的名稱,對大小寫敏感。
1.建立物件
本文主要是複習物件的使用,至於物件的建立,下面這裡簡單介紹一下常見的建立物件的方法:
建立方法1:
let obj = new Object(); // 宣告一個空物件
建立方法2:
let obj = {}; // 宣告一個空物件
上面的name
是物件obj
中的一個屬性,對應的值為"leo"
。
2.設定物件屬性和訪問屬性
設定物件屬性,也有兩種方法:
直接設定:
let obj = {};
obj.name = "leo"; // 為物件新增屬性和值
建立時設定:
let obj = {name : `leo`};
obj.name = "leo"; // 為物件新增屬性和值
當一個物件定義了一個屬性,我們可以用點符號(.
)來訪問它。
obj.name; // "leo"
注意:
- 屬性的另外一種寫法:
let obj = {};
let n = "name";
obj[n] = "leo"; // 屬性名使用變數
obj["height"] = 188; // 字串
obj["age" + "Is"] = 15; // 字串拼接
console.log(obj);
/*
obj = {
ageIs: 15
height: 188
name: "leo"
}
*/
- 屬性的值也可以是個方法:
let obj = {};
obj.getTime = function(){
return new Date();
}
obj.getTime(); // 訪問屬性的方法
//Wed Jan 02 2019 21:07:59 GMT+0800 (中國標準時間)
3.列舉物件的所有屬性
從 ECMAScript 5 開始,有三種原生的方法用於列出或列舉物件的屬性:
3.1 for…in 迴圈
該方法依次訪問一個物件及其原型鏈中所有可列舉的屬性。
let obj = {a:1, b:2, c:3};
for (let i in obj) {
console.log("obj." + i + " = " + obj[i]);
}
/*
"obj.a = 1"
"obj.b = 2"
"obj.c = 3"
*/
3.2 Object.keys(o)
該方法返回一個物件 o
自身包含(不包括原型中)的所有屬性的名稱的陣列。
let obj = {a:1, b:2, c:3};
let arr = Object.keys(obj);
console.log(arr);
/*
["a", "b", "c"]
*/
arr.forEach(function(val,index,array){
console.log("key:" + val+",val:"+obj[val])
})
/*
key:a,val:1
key:b,val:2
key:c,val:3
*/
3.3 Object.getOwnPropertyNames(o)
該方法返回一個陣列,它包含了物件 o
所有擁有的屬性(無論是否可列舉)的名稱。
let obj = {a:1, b:2, c:3};
let arr = Object.getOwnPropertyNames(obj);
console.log(arr);
/*
["a", "b", "c"]
*/
arr.forEach(function(val,index,array){
console.log("key:" + val+",val:"+obj[val])
})
/*
key:a,val:1
key:b,val:2
key:c,val:3
*/
4.ES6新增:物件的擴充
4.1 屬性的簡潔表示
let a = `a1`;
let b = { a }; // b => { a : `a1` }
// 等同於
let b = { a : a };
function f(a, b){
return {a, b};
}
// 等同於
function f (a, b){
return {a:a ,b:b};
}
let a = {
fun () {
return `leo`;
}
}
// 等同於
let a = {
fun : function(){
return `leo`;
}
}
4.2 屬性名錶達式
JavaScript
提供2種方法定義物件的屬性。
// 方法1 識別符號作為屬性名
a.f = true;
// 方法2 字串作為屬性名
a[`f` + `un`] = true;
延伸出來的還有:
let a = `hi leo`;
let b = {
[a]: true,
[`a`+`bc`]: 123,
[`my` + `fun`] (){
return `hi`;
}
};
// b.a => undefined ; b.abc => 123 ; b.myfun() => `hi`
// b[a] => true ; b[`abc`] => 123 ; b[`myfun`] => ƒ [`my` + `fun`] (){ return `hi`; }
注意:
屬性名錶達式不能與簡潔表示法同時使用,否則報錯。
// 報錯
let a1 = `aa`;
let a2 = `bb`;
let b1 = {[a1]};
// 正確
let a1 = `aa`;
let b1 = { [a1] : `bb`};
4.3 Object.is()
Object.is()
用於比較兩個值是否嚴格相等,在ES5時候只要使用相等運算子(==
)和嚴格相等運算子(===
)就可以做比較,但是它們都有缺點,前者會自動轉換資料型別,後者的NaN
不等於自身,以及+0
等於-0
。
Object.is(`a`,`a`); // true
Object.is({}, {}); // false
// ES5
+0 === -0 ; // true
NaN === NaN; // false
// ES6
Object.is(+0,-0); // false
Object.is(NaN,NaN); // true
4.4 Object.assign()
Object.assign()
方法用於物件的合併,將原物件的所有可列舉屬性複製到目標物件。
基礎用法:
第一個引數是目標物件,後面引數都是源物件。
let a = {a:1};
let b = {b:2};
Object.assign(a,b); // a=> {a:1,b:2}
注意:
- 若目標物件與源物件有同名屬性,則後面屬性會覆蓋前面屬性。
let a = {a:1, b:2};
let b = {b:3, c:4};
Object.assign(a, b); // a => {a:1, b:3, c:4}
- 若只有一個引數,則返回該引數。
let a = {a:1};
Object.assign(a) === a; // true
- 若引數不是物件,則先轉成物件後返回。
typeof Object.assign(2); // `object`
- 由於
undefined
或NaN
無法轉成物件,所以做為引數會報錯。
Object.assign(undefined) // 報錯
Object.assign(NaN); // 報錯
-
Object.assign()
實現的是淺拷貝。
Object.assign()
拷貝得到的是這個物件的引用。這個物件的任何變化,都會反映到目標物件上面。
let a = {a: {b:1}};
let b = Object.assign({},a);
a.a.b = 2;
console.log(b.a.b); // 2
- 將陣列當做物件處理,鍵名為陣列下標,鍵值為陣列下標對應的值。
Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]
5.ES8新增:Object.values(),Object.entries()
ES7中新增加的 Object.values()
和Object.entries()
與之前的Object.keys()
類似,返回陣列型別。
回顧下Object.keys()
:
var a = { f1: `hi`, f2: `leo`};
Object.keys(a); // [`f1`, `f2`]
5.1 Object.values()
返回一個陣列,成員是引數物件自身的(不含繼承的)所有可遍歷屬性的鍵值。
let a = { f1: `hi`, f2: `leo`};
Object.values(a); // [`hi`, `leo`]
如果引數不是物件,則返回空陣列:
Object.values(10); // []
Object.values(true); // []
5.2 Object.entries()
返回一個陣列,成員是引數物件自身的(不含繼承的)所有可遍歷屬性的鍵值對陣列。
let a = { f1: `hi`, f2: `leo`};
Object.entries(a); // [[`f1`,`hi`], [`f2`, `leo`]]
- 用途1:
遍歷物件屬性。
let a = { f1: `hi`, f2: `leo`};
for (let [k, v] of Object.entries(a)){
console.log(
`${JSON.stringfy(k)}:${JSON.stringfy(v)}`
)
}
// `f1`:`hi`
// `f2`:`leo`
- 用途2:
將物件轉為真正的Map結構。
let a = { f1: `hi`, f2: `leo`};
let map = new Map(Object.entries(a));
手動實現Object.entries()
方法:
// Generator函式實現:
function* entries(obj){
for (let k of Object.keys(obj)){
yield [k ,obj[k]];
}
}
// 非Generator函式實現:
function entries (obj){
let arr = [];
for(let k of Object.keys(obj)){
arr.push([k, obj[k]]);
}
return arr;
}
6.ES8新增:Object.getOwnPropertyDescriptors()
之前有Object.getOwnPropertyDescriptor
方法會返回某個物件屬性的描述物件,新增的Object.getOwnPropertyDescriptors()
方法,返回指定物件所有自身屬性(非繼承屬性)的描述物件,所有原物件的屬性名都是該物件的屬性名,對應的屬性值就是該屬性的描述物件
let a = {
a1:1,
get f1(){ return 100}
}
Object.getOwnPropetyDescriptors(a);
/*
{
a:{ configurable:true, enumerable:true, value:1, writeable:true}
f1:{ configurable:true, enumerable:true, get:f, set:undefined}
}
*/
實現原理:
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}
引入這個方法,主要是為了解決Object.assign()
無法正確拷貝get
屬性和set
屬性的問題。
let a = {
set f(v){
console.log(v)
}
}
let b = {};
Object.assign(b, a);
Object.a(b, `f`);
/*
f = {
configurable: true,
enumable: true,
value: undefined,
writeable: true
}
*/
value
為undefined
是因為Object.assign
方法不會拷貝其中的get
和set
方法,使用getOwnPropertyDescriptors
配合Object.defineProperties
方法來實現正確的拷貝:
let a = {
set f(v){
console.log(v)
}
}
let b = {};
Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));
Object.getOwnPropertyDescriptor(b, `f`)
/*
configurable: true,
enumable: true,
get: undefined,
set: function(){...}
*/
Object.getOwnPropertyDescriptors
方法的配合Object.create
方法,將物件屬性克隆到一個新物件,實現淺拷貝。
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
// 或者
const shallowClone = (obj) => Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
7.ES9新增:物件的擴充運算子
7.1 介紹
物件的擴充運算子,即物件的Rest/Spread屬性,可將物件解構賦值用於從一個物件取值,搜鍵值對分配到指定物件上,與陣列的擴充運算子類似:
let {x, y, ...z} = {x:1, y:2, a:3, b:4};
x; // 1
y; // 2
z; // {a:3, b:4}
物件的解構賦值要求等號右邊必須是個物件,所以如果等號右邊是undefined
或null
就會報錯無法轉成物件。
let {a, ...b} = null; // 執行時報錯
let {a, ...b} = undefined; // 執行時報錯
解構賦值必須是最後一個引數,否則報錯。
let {...a, b, c} = obj; // 語法錯誤
let {a, ...b, c} = obj; // 語法錯誤
注意:
- 1.解構賦值是淺拷貝。
let a = {a1: {a2: `leo`}};
let {...b} = a;
a.a1.a2 = `leo`;
b.a1.a2 = `leo`;
- 2.擴充運算子的解構賦值,不能複製繼承自原型物件的屬性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3; // { b: 2 }
o3.a; // undefined
7.2 使用場景
- 1.取出引數物件所有可遍歷屬性,拷貝到當前物件中。
let a = { a1:1, a2:2 };
let b = { ...a };
b; // { a1:1, a2:2 }
// 類似Object.assign方法
- 2.合併兩個物件。
let a = { a1:1, a2:2 };
let b = { b1:11, b2:22 };
let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22}
// 等同於
let ab = Object.assign({}, a, b);
- 3.將自定義屬性放在擴充運算子後面,覆蓋物件原有的同名屬性。
let a = { a1:1, a2:2, a3:3 };
let r = { ...a, a3:666 };
// r {a1: 1, a2: 2, a3: 666}
// 等同於
let r = { ...a, ...{ a3:666 }};
// r {a1: 1, a2: 2, a3: 666}
// 等同於
let r = Object.assign({}, a, { a3:666 });
// r {a1: 1, a2: 2, a3: 666}
- 4.將自定義屬性放在擴充運算子前面,就會成為設定新物件的預設值。
let a = { a1:1, a2:2 };
let r = { a3:666, ...a };
// r {a3: 666, a1: 1, a2: 2}
// 等同於
let r = Object.assign({}, {a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
// 等同於
let r = Object.assign({a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
- 5.擴充運算子後面可以使用表示式。
let a = {
...(x>1? {a:!:{}),
b:2
}
- 6.擴充運算子後面如果是個空物件,則沒有任何效果。
{...{}, a:1}; // {a:1}
- 7.若引數是
null
或undefined
則忽略且不報錯。
let a = { ...null, ...undefined }; // 不報錯
- 8.若有取值函式
get
則會執行。
// 不會列印 因為f屬性只是定義 而不沒執行
let a = {
...a1,
get f(){console.log(1)}
}
// 會列印 因為f執行了
let a = {
...a1,
...{
get f(){console.log(1)}
}
}
參考資料
1.MDN 使用物件
2.W3school JavaScript 物件
本部分內容到這結束
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | https://github.com/pingan8787… |
JS小冊 | js.pingan8787.com |
歡迎關注微信公眾號【前端自習課】每天早晨,與您一起學習一篇優秀的前端技術博文 .