一、實現一個函數,判斷輸入是否是迴文字符串javascript
function run(str){ if(typeOf str !== 'string') return false; return str.split('').reverse().join('') === str; }
二、實現Storage, 使得該對象爲單例,並對localStorage進行封裝設置值setItem(key, value)和getItem(key)css
var instance = null; class Storage{ static getInstance(){ if(!instance){ instance = new Storage(); } return instance; } setItem = (key, value) => localStorage.setItem(key, value); getItem = (key) => return localStorage.getItem(key); }
全部在類中定義的方法都會被實例繼承,但若是一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用的。若是在實例上調用靜態方法,會報錯,表示不存在該方法。類Storage上可直接調用getInstance方法:Storage.getInstance();java
父類的靜態方法能夠被子類繼承。也可在子類中經過super()方法調用父類的靜態方法。node
三、簡單實現雙向數據綁定mvvm。webpack
<input id="input" />
let data = {}; const input = document.getElementById('input'); Object.defineProperty(data, 'text', { set: function(value){ input.value = value; this.value = value; } }) input.onChange = function(e){ data.text = e.target.value; }
四、實現效果,點擊容器內的圖標,圖標邊框變成border: 1px solid red, 點擊空白處重置。es6
<div id="box"> <a class="icon"><i class="fa fa-camera"></i></a> </div> //css #box{
display: flex;
justify-content: center;
align-items: center; width: 100px; height: 100px; background-color: #0f0; } .icon{ display: inline-block; font-size: 20px; }
js:web
function isIcon(target){ return target.className.includes('icon'); } var box = document.getElementById('box'); box.onclick = function(e){ e.stopPropagation(); target = e.target; if(isIcon(target)){ target.style.border= '1px solid red'; } else { var children = box.children; for(var i=0; i < children.length; i++) { if (isIcon(children[i])) { children[i].style.border = 'none'; } } } }
五、說說event loop算法
js是單線程的,主要任務是與用戶交互,而用戶的交互無非就是響應DOM的增刪改,使用事件隊列的形式,用來存儲待執行的事件,數組
全部任務分爲同步任務和異步任務,同步任務指的是在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程,而是進入「任務隊列」的任務,只有「任務隊列」通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。promise
任務隊列是一個事件的隊列(包括:IO設備的事件,鼠標點擊、頁面滾動等用戶產生的事件),只要執行棧中的全部同步任務執行完畢,系統就會讀取「任務隊列」,看看裏面有哪些事件。那些對應的異步任務因而結束等待狀態,進入執行棧,開始執行。主線程從「任務隊列」中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。
六、說說事件流
事件發生時會在元素節點與根節點之間按照特定的順序傳播,路徑所通過的全部節點都會收到該事件,這個傳播過程即DOM事件流。
事件傳播的順序對應瀏覽器的兩種事件流模型:捕獲事件流和冒泡事件流。
捕獲事件流從根節點開始執行,一直往子節點查找執行,直到查找執行到目標節點。
冒泡事件流從目標節點開始執行,一直往父節點冒泡查找執行,直到查到到根節點。
DOM標準規定事件流包括三個階段,事件捕獲階段,處於目標節點階段,事件冒泡階段。實際目標(<div>)在捕獲階段時不會接收事件的,事件在目標階段發生並處理,可是事件處理會被當作是冒泡階段的一部分。note: 全部事件都要通過捕獲階段和處於目標階段,但有些事件會跳過冒泡階段,如,得到輸入焦點的focus事件和失去輸入焦點的blur事件。
七、有一個數組[1,2,3,4],實現一個算法,獲得這個數組的全排列的數組,好比[2,1,3,4],[2,1,4,3]....,算法時間複雜度是多少
方法一:按字典序生成全排列:從最後一個元素往前走,咱們想讓它是遞增的,若是遇見了不遞增的,那麼就讓右邊中略大於該數字的數字出頭頂替這個違背了遞增規律的數字。翻轉被替換掉的數字右邊的數字
八、說說從輸入URL到看到頁面發生的全功成,越詳細越好。
在瀏覽器中輸入url到顯示網頁主要包含兩個部分:網絡通訊和頁面渲染
瀏覽器地址欄輸入url並按下回車後,先進行DNS域名解析,實現網址到IP地址的轉換,獲取到服務器IP地址後,便會開始創建一次鏈接,這是由TCP協議完成的,主要經過三次握手進行鏈接。
第一次握手: 創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SENT狀態,等待服務器確認; 第二次握手: 服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態; 第三次握手: 客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP鏈接成功)狀態,完成三次握手
鏈接創建後瀏覽器向服務器發送HTTP請求,完整的HTTP請求包含請求起始行,請求頭部,請求主題三部分。服務器在收到瀏覽器的HTTP請求後,會將收到的HTTP報文封裝成HTTP的Request對象,並經過不一樣的Web服務器進行處理,處理完的結果以HTTP的Response對象返回,主要包括狀態碼,響應頭,響應報文三個部分。若是說響應的內容是HTML文檔的話, 就須要瀏覽器進行解析渲染呈現給用戶。整個過程涉及兩個方面:解析和渲染。在渲染頁面以前,須要構建DOM樹和CSSOM樹,在瀏覽器還沒接收到完整的HTML文件時,它就開始渲染頁面了,在遇到外部鏈入的腳本標籤或樣式標籤或圖片時,會再次發送HTTP請求重複上述的步驟。在收到CSS文件後會對已經渲染的頁面從新渲染,加入它們應有的樣式,圖片文件加載完馬上顯示在相應的位置。在這一過程當中可能會觸發頁面的重繪(Repaint)和重排(Reflow)。
Reflow,也稱做Layout,中文叫回流,通常意味着元素的內容、結構、位置或尺寸發生了變化,須要從新計算樣式和渲染樹,這個過程稱爲Reflow。 Repaint,中文重繪,意味着元素髮生的改變只是影響了元素的一些外觀之類的時候(例如,背景色,邊框顏色,文字顏色等),此時只須要應用新樣式繪製這個元素就OK了,這個過程稱爲Repaint。
最後關閉TCP鏈接或繼續保持鏈接。經過四次揮手關閉鏈接。
第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。 第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。 第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。 第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。
九、說一下webpack一些plugin,怎麼使用wepack對項目進行優化,
十、使下面代碼運行正常
a = [1,2,3,4,5]; // Implement this a.multiply(); console.log(a); // [1,2,3,4,5,1,4,9,16,25]
解:給Array添加一個原型方法
Array.prototype.multiply = function(){ Array.prototype.push.apply(a, this.map(item=>item*item)); };
十一、如下代碼在javascript中返回‘false’,請說明爲何
0.2+0.1===0.3 //false
由於浮點數在計算機中以二進制形式存儲時除了能表示成x/2^n的數能夠被精確表示外,其他小數都只能存成一個近似值,此例中0.2,0.1都是近似表示,相加後值爲0.30000000000000004
十二、javascript中有哪幾種數據類型?
5種基本數據類型:undefined, null, boolean, string, number,
1種引用數據類型:object,
es6中新增了一種原始數據類型:symbol
1三、手寫實現一個promise
1四、現有一個函數A和函數B,請實現B繼承A
//方法一、原型鏈繼承,(核心:將父類的實例做爲子類的原型) function A(){} function B(){} B.prototype = new A(); //方法二、構造繼承,(核心:使用父類的構造函數來加強子類實例,等因而複製父類的實例屬性給子類) function A(){} function B(){ A.call(this); } //方法三、實例繼承,(核心:爲父類實例添加新特性,做爲子類實例返回) function A(){} function B(){ var instance = new A(); return instance; } //方法四、拷貝繼承 function A(){} function B(){ var instance = new A(); for(var p in instance){ B.prototype[p] = instance[p] } } //方法五、組合繼承,(核心:經過調用父類構造函數,繼承父類的屬性並保留傳參的優//點,而後經過將父類實例做爲子類原型,實現函數複用) function A(){} function B(){ A.call(this); } B.prototype = new A();
1五、說出js中的幾種繼承方式,以及他們的優缺點
原型鏈繼承:
構造繼承:
實例繼承:
拷貝繼承:
組合繼承:
1六、描述一下this
this, 函數執行的上下文,能夠經過apply, call, bind改變this的指向。
對於匿名函數或是直接調用的函數來講,this指向全局上下文(瀏覽器爲window, nodejs爲globa),做爲對象的方法調用,此時this就指向這個上級對象,做爲構造函數調用,這時this就指向這個新對象。
es6的箭頭函數,箭頭函數中this的指向取決於該剪頭函數聲明的位置,在哪裏聲明,this就指向哪裏。
setTimeout()裏匿名函數和箭頭函數中this指向?
apply()參數爲空時,默認調用全局對象,參數不爲空,此時this指的就是第一個參數表示的對象。
1七、做用域
(function(){ var a = b =5; })(); console.log(b); //5
當即執行函數中變量a用關鍵字var聲明,因此a是一個局部變量,而b是全局變量
1八、在String對象上定義一個repeatify函數,這個函數接受一個整數參數,來明確字符串須要重複幾回,這個函數要求字符串重複指定的次數。例如:
console.log('hello'.repeatify(3)); 應該打印出hellohellohello
String.prototype.repeatify = String.prototype.repeatify || function(times){ var str=''; for(var i=0; i<times; i++){ str += this; } return str; }
1九、變量提高
function test(){ console.log(a); console.log(foo()); var a = 1; function foo(){return 2;} } test();
打印結果:undefined 2
20、this
var fullname = 'John Doe'; var obj = { fullname: 'Colin Ihrig', prop: { fullname: 'Aurelio De Rosa', getFullname: function(){ return this.fullname; } } }; console.log(obj.prop.getFullname()); var test = obj.prop.getFullname; console.log(test());
打印結果:Aurelio De Rosa. 和. John Doe
2一、修復前一個問題,讓最後一個console.log()打印輸出Aurelio De Rosa
console.log(test.call(obj.prop));
2二、閉包
var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', function() { console.log('You clicked element #' + i); }); }
若是用戶點擊第一個和第四個按鈕的時候,控制檯分別打印的結果是什麼?
答案:兩次打印You clicked element #node_length, 其中node_length是nodes節點的個數
2三、修復上題,使得點擊第一個按鈕輸出0,點擊第二個按鈕時輸出1,以此類推
方法1、將執行函數移到循環的外面
function handlerWrapper(i) { return function() { console.log('You clicked element #' + i); } } var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', handlerWrapper(i)); }
方法2、使用當即執行函數表達式再建一個閉包
var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', (function(i){ return function(){ console.log('You clicked element #' + i); } })(i)); }
2四、寫一個isPrime()函數,當其爲質數時返回true,不然返回false
function isPrime(number) { // If your browser doesn't support the method Number.isInteger of ECMAScript 6, // you can implement your own pretty easily if (typeof number !== 'number' || !Number.isInteger(number)) { // Alternatively you can throw an error. return false; } if (number < 2) { return false; } if (number === 2) { return true; } else if (number % 2 === 0) { return false; } var squareRoot = Math.sqrt(number); for(var i = 3; i <= squareRoot; i += 2) { if (number % i === 0) { return false; } } return true; }