【重溫基礎】12.使用對象

本文是 重溫基礎 系列文章的第十二篇。
今日感覺:須要總結下2018。前端

這幾天,重重的感冒發燒,在家休息好幾天,傷···。git

系列目錄:github

本章節複習的是JS中對象的使用,這是重點。 正則表達式

前置知識:
JavaScrip的設計是一個簡單的基於對象的範式。
對象是一系列屬性的集合,一個屬性包含一個鍵和一個值,也叫鍵值對
若一個屬性的值,是一個函數,則稱這個屬性爲方法。 segmentfault

一個對象,能夠有不少方法,就像一個杯子,能夠有顏色,重量,形狀等屬性。 數組

注意:
對象的名稱,對大小寫敏感。微信

1.建立對象

本文主要是複習對象的使用,至於對象的建立,下面這裏簡單介紹一下常見的建立對象的方法: ide

建立方法1函數

let obj = new Object();  // 聲明一個空對象

建立方法2post

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'
  • 因爲undefinedNaN沒法轉成對象,因此作爲參數會報錯。
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
}
*/

valueundefined是由於Object.assign方法不會拷貝其中的getset方法,使用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}

對象的解構賦值要求等號右邊必須是個對象,因此若是等號右邊是undefinednull就會報錯沒法轉成對象。

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.若參數是nullundefined則忽略且不報錯。
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 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 https://github.com/pingan8787...
JS小冊 js.pingan8787.com

歡迎關注微信公衆號【前端自習課】天天早晨,與您一塊兒學習一篇優秀的前端技術博文 .

相關文章
相關標籤/搜索