成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

javascript系列--Object.assign實(shí)現(xiàn)淺拷貝的原理以及實(shí)現(xiàn)

sunsmell / 2237人閱讀

摘要:傳送門(mén)本文會(huì)介紹淺拷貝的實(shí)現(xiàn)原理,然后咱們?cè)囍鴮?shí)現(xiàn)一個(gè)淺拷貝。返回的對(duì)象就是目標(biāo)對(duì)象。使用轉(zhuǎn)成對(duì)象,并保存為,最后返回這個(gè)對(duì)象。

一、前言

之前在前面一篇學(xué)習(xí)了賦值,淺拷貝和深拷貝。介紹了這三者的相關(guān)知識(shí)和區(qū)別。

傳送門(mén):https://www.mwcxs.top/page/59...

本文會(huì)介紹淺拷貝Object.assign()的實(shí)現(xiàn)原理,然后咱們?cè)囍鴮?shí)現(xiàn)一個(gè)淺拷貝。

二、淺拷貝Object.assign()

什么是淺拷貝?淺拷貝就是創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。

淺拷貝Object.assign()是什么?主要將所有可枚舉屬性的值從一個(gè)或者多個(gè)數(shù)據(jù)源對(duì)象復(fù)制到目標(biāo)對(duì)象,同時(shí)返回目標(biāo)對(duì)象。

語(yǔ)法規(guī)則:

Object.assign(target,...sources)

其中target是目標(biāo)對(duì)象,source是源對(duì)象,可以是多個(gè),修改返回的是目標(biāo)對(duì)象target。

1、如果目標(biāo)對(duì)象中的屬性具有相同的屬性鍵,則屬性將被源對(duì)象中的屬性覆蓋;

2、源對(duì)象的屬相將類(lèi)似覆蓋早先的屬性。

強(qiáng)調(diào)兩點(diǎn):

1、可枚舉的屬性(自有屬性)

2、string或者symbol類(lèi)型是可以被直接分配的

2.1栗子1

淺拷貝就是拷貝第一層的基本類(lèi)型值,以及第一層的引用類(lèi)型地址。

// saucxs
// 第一步
let a = {
    name: "advanced",
    age: 18
}
let b = {
    name: "saucxs",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let c = Object.assign(a, b);
console.log(c);
// {
//     name: "saucxs",
//  age: 18,
//     book: {title: "You Don"t Know JS", price: "45"}
// }
console.log(a === c);
// true

// 第二步
b.name = "change";
b.book.price = "55";
console.log(b);
// {
//     name: "change",
//     book: {title: "You Don"t Know JS", price: "55"}
// }

// 第三步
console.log(a);
// {
//     name: "saucxs",
//  age: 18,
//     book: {title: "You Don"t Know JS", price: "55"}
// }

分析:

1、第一步中,使用Object.assign把源對(duì)象b的值復(fù)制到目標(biāo)對(duì)象a中,這里把返回值定義為對(duì)象c,可以看出b會(huì)替換掉a中具有相同鍵的值,即如果目標(biāo)對(duì)象a中的屬性具有相同的鍵,則屬相將被源對(duì)象b中的屬性覆蓋。返回的對(duì)象c就是目標(biāo)對(duì)象a。

2、第二步中,修改源對(duì)象b的基本類(lèi)型值(name)和引用類(lèi)型值(book)。

3、第三步中,淺拷貝之后目標(biāo)對(duì)象a的基本類(lèi)型值沒(méi)有改變,但是引用類(lèi)型值發(fā)生了改變,因?yàn)镺bject.assign()拷貝的是屬性值。加入源對(duì)象的屬性值是一個(gè)指向?qū)ο蟮囊?,只拷貝那個(gè)引用地址。

2.2栗子2

string類(lèi)型和symbol類(lèi)型的屬性都會(huì)被拷貝,而且不會(huì)跳過(guò)那些值為null或undefined的源對(duì)象。

// saucxs
// 第一步
let a = {
    name: "saucxs",
    age: 18
}
let b = {
    b1: Symbol("saucxs"),
    b2: null,
    b3: undefined
}
let c = Object.assign(a, b);
console.log(c);
// {
//     name: "saucxs",
//  age: 18,
//     b1: Symbol(saucxs),
//     b2: null,
//     b3: undefined
// }
console.log(a === c);
// true
三、Object.assign模擬實(shí)現(xiàn)

