簡答題:
settimeout 與 setInterval的區別, 及對他們的內存的分析
區別
- setTimeout是在一段時間后調用指定函數(僅一次)
- setInterval是每隔一段時間調用指定函數(N次)
function run(){
// 其他代碼
setTimeout(function(){
run();
}, 10000);
}
run();
以上面的代碼來說, 雖然設置的是10s執行一次, 但是實際時間卻是需要// 其他代碼的執行時間來確定
即setTimeout的間隔時間是, // setTimeout 的間隔時間 === 最小時間是(10s+)
setInterval(function(){
run();
}, 10000);
而setInterval, 不會有上面的問題, 但是如果run()的執行時間, 操作大于10s, 那么甚至可能跳過任務;
setInterval 和 setTimeout 會產生內存溢出
JavaScript setInterval()方法是否導致內存泄漏?
關于內存泄漏
內存
程序的運行需要內存。只要程序提出要求,操作系統或者運行時(runtime)就必須供給內存。
對于持續運行的服務進程(daemon),必須及時釋放不再用到的內存。否則,內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。
不再用到的內存,沒有及時釋放,就叫做內存泄漏(memory leak)。
(比如 C 語言)必須手動釋放內存,程序員負責內存管理。
char * buffer;
buffer = (char*) malloc(42);
//...
free(buffer) //手動釋放內存
上面是 C 語言代碼,malloc方法用來申請內存,使用完畢之后,必須自己用free方法釋放內存。
這很麻煩,所以大多數語言提供自動內存管理,減輕程序員的負擔,這被稱為"垃圾回收機制"(garbage collector)。
垃圾回收機制
怎么知道哪些內存不再需要呢?常用的方法是 '引用計數', 語言的引擎有一張 '引用表', 保存了內存里面所有的資源(通常是各種值)的引用次數,當一個值的引用次數為 0 時,表示這個值用不到了,因此可將其釋放。
但是如果一個值不再用到了,引用次數卻不為 0 ,垃圾回收機制卻無法釋放這塊內存,從而導致內存泄漏。
const arr = [1, 2, 3, 4];
console.log(arr);
打印完 arr 之后, arr 便用不到了,引用次數為 1, 但是它還會繼續占用內存。
const arr = [1, 2, 3, 4];
console.log(arr);
arr = null;
arr 重置為 null,就解除了對 [1, 2, 3, 4] 的引用,引用次數變成了 0 ,內存就可以釋放了。
JavaScript 內存管理
JavaScript 是一種垃圾回收語言。垃圾回收語言通過周期性地檢查先前分配的內存是否可達,幫助開發者管理內存。換言之,垃圾回收語言減輕了“內存仍可用”及“內存仍可達”的問題。兩者的區別是微妙而重要的:僅有開發者了解哪些內存在將來仍會使用,而不可達內存通過算法確定和標記,適時被操作系統回收。
JavaScript 內存泄漏
垃圾回收語言的內存泄漏主因是不需要的引用。理解它之前,還需了解垃圾回收語言如何辨別內存的可達與不可達。
Mark-and-sweep
- 大部分垃圾回收語言用的算法稱之為 Mark-and-sweep 。算法由以下幾步組成:
垃圾回收器創建了一個“roots”列表。Roots 通常是代碼中全局變量的引用。JavaScript 中,“window” 對象是一個全局變量,被當作 root 。window 對象總是存在,因此垃圾回收器可以檢查它和它的所有子對象是否存在(即不是垃圾); - 所有的 roots 被檢查和標記為激活(即不是垃圾)。所有的子對象也被遞歸地檢查。從 root 開始的所有對象如果是可達的,它就不被當作垃圾。
- 所有未被標記的內存會被當做垃圾,收集器現在可以釋放內存,歸還給操作系統了。
現代的垃圾回收器改良了算法,但是本質是相同的:可達內存被標記,其余的被當作垃圾回收。
不需要的引用是指開發者明知內存引用不再需要,卻由于某些原因,它仍被留在激活的 root 樹中。在 JavaScript 中,不需要的引用是保留在代碼中的變量,它不再需要,卻指向一塊本該被釋放的內存。有些人認為這是開發者的錯誤。
為了理解 JavaScript 中最常見的內存泄漏,我們需要了解哪種方式的引用容易被遺忘。
常見 JavaScript 內存泄漏
意外的全局變量
JavaScript 處理未定義變量的方式比較寬松:未定義的變量會在全局對象創建一個新變量。在瀏覽器中,全局對象是 window 。
function foo(arg) {
bar = "this is a hidden global variable";
}
真相是:
function foo(arg) {
window.bar = "this is an explicit global variable";
}
函數 foo 內部忘記使用 var ,意外創建了一個全局變量。此例泄漏了一個簡單的字符串,無傷大雅,但是有更糟的情況。
另一種意外的全局變量可能由 this 創建:
function foo() {
this.variable = "potential accidental global";
}
// Foo 調用自己,this 指向了全局對象(window)
// 而不是 undefined
foo();
在 JavaScript 文件頭部加上 'use strict',可以避免此類錯誤發生。啟用嚴格模式解析 JavaScript ,避免意外的全局變量。
全局變量注意事項:
盡管我們討論了一些意外的全局變量,但是仍有一些明確的全局變量產生的垃圾。它們被定義為不可回收(除非定義為空或重新分配)。尤其當全局變量用于臨時存儲和處理大量信息時,需要多加小心。如果必須使用全局變量存儲大量數據時,確保用完以后把它設置為 null 或者重新定義。與全局變量相關的增加內存消耗的一個主因是緩存。緩存數據是為了重用,緩存必須有一個大小上限才有用。高內存消耗導致緩存突破上限,因為緩存內容無法被回收。
被遺忘的計時器或回調函數
在 JavaScript 中使用 setInterval 非常平常。一段常見的代碼:
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// 處理 node 和 someResource
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
此例說明了什么:與節點或數據關聯的計時器不再需要,node 對象可以刪除,整個回調函數也不需要了。可是,計時器回調函數仍然沒被回收(計時器停止才會被回收)。同時,someResource 如果存儲了大量的數據,也是無法被回收的。
對于觀察者的例子,一旦它們不再需要(或者關聯的對象變成不可達),明確地移除它們非常重要。老的 IE 6 是無法處理循環引用的。如今,即使沒有明確移除它們,一旦觀察者對象變成不可達,大部分瀏覽器是可以回收觀察者處理函數的。
觀察者代碼示例:
var element = document.getElementById('button');
function onClick(event) {
element.innerHTML = 'text';
}
element.addEventListener('click', onClick); // => 循環調用
對象觀察者和循環引用注意事項
老版本的 IE 是無法檢測 DOM 節點與 JavaScript 代碼之間的循環引用,會導致內存泄漏。如今,現代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進的垃圾回收算法,已經可以正確檢測和處理循環引用了。換言之,回收節點內存時,不必非要調用 removeEventListener 了。
脫離 DOM 的引用
有時,保存 DOM 節點內部數據結構很有用。假如你想快速更新表格的幾行內容,把每一行 DOM 存成字典(JSON 鍵值對)或者數組很有意義。此時,同樣的 DOM 元素存在兩個引用:一個在 DOM 樹中,另一個在字典中。將來你決定刪除這些行時,需要把兩個引用都清除.
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text')
};
function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
// 更多邏輯
}
function removeButton() {
// 按鈕是 body 的后代元素
document.body.removeChild(document.getElementById('button'));
// 此時,仍舊存在一個全局的 #button 的引用
// elements 字典。button 元素仍舊在內存中,不能被 GC 回收。
}
此外還要考慮 DOM 樹內部或子節點的引用問題。假如你的 JavaScript 代碼中保存了表格某一個 <td> 的引用。將來決定刪除整個表格的時候,直覺認為 GC 會回收除了已保存的 <td> 以外的其它節點。實際情況并非如此:此 <td> 是表格的子節點,子元素與父元素是引用關系。由于代碼保留了 <td> 的引用,導致整個表格仍待在內存中。保存 DOM 元素引用的時候,要小心謹慎。
閉包
如果閉包的作用域中保存著一個 HTML 元素,則該元素無法被銷毀。(下面代碼來自高程)
閉包是 JavaScript 開發的一個關鍵方面:匿名函數可以訪問父級作用域的變量。
function assgin() {
var ele = document.getElementById('someEle');
ele.onclick = function(){
alert(ele.id);
}
}
以上代碼創建了一個作為 ele 元素事件處理程序的閉包,而這個閉包有創建了一個循環的引用,由于匿名函數保存了一個 assgin() 的活動對象的引用 ,因此無法減少對 ele 的引用次數 , 只要匿名函數存在,ele的引用次數至少是 1。我們可以稍微改寫一下:
function assgin() {
var ele = document.getElementById('someEle');
var id = ele.id
ele.onclick = function(){
alert(id);
}
ele = null;
}
上面代碼中,通過把 ele.id 的一個副本保存在一個變量中,并且在比保重引用該變量消除了循環引用,但是這樣還不能解決內存泄露,閉包會引用包含函數的整個活動對象,而其中包含著 ele ,即使閉包不直接引用 ele ,包含函數的活動對象中也會保存 一個引用,因此需要把 ele 變量設置為 null ,這樣就解除了對 DOM 對象的引用,減少其引用數,確保能正常回收。
關于內存的發現 chrome 的使用~暫時沒有使用過,看不太明白,就不 copy 了。
js閉包測試 => 看不懂~
上述內容 copy 自下面二者:
JavaScript 內存泄漏教程-阮一峰
4類 JavaScript 內存泄漏及如何避免
ajax 原生實現
var xhr = createXHR()
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if(xhr.status == 200) {
console.log(xhr.responeText)
//do sth...
} else {
console.log('request fail' + xhr.status)
}
}
};
xhr.open('get', 'hello.com', true)
xhr.send(null);
閉包的理解
閉包是指有權訪問另一個函數作用域中的變量的函數。
這個口述我還是不知道怎么說,或許是應用不夠看了無數文章到頭來敵不過忘記也可能我理解的還是不到位吧~個人不解釋了,放參考鏈接吧
How do JavaScript closures work?--StackOverflow
學習Javascript閉包(Closure)--阮一峰的網絡日志
JS 中的閉包是什么--方應杭
JavaScript 中 閉包 的詳解
閉包--MDN
閉包的應用
html中一段文本內容 hdslakddnska8das ,將文本中含有數組['d', 'a', '', '8'] 中的內容標記為紅色文本(字符串有改動)
設定 html 結構
<style>
.mark {
color: red;
}
</style>
<html>
<body>
<div class='textToMark'>
hdslakddnska8das
</div>
</body>
</html>
方法一:循環
const textToMark = document.querySelector('.textToMark');
const text = textToMark.innerHTML;
const arr = ['d', 'a', '*', '8'];
const newText = text.split('');
function toMark (textArr, arr) {
for(let i = 0; i < newText.length; i++) {
for(let j = 0; j < arr.length; j++) {
if(newText[i] == arr[j]) {
newText[i] = `<span class='mark'>${newText[i]}</span>`;
}
}
}
return newText;
}
toMark(newText, arr);
textToMark.innerHTML = newText.join('');
方法二: 字符串的 replace
const textToMark = document.querySelector('.textToMark');
const text = textToMark.innerHTML;
const reg = /[da\*8]+/g;
var newtext = text.replace(reg, (match) => {
return match = `<span class='mark'>${match}</span>`;
});
textToMark.innerHTML = newtext;
代碼為個人寫出,如果有更好的辦法歡迎指教
原生JS創建這樣的 dom 結構 < div id='hello'> < p class='textToMark'>hdslakddnska8das< p>< /div>
function createElement() {
var body = document.body;
var fragment = document.createDocumentFragment()
var div = document.createElement('div')
div.setAttribute('id', 'hello')
fragment.appendChild(div)
var p = document.createElement('p')
p.className = 'textToMark'
p.innerHTML = 'hdslakddnska8das'
div.appendChild(p);
body.appendChild(fragment)
}
createElement();
感謝評論指出,已改正,關于節點創建 createElement 的效率問題,如果當插入的節點很多的時候,createElement 的效率會不如 createDocumentFragment .
createElement 每次 append 一個節點的時候,都會導致頁面的重排,例如:
數據為這樣:
<ul id="myList">
<li>
<a href="www.baidu.com"></a>
</li>
<li>
<a href="www.helloworld.com"></a>
</li>
</ul>
var data = [
{ name: '36O秋招', url: 'http://campus.360.cn/2015/grad.html'},
{ name: 'TX校招', url: 'http://join.qq.com/index.php'}
]
function appendChildToElement(appendToElement, data) {
var a, li;
for (var i = 0, len = data.length; i < len; i++) {
a = document.createElement('a');
a.href = data[i].url;
a.appChild(document.createTextNode(data[i].name))
li = document.createElement('li');
li.appendChild(a);
appendChildToElement(li);
}
}
這種情況下,data 內的每一個對象插入到 DOM 結構的時候都會觸發一次重排,因此效率會較低。
但是我們可以改變他的 display 屬性,臨時從文檔移除 ul ,即可有效減少重排次數。
var ul = document.getElementById('myList');
ur.style.display = 'none';
appendChildToElement(ul, data);
ul.style.display = 'block';
當然,更好的辦法就是利用 createDocumentFragment 來創建一個文檔片段.
var fragment = document.createElementFragment();
appendChildToElement(fragment, data);
document.getElementById('myList').appendChild(fragment);
只訪問了一次 DOM 節點,只觸發了一次重排;再次感謝 @xaclincoln 的指出。
查了一些關于 createDocumentFragment 和 createElement 比較的文章。
- createDocumentFragment or createElement--StackOverflow
- createElement vs createDocumentFragment
- createElement 與 createDocumentFragment 的點點區別
- CreateDocumentFragment 的用處
創建一個函數對 JS 基礎類型 ( function, boolean, array, number, string, object) 進行值復制
function valueToCopy (valueBeCopy) {
var copyValue;
if (typeof (+valueBeCopy) === 'number' && typeof valueBeCopy !== 'object') {
copyValue = +valueBeCopy;
} else if (typeof valueBeCopy === 'string') {
copyValue = parseInt(copyValue);
} else if (typeof valueBeCopy === 'object'){
if(Array.isArray(valueBeCopy)) {
copyValue = valueBeCopy.slice();
}
copyValue = JSON.parse(JSON.stringify(valueBeCopy))
}
copyValue = valueBeCopy;
// console.log(copyValue)
return copyValue;
}

