第一章 JavaScript基礎(chǔ)
第一節(jié) 引用傳遞
什么是引用傳遞
JavaScript數(shù)據(jù)類型分為基本類型和引用類型烫扼,對應(yīng)的傳遞類型是值傳遞和引用傳遞
其中基本數(shù)據(jù)類型為Number
弟孟、String
金踪、Boolean
勺阐、Symbol
尼夺、undefined
军援、Null
,
引用數(shù)據(jù)類型為 Object
鸠补、Array
、Function
藐翎,
值傳遞:將這些變量賦值到另外的變量材蹬,實際上是將對應(yīng)的值拷貝了一份实幕,然后賦值給新的變量。我們把它稱作值傳遞堤器。
引用傳遞:引用類型如對象是通過引用傳遞昆庇,而不是值傳遞。也就是說闸溃,變量賦值只會將地址傳遞過去整吆。
什么又是傳遞引用
我猜是把引用類型賦值給新變量的這個過程
什么類型有這種行為
引用類型 Object
、Array
辉川、Function
什么是包裝類型
為了便于操作基本類型值表蝙,ECMAScript 提供了 3 個特殊的引用類型:Boolean
、Number
和 String
乓旗。每當(dāng)讀取一個基本類型值的時候府蛇,后臺就會創(chuàng)建一個對應(yīng)的基本包裝類型的對象,從而能夠調(diào)用一些方法來操作這些數(shù)據(jù)寸齐。
var box = 'Mr. Lee';//定義一個字符串
var box2 = box.substring(2);//截掉字符串前兩位
alert(box2);//輸出新字符串
怎么從內(nèi)存的角度解釋引用問題
JavaScript基本類型放在棧區(qū)欲诺。引用類型的指針(堆區(qū)內(nèi)存地址)存在棧區(qū),實際對象本體存在堆區(qū)
什么是深拷貝和淺拷貝
淺拷貝是創(chuàng)建一個新對象渺鹦,這個對象有著原始對象屬性值的一份精確拷貝扰法。如果屬性是基本類型,拷貝的就是基本類型的值毅厚,如果屬性是引用類型塞颁,拷貝的就是內(nèi)存地址 ,所以如果其中一個對象改變了這個地址吸耿,就會影響到另一個對象祠锣。
深拷貝是將一個對象從內(nèi)存中完整的拷貝一份出來,從堆內(nèi)存中開辟一個新的區(qū)域存放新對象,且修改新對象不會影響原對象。
如何實現(xiàn)深拷貝
為了更好的區(qū)分深拷貝淺拷貝咽安,所以深淺拷貝的方法
淺拷貝的實現(xiàn)方式
- Object.assign()
Object.assign()
方法可以吧任意多的源對象自身的可枚舉屬性拷貝給目標(biāo)對象伴网,并返回目標(biāo)對象。
Object.assign()
拷貝的是屬性值妆棒,也就是說
假如源對象的屬性值是一個對象的引用澡腾,那么它也只指向那個引用。
如果對象的屬性值為簡單類型(如string
糕珊,number
)动分,通過Object.assign({},srcObj)
;得到的新對象為深拷貝;
如果屬性值為對象或其它引用類型红选,那對于這個對象而言其實是淺拷貝的澜公。
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = Object.assign({}, obj1);
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // { person: { name: 'wade', age: 41 }, sports: 'basketball' }
- lodash的_.clone方法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true
- ES6展開運算符
...
展開運算符是一個 es6 / es2015特性,它提供了一種非常方便的方式來執(zhí)行淺拷貝喇肋,這與 Object.assign
的功能相同坟乾。
let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
- Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
- Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
深拷貝的實現(xiàn)方式
- JSON.parse(JSON.stringify())
JSON.stringify
將對象轉(zhuǎn)成JSON字符串迹辐,再用JSON.parse
把字符串解析成對象。
這種方法雖然可以實現(xiàn)數(shù)組或?qū)ο笊羁截?但不能處理函數(shù)和正則甚侣。
因為這兩者基于JSON.stringify和JSON.parse處理后右核,得到的正則就不再是正則(變?yōu)榭諏ο螅玫降暮瘮?shù)就不再是函數(shù)(變?yōu)閚ull)了渺绒。
- lodash的_.cloneDeep方法
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.extend()方法
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
- 手寫遞歸方法
遞歸方法實現(xiàn)深度克隆原理:遍歷對象、數(shù)組直到里邊都是基本數(shù)據(jù)類型菱鸥,然后再去復(fù)制宗兼,就是深度拷貝。
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;// 如果是null或者undefined我就不進行拷貝操作
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
// 可能是對象或基礎(chǔ)值 如果是函數(shù)的話不需要深拷貝
if(typepf obj !== 'object') return obj;
// 是對象的話就要進行深拷貝
if(hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到所屬類原型上的constructor, 而原型上的 constructor指向的是當(dāng)前類本身
hash.set(obj, cloneObj);
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
// 實現(xiàn)一個遞歸拷貝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
webworker之間傳遞數(shù)據(jù)是引用傳遞嗎
Worker 與“主線程”之間的數(shù)據(jù)傳遞默認(rèn)是通過結(jié)構(gòu)化克碌伞(Structured Clone)完成的殷绍。數(shù)據(jù)量較大時,克隆過程會比較耗時鹊漠,這會影響 postMessage 和 onmessage 函數(shù)的執(zhí)行時間主到。
解決的辦法一是先通過 JSON.stringify 將對象序列化,接收之后再用 JSON.parse 還原躯概。因為:stringfiy + 傳遞字符串的耗時 < 傳遞對象的耗時 登钥。