Array.prototype.concat() 和 Object.assign() 對比

Array.prototype.concat() 和 Object.assign() 對比

ES6 中提供了一些對象的方法擴展,其中的 Object.assgin() 讓我印象深刻,因爲他的使用方式和效果,讓我想起了數組的拼接方法:Array.prototype.concant()。他們有着一些相似的寫法和功能,當然也有着一些區別,讓我們來一起探討吧!

先來看看下面兩段代碼

let arr1 = ['a', 'b'];
let arr2 = ['a', 'c'];
let arr3 = arr1.concat(arr2);
// arr3: ['a', 'b', 'a', 'c']
let obj1 = {name: '張三', age: 22};
let obj2 = {name: '李四', sex: 'boy'};
let obj3 = Object.assgin(obj1, obj2);
// obj3: {name: '李四', age: 22, sex: 'boy'}

很顯然,他們都出色地完成了多個目標的合併,但是仔細一看,還是有着一些不同之處:

  1. concat() 方法與其說是合併,更像是拼接,把多個目標按順序拼接到一起

  2. Object.assgin() 方法則更傾向於合併,它同樣按照順序進行合併,不同之處是重名的屬性,會保留最新的

這是他們最爲初步的不同,再把這幾個數組和對象進行逐個打印觀察:

arr1: ['a', 'b']
arr2: ['a', 'c']
arr3: ['a', 'b', 'a', 'c']
obj1: {name: '李四', age: 22, sex: 'boy'}
obj2: {name: '李四', sex: 'boy'}
obj3: {name: '李四', age: 22, sex: 'boy'}

很好,我們又發現新的不同了:

  1. concat 方法是創建一個新的數組,不會對參與拼接的數組內部進行直接的操作,他們本身不會發生改變

  2. Object.assgin() 方法,第一個參數是源對象,其他參數對象的任務,就是把他們的屬性都賦值到這個源對象上,所以是對這個源對象 obj1 進行的操作(改造),它不會創造新的對象,除了源對象 obj1 發生改變外,其他的對象並不會發生改變

那麼,他們的本身的屬性,和原型對象對象上的屬性,會參與合併嗎?

老規矩,上代碼:

// 在 concat 合併之前,我們給 arr2 本身,和它的原型對象,分別加上不同的屬性
arr2.myLeng = function () {
    console.log(this.length);
};
arr2.__proto__.proLeng = function () {
    console.log(this.length);
};
// 使用 concat 進行合併後,調用這兩個方法
arr3.myLeng();  // 報錯
arr3.proLeng();  // 4

我們驚訝地發現,原型對象上的方法 proLeng 也被複制過來了,但是數組本身上的方法 myLeng 並沒有成功被複制過來。

再看看 Object.assgin() 的表現:

// 和上段代碼相似的插入位置
obj2.from = 'China';
obj2.__proto__.myAge = function () {
    console.log(this.age);
};

console.log(obj3.from); // China
obj3.myAge(); // 22

顯然,複製過來的就是它本身的屬性,所以 from 存在沒有什麼爭議,關鍵是 obj2 原型對象上的 myAge 方法同樣被繼承了

又可以總結一條:

concat() 方法創建出來的新數組,會繼承參與拼接數組原型對象上的屬性和方法,但是原數組本身的屬性和方法並不會被繼承。

Object.assgin() 方法操作的源對象,會繼承參與合併對象“本身”和其“原型對象”上的屬性和方法

但是!熟悉原型鏈和原型對象的同學發現有點問題了!

注意:因爲使用 __proto__ 直接操作的是對應構造函數的原型對象,如數組的是 Array.prototype,對象的是 Object.prototype,所以 concatObject.assgin() 嚴格上來講並不是把這些原型對象上的方法給賦值繼承了過來,而是操作 __proto__ 的時候,會影響全部由對應構造函數生成出來的對象或數組。

那要怎麼辦呢,很簡單,咱們自己自定義一個構造函數,給這個構造函數的原型對象添加方法。然後用這個構造函數進行實例化,讓他參與 Object.assgin(),看源對象是否會繼承:

let obj = {name: 'obj', type: 'a'};

// 自定義構造函數
let Cobj = function (name) {
    this.name = name;
};

// 給原型對象上添加方法
Cobj.prototype.getName = function () {
    console.log(this.name);
};

let obj2 = new Cobj('obj2');  // 實例化
Object.assgin(obj, obj2);
console.log(obj);  // {name: 'obj2', type: 'a'}
obj.getName();  // 報錯

很遺憾,如果是自定義構造函數實例出來的對象,則不會繼承他原型對象上的方法。

結合上面的注意點,我們可以很容易看出來,其實 Object.assgin() 並不會繼承參與合併對象的原型對象 prototype

最後把前面列舉的不同進行一些彙總:
1. 兩個方法都是按順序進行和並,但是 Object.assgin 會把重複的屬性進行保留最新值的操作,concat 不會
2. concat 會創建一個新的數組,不會影響參與合併的原數組。而 Object.assgin 則是把第一個參數對象當成操作對象,把其他參數對象的屬性往它身上進行合併,不會創建新對象,是對第一個參數對象的直接操作
3. concat 創建出來的新數組,和Object.assgin 操作的對象,並不會繼承有參與合併對象“本身”及“原型對象”上的方法和屬性

以上就是一些小小的實驗總結了,爲了詳細之餘難免有些亢長了,可能有些觀點並不完全準確,歡迎大家一起留言探討!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章