實(shí)現(xiàn)Object.assign模擬實(shí)現(xiàn)大致思路:

1、判斷原生的Object是否支持assign這個(gè)函數(shù),如果不存在的話就會(huì)創(chuàng)建一個(gè)assign函數(shù),并使用Object.defineProperty將函數(shù)綁定到Object上。

2、判斷參數(shù)是否正確(目標(biāo)參數(shù)不能為空,可以直接設(shè)置{}傳遞進(jìn)去,但是必須有值)。

3、使用Object()轉(zhuǎn)成對(duì)象,并保存為to,最后返回這個(gè)對(duì)象to。

4、使用for in 循環(huán)遍歷出所有的可枚舉的自有屬性,并復(fù)制給新的目標(biāo)對(duì)象(使用hasOwnProperty獲取自有屬性,即非原型鏈上的屬性)

參考原生,實(shí)現(xiàn)代碼如下,使用assign2代替assign。此處的模擬不支持symbol屬性,因?yàn)閑s5中沒(méi)有symbol。

// saucxs
if (typeof Object.assign2 != "function") {
  // 注意 1
  Object.defineProperty(Object, "assign2", {
    value: function (target) {
      "use strict";
      if (target == null) { // 注意 2
        throw new TypeError("Cannot convert undefined or null to object");
      }

      // 注意 3
      var to = Object(target);
        
      for (var index = 1; index < arguments.length; index++) {
        var nextSource = arguments[index];

        if (nextSource != null) {  // 注意 2
          // 注意 4
          for (var nextKey in nextSource) {
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
      }
      return to;
    },
    writable: true,
    configurable: true
  });
}

測(cè)試一下:

// saucxs
// 測(cè)試用例
let a = {
    name: "advanced",
    age: 18
}
let b = {
    name: "saucxs",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let c = Object.assign2(a, b);
console.log(c);
// {
//     name: "saucxs",
//  age: 18,
//     book: {title: "You Don"t Know JS", price: "45"}
// } 
console.log(a === c);
// true
3.1 注意1:可枚舉性

原生情況下掛載在Object上的屬性時(shí)不可枚舉的,但是直接在Object上掛載屬性a之后就可以枚舉的,所以必須使用Object.defineProperty,并設(shè)置enumerable: false 以及 writable: true,configurable: true

// saucxs
for(var i in Object) {
    console.log(Object[i]);
}
// 無(wú)輸出

Object.keys( Object );
// []

上面說(shuō)明,原生的Object上的屬性不可枚舉。

我們可以使用2種方法查看Object.assign是否可枚舉,使用Object.getOwnPropertyDescriptor或者Object.propertyIsEnumberable都可以,其中propertyIsEnumerable(..)會(huì)檢查給定的屬性名是否直接存在于對(duì)象中(而不是在原型鏈上)并且滿足enumerable:true。具體用法如下:

// saucxs
Object.getOwnPropertyDescriptor(Object, "assign");
// {
//     value: ?, 
//  writable: true,     // 可寫(xiě)
//  enumerable: false,  // 不可枚舉,注意這里是 false
//  configurable: true    // 可配置
// }
// saucxs
Object.propertyIsEnumerable("assign");
// false

說(shuō)明Object.assign是不可枚舉的。

直接在Object上掛載屬性a之后是可以枚舉的。我們來(lái)看一下代碼:

// saucxs
Object.a = function () {
    console.log("log a");
}

Object.getOwnPropertyDescriptor(Object, "a");
// {
//     value: ?, 
//  writable: true, 
//  enumerable: true,  // 注意這里是 true
//  configurable: true
// }

Object.propertyIsEnumerable("a");
// true

所以要實(shí)現(xiàn) Object.assign 必須使用 Object.defineProperty,并設(shè)置 writable: true, enumerable: false, configurable: true,當(dāng)然默認(rèn)情況下不設(shè)置就是 false。

// saucxs
Object.defineProperty(Object, "b", {
    value: function() {
        console.log("log b");
    }
});

Object.getOwnPropertyDescriptor(Object, "b");
// {
//     value: ?, 
//  writable: false,     // 注意這里是 false
//  enumerable: false,  // 注意這里是 false
//  configurable: false    // 注意這里是 false
// }

模擬實(shí)現(xiàn)涉及到代碼

// saucxs
// 判斷原生 Object 中是否存在函數(shù) assign2
if (typeof Object.assign2 != "function") {
  // 使用屬性描述符定義新屬性 assign2
  Object.defineProperty(Object, "assign2", {
    value: function (target) { 
      ...
    },
    // 默認(rèn)值是 false,即 enumerable: false
    writable: true,
    configurable: true
  });
}
3.2 注意2:判斷參數(shù)是否正確

有些文章判斷參數(shù)是否正確是這樣的。

// saucxs
if (target === undefined || target === null) {
    throw new TypeError("Cannot convert undefined or null to object");
}

這樣肯定沒(méi)問(wèn)題,但是這樣寫(xiě)沒(méi)有必要,因?yàn)?undefinednull 是相等的(高程 3 P52 ),即 undefined == null 返回 true,只需要按照如下方式判斷就好了。

// saucxs
if (target == null) { // TypeError if undefined or null
    throw new TypeError("Cannot convert undefined or null to object");
}
3.3 注意3:原始類(lèi)型被包裝為對(duì)象
// saucxs
var v1 = "abc";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo");

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 

// 原始類(lèi)型會(huì)被包裝,null 和 undefined 會(huì)被忽略。
// 注意,只有字符串的包裝對(duì)象才可能有自身可枚舉屬性。
console.log(obj); 
// { "0": "a", "1": "b", "2": "c" }

上面代碼中的源對(duì)象 v2、v3、v4 實(shí)際上被忽略了,原因在于他們自身沒(méi)有可枚舉屬性。

// saucxs
var v1 = "abc";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo");
var v5 = null;

// Object.keys(..) 返回一個(gè)數(shù)組,包含所有可枚舉屬性
// 只會(huì)查找對(duì)象直接包含的屬性,不查找[[Prototype]]鏈
Object.keys( v1 ); // [ "0", "1", "2" ]
Object.keys( v2 ); // []
Object.keys( v3 ); // []
Object.keys( v4 ); // []
Object.keys( v5 ); // TypeError: Cannot convert undefined or null to object

上面代碼說(shuō)明:Object.keys(..)返回一個(gè)數(shù)組,包含所有可枚舉的屬性,只會(huì)查找對(duì)象直接包含的屬性,而不會(huì)查找[[prototype]]鏈。

// Object.getOwnPropertyNames(..) 返回一個(gè)數(shù)組,包含所有屬性,無(wú)論它們是否可枚舉
// 只會(huì)查找對(duì)象直接包含的屬性,不查找[[Prototype]]鏈
Object.getOwnPropertyNames( v1 ); // [ "0", "1", "2", "length" ]
Object.getOwnPropertyNames( v2 ); // []
Object.getOwnPropertyNames( v3 ); // []
Object.getOwnPropertyNames( v4 ); // []
Object.getOwnPropertyNames( v5 ); 
// TypeError: Cannot convert undefined or null to object

上面代碼說(shuō)明:Object.getOwnPropertyNames(..)返回一個(gè)數(shù)組,保護(hù)焊所有屬性,無(wú)論他們是否可以枚舉,只會(huì)查找對(duì)象直接包含的屬性,不查找[[prototype]]鏈。

但是這樣是可以執(zhí)行的:

// saucxs
var a = "abc";
var b = {
    v1: "def",
    v2: true,
    v3: 10,
    v4: Symbol("foo"),
    v5: null,
    v6: undefined
}

var obj = Object.assign(a, b); 
console.log(obj);
// { 
//   [String: "abc"]
//   v1: "def",
//   v2: true,
//   v3: 10,
//   v4: Symbol(foo),
//   v5: null,
//   v6: undefined 
// }

為什么?因?yàn)閡ndefined,true等不適作為對(duì)象,而是作為對(duì)象b的屬性值,對(duì)象b是可枚舉的。

// saucxs
// 接上面的代碼
Object.keys( b ); // [ "v1", "v2", "v3", "v4", "v5", "v6" ]

這里其實(shí)又可以看出一個(gè)問(wèn)題來(lái),那就是目標(biāo)對(duì)象是原始類(lèi)型,會(huì)包裝成對(duì)象,對(duì)應(yīng)上面的代碼就是目標(biāo)對(duì)象 a 會(huì)被包裝成 [String: "abc"],那模擬實(shí)現(xiàn)時(shí)應(yīng)該如何處理呢?很簡(jiǎn)單,使用 Object(..) 就可以了。

// saucxs
var a = "abc";
console.log( Object(a) );
// {0: "a", 1: "b", 2: "c"}

我們?cè)賮?lái)看看下面代碼能不能執(zhí)行:

