正值金九銀十的招聘旺季,我把我珍藏整理多年的前端面試題分享給你們,分三部分。這是第二部分,js相關的頗有用的基礎知識。javascript
實現一個LazyMan,能夠按照如下方式調用:
LazyMan(「Hank」)輸出:
Hi! This is Hank!
LazyMan(「Hank」).sleep(10).eat(「dinner」)輸出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~
LazyMan(「Hank」).eat(「dinner」).eat(「supper」)輸出
Hi This is Hank!
Eat dinner~
Eat supper~
LazyMan(「Hank」).sleepFirst(5).eat(「supper」)輸出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此類推。
複製代碼
這是典型的JavaScript流程控制,問題的關鍵是如何實現任務的順序執行。在Express有一個相似的東西叫中間件,這個中間件和咱們這裏的吃飯、睡覺等任務很相似,每個中間件執行完成後會調用next()函數,這個函數用來調用下一個中間件。css
對於這個問題,咱們也能夠利用類似的思路來解決,首先建立一個任務隊列,而後利用next()函數來控制任務的順序執行:html
function _LazyMan(name){
this.tasks=[];
var self=this;
var fn=(function(n){
var name=n;
return function(){
console.log("Hi! this is "+name+"!");
self.next();
}
})(name);
this.tasks.push(fn);
setTimeout(function(){
self.next();
},0); // 在下一個事件循環啓動任務
}
/* 事件調度函數 */
_LazyMan.prototype.next=function(){
var fn=this.tasks.shift();
fn && fn();
}
_LazyMan.prototype.eat=function(name){
var self=this;
var fn=(function(name){
return function(){
console.log("Eat "+name+" ~");
self.next()
}
})(name);
this.tasks.push(fn);
return this; // 實現鏈式調用
}
_LazyMan.prototype.sleep=function(time){
var self=this;
var fn=(function(time){
return function(){
setTimeout(function(){
console.log("Wake up after "+time+" s!");
self.next();
},time*1000);
}
})(time);
this.tasks.push(fn);
return this;
}
_LazyMan.prototype.sleepFirst=function(time){
var self=this;
var fn=(function(time){
return function(){
setTimeout(function(){
console.log("Wake up after "+time+" s!");
},time*1000);
}
})(time);
this.tasks.unshift(fn);
return this;
}
/* 封裝 */
function LazyMan(name){
return new _LazyMan(name);
}
複製代碼
lazyman裏邊含有鏈式調用,那麼每個子任務 return this;這個程序支持任務優先順序,那麼就須要兩個貫穿全場的Promise對象:第一,普通順序promise;第二,插入順序promise,同時插入順序是阻塞普通順序的,代碼以下:前端
function _LazyMan(name){
this.orderPromise=this.newPromise(); // 定義順序promise對象
this.insertPromise=this.newPromise(); // 定義插入promise對象
this.order(function(resolve){
console.log(name);
resolve();
})
}
_LazyMan.prototype={
/*實例化promise對象工廠*/
newPromise:function(){
return new Promise(function(resolve,reject){
resolve();
})
},
order:function(fn){
var self=this;
this.orderPromise=this.orderPromise.then(function(){
return new Promise(function(resolve,reject){
//若是有insertPromise,阻塞orderPromise.
self.fir?self.insertPromise.then(function(){
fn(resolve)
}):fn(resolve)
})
})
},
insert:function(fn){
var self=this;
this.fir=true;
this.insertPromise=this.insertPromise.then(function(){
return new Promise(function(resolve,reject){
fn(resolve);
self.fir=false;
})
})
},
sleepFirst:function(time){
this.insert(function(resolve){
setTimeout(function(){
console.log('wait '+time+' s,other logic');
resolve();
},time*1000)
})
return this;
},
eat:function(something){
this.order(function(resolve){
console.log(something+' ~~');
resolve();
})
return this;
},
sleep:function(time){
this.order(function(resolve){
setTimeout(function(){
console.log('sleep '+time+' s');
},time*1000);
})
return this;
}
}
//接口封裝。
function LazyMan(name) {
return new _LazyMan(name);
}
//調用測試
LazyMan(‘RoryWu‘).firstTime(1).sleep(2).firstTime(3).eat(‘dinner‘).eat(‘breakfast‘);
// 彈出:
// wait 1 s, other logic
// wait 3 s, other logic
// RoryWu
// sleep 2 s
// dinner~~
// breakfast~~
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.button {
height: 2em;
border: 0;
border-radius: .2em;
background-color: #34538b;
color: #fff;
font-size: 12px;
font-weight: bold;
}
</style>
</head>
<body>
<input type="button" id="button" class="button" value="點擊我,顯示背景色" />
<script>
document.getElementById("button").onclick = function() {
var oStyle =window.getComputedStyle(this, null); // null不是必須
// 若是考慮IE var oStyle = this.currentStyle? this.currentStyle : window.getComputedStyle(this, null);
alert(oStyle.getPropertyValue("background-color")); //這裏也能夠用鍵值獲取,建議用getPropertyValue("background-color")
};
</script>
</body>
</html>
複製代碼
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
}
複製代碼
結果是:0 1 2 3 3 3 不少公司面試都愛出這道題,此題考察的知識點仍是蠻多的。 爲了防止初學者栽在此問題上,此文稍微分析一下。 都考察了那些知識點呢? 異步、做用域、閉包,你沒聽錯,是閉包。 咱們來簡化此題:html5
setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
複製代碼
先打印2,後打印1。 由於是setTimeout是異步的。 正確的理解setTimeout的方式(註冊事件): 有兩個參數,第一個參數是函數,第二參數是時間值。 調用setTimeout時,把函數參數,放到事件隊列中。等主程序運行完,再調用。 沒啥很差理解的。就像咱們給按鈕綁定事件同樣:java
btn.onclick = function() {
alert(1);
};
複製代碼
這麼寫完,會彈出1嗎。不會!!只是綁定事件而已! 必須等咱們去觸發事件,好比去點擊這個按鈕,纔會彈出1。 setTimeout也是這樣的!只是綁定事件,等主程序運行完畢後,再去調用。 setTimeout的時間值是怎麼回事呢? 好比:jquery
setTimeout(fn, 2000)
複製代碼
咱們能夠理解爲2000以後,再放入事件隊列中,若是此時隊列爲空,那麼就直接調用fn。若是前面還有其餘的事件,那就等待。 所以setTimeout是一個約會歷來都不許時的童鞋。 繼續看:git
setTimeout(function() {
console.log(i);
}, 0);
var i = 1;
複製代碼
程序會不會報錯? 不會!並且還會準確得打印1。 爲何? 由於真正去執行console.log(i)這句代碼時,var i = 1已經執行完畢了! 因此咱們進行dom操做。能夠先綁定事件,而後再去寫其餘邏輯。程序員
window.onload = function() {
fn();
}
var fn = function() {
alert('hello')
};
複製代碼
這麼寫,徹底是能夠的。由於異步!es6
es5中是沒有塊級做用域的
for (var i = 0; i < 3; i++) {}
console.log(i);
複製代碼
也就說i能夠在for循環體外訪問到。因此是沒有塊級做用域。 但此問題在es6裏終結了,由於es6,發明了let。 這回咱們再來看看原題。 原題使用了for循環。循環的本質是幹嗎的? 是爲了方便咱們程序員,少寫重複代碼。 讓咱們倒退50年,原題等價於:
var i = 0;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
複製代碼
由於setTimeout是註冊事件。根據前面的討論,能夠都放在後面。 原題又等價於以下的寫法:
var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
複製代碼
這回你明白了爲啥結果是0 1 2 3 3 3了吧。
那個,說它是閉包,又是怎麼回事? 爲了很好的說明白這個事情,咱們把它放到一個函數中:
var fn = function() {
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
}
};
fn();
複製代碼
上面的函數跟咱們常見另外一個例子(div綁定事件)有什麼區別:
var fn = function() {
var divs = document.querySelectorAll('div');
for (var i = 0; i < 3; i++) {
divs[i].onclick = function() {
alert(i);
};
}
};
fn();
複製代碼
點擊每一個div都會彈出3。道理是同樣的。由於alert(i)中的i是fn做用越中的,於是這是閉包。 《javascript忍者祕籍》書裏把一個函數能調用全局變量,也稱閉包。 由於做者認爲全局環境也能夠想象成一個大的頂級函數。 怎麼保證能彈出0,1, 2呢。 解決之道:以毒攻毒! 再建立個閉包!!
var fn = function() {
var divs = document.querySelectorAll('div');
for (var i = 0; i < 3; i++) {
divs[i].onclick = (function(i) {
return function() {
alert(i);
};
})(i);
}
};
fn();
複製代碼
或者以下的寫法:
var fn = function() {
var divs = document.querySelectorAll('div');
for (var i = 0; i < 3; i++) {
(function(i) {
divs[i].onclick = function() {
alert(i);
};
})(i);
}
};
fn();
複製代碼
所以原題若是也想setTimeout也彈出0,1,2的話,改爲以下:
for (var i = 0; i < 3; i++) {
setTimeout((function(i) {
return function() {
console.log(i);
};
})(i), 0);
console.log(i);
}
複製代碼
想了解更多關於setTimeout(fn,0),能夠參考setTimeout(fn,0)
先看題目: 請用js實現一個類P,包含成員變量a,成員變量b,成員函數sum,sum輸出a與b的和,a,b默認值都爲0。實現一個類M,M繼承自P,在P的基礎上增長成員變量c,成員函數sum變成輸出a,b,c的和。
題目分析 Js全部的函數都有一個prototype屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。這個函數包括構造函數和普通函數,咱們講的更可能是構造函數的原型,可是也不可否定普通函數也有原型,實現繼承的方法不少,這裏使用原型鏈和構造繼承,即組合繼承的方式。
function P(a,b){
this.a=a||0;
this.b=b||0;
this.sum=function(){
return this.a+this.b;
}
}
function M(a,b,c){
P.call(this,a,b)
this.c=c;
this.sum=function(){
return this.a+this.b+this.c;
}
}
M.prototype=new P();
var m=new M(2,2,2);
m.sum() //輸出6
複製代碼
接下來,咱們深刻js繼承,看看js實現繼承幾種方式的特色。
既然要實現繼承,那麼首先咱們得有一個父類,代碼以下:
// 定義一個動物類
function Animal (name,eye,skin) {
// 屬性
this.name = name || 'Animal';
this.eye=eye;
this.skin=skin;
// 實例方法
this.sleep = function(){
console.log(this.name + '正在睡覺!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
複製代碼
下面給你們列出幾種繼承方式的實現
實現父類代碼在(4.2 js繼承的實現方式中) 核心: 將父類的實例做爲子類的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
複製代碼
特色:
缺點:
推薦指數:★★(三、4兩大體命缺陷)
**核心:**使用父類的構造函數來加強子類實例,等因而複製父類的實例屬性給子類(沒用到原型)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
複製代碼
特色:
缺點:
推薦指數:★★(缺點3)
**核心:**爲父類實例添加新特性,做爲子類實例返回
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
複製代碼
特色:
缺點:
推薦指數:★★
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
複製代碼
特色:
缺點:
推薦指數:★(缺點1)
**核心:**經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
複製代碼
特色:
缺點:
推薦指數:★★★★(僅僅多消耗了一點內存)
**核心:**經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 建立一個沒有實例方法的類
var Super = function(){};
Super.prototype = Animal.prototype;
//將實例做爲子類的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
複製代碼
特色:
缺點:
推薦指數:★★★★(實現複雜,扣掉一顆星)
示例:
function Animal (name) {
// 屬性
this.name = name || 'Animal';
// 實例方法
this.sleep = function(){
console.log(this.name + '正在睡覺!');
}
//實例引用屬性
this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();
var tom = new Cat('Tom');
var kissy = new Cat('Kissy');
console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []
tom.name = 'Tom-New Name';
tom.features.push('eat');
//針對父類實例值類型成員的更改,不影響
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//針對父類實例引用類型成員的更改,會經過影響其餘子類實例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']
緣由分析:
關鍵點:屬性查找過程
執行tom.features.push,首先找tom對象的實例屬性(找不到),
那麼去原型對象中找,也就是Animal的實例。發現有,那麼就直接在這個對象的
features屬性中插入值。
在console.log(kissy.features); 的時候。同上,kissy實例上沒有,那麼去原型上找。
恰好原型上有,就直接返回,可是注意,這個原型對象中features屬性值已經變化了。
複製代碼
其實這裏主要是理解js值類型和引用類型
假設已經定義了父類Base對象 咱們執行以下代碼
var obj = new Base();
複製代碼
這樣代碼的結果是什麼,咱們在Javascript引擎中看到的對象模型是:
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
複製代碼
__proto__
成員指向了Base函數對象prototype成員對象注意:new的過程會執行構造函數Base() 再對空對象進行構造
推薦閱讀 瀏覽器同源政策及其規避方法
棧:原始數據類型(Undefined,Null,Boolean,Number、String)
堆:引用數據類型(對象、數組和函數)
兩種類型的區別是:存儲位置不一樣;
原始數據類型直接存儲在棧(stack)中的簡單數據段,佔據空間小、大小固定,屬於被頻繁使用數據,因此放入棧中存儲;
引用數據類型存儲在堆(heap)中的對象,佔據空間大、大小不固定,若是存儲在棧中,將會影響程序運行的性能;引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其
在棧中的地址,取得地址後從堆中得到實體
複製代碼
// event(事件)工具集,來源:github.com/markyun
markyun.Event = {
// 頁面加載完成後
readyEvent : function(fn) {
if (fn==null) {
fn=document;
}
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = fn;
} else {
window.onload = function() {
oldonload();
fn();
};
}
},
// 視能力分別使用dom0||dom2||IE方式 來綁定事件
// 參數: 操做的元素,事件名稱 ,事件處理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件類型、須要執行的函數、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function() {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,由於IE不支持事件捕獲)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默認行爲
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 獲取事件目標
getTarget : function(event) {
return event.target || event.srcElement;
},
// 獲取event對象的引用,取到事件的全部信息,確保隨時能使用event;
getEvent : function(e) {
var ev = e || window.event;
if (!ev) {
var c = this.getEvent.caller;
while (c) {
ev = c.arguments[0];
if (ev && Event == ev.constructor) {
break;
}
c = c.caller;
}
}
return ev;
}
};
複製代碼
[1, NaN, NaN] 由於 parseInt 須要兩個參數 (val, radix),
其中 radix 表示解析時用的基數。
map 傳了 3 個 (element, index, array),對應的 radix 不合法致使解析失敗。
複製代碼
內存泄漏指任何對象在您再也不擁有或須要它以後仍然存在。
垃圾回收器按期掃描對象,並計算引用了每一個對象的其餘對象的數量。若是一個對象的引用數量爲 0(沒有其餘對象引用過該對象),或對該對象的唯一引用是循環的,那麼該對象的內存便可回收。
setTimeout 的第一個參數使用字符串而非函數的話,會引起內存泄漏。
閉包、控制檯日誌、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)
複製代碼
polyfill 是「在舊版瀏覽器上覆制標準 API 的 JavaScript 補充」,能夠動態地加載 JavaScript 代碼或庫,在不支持這些標準 API 的瀏覽器中模擬它們。
例如,geolocation(地理位置)polyfill 能夠在 navigator 對象上添加全局的 geolocation 對象,還能添加 getCurrentPosition 函數以及「座標」回調對象,
全部這些都是 W3C 地理位置 API 定義的對象和函數。由於 polyfill 模擬標準 API,因此可以以一種面向全部瀏覽器將來的方式針對這些 API 進行開發,
一旦對這些 API 的支持變成絕對大多數,則能夠方便地去掉 polyfill,無需作任何額外工做。
複製代碼
作的項目中,有沒有用過或本身實現一些 polyfill 方案(兼容性處理方案)?
好比: html5shiv、Geolocation、Placeholder
複製代碼
1. 定義 因爲JavaScript容許函數有不定數目的參數,因此咱們須要一種機制,能夠在函數體內部讀取全部參數。這就是arguments對象的由來。
arguments對象包含了函數運行時的全部參數,arguments[0]就是第一個參數,arguments[1]就是第二個參數,以此類推。這個對象只有在函數體內部,纔可使用。
var f = function(one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
// 1
// 2
// 3
複製代碼
arguments對象除了能夠讀取參數,還能夠爲參數賦值(嚴格模式不容許這種用法)
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1)
// 5
複製代碼
能夠經過arguments對象的length屬性,判斷函數調用時到底帶幾個參數。
function f() {
return arguments.length;
}
f(1, 2, 3) // 3
f(1) // 1
f() // 0
複製代碼
2. 與數組的關係 須要注意的是,雖然arguments很像數組,但它是一個對象。數組專有的方法(好比slice和forEach),不能在arguments對象上直接使用。
可是,能夠經過apply方法,把arguments做爲參數傳進去,這樣就可讓arguments使用數組方法了。
// 用於apply方法
myfunction.apply(obj, arguments).
// 使用與另外一個數組合並
Array.prototype.concat.apply([1,2,3], arguments)
複製代碼
要讓arguments對象使用數組方法,真正的解決方法是將arguments轉爲真正的數組。下面是兩種經常使用的轉換方法:slice方法和逐一填入新數組。
var args = Array.prototype.slice.call(arguments);
// or
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
複製代碼
3. callee屬性 arguments對象帶有一個callee屬性,返回它所對應的原函數。
var f = function(one) {
console.log(arguments.callee === f);
}
f() // true
複製代碼
能夠經過arguments.callee,達到調用函數自身的目的。這個屬性在嚴格模式裏面是禁用的,所以不建議使用。
4. 題目sum(2)(3)
// 寫一個 function 讓下面兩行代碼輸出的結果都爲 5
console.log(sum(2, 3));
console.log(sum(2)(3));
複製代碼
說實話,第一眼看到的時候內心是有點虛的(由於第一次看到它)。sum(2)(3),這種形式的代碼確實少見。可是第一反應就是鏈式調用。 鏈式調用咱們熟悉啊,特別是 jQuery 裏面,咱們經常能看到連着寫的代碼。實現原理就是在方法結束時 return 合適的元素對象。
$('#id').parent().siblings('selector').css({
color: 'red'
});
複製代碼
這道題考什麼呢?認真分析了一下,應該有鏈式調用,toString,柯里化,數組操做等相關內容。大概這些能夠知足需求吧? 如何寫代碼,腦海中大致上有構思了,可是當時手上僅有筆和紙,思路連不上來啊。還好面前放着一臺臺式機(嘿嘿嘿,機器上寫完再抄回紙上)
個人實現大概是這樣的。
var sum = (function() {
var list = [];
var add = function() {
// 拼接數組
var args = Array.prototype.slice.call(arguments);
list = list.concat(args);
return add;
}
// 覆蓋 toString 方法
add.toString = function() {
// 計算總和
var sum = list.reduce(function(pre, next) {
return pre + next;
});
// 清除記錄
list.length = 0;
return sum;
}
return add;
})();
sum(2, 3);
// 5
sum(2)(3);
// 5
複製代碼
這個方法比較複雜,下面介紹個簡便的。
var add = function add() {
var cache;
if (arguments.length === 1) {
cache = arguments[0];
return function ( number ) {return cache + number;};
}
else return arguments[0] + arguments[1];
};
複製代碼
簡單的淺複製實現:
var obj={ a:1,arr:[2,3] };
var shallowObj=shallowCopy(obj);
function shallowCopy(src){
var dst = {};
for (var prop in src){
if(src.hasOwnProperty(prop)){
dst[prop]=src[prop];
}
}
return dst;
}
複製代碼
由於淺複製只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,而 JavaScript 存儲對象都是存地址的,因此淺複製會致使 obj.arr 和 shadowObj.arr 指向同一塊內存地址,大概的示意圖以下。
shadowObj.arr[1] = 5;
obj.arr[1] // = 5
複製代碼
而深複製則不一樣,它不只將原對象的各個屬性逐個複製出去,並且將原對象各個屬性所包含的對象也依次採用深複製的方法遞歸複製到新對象上。這就不會存在上面 obj 和 shadowObj 的 arr 屬性指向同一個對象的問題。
var obj = { a:1, arr: [1,2] };
var obj2 = deepCopy(obj);
複製代碼
結果以下面的示意圖所示:
須要注意的是,若是對象比較大,層級也比較多,深複製會帶來性能上的問題。在遇到須要採用深複製的場景時,能夠考慮有沒有其餘替代的方案。在實際的應用場景中,也是淺複製更爲經常使用。
代碼遞歸實現以下:
function deepCopy(o,c){
var c = c || {}
for(var i in o){
if(typeof o[i] == 'object'){
if(o[i].constructor===Array){
c[i]=[]
}else{
c[i]={}
}
deepCopy(o[i],c[i])
}else{
c[i]=o[i]
}
}
return c
}
複製代碼
jquery 中如何將數組轉化爲json字符串,而後再轉化回來?
$.fn.stringifyArray = function(array) {
return JSON.stringify(array)
}
$.fn.parseArray = function(array) {
return JSON.parse(array)
}
而後調用:
$("").stringifyArray(array)
複製代碼
針對 jQuery 的優化方法?
*基於Class的選擇性的性能相對於Id選擇器開銷很大,由於需遍歷全部DOM元素。
*頻繁操做的DOM,先緩存起來再操做。用Jquery的鏈式調用更好。
好比:var str=$("a").attr("href");
*for (var i = size; i < arr.length; i++) {}
for 循環每一次循環都查找了數組 (arr) 的.length 屬性,在開始循環的時候設置一個變量來存儲這個數字,可讓循環跑得更快:
for (var i = size, length = arr.length; i < length; i++) {}
複製代碼
在線電子書閱讀前端常見面試題彙總
轉載請註明
極客教程-極客教程