url 輸入到頁面完成經歷了什么
感覺這篇文章非常非常詳細了太長了,過段時間再整理(抄襲)
老生常談-從輸入url到頁面展示到底發生了什么
選擇題
執行順序
var input = document.getElementById('cls')
input.onmouseup = function() {
console.log('onmouseup')
}
input.onmousedown = function() {
console.log('onmousedown')
}
input.onclick = function() {
console.log('onclick')
}
input.onfocus = function() {
console.log('onfocus')
}
onmousedown => onfocus => onmouseup => onclick
a 鏈接默認事件的阻止
A. a.onmouseup = function(e) {
e.preventDefault()
}
B. a.onmousedown = function(e) {
e.preventDefault()
}
C. a.onclick = function(e) {
e.preventDefault()
}
D. A B C 都可以~
- => 經測試只有 onclick 可以
IE瀏覽器中 attachEvent 方式的事件綁定
attachEvent的this總是Window。
el.attachEvent('onclick', function(){
alert(this);
});
HTTP狀態碼
- 400 Bad Request
由于明顯的客戶端錯誤(例如,格式錯誤的請求語法,太大的大小,無效的請求消息或欺騙性路由請求),服務器不能或不會處理該請求。[31] - 401 Unauthorized(RFC 7235)
參見:HTTP基本認證、HTTP摘要認證
類似于403 Forbidden,401語義即“未認證”,即用戶沒有必要的憑據。[32]該狀態碼表示當前請求需要用戶驗證。
注意:當網站(通常是網站域名)禁止IP地址時,有些網站狀態碼顯示的401,表示該特定地址被拒絕訪問網站。
- 402 Payment Required
該狀態碼是為了將來可能的需求而預留的。該狀態碼最初的意圖可能被用作某種形式的數字現金或在線支付方案的一部分,但幾乎沒有哪家服務商使用,而且這個狀態碼通常不被使用。如果特定開發人員已超過請求的每日限制,Google Developers API會使用此狀態碼。[34] - 403 Forbidden
服務器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證并不能提供任何幫助,而且這個請求也不應該被重復提交。如果這不是一個HEAD請求,而且服務器希望能夠講清楚為何請求不能被執行,那么就應該在實體內描述拒絕的原因。當然服務器也可以返回一個404響應,假如它不希望讓客戶端獲得任何信息。
選擇正確答案(構造函數的引用地址)
var str = 'asd;
var str2 = new String(str) var str1 = new String(str)
console.log(str1 == str2 , str1 === str2)
A. true true
B. true false
C. false true
D. false false
// => 輸出 => false false
因為 new 出來的倆個字符串引用地址不同
下面的輸出結果 (this 指向問題)
function one () {
this.name = 1;
return function two () {
name = 2;
return function three() {
var name = 3;
console.log(this.name);
}
}
}
one()()() // => 2;
還有一部分題忘掉嘍 ~ 還有一些題具體的記不太清了,稍作修改,考點計本差不多,上面答案有的是我自己寫的,有的是我 google 整理出來的,筆試期間攝像頭壞了,而且不小心彈出去了三四次~就當練習了吧,反正簡歷也沒準備好呢,哦,對了,考點大多都在高程中有詳細講解,需要好好看一下高程,面試應該會問一些 Node 和 ES6吧,如果有錯誤或者更好的方法請告訴我