一鬼吵、JS的基本數(shù)據(jù)類(lèi)型
- 基本數(shù)據(jù)類(lèi)型:String,Boolean篮赢,Number齿椅,Undefined,Null启泣;
- 引用數(shù)據(jù)類(lèi)型:Object(Array涣脚,Date,RegExp寥茫,F(xiàn)unction)遣蚀;
- 基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的區(qū)別:
1、保存位置不同:基本數(shù)據(jù)類(lèi)型保存在棧內(nèi)存中纱耻,引用數(shù)據(jù)類(lèi)型保存在堆內(nèi)存中芭梯,然后在棧內(nèi)存中保存了一個(gè)對(duì)堆內(nèi)存中實(shí)際對(duì)象的引用,即數(shù)據(jù)在堆內(nèi)存中的地址弄喘,JS對(duì)引用數(shù)據(jù)類(lèi)型的操作都是操作對(duì)象的引用而不是實(shí)際的對(duì)象玖喘,如果obj1拷貝了obj2,那么這兩個(gè)引用數(shù)據(jù)類(lèi)型就指向了同一個(gè)堆內(nèi)存對(duì)象限次,具體操作是obj1將棧內(nèi)存的引用地址復(fù)制了一份給obj2芒涡,因而它們共同指向了一個(gè)堆內(nèi)存對(duì)象;
為什么基本數(shù)據(jù)類(lèi)型保存在棧中卖漫,而引用數(shù)據(jù)類(lèi)型保存在堆中费尽?
1)堆比棧大,棧比堆速度快羊始;
2)基本數(shù)據(jù)類(lèi)型比較穩(wěn)定旱幼,而且相對(duì)來(lái)說(shuō)占用的內(nèi)存小突委;
3)引用數(shù)據(jù)類(lèi)型大小是動(dòng)態(tài)的柏卤,而且是無(wú)限的,引用值的大小會(huì)改變匀油,不能把它放在棧中缘缚,否則會(huì)降低變量查找的速度,因此放在變量椀醒粒空間的值是該對(duì)象存儲(chǔ)在堆中的地址桥滨,地址的大小是固定的,所以把它存儲(chǔ)在棧中對(duì)變量性能無(wú)任何負(fù)面影響;
4)堆內(nèi)存是無(wú)序存儲(chǔ)齐媒,可以根據(jù)引用直接獲绕衙俊;
按引用訪問(wèn):js不允許直接訪問(wèn)保存在堆內(nèi)存中的對(duì)象喻括,所以在訪問(wèn)一個(gè)對(duì)象時(shí)邀杏,首先得到的是這個(gè)對(duì)象在堆內(nèi)存中的地址,然后再按照這個(gè)地址去獲得這個(gè)對(duì)象中的值唬血;
ECMAScript中所有函數(shù)的參數(shù)都是按值來(lái)傳遞的望蜡,對(duì)于原始值,只是把變量里的值傳遞給參數(shù)拷恨,之后參數(shù)和這個(gè)變量互不影響泣特,對(duì)于引用值,對(duì)象變量里面的值是這個(gè)對(duì)象在堆內(nèi)存中的內(nèi)存地址挑随,因此它傳遞的值也就是這個(gè)內(nèi)存地址,這也就是為什么函數(shù)內(nèi)部對(duì)這個(gè)參數(shù)的修改會(huì)體現(xiàn)在外部的原因勒叠,因?yàn)樗鼈兌贾赶蛲粋€(gè)對(duì)象兜挨;
2、基本數(shù)據(jù)類(lèi)型使用typeof可以返回其基本數(shù)據(jù)類(lèi)型眯分,但是NULL類(lèi)型會(huì)返回object拌汇,因此null值表示一個(gè)空對(duì)象指針;引用數(shù)據(jù)類(lèi)型使用typeof會(huì)返回object弊决,此時(shí)需要使用instanceof來(lái)檢測(cè)引用數(shù)據(jù)類(lèi)型噪舀;
3、定義引用數(shù)據(jù)類(lèi)型需要使用new操作符飘诗,后面再跟一個(gè)構(gòu)造函數(shù)來(lái)創(chuàng)建与倡;
1)使用new操作符創(chuàng)建對(duì)象;
var obj1 = new Object();
obj1.a = 1;
2)使用對(duì)象字面量表示法創(chuàng)建對(duì)象昆稿;
var obj1 = {
a: 1,
b: 2
}
3)可以通過(guò)點(diǎn)表示法訪問(wèn)對(duì)象的屬性纺座,也可以使用方括號(hào)表示法來(lái)訪問(wèn)對(duì)象的屬性;
- ES6新增數(shù)據(jù)類(lèi)型:Map溉潭,Set净响,Generator,Symbol
- 本地對(duì)象:ECMA-262 把本地對(duì)象(native object)定義為“獨(dú)立于宿主環(huán)境的 ECMAScript 實(shí)現(xiàn)提供的對(duì)象”喳瓣,即本地對(duì)象就是 ECMA-262 定義的類(lèi)(引用類(lèi)型)馋贤;
- 宿主對(duì)象:宿主”就是我們網(wǎng)頁(yè)的運(yùn)行環(huán)境,即“操作系統(tǒng)”和“瀏覽器”畏陕,所有非本地對(duì)象都是宿主對(duì)象(host object)配乓,即由 ECMAScript 實(shí)現(xiàn)的宿主環(huán)境提供的對(duì)象,所有的BOM和DOM對(duì)象都是宿主對(duì)象,因?yàn)槠鋵?duì)于不同的“宿主”環(huán)境所展示的內(nèi)容不同扰付,即ECMAScript官方未定義的對(duì)象都屬于宿主對(duì)象堤撵,因?yàn)槠湮炊x的對(duì)象大多數(shù)是自己通過(guò)ECMAScript程序創(chuàng)建的對(duì)象;
- JS內(nèi)置對(duì)象:是指JS語(yǔ)言自帶的一些對(duì)象羽莺,供開(kāi)發(fā)者使用实昨,這些對(duì)象提供了一些常用的或是最基本而必要的功能;
1盐固、Arguments:函數(shù)參數(shù)集合荒给;
2、Array對(duì)象:length,instanceof,isArray(),toString()返回字符串,valueOf()返回?cái)?shù)組的值,join()可以將數(shù)組轉(zhuǎn)為字符串,push(),pop(),shift(),unshift(),reverse(),sort(),
slice(),splice(),indexOf(),lastIndexOf(),迭every(),filter(),forEach(),map(),some(),
歸并方法reduce(),reduceRight()刁卜;
3志电、Boolean:布爾對(duì)象;
4蛔趴、Error:異常對(duì)象挑辆;
5、Number:數(shù)值對(duì)象孝情;
6鱼蝉、String對(duì)象:length,charAt()返回指定位置的字符,concat(),slice(),subString(),
subStr(),indexOf(),lastIndexOf(),trim(),toLowerCase(),toUpperCase(),split(),
text.match(),text.splice();
7箫荡、Date對(duì)象:toUTCstring(),getTime()魁亦;
8、RegExp對(duì)象:test()羔挡;
9洁奈、Function對(duì)象:arguments,this,apply(this,arguments),call(this,num1,num2);
10绞灼、Math對(duì)象:min(),max(),ceil(),floor(),round(),random()利术;
11、Global對(duì)象:encodeURI,encodeURIComponent低矮,parseInt(),eval()氯哮;
12、Object對(duì)象:prototype,constructor商佛; - 基本包裝類(lèi)型:Boolean,Number,String
1喉钢、轉(zhuǎn)換為數(shù)值:parseInt()專(zhuān)門(mén)用于把字符串轉(zhuǎn)換成數(shù)值,Number()用于任何類(lèi)型;
2良姆、非字符轉(zhuǎn)換成字符:toString()肠虽;
3、數(shù)組轉(zhuǎn)成字符:join()玛追;
4税课、字符串轉(zhuǎn)換成數(shù)組:split()闲延;
// 基本數(shù)據(jù)類(lèi)型的復(fù)制,基本數(shù)據(jù)類(lèi)型是按值傳遞的
var a = 1;
var b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
// 引用數(shù)據(jù)類(lèi)型的復(fù)制韩玩,引用數(shù)據(jù)類(lèi)型按引用傳值
var obj1 = {
a: 1,
b: 2
}
var obj2 = obj1;
obj2.a = 3;
console.log(obj1.a); // 3
console.log(obj2.a); // 3
二垒玲、JS淺拷貝
- 深拷貝和淺拷貝簡(jiǎn)單解釋
?????淺拷貝和深拷貝都只針對(duì)于引用數(shù)據(jù)類(lèi)型,淺拷貝只復(fù)制指向某個(gè)對(duì)象的指針找颓,而不復(fù)制對(duì)象本身合愈,新舊對(duì)象還是共享同一塊內(nèi)存;但深拷貝會(huì)另外創(chuàng)造一個(gè)一模一樣的對(duì)象击狮,新對(duì)象跟原對(duì)象不共享內(nèi)存佛析,修改新對(duì)象不會(huì)改到原對(duì)象;
?????區(qū)別:淺拷貝只復(fù)制對(duì)象的第一層屬性彪蓬、深拷貝可以對(duì)對(duì)象的屬性進(jìn)行遞歸復(fù)制寸莫;
// 只復(fù)制第一層的淺拷貝
function simpleCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
for (let i in obj1) {
obj2[i] = obj1[i];
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
- Object.assign()實(shí)現(xiàn)淺拷貝及一層的深拷貝
let obj1 = {
a: {
b: 1
},
c: 2
}
let obj2 = Object.assign({},obj1)
obj2.a.b = 3;
obj2.c = 3
console.log(obj1.a.b); // 3
console.log(obj2.a.b); // 3
console.log(obj1.c); // 2
console.log(obj2.c); // 3
二、JS深拷貝
- 手動(dòng)實(shí)現(xiàn)深拷貝
let obj1 = {
a: 1,
b: 2
}
let obj2 = {
a: obj1.a,
b: obj1.b
}
obj2.a = 3;
alert(obj1.a); // 1
alert(obj2.a); // 3
let obj1 = {
a: {
b: 2
}
}
let obj2 = {
a: obj1.a
}
obj2.a.b = 3;
console.log(obj1.a.b); // 3
console.log(obj2.a.b); // 3
- 遞歸實(shí)現(xiàn)深拷貝
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
// 如果子屬性為引用數(shù)據(jù)類(lèi)型档冬,遞歸復(fù)制
if (obj1[i] && typeof obj1[i] === "object") {
obj2[i] = deepCopy(obj1[i]);
} else {
// 如果是基本數(shù)據(jù)類(lèi)型膘茎,只是簡(jiǎn)單的復(fù)制
obj2[i] = obj1[i];
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
缺陷:當(dāng)遇到兩個(gè)互相引用的對(duì)象,會(huì)出現(xiàn)死循環(huán)的情況酷誓,為了避免相互引用的對(duì)象導(dǎo)致死循環(huán)的情況辽狈,則應(yīng)該在遍歷的時(shí)候判斷是否相互引用對(duì)象,如果是則退出循環(huán)呛牲;
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循環(huán),如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子屬性為引用數(shù)據(jù)類(lèi)型驮配,遞歸復(fù)制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj2[i]); // 遞歸調(diào)用
} else {
// 如果是基本數(shù)據(jù)類(lèi)型娘扩,只是簡(jiǎn)單的復(fù)制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
// Object.create實(shí)現(xiàn)深拷貝1,但也只能拷貝一層
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循環(huán)壮锻,如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子屬性為引用數(shù)據(jù)類(lèi)型琐旁,遞歸復(fù)制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
// 如果是基本數(shù)據(jù)類(lèi)型,只是簡(jiǎn)單的復(fù)制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
// Object實(shí)現(xiàn)拷貝2猜绣,淺拷貝
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = Object.create(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
- 使用JSON.stringify和JSON.parse實(shí)現(xiàn)深拷貝:JSON.stringify把對(duì)象轉(zhuǎn)成字符串灰殴,再用JSON.parse把字符串轉(zhuǎn)成新的對(duì)象;
function deepCopy(obj1){
let _obj = JSON.stringify(obj1);
let obj2 = JSON.parse(_obj);
return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 2,2,2,3,4
缺陷:它會(huì)拋棄對(duì)象的constructor掰邢,深拷貝之后牺陶,不管這個(gè)對(duì)象原來(lái)的構(gòu)造函數(shù)是什么,在深拷貝之后都會(huì)變成Object辣之;這種方法能正確處理的對(duì)象只有 Number, String, Boolean, Array, 扁平對(duì)象掰伸,也就是說(shuō),只有可以轉(zhuǎn)成JSON格式的對(duì)象才可以這樣用怀估,像function沒(méi)辦法轉(zhuǎn)成JSON狮鸭;
let obj1 = {
fun:function(){
alert(123);
}
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined
- 熱門(mén)的函數(shù)庫(kù)lodash合搅,也有提供_.cloneDeep用來(lái)做深拷貝;
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
- jquery實(shí)現(xiàn)深拷貝
jquery 提供一個(gè)$.extend
可以用來(lái)做深拷貝歧蕉;
var $ = require('jquery');
var obj1 = {
a: 1,
b: {
f: {
g: 1
}
},
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
- slice是否為深拷貝
// 對(duì)只有一級(jí)屬性值的數(shù)組對(duì)象使用slice
var a = [1,2,3,4];
var b = a.slice();
b[0] = 2;
alert(a); // 1,2,3,4
alert(b); // 2,2,3,4
// 對(duì)有多層屬性的數(shù)組對(duì)象使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4
結(jié)論:slice()和concat()都并非深拷貝灾部;