前端面试JS三部分(三)
生活随笔
收集整理的這篇文章主要介紹了
前端面试JS三部分(三)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前端面試JS三部分(三)
1、Generator了解?
ES6 提供的一種異步編程解決方案, Generator 函數是一個狀態機,封裝了多個內部狀態。 function* helloWorldGenerator() {yield 'hello';yield 'world';return 'ending'; }var hw = helloWorldGenerator(); 調用后返回指向內部狀態的指針, 調用next()才會移向下一個狀態, 參數: hw.next() // { value: 'hello', done: false }hw.next() // { value: 'world', done: false }hw.next() // { value: 'ending', done: true }hw.next() // { value: undefined, done: true }2、手寫Promise實現?
var myPromise = new Promise((resolve, reject) => {// 需要執行的代碼...if (/* 異步執行成功 */) {resolve(value)} else if (/* 異步執行失敗 */) {reject(error)} })myPromise.then((value) => {// 成功后調用, 使用value值 }, (error) => {// 失敗后調用, 獲取錯誤信息error })3. Promise優缺點?
優點: 解決回調地獄, 對異步任務寫法更標準化與簡潔化 缺點: 首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消; 其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部; 第三,當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成). 極簡版promise封裝: function promise () {this.msg = '' // 存放value和errorthis.status = 'pending'var that = thisvar process = arguments[0]process (function () {that.status = 'fulfilled'that.msg = arguments[0]}, function () {that.status = 'rejected'that.msg = arguments[0]})return this }promise.prototype.then = function () {if (this.status === 'fulfilled') {arguments[0](this.msg)} else if (this.status === 'rejected' && arguments[1]) {arguments[1](this.msg)} }4.手寫實現bind?
Function.prototype.bind = function () {// 保存原函數var self = this// 取出第一個參數作為上下文, 相當于[].shift.call(arguments)var context = Array.prototype.shift.call(arguments)// 取剩余的參數作為arg; 因為arguments是偽數組, 所以要轉化為數組才能使用數組方法var arg = Array.prototype.slice.call(arguments)// 返回一個新函數return function () {// 綁定上下文并傳參self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments)))} }5.手寫實現4種繼承
function Father () {} function Child () {} // 1\. 原型繼承 Child.prototype = new Father() // 2\. 構造繼承 function Child (name) {Father.call(this, name) } // 3\. 組合繼承 function Child (name) {Father.call(this, name) } Child.prototype = new Father() // 4\. 寄生繼承 function cloneObj (o) {var clone = object.create(o)clone.sayName = ...return clone } // 5\. 寄生組合繼承 // 6\. ES6 class extend繼承6.封裝JSONP
``
function jsonp ({url, param, callback}) {return new Promise((resolve, reject) => {var script = document.createElement('script')window.callback = function (data) {resolve(data)document.body.removeChild('script')}var param = {...param, callback}var arr = []for (let key in param) {arr.push(`${key}=${param[key]}`)}script.src = `${url}?${arr.join('&')}`document.body.appendChild(script)}) }7.手動實現map(forEach以及filter也類似)
// for循環實現 Array.prototype.myMap = function () {var arr = thisvar [fn, thisValue] = Array.prototype.slice.call(arguments)var result = []for (var i = 0; i < arr.length; i++) {result.push(fn.call(thisValue, arr[i], i, arr))}return result } var arr0 = [1, 2, 3] console.log(arr0.myMap(v => v + 1))// forEach實現(reduce類似) Array.prototype.myMap = function (fn, thisValue) {var result = []this.forEach((v, i, arr) => {result.push(fn.call(thisValue, v, i, arr))})return result } var arr0 = [1, 2, 3] console.log(arr0.myMap(v => v + 1))8. js實現checkbox全選以及反選
<body><button id="other">反選</button><input type="checkbox" id="all" />全選<input type="checkbox" class="check" />1<input type="checkbox" class="check" />2<input type="checkbox" class="check" />3<script>var checkbox = document.getElementsByClassName('check')var checkAll = document.getElementById('all')var checkOther = document.getElementById('other')checkAll.onclick = function() {var flag = truefor (var i = 0; i < checkbox.length; i++) {if (!checkbox[i].checked) flag = false}if (flag) {for (var i = 0; i < checkbox.length; i++) {checkbox[i].checked = false}} else {for (var i = 0; i < checkbox.length; i++) {checkbox[i].checked = true}}}checkOther.onclick = function() {for (var i = 0; i < checkbox.length; i++) {checkbox[i].checked = !checkbox[i].checked}}</script></body>9.節流和防抖
函數節流是指一定時間內js方法只跑一次。比如人的眨眼睛,就是一定時間內眨一次。這是函數節流最形象的解釋。 // 函數節流 滾動條滾動 var canRun = true; document.getElementById("throttle").onscroll = function(){if(!canRun){// 判斷是否已空閑,如果在執行中,則直接returnreturn;}canRun = false;setTimeout(function(){console.log("函數節流");canRun = true;}, 300); }; 函數防抖是指頻繁觸發的情況下,只有足夠的空閑時間,才執行代碼一次。比如生活中的坐公交,就是一定時間內,如果有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機才開車。 // 函數防抖 var timer = false; document.getElementById("debounce").onscroll = function(){clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態timer = setTimeout(function(){console.log("函數防抖");}, 300); };10.實現一個sleep函數
// 這種實現方式是利用一個偽死循環阻塞主線程。因為JS是單線程的。所以通過這種方式可以實現真正意義上的sleep()。 function sleep(delay) {var start = (new Date()).getTime();while ((new Date()).getTime() - start < delay) {continue;} }function test() {console.log('111');sleep(2000);console.log('222'); }test()11.js實現instanceof
// 檢測l的原型鏈(__proto__)上是否有r.prototype,若有返回true,否則false function myInstanceof (l, r) {var R = r.prototypewhile (l.__proto__) {if (l.__proto__ === R) return true}return false }12.for in 和 for of區別
for in遍歷數組會遍歷到數組原型上的屬性和方法, 更適合遍歷對象 forEach不支持break, continue, return等 使用for of可以成功遍歷數組的值, 而不是索引, 不會遍歷原型 for in 可以遍歷到myObject的原型方法method,如果不想遍歷原型方法和屬性的話,可以在循環內部判斷一下,hasOwnPropery方法可以判斷某屬性是否是該對象的實例屬性13.ajax和axios、fetch的區別
1. XMLHttpRequest對象 現代瀏覽器,最開始與服務器交換數據,都是通過XMLHttpRequest對象。它可以使用JSON、XML、HTML和text文本等格式發送和接收數據。它給我們帶來了很多好處。1.不重新加載頁面的情況下更新網頁2.在頁面已加載后從服務器請求/接收數據3.在后臺向服務器發送數據。 但是,它也有一些缺點:1.使用起來也比較繁瑣,需要設置很多值。2.早期的IE瀏覽器有自己的實現,這樣需要寫兼容代碼。 if (window.XMLHttpRequest) { // model browserxhr = new XMLHttpRequest() } else if (window.ActiveXObject) { // IE 6 and olderxhr = new ActiveXObject('Microsoft.XMLHTTP') } xhr.open('POST', url, true) xhr.send(data) xhr.onreadystatechange = function () {try {// TODO 處理響應if (xhr.readyState === XMLHttpRequest.DONE) {// XMLHttpRequest.DONE 對應值是 4// Everything is good, the response was received.if (xhr.status === 200) {// Perfect!} else {// There was a problem with the request.// For example, the response may hava a 404 (Not Found)// or 500 (Internal Server Error) response code.}} else {// Not ready yet}} catch (e) {// 通信錯誤的事件中(例如服務器宕機)alert('Caught Exception: ' + e.description)} } 2. jQuery ajax 為了更快捷的操作DOM,并且規避一些瀏覽器兼容問題,產生了jQuery。它里面的AJAX請求也兼容了各瀏覽器,可以有簡單易用的方法$.get,$.post。簡單點說,就是對XMLHttpRequest對象的封裝。$.ajax({type: 'POST',url: url, data: data,dataType: dataType,success: function () {},error: function () {} }) 優點:1.對原生XHR的封裝,做了兼容處理,簡化了使用。2.增加了對JSONP的支持,可以簡單處理部分跨域。 缺點:1.如果有多個請求,并且有依賴關系的話,容易形成回調地獄。2.本身是針對MVC的編程,不符合現在前端MVVM的浪潮。3.ajax是jQuery中的一個方法。如果只是要使用ajax卻要引入整個jQuery非常的不合理。 3. axios Axios是一個基于promise的HTTP庫,可以用在瀏覽器和 node.js 中。它本質也是對原生XMLHttpRequest的封裝,只不過它是Promise的實現版本,符合最新的ES規范。axios({method: 'post',url: '/user/12345',data: {firstName: 'liu',lastName: 'weiqin'}}).then(res => console.log(res)).catch(err => console.log(err)) Vue2.0之后,推薦大家使用axios來請求數據。 優點:從瀏覽器中創建XMLHttpRequests從 node.js 創建 http 請求支持 Promise API攔截請求和響應轉換請求數據和響應數據取消請求自動轉換 JSON 數據客戶端支持防御 XSRF 缺點:只持現代代瀏覽器. 4. fetch Fetch API提供了一個 JavaScript 接口,用于訪問和操作HTTP管道的部分,例如請求和響應。它還提供了一個全局fetch()方法,該方法提供了一種簡單,合理的方式來跨網絡異步獲取資源。 fetch是低層次的API,代替XHR,可以輕松處理各種格式,非文本化格式。可以很容易的被其他技術使用,例如Service Workers。但是想要很好的使用fetch,需要做一些封裝處理。fetch('http://example.com/movies.json').then(function(response) {return response.json();}).then(function(myJson) {console.log(myJson);}); **優勢:跨域的處理**在配置中,添加mode: 'no-cors'就可以跨域了fetch('/users.json', {method: 'post', mode: 'no-cors',data: {} }).then(function() { /* handle response */ }); fetch目前遇到的問題:fetch只對網絡請求報錯,對400,500都當做成功的請求,需要封裝去處理fetch默認不會帶cookie,需要添加配置項。fetch不支持abort,不支持超時控制,使用setTimeout及Promise.reject的實現超時控制并不能阻止請求過程繼續在后臺運行,造成了流量的浪費。fetch沒有辦法原生監測請求的進度,而XHR可以。14.promise.finally實現
Promise.prototype.finally = function (callback) {let P = this.constructor;return this.then(value => P.resolve(callback()).then(() => value),reason => P.resolve(callback()).then(() => { throw reason })); };15.檢測瀏覽器版本版本有哪些方式?
功能檢測、userAgent特征檢測比如:navigator.userAgent//"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36(KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36"16.What is a Polyfill?
polyfill 是“在舊版瀏覽器上復制標準 API 的 JavaScript 補充”,可以動態地加載 JavaScript 代碼或庫,在不支持這些標準 API 的瀏覽器中模擬它們。例如,geolocation(地理位置)polyfill 可以在 navigator 對象上添加全局的 geolocation 對象,還能添加 getCurrentPosition 函數以及“坐標”回調對象,所有這些都是 W3C 地理位置 API 定義的對象和函數。因為 polyfill 模擬標準 API,所以能夠以一種面向所有瀏覽器未來的方式針對這些 API 進行開發,一旦對這些 API 的支持變成絕對大多數,則可以方便地去掉 polyfill,無需做任何額外工作。17.做的項目中,有沒有用過或自己實現一些 polyfill 方案(兼容性處理方案)?
比如: html5shiv、Geolocation、Placeholder18.我們給一個dom同時綁定兩個點擊事件,一個用捕獲,一個用冒泡。會執行幾次事件,會先執行冒泡還是捕獲?
`綁定在被點擊元素的事件是按照代碼順序發生,其他元素通過冒泡或者捕獲“感知”的事件,按照W3C的標準,先發生捕獲事件,后發生冒泡事件。所有事件的順序是:其他元素捕獲階段事件 -> 本元素代碼順序事件 -> 其他元素冒泡階段事件 。` addEventListener參數 element.addEventListener(type, function, useCapture) //【事件類型】,【事件處理程序】,【可選。布爾值,指定事件是否在捕獲或冒泡階段執行。true:事件句柄在捕獲階段執行;false:默認。事件句柄在冒泡階段執行】 1.冒泡 冒泡是從下向上,DOM元素綁定的事件被觸發時,此時該元素為目標元素,目標元素執行后,它的的祖元素綁定的事件會向上順序執行。如下代碼所示,四個嵌套的div:addEventListener函數的第三個參數設置為false說明不為捕獲事件,即為冒泡事件。<div id='one'><div id='two'><div id='three'><div id='four'></div></div></div> </div><script type='text/javascript'>var one=document.getElementById('one');var two=document.getElementById('two');var three=document.getElementById('three');var four=document.getElementById('four');one.addEventListener('click',function(){alert('one');},false);two.addEventListener('click',function(){alert('two');},false);three.addEventListener('click',function(){alert('three');},false);four.addEventListener('click',function(){alert('four');},false); </script>代碼的執行順序是: 點擊one元素,輸出one; 點擊two元素,輸出two one; 點擊three元素,輸出 three two one; 點擊four元素,輸出 four three two one;2.捕獲 捕獲則和冒泡相反,目標元素被觸發后,會從目標元素的最頂層的祖先元素事件往下執行到目標元素為止。將上面的代碼第三個參數均改為true,事件為捕獲階段執行。 注意: 在事件處理程序中刪除目標元素也能阻止事件冒泡,目標元素在文檔中是事件冒泡的前提。則執行結果如下:點擊one,輸出one; 點擊two,輸出one two; 點擊three,輸出one two three; 點擊four,輸出one two three four;很明顯執行順序是不同的。3.當一個元素綁定兩個事件,一個冒泡,一個捕獲 首先,無論是冒泡事件還是捕獲事件,元素都會先執行捕獲階段。從上往下,如有捕獲事件,則執行;一直向下到目標元素后,從目標元素開始向上執行冒泡元素,即第三個參數為true表示捕獲階段調用事件處理程序,如果是false則是冒泡階段調用事件處理程序。(在向上執行過程中,已經執行過的捕獲事件不再執行,只執行冒泡事件。)如下代碼:one.addEventListener('click',function(){ alert('one'); },true); two.addEventListener('click',function(){ alert('two'); },false); three.addEventListener('click',function(){ alert('three'); },true); four.addEventListener('click',function(){ alert('four'); },false);此時點擊four元素,four元素為目標元素,one為根元素祖先,從one開始向下判斷執行。分析: one為捕獲事件,輸出one; two為冒泡事件,忽略; three為捕獲時間,輸出three; four為目標元素,開始向上冒泡執行,輸出four;(從此處分為兩部分理解較容易。) three為捕獲已執行,忽略; two為冒泡事件,輸出two; one為捕獲已執行,忽略。最終執行結果為: one three four two例如,three作為目標元素,執行結果為:one three two(因為two是冒泡事件,在向下執行時沒有執行到)。執行次數:綁定了幾個事件便執行幾次。如下代碼,two元素綁定了兩個不同事件,點擊two都會執行這兩個事件。而執行順序有所差異。one.addEventListener('click',function(){ alert('one'); },true); two.addEventListener('click',function(){ alert('two,bubble'); },false); two.addEventListener('click',function(){ alert('two,capture'); },true); three.addEventListener('click',function(){ alert('three,bubble'); },true); four.addEventListener('click',function(){ alert('four'); },true);1、如果two為目標元素,目標元素的同類型事件按順序執行,而其他元素根據W3C的標準執行,即先捕獲后冒泡。點擊two執行結果:one(因為是two的父元素支持捕獲事件所以先執行) two,bubble two,capture(順序執行,注意逗號不是間隔,是輸出內容。)2、如果目標元素不是two,則two的同類型事件按先捕獲后冒泡觸發執行,也就是跟前面討論的執行過程是一樣的,只不過兩個事件都綁定在同一個DOM元素上。點擊three執行結果:one two,capture three,bubble two,bubble總結 所以,看到這里,你就應該明白了:綁定在被點擊元素的事件是按照代碼順序發生。 其他元素通過冒泡或者捕獲“感知”的事件。 按照W3C的標準,先發生捕獲事件,后發生冒泡事件。所有事件的順序是:其他元素捕獲階段事件 -> 本元素代碼順序事件 -> 其他元素冒泡階段事件 。舉個例子 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title><style type="text/css"></style> </head> <body><div class="outer"><div class="inner"><button id="btn">click</button></div></div><script>const inner = document.querySelector('.inner');const outer = document.querySelector('.outer');const body = document.body;function h(stopPropagation){return function(e){console.log(`${this.id||this.className||this.tagName}`);if(stopPropagation){e.stopPropagation();}}}body.addEventListener('click',h()); //冒泡階段執行outer.addEventListener('click',h(),true); //捕獲階段執行inner.addEventListener('click',h(true)); //冒泡階段執行,取消冒泡//解析:W3c執行順序:其他元素的捕獲事件,自身元素的順序事件,其他元素的冒泡事件。//此處,//body的click事件為冒泡階段,暫不執行;//outer的click事件為捕獲階段執行,觸發。輸出outer//inner的click事件為冒泡階段執行,本身觸發,輸出inner。//但是因為inner在這里取消了冒泡,所以body的click冒泡事件也不能執行了。</script> </body> </html>結果是: outer inner改動,若此處:inner.addEventListener(‘click’,h()); //不取消冒泡 輸出: outer inner body19.使用JS實現獲取文件擴展名?
function getFileExtension(filename) {return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);}String.lastIndexOf() 方法返回指定值(本例中的'.')在調用該方法的字符串中最后出現的位置,如果沒找到則返回 -1。對于'filename'和'.hiddenfile',lastIndexOf的返回值分別為0和-1無符號右移操作符(?>) 將-1轉換為4294967295,將-2轉換為4294967294,這個方法可以保證邊緣情況時文件名不變。String.prototype.slice() 從上面計算的索引處提取文件的擴展名。如果索引比文件名的長度大,結果為""。暫時只有這么多,后期會經常更新~~ 明天更新內容是 ES6~~
總結
以上是生活随笔為你收集整理的前端面试JS三部分(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html:link标签
- 下一篇: 高级Web前端必会面试题知识点,大厂面试