// saucxs
var a = "abc";
var b = "def";
Object.assign(a, b); // TypeError: Cannot assign to read only property "0" of object "[object String]"

還是會(huì)報(bào)錯(cuò)的,原因在于:Object("abc")時(shí)候,其屬性描述符writable為不可寫(xiě),即writeable: false。

// saucxs
var myObject = Object( "abc" );

Object.getOwnPropertyNames( myObject );
// [ "0", "1", "2", "length" ]

Object.getOwnPropertyDescriptor(myObject, "0");
// { 
//   value: "a",
//   writable: false, // 注意這里
//   enumerable: true,
//   configurable: false 
// }
3.4 注意4:存在性

如何在不訪問(wèn)屬性值的情況下判斷對(duì)象中是否存在某個(gè)屬性,看下面代碼:

// saucxs
var anotherObject = {
    a: 1
};

// 創(chuàng)建一個(gè)關(guān)聯(lián)到 anotherObject 的對(duì)象
var myObject = Object.create( anotherObject );
myObject.b = 2;

("a" in myObject); // true
("b" in myObject); // true

myObject.hasOwnProperty( "a" ); // false
myObject.hasOwnProperty( "b" ); // true

使用in和hasOwnProperty方法,區(qū)別如下:

1、in 操作符會(huì)檢查屬性是否在對(duì)象及其[[propertype]]原型鏈中;

