咱們先說一下JS的數據類型,咱們通常說JS有六大數據類型(ES6之前)分別是:前端
基本數據類型數組
引用數據類型post
在ES6中新增了Symbol數據類型。學習
有時咱們須要知道數據的類型其判斷一些事情,咱們常常會用typeof去判斷數據類型。大數據
那麼typeof能判斷什麼類型呢?this
就這幾個數據類型,但這些咱們夠用嗎?或者說準確嗎?spa
咱們沒有看到null那麼他是什麼類型呢?咱們用typeof null
發現它是object
,是否是很奇怪,其實這是一個bug,可是這個bug是能修復的可是不能修復,由於null通常是用來表示一個對象是空的,有時咱們用null來取消事件,我理解的它好像是一個佔位符,表示這個對象是空的。那爲何不能修復呢?由於修復它好說,可是修復了它會帶來許多麻煩。prototype
咱們來看一下typeof判斷的狀況。3d
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這個方法,我相信你們不陌生吧。它也能判斷數據類型,可是是這樣的。code
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);
咱們發現
又出問題了。
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
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; }
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.數組克隆
有了上面的鋪墊,咱們知道數組也是引用類型,就不能簡單的等於來複制。
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);
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(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;
}) ```
下一篇文章我想說一下數組去重好像不是最全的數組去重方法,由於內容挺多,我就不一塊兒寫了,喜歡的能夠點一個贊,或者關注一下。鼓勵一下一名自學前端的大學生。