摘要:對象克隆我們經常會用到一個對象去做一些事情,可能有時候我們不想改變原有的數據。如果是對象接著遞歸復制判斷是對象還是數組其實這還不是最終的深克隆,因為這一個也有它自己的問題,但是面對一般的情況應該沒問題,跟高級的用法請自行學習。
類型判斷
我們先說一下JS的數據類型,我們一般說JS有六大數據類型(ES6以前)分別是:
基本數據類型
Number
String
Boolean
null
undefined
引用數據類型
object
在ES6中新增了Symbol數據類型。
有時我們需要知道數據的類型其判斷一些事情,我們經常會用typeof去判斷數據類型。
那么typeof能判斷什么類型呢?
Number
String
Boolean
object
undefined
function
就這幾個數據類型,但這些我們夠用嗎?或者說準確嗎?
我們沒有看到null那么他是什么類型呢?我們用typeof null發現它是object,是不是很奇怪,其實這是一個bug,但是這個bug是能修復的但是不能修復,因為null一般是用來表示一個對象是空的,有時我們用null來取消事件,我理解的它好像是一個占位符,表示這個對象是空的。那為什么不能修復呢?因為修復它好說,但是修復了它會帶來許多麻煩。
專業點說就是:不同的對象在底層都是用二進制來表示,在JS中二進制前三位都是0的就會判斷為object類型,因為null全是0所以會判斷null也是object類型。
我們來看一下typeof判斷的情況。
console.log(typeof(1)); //number console.log(typeof("1")); // string console.log(typeof(true)); // boolean console.log(typeof({})); // object console.log(typeof(function (){})); // function console.log(typeof(null)); // object console.log(typeof(undefined)); // undefined
當我們想判斷一個對象那個是不是null或者是不是Date、RegExp等類型時會怎么樣呢?我們發現都是object,那我們有沒有辦法區分他們呢?
在這之前我先介紹一下Object.prototype.toString這個方法,我相信大家不陌生吧。它也能判斷數據類型,但是是這樣的。
console.log(Object.prototype.toString.call(1)); console.log(Object.prototype.toString.call("1")); console.log(Object.prototype.toString.call(true)); console.log(Object.prototype.toString.call({})); console.log(Object.prototype.toString.call(function (){})); console.log(Object.prototype.toString.call(null)); console.log(Object.prototype.toString.call(undefined)); console.log(Object.prototype.toString.call(new RegExp())); console.log(Object.prototype.toString.call(new Date())); [object Number] [object String] [object Boolean] [object Object] [object Function] [object Null] [object Undefined] [object RegExp] [object Date]
我們發現它比typeof高級點,能分辨的更準確,但是格式好像不是我們要的。
接下來進入主題,直接上代碼。
//首先定義好數據類型。 let types = { "[object Object]": "Object", "[object RegExp]": "RegExp", "[object Date]": "Date" }; function type(regs) { let result = typeof(regs); // 先獲取傳過來的參數 // 如果是對象在進行判斷,不是則返回。 if(result === "object"){ if(regs === null){ return "null"; }else{ return types[Object.prototype.toString.call(regs)]; } }else{ return result; } } console.log(type(1)); //number console.log(type("1")); // string console.log(type(true)); // boolean console.log(type({})); // object console.log(type(function (){})); // function console.log(type(null)); // null console.log(type(undefined)); // undefined console.log(type(new RegExp())); //RegExp console.log(type(new Date())); //Date
對象克隆
我們經常會用到一個對象去做一些事情,可能有時候我們不想改變原有的數據。,時候我們就需要對象克隆了,你可能簡單的以為就是 = 就行了,那我們來看一看。
let obj = { a: 1 } let obj2 = obj; console.log(obj); //{a: 1} console.log(obj2); //{a: 1}
我們看到復制過來了,這樣我們就可以隨便使用了。那我們來修改一下obj看看。
obj.a = 2; console.log(obj); //{a: 2} console.log(obj2); //{a: 2}
發現都變成了{a: 2},是不是很奇怪。因為對象是引用類型的,他們賦值其實是賦的地址值,就是他們指向同一個地方,那么我們應該怎么做呢?你應該知道怎么遍歷對象,我們就把它遍歷一遍再復制。看代碼
let obj = { a: 1 } function clone(obj) { let a = {}; for(let o in obj){ a[o] = obj[o] } return a; } let obj2 = clone(obj); console.log(obj); //{a: 1} console.log(obj2); //{a: 1} obj.a = 2; console.log(obj); //{a: 2} console.log(obj2); //{a: 1}
沒有改變,看來我們成功了,那這篇文章就到這了。呵呵,其實遠沒有,我們來看一下有沒有什么問題。
當里面的數據為引用類型時:
let obj = { a: { b: 1 }, c: 3 } function clone(obj) { let a = {}; for(let o in obj){ a[o] = obj[o] } return a; } let obj2 = clone(obj); console.log(obj); console.log(obj2); obj.a .b = 2; console.log(obj); console.log(obj2);
我們發現
又出問題了。
如果你知道for...in你就會知道它的另一個錯誤。就是它會遍歷它原型上的可枚舉屬性和非Symbol的屬性。那么我們怎么改善一下呢?現在介紹一下hasOwnProperty這個屬性,它就是判斷自身有沒有這個屬性,而不會去原型上找。
function clone(obj) { let a = {}; for(let o in obj){ if(obj.hasOwnProperty(o)){ a[o] = obj[o]; } } return a; }
這個問題解決了,就差上一個了,我們接著用判斷數據的類型來判斷是否還需要復制的方法解決上一個問題。
let obj = { a: { b: 1, d: { e:[{f: 2}], g: { h:{ l: 5 } } } }, c: 3 } function deepClone(origin, target) { let tar = target || {}, arr = "[object Array]", str = Object.prototype.toString; for(let o in origin){ if(origin.hasOwnProperty(o)){ // 如果是對象接著遞歸復制 if(typeof origin[o] === "object"){ // 判斷是對象還是數組 tar[o] = str.call(origin[o]) === arr ? [] : {}; deepClone(origin[o], tar[o]); }else{ tar[o] = origin[o]; } } } return tar; } let obj2 = deepClone(obj, {}); console.log(obj); console.log(obj2); obj.a.d.g.h.l = 6; console.log(obj.a.d.g.h.l); //6 console.log(obj2.a.d.g.h.l); //5
其實這還不是最終的深克隆,因為這一個也有它自己的問題,但是面對一般的情況應該沒問題,跟高級的用法請自行學習。
模擬實現JQ的$.extend()方法(只是粗略的寫了一下,如有錯誤歡迎指出):
function extend() { let origin, // 要拷貝的源 target = arguments[0], // 獲取第一個參數 isDeepClone = false; // 是否深拷貝 length = arguments.length, //拷貝的個數 arr = "[object Array]", str = Object.prototype.toString, i = 0; if(typeof target === "boolean"){ isDeepClone = target; i ++; target = arguments[i]; //獲取目標元素 } //防止循環引用 if(origin === target){ return; } // 兼容function if(typeof target !== "object" && typeof target !== "function" ){ target = {}; } for ( ; i < length; i++) { origin = arguments[i]; for(let o in origin){ if(origin.hasOwnProperty(o)){ if(origin[o] === "object"){ if(isDeepClone){ target[o] = str.call(origin[o]) === arr ? [] : {}; extend(true, target[o], origin[o]); } }else{ target[o] = origin[o]; } } } } return target; }
補充:其實不止這一種深克隆的方法,不如我們處理數據最常使用的JSON
let obj = { a: { b: function (argument) { }, d: { e:[{f: 2}], g: { h:{ l: 5 } } } }, c: 3 } let r = JSON.stringify(obj); r = JSON.parse(r); obj.a.d.g.h.l = 6; console.log(r.a.d.g.h.l); // 5
也是可以的,我們輸出一下r看看。
有沒有發現少了什么?對,就是function,它不僅不能復制function還有undefined也不行,還有別的自己查一下吧。
3.數組克隆
有了上面的鋪墊,我們知道數組也是引用類型,就不能簡單的等于來復制。
concat
let arr = [8,5,6,6,8]; let arr2 = arr.concat(); arr2[3] = 1; console.log(arr); //[8, 5, 6, 6, 8] console.log(arr2); //[8, 5, 6, 1, 8]
可以復制成功,那么引用類型呢?
let arr = [8,{a: 1},6,6,8]; let arr2 = arr.concat(); arr2[1].a = 2; console.log(arr); console.log(arr2);
還有我們常用的slice也是一樣
let arr = [8,{a: 1},6,6,8]; let arr2 = arr.slice(); arr2[1].a = 2; arr2[2] = 2; console.log(arr); console.log(arr2);
還有一些別的方法,我就不一一列舉了,這些都是淺復制。
如果想深度克隆數組,也可以使用上面介紹的使用JSON也是可以的。
let arr = [8,{a: 1},6,6,8]; let arr2 = JSON.parse( JSON.stringify(arr) ); arr2[1].a = 2; arr2[2] = 2; console.log(arr); console.log(arr2);
目前想到的就這些,總感覺拉下了什么,如果我想起來了我會繼續補充的。
4.閑聊
上面寫的我意猶未盡,可能是自己知識的局限性暫時只能想到那些,上面說到了for...in,那么我們來簡單的說一下for...of和它的區別。
他們都是遍歷用的,每次遍歷數組和對象都會想起它們,那么你會不會弄混呢。
那我們直接遍歷一次,看看有什么區別。
let arr = [8,{a: 1},6,6,8]; let a = { b:1, r: 8, h:{ e:6 } } console.log("for...of"); for(let i of arr){ console.log(i); } console.log("for...in"); for(let i in a){ console.log("key:" + i); }
是不是感覺挺好的,我們再來看看。
let arr = [8,{a: 1},6,6,8]; let a = { b:1, r: 8, h:{ e:6 } } console.log("for...in遍歷數組"); for(let i in arr){ console.log(i); } console.log("for...of遍歷對象"); for(let i of a){ console.log("key:" + i); }
for...of遍歷對象直接報錯了,你有沒有注意到報錯的信息。就是不可遍歷,因為不是iterable。數組、字符串、Set、Map,內置好了Iterator(迭代器),它們的原型中都有一個Symbol.iterator方法,而Object對象并沒有實現這個接口,使得它無法被for...of遍歷。
至于for...of遍歷對象就需要實現Iterator,這里我就不寫了,百度好多。
for...in遍歷數組遍歷出來的是索引。不過我們也可以得到數組的值。
for(let i in arr){ console.log(arr[i]); //[8,{a: 1},6,6,8] }
我覺得你看到這里應該知道遍歷什么用哪個更合適了。
補充
__proto__的實現
Object.defineProperty(Object.prototype, __proto__, { get: function(){ return Object.getPrototypeOf(this); }, set: function(ob){ Object.setPrototypeOf(this, ob); return ob;
}) ```
下一篇文章我想說一下數組去重好像不是最全的數組去重方法,因為內容挺多,我就不一起寫了,喜歡的可以點一個贊,或者關注一下。鼓勵一下一名自學前端的大學生。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100699.html
摘要:在聊以下簡稱深度克隆之前,我們先來了解一下中對象的組成。克隆或者拷貝分為種淺度克隆深度克隆。淺度克隆基本類型為值傳遞,對象仍為引用傳遞。 該文轉載自http://www.cnblogs.com/zichi/p/4568150.html,有部分修改。 在聊JavaScript(以下簡稱js)深度克隆之前,我們先來了解一下js中對象的組成。在 js 中一切實例皆是對象,具體分為 原始類型 ...
摘要:對象詳解對象深度剖析,深度理解對象這算是醞釀很久的一篇文章了。用空構造函數設置類名每個對象都共享相同屬性每個對象共享一個方法版本,省內存。 js對象詳解(JavaScript對象深度剖析,深度理解js對象) 這算是醞釀很久的一篇文章了。 JavaScript作為一個基于對象(沒有類的概念)的語言,從入門到精通到放棄一直會被對象這個問題圍繞。 平時發的文章基本都是開發中遇到的問題和對...
摘要:多個窗口意味著多個全局環境,不同的全局環境擁有不同的全局對象,從而擁有不同的內置類型構造函數。比如,表達式會返回,因為屬性得到的僅僅是構造函數,而且是可以被手動更改的,只是返回的構造函數的名字,它并不返回類名。 原文:ES6時代,你真的會克隆對象嗎(二) 上一篇,我們從Symbol和是否可枚舉以及屬性描述符的角度分析了ES6下怎么淺拷貝一個對象,發表在掘金和segmentfault上(...
摘要:原文你真的會克隆對象嗎開始之前在開始聊克隆之前,我們還是先來看看數據類型。值通過函數生成,是獨一無二的。同時,中規定了對象的屬性名有兩種類型,一種是字符串,另一種就是類型。返回一個數組,包含對象自身的所有屬性的鍵名。 原文:你真的會克隆對象嗎 開始之前 在開始聊克隆之前,我們還是先來看看js數據類型。js的數據類型分為基本數據類型和復雜數據類型。 基本數據類型:Number、Bool...
摘要:判斷參數是否為待判斷的參數克隆一個對象要克隆的目標對象克隆節點,綁定事件的有問題,暫不處理克隆在當前作用域,在全局克隆其它對象,通過識別復制后的對象與原對象是否相同來決定傳不傳參數,像數組是不能傳參數的使用防止對象重寫了方法支持節點克隆 (function(){ var toString=Object.prototype.toString,gObj={},cloneHelper=f...
閱讀 3384·2023-04-25 20:37
閱讀 3142·2021-09-07 09:59
閱讀 1665·2019-08-29 12:43
閱讀 1185·2019-08-28 18:27
閱讀 479·2019-08-26 13:50
閱讀 2025·2019-08-26 10:33
閱讀 3591·2019-08-23 18:39
閱讀 2390·2019-08-23 18:09