2、hasOwnProperty(..)只會(huì)檢查是否在myObject對(duì)象中,不會(huì)檢查[[prototype]]原型鏈中。

Object.assign方法肯定是不會(huì)拷貝原型鏈上的屬性,所以模擬實(shí)現(xiàn)時(shí)需要用hasOwnProperty(..)判斷處理下,但是直接使用myObject.hasOwnProperty(..)是有問(wèn)題的,因?yàn)橛械膶?duì)象可能沒(méi)有連接到Object.prototype上(通過(guò)Object.create(null)來(lái)創(chuàng)建),這種情況下,使用myObject.hasOwnProperty(..)就會(huì)失敗。

// saucxs
var myObject = Object.create( null );
myObject.b = 2;

("b" in myObject); 
// true

myObject.hasOwnProperty( "b" );
// TypeError: myObject.hasOwnProperty is not a function

解決辦法,使用call就可以了,如下:

// saucxs
var myObject = Object.create( null );
myObject.b = 2;

Object.prototype.hasOwnProperty.call(myObject, "b");
// true
所以具體到本次模擬實(shí)現(xiàn)中,相關(guān)代碼如下。

// saucxs
// 使用 for..in 遍歷對(duì)象 nextSource 獲取屬性值
// 此處會(huì)同時(shí)檢查其原型鏈上的屬性
for (var nextKey in nextSource) {
    // 使用 hasOwnProperty 判斷對(duì)象 nextSource 中是否存在屬性 nextKey
    // 過(guò)濾其原型鏈上的屬性
    if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
        // 賦值給對(duì)象 to,并在遍歷結(jié)束后返回對(duì)象 to
        to[nextKey] = nextSource[nextKey];
    }
}
四、參考

1、MDN的Object.assign()

2、理解Object.assign()

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/109620.html

