2017拼多多前端筆試

簡答題:

settimeout 與 setInterval的區別, 及對他們的內存的分析

區別

  1. setTimeout是在一段時間後調用指定函數(僅一次)javascript

  2. setInterval是每隔一段時間調用指定函數(N次)php

function run(){
    // 其餘代碼
    setTimeout(function(){
        run();
    }, 10000);
}
run();

以上面的代碼來講, 雖然設置的是10s執行一次, 可是實際時間倒是須要// 其餘代碼的執行時間來肯定
即setTimeout的間隔時間是, // setTimeout 的間隔時間 === 最小時間是(10s+)html

setInterval(function(){
    run();
}, 10000);

而setInterval, 不會有上面的問題, 可是若是run()的執行時間, 操做大於10s, 那麼甚至可能跳過任務;java

setInterval 和 setTimeout 會產生內存溢出
JavaScript setInterval()方法是否致使內存泄漏?node

關於內存泄漏

內存

程序的運行須要內存。只要程序提出要求,操做系統或者運行時(runtime)就必須供給內存。
對於持續運行的服務進程(daemon),必須及時釋放再也不用到的內存。不然,內存佔用愈來愈高,輕則影響系統性能,重則致使進程崩潰。
再也不用到的內存,沒有及時釋放,就叫作內存泄漏(memory leak)。
(好比 C 語言)必須手動釋放內存,程序員負責內存管理。git

char * buffer;
buffer = (char*) malloc(42);

//...

free(buffer)    //手動釋放內存

上面是 C 語言代碼,malloc方法用來申請內存,使用完畢以後,必須本身用free方法釋放內存。
這很麻煩,因此大多數語言提供自動內存管理,減輕程序員的負擔,這被稱爲"垃圾回收機制"(garbage collector)。程序員

垃圾回收機制

怎麼知道哪些內存再也不須要呢?經常使用的方法是 '引用計數', 語言的引擎有一張 '引用表', 保存了內存裏面全部的資源(一般是各類值)的引用次數,當一個值的引用次數爲 0 時,表示這個值用不到了,所以可將其釋放。github

可是若是一個值再也不用到了,引用次數卻不爲 0 ,垃圾回收機制卻沒法釋放這塊內存,從而致使內存泄漏。面試

const arr = [1, 2, 3, 4];
console.log(arr);

打印完 arr 以後, arr 便用不到了,引用次數爲 1, 可是它還會繼續佔用內存。ajax

const arr = [1, 2, 3, 4];
console.log(arr);
arr = null;

arr 重置爲 null,就解除了對 [1, 2, 3, 4] 的引用,引用次數變成了 0 ,內存就能夠釋放了。

JavaScript 內存管理

JavaScript 是一種垃圾回收語言。垃圾回收語言經過週期性地檢查先前分配的內存是否可達,幫助開發者管理內存。換言之,垃圾回收語言減輕了「內存仍可用」及「內存仍可達」的問題。二者的區別是微妙而重要的:僅有開發者瞭解哪些內存在未來仍會使用,而不可達內存經過算法肯定和標記,適時被操做系統回收。

JavaScript 內存泄漏

垃圾回收語言的內存泄漏主因是不須要的引用。理解它以前,還需瞭解垃圾回收語言如何辨別內存的可達與不可達。

Mark-and-sweep

  1. 大部分垃圾回收語言用的算法稱之爲 Mark-and-sweep 。算法由如下幾步組成:
    垃圾回收器建立了一個「roots」列表。Roots 一般是代碼中全局變量的引用。JavaScript 中,「window」 對象是一個全局變量,被看成 root 。window 對象老是存在,所以垃圾回收器能夠檢查它和它的全部子對象是否存在(即不是垃圾);

  2. 全部的 roots 被檢查和標記爲激活(即不是垃圾)。全部的子對象也被遞歸地檢查。從 root 開始的全部對象若是是可達的,它就不被看成垃圾。

  3. 全部未被標記的內存會被當作垃圾,收集器如今能夠釋放內存,歸還給操做系統了。

現代的垃圾回收器改良了算法,可是本質是相同的:可達內存被標記,其他的被看成垃圾回收。

不須要的引用是指開發者明知內存引用再也不須要,卻因爲某些緣由,它仍被留在激活的 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'] 中的內容標記爲紅色文本(字符串有改動)](http://hexin.life/more/pdd.html)

設定 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 比較的文章。

建立一個函數對 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;   
    }

test img

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吧,若是有錯誤或者更好的方法請告訴我

更多筆試整理更新在我的博客Github,歡迎小夥伴來一塊兒準備秋招(求大腿抱)。

相關文章
相關標籤/搜索