相關(guān)文章

  • 「前端面試題系列9」拷貝與深拷貝含義、區(qū)別及實(shí)現(xiàn)(文末有崗位內(nèi)推哦~)

    摘要:深拷貝與淺拷貝的出現(xiàn),就與這兩個(gè)數(shù)據(jù)類(lèi)型有關(guān)。這時(shí),就需要用淺拷貝來(lái)實(shí)現(xiàn)了。數(shù)據(jù)一但過(guò)多,就會(huì)有遞歸爆棧的風(fēng)險(xiǎn)。這個(gè)方法是在解決遞歸爆棧問(wèn)題的基礎(chǔ)上,加以改進(jìn)解決循環(huán)引用的問(wèn)題。但如果你并不想保持引用,那就改用用于解決遞歸爆棧即可。 前言 這是前端面試題系列的第 9 篇,你可能錯(cuò)過(guò)了前面的篇章,可以在這里找到: 數(shù)組去重(10 種濃縮版) JavaScript 中的事件機(jī)制(從原生到...

    caige 評(píng)論0 收藏0
  • 【進(jìn)階4-2期】Object.assign 原理及其實(shí)現(xiàn)

    摘要:木易楊注意原始類(lèi)型被包裝為對(duì)象木易楊原始類(lèi)型會(huì)被包裝,和會(huì)被忽略。木易楊原因在于時(shí),其屬性描述符為不可寫(xiě),即。木易楊解決方法也很簡(jiǎn)單,使用我們?cè)谶M(jìn)階期中介紹的就可以了,使用如下。 引言 上篇文章介紹了賦值、淺拷貝和深拷貝,其中介紹了很多賦值和淺拷貝的相關(guān)知識(shí)以及兩者區(qū)別,限于篇幅只介紹了一種常用深拷貝方案。 本篇文章會(huì)先介紹淺拷貝 Object.assign 的實(shí)現(xiàn)原理,然后帶你手動(dòng)實(shí)...

    layman 評(píng)論0 收藏0
  • JavaScript系列--JavaScript解析賦值、拷貝和深拷貝區(qū)別

    摘要:它將返回目標(biāo)對(duì)象。有些文章說(shuō)是深拷貝,其實(shí)這是不正確的。深拷貝相比于淺拷貝速度較慢并且花銷(xiāo)較大??截惽昂髢蓚€(gè)對(duì)象互不影響。使用深拷貝的場(chǎng)景完全改變變量之后對(duì)沒(méi)有任何影響,這就是深拷貝的魔力。 一、賦值(Copy) 賦值是將某一數(shù)值或?qū)ο筚x給某個(gè)變量的過(guò)程,分為: 1、基本數(shù)據(jù)類(lèi)型:賦值,賦值之后兩個(gè)變量互不影響 2、引用數(shù)據(jù)類(lèi)型:賦址,兩個(gè)變量具有相同的引用,指向同一個(gè)對(duì)象,相互之間有...

    laznrbfe 評(píng)論0 收藏0
  • 【進(jìn)階4-1期】詳細(xì)解析賦值、拷貝和深拷貝區(qū)別

    摘要:展開(kāi)語(yǔ)法木易楊通過(guò)代碼可以看出實(shí)際效果和是一樣的。木易楊可以看出,改變之后的值并沒(méi)有發(fā)生變化,但改變之后,相應(yīng)的的值也發(fā)生變化。深拷貝使用場(chǎng)景木易楊完全改變變量之后對(duì)沒(méi)有任何影響,這就是深拷貝的魔力。木易楊情況下,轉(zhuǎn)換結(jié)果不正確。 一、賦值(Copy) 賦值是將某一數(shù)值或?qū)ο筚x給某個(gè)變量的過(guò)程,分為下面 2 部分 基本數(shù)據(jù)類(lèi)型:賦值,賦值之后兩個(gè)變量互不影響 引用數(shù)據(jù)類(lèi)型:賦址,兩個(gè)...

    silvertheo 評(píng)論0 收藏0
  • JavaScript·隨記 深拷貝 vs. 拷貝

    摘要:而在這個(gè)運(yùn)算符的相關(guān)用例中,往往會(huì)涉及到其他知識(shí)點(diǎn),深拷貝和淺拷貝就是其中之一。即對(duì)象的淺拷貝會(huì)對(duì)主對(duì)象的值進(jìn)行拷貝,而該值有可能是一個(gè)指針,指向內(nèi)存中的同一個(gè)對(duì)象。,可以看到深拷貝和淺拷貝是對(duì)復(fù)制引用類(lèi)型變量而言的。 在ES6的系列文章中,基本都會(huì)提到Spread——擴(kuò)展運(yùn)算符(...)。而在這個(gè)運(yùn)算符的相關(guān)用例中,往往會(huì)涉及到其他知識(shí)點(diǎn),深拷貝和淺拷貝就是其中之一。 背景知識(shí) 在討...

    RyanQ 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<