面試中遇到的平常沒注意的問題——持續(xù)更新

面試中遇到的平常沒注意的問題

1. for循環(huán)中l(wèi)et 聲明的變量i 和 var 聲明的變量i 有什么區(qū)別

  • let 聲明的是塊級作用域;var 聲明的是在全局范圍內(nèi)都有效
  • let 聲明的變量只在本輪循環(huán)有效;var 聲明的全局范圍有效,全局只有這一個i值,所以每次循環(huán)變量i的值都會發(fā)生改變

實例代碼

 var a = []
 for (let i=0; i<10;i++){
    a[i] = function(){
       console.log(i)
    }
 }
 
 console.log(a[6]())  // 6
 
 var a = []
 
 for (var i=0; i<10; i++) {
   a[i] = function() {
      console.log(i)
   }
 }
 
 console.log(a[6]()) // 10

for循環(huán)還有一個特別的地方, 就是設(shè)置循環(huán)變量的部分是一個父作用域,循環(huán)體內(nèi)部是一個單獨的子作用域

 for(let i = 0;i?<3;i++){
   let i = 'abc'
   console.log(i)
 }
 // abc
 // abc
 // abc

2.如何才能實現(xiàn)深復制(深拷貝)

使用遞歸的方式進行深拷貝

1 第一種遞歸方式,復制目標對象是函數(shù)參數(shù)

  // 深拷貝函數(shù) 復制目標對象
  function deepCopy(targetObject) {
       let newObj = {}
        if(typeof targetObject !== 'object'){
            return targetObject
        } else {
               for(let property in targetObject){
             newObj[property] = deepCopy(targetObject[property])
           }
        }
        return newObj
  }

2 序列化反序列化 (一句代碼搞定,裝逼爽歪歪)

  let newObject = JSON.parse(JSON.stringify(targetObject))

3.實現(xiàn)一個function sum 達到以下目的

  sum(1,2,3,4,5).valueOf()    // 15
  sum(1,2,3,4)(5).valueOf()   // 15
  sum(1,2,3)(4)(5).valueOf()  // 15
  sum(1,2)(3)(4)(5).valueOf() // 15
  sum(1,2)(3,4)(5).valueOf()  // 15
   
  • 這道題考的是 函數(shù) 的柯里化
  • valueOf 涉及到的是 js 的隱式轉(zhuǎn)換

柯里化:(Currying) 又稱為部分求值,是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回一個新的函數(shù)的技術(shù),新函數(shù)接受余下參數(shù)并返回運算結(jié)果。

1.接收單一參數(shù),因為要攜帶不少信息,因此常常以回調(diào)函數(shù)的理由來解決

2.將部分參數(shù)通過回調(diào)函數(shù)等方式傳入函數(shù)中

3.返回一個新函數(shù),用于處理所有的想要傳入的參數(shù)

概念我晚點查閱一下專業(yè)的書籍來補充,包括柯里化的好處這些

由淺入深的柯里化

  // 普通的add函數(shù)
  function add(x, y){
     return x + y
  }
  
  // 柯里化后
  function curryingAdd(x){
    return function(y){
       return x+y
    }
  }
  
  add(1, 2)           // 3
  curryingAdd(1)(2)   // 3

接著,補充幾個隱式轉(zhuǎn)換的小問題

  function fn(){
    return 20
  }
  
  console.log(fn + 10)
  // 輸出什么
  
  function fn(){
    return 20
  }10
  function fn() {
    return 20
  }
  
  fn.toString = function() {
    return 10
  }
  
  console.log(fn + 10)
  // 輸出什么
  
  20
  function fn() {
    return 20
  }
  
  fn.toString = function() {
    return 10
  }
  
  fn.valueOf = function() {
    return 5
  }
  
  console.log(fn + 10)
  // 輸出什么
  
  15
  • 這里是JavaScript 中隱式轉(zhuǎn)換的知識點

  • 其中 valueOf 的優(yōu)先級比 toString 要高

開始解這個題

  function add() {
     // 第一次執(zhí)行時,定義一個數(shù)組專門用來存儲所有的參數(shù)
     var _args = Array.prototype.slice.call(arguments)
     
     // 在內(nèi)部聲明一個函數(shù),利用閉包特性保存_args并收集所有的參數(shù)
     var _adder = function() {
       _args.push(...arguments);
       return _adder
     }
     
     // 利用toString隱式轉(zhuǎn)換的特性,當最后執(zhí)行時隱式轉(zhuǎn)換,并計算最終的值返回(valueOf 也可以,優(yōu)先級比toString高)
     _adder.toString = function() {
        return _args.reduce(function(a,b){
            return a+b; 
        })
     }
     
     _adder.valueOf = function() {
        return _args.reduce(function(a,b){
            return a+b; 
        })
     }
          
     return _adder
  }

4.如何自己實現(xiàn)雙向綁定

使用 Object.defineProperty

  html 部分
  <html>
     <head></head>
     <body>
        <input type='text' id='test-input' />
        <span id='test-label'></span>
        <script>
          (function(){
             const obj = {}
             const _input = document.getElementById('test-input')
             const _label = document.getElementById('test-label')
             
            Object.defineProperty(obj,'val', {
                 get: function(){
                    return obj
                 },
                 set: function(newValue){
                     _input.value = newValue;
                     _label.innerHTML = newValue
                 }
            })
            
            // 監(jiān)聽input的改值事件
            _input.addEventListener('keyup', function(e){
                obj.val = e.target.value 
            })
            
          })()
        </script>
     </body>
  </html>

5. react 中的key的作用,和為什么最好不要使用索引index作為key

和react的diff算法有關(guān)

  • 當使用React,在某一個時間點,可以認為render()函數(shù)是在創(chuàng)建React元素樹
  • 在下一個狀態(tài)或?qū)傩愿聲r,render()函數(shù)將返回一個不同的React元素樹。
  • React需要算出如何高效更新UI以匹配最新的樹
  • react 基于兩點假設(shè),實現(xiàn)了一個啟發(fā)的O(n)算法
    1. 兩個不同類型的元素將產(chǎn)生不同的樹
    1. 開發(fā)者可以使用Key屬性來提示那些子元素貫穿不同渲染是穩(wěn)定的

所以使用key屬性是為了區(qū)分相同類型的元素,避免重新渲染所有元素樹來使用的

  • 當子節(jié)點有key時,react使用Key來匹配原始樹的子節(jié)點和隨后樹的子節(jié)點
  • 這個key在兄弟元素中必須是惟一的
  • 如果使用索引來作為Key, 如果元素順序不變,效果不錯,但是重新排序就會很慢

6.實現(xiàn)一個閉包,每調(diào)用一次就自增加1

  const addSelf = (function(){
     var num = 0;
     return function(){
        return num++
     }
  })()

6.盒子模型描述

標準盒子模型: 盒子的總寬度/總高度 = margin+padding+border+width/height

IE盒子模型: 盒子的總寬度/總高度 = marging+width/height

7. ajax 的事件是

ajaxStart()——————用于為ajax請求的ajaxStart事件綁定處理函數(shù)

ajaxStop()——————用于為ajax請求ajaxStop事件綁定處理函數(shù)

ajaxSend()——————用于設(shè)置當Ajax請求即將被發(fā)送時執(zhí)行的回調(diào)函數(shù)

ajaxComplete()——————用于設(shè)置當Ajax請求完成(無論成功或失敗)時執(zhí)行的回調(diào)函數(shù)

ajaxSuccess()——————用于設(shè)置當Ajax請求成功完成時執(zhí)行的回調(diào)函數(shù)

ajaxError()———————用于設(shè)置當Ajax請求失敗時執(zhí)行的回調(diào)函數(shù)

8. JavaScript 異步編程,如何解決回調(diào)地獄

  1. 拆解function
  2. 事件發(fā)布/監(jiān)聽模式
  3. Promise
  4. generator 函數(shù)
  5. async/await
  • 拆解function // 缺點:缺少通用性
  // 將各步拆解為單個的function
  function getData(count){
    get(`/sampleget?count=${count}`, data=>{
      console.log(data)
    })
  }
  
  function queryDB(kw){
    db.find(`select * from sample where kw = ${kw}`, (err, res)=>{
        getData(res.length)
    })
  }
  
  function readFile(filepath){
    fs.readFile(filepath, 'utf-8',(err, content)=>{
       let keyword = content.substring(0, 5);
       queryDB(keyword)
    })
  }

  readFile('./sample.txt')
  • 事件發(fā)布/監(jiān)聽模式 // event.emitter 一方面監(jiān)聽事件,事件發(fā)生時,進行相應回調(diào)操作; 另一方面,當某些操作完成后,通過發(fā)布事件觸發(fā)回調(diào)
  const events = require('events')
  const eventEmitter = new events.EventEmitter();
  
  // 訂閱
  eventEmitter.on('db',(err, kw)=>{
     db.find(`select * from sample where kw = ${kw}`, (err, res)=>{
        // 發(fā)布
        eventEmitter.emit('get', res.length);
     })
  })
 
 // 訂閱 
  eventEmitter.on('get',(err,count)=>{
     get(`/sampleget?count=${count}`, data=>{
        console.log(data);
     })
  })
  
  fs.readFile('./sample.txt', 'utf-8', (err, content)=>{
    let keyword = content.substring(0, 5);
    // 發(fā)布
    eventEmitter.emit('db', keyword)
  })

  • Promise 鏈式調(diào)用
  // 借助原生promise來改造需要用到的方法, 以 fs.readFile為例
  // 對其他一些異步方法也可以參照這種方法進行改造
  const readFile = function(filepath){
     let resolve;
     let reject;
     let promise = new Promise((_resolve, _reject)=>{
        resolve = _resolve;
        reject = _reject;
     })
     let deferred = {
        resolve,
        reject,
        promise
     };
     fs.readFile(filepath, 'utf-8', function(err, ...args){
       if(err){
          deferred.reject(err)
       } else {
          deferred.resolve(...args)
       }
     })
     return deferred.promise
  }

使用中就變成了

  readFile('./sample.txt').then(content=>{
      let keyword = content.substring(0, 5);
      return queryDB(keyword)
  }).then(res => {
      return getData(res.length)
  }).then(data => {
     console.log(data)
  }).catch(err => {
     console.log(err);
  })

  • generator 函數(shù) // 在function關(guān)鍵字后面添加*即可 通過 yield暫停; 通過next方法來恢復yield 后面的代碼執(zhí)行
  • async/await 函數(shù);相當于generator函數(shù)的語法糖,在async函數(shù)中可以使用await語句,await后一般是一個Promise對象

8. 隱式轉(zhuǎn)換類問題 下面代碼輸出什么

console.log(1+"2"+"2") // 122
console.log(1+ +"2"+"2") // 32 這種隱式轉(zhuǎn)換只有+和-才能轉(zhuǎn),* 和 / 皆要報錯
console.log('A'-'B'+ 2) // NaN
console.log('A'-'B'+'2') // NaN2 字母和數(shù)字字符串的轉(zhuǎn)換也只能用 +

9.以下js表達式返回false的是; 這個只有輸出看,考隱式轉(zhuǎn)換

1 == true;
''== false;
false == null; // false undefined == false; false == undefined 也是false
null==undefined;

9.關(guān)于屏幕尺寸標準

col-xs-* < 768 手機
col-sm-* >= 768 平板
col-md-* >= 992 桌面
col-lg-* >= 1200 大屏幕桌面

10. var a = b = 3 ===> var a = 3; b = 3; 變量聲明提升是將var a;提升到最上面,賦值不會

11 什么是事件委托,事件委托的好處是什么?

事件委托就是利用事件冒泡機器指定一個事件處理程序,來管理某一類型的所有事件。
即: 利用冒泡的原理,給父元素綁定事件,用來監(jiān)聽子元素的冒泡事件,并找到是哪個子元素的事件
好處:

只在內(nèi)存中開辟了一塊空間,避免對每個子元素添加事件監(jiān)聽器,節(jié)省資源同時減少了dom操作,提高性能

對于新添加的元素也會有之前的事件

12 用setTimeout來模擬setInterval();有什么區(qū)別

 function simulateInterval(){
   setTimeout(function(){
      simulateInterval();
   }, 1000)
 }
 
 setTimeout(simulateInterval, 1000)
 

區(qū)別是setTimeout是調(diào)用自身函數(shù)

13 垂直居中的方式

常用的
flex布局

  父元素{
     display: flex;
     justify-content: center;
     align-items: center;
  } 

萬能居中

  父元素{
    position: relative;
  }
  
  子元素{
     position: absoluted;
     top: 50%;
     left:50%;
     transform: translate(-50%,-50%)
  }
 

確定子元素高度的居中

  父元素{
    position: 除了static之外的定位
  }
  
  子元素{
    position: absoluted;
    top:0;
    left:0;
    right:0;
    bottom:0;
    margin:auto;
    overflow:auto;
    width: XX px;
    height: XX px;
  }

14 js中的new()到底做了什么?

  1. 創(chuàng)建了一個新的對象實例
  2. 將構(gòu)造函數(shù)的作用域賦給了新對象實例(使this指向了這個新的對象實例)
  3. 調(diào)用構(gòu)造函數(shù)(為新對象添加屬性和方法)
  4. 返回新對象

15 js垃圾回收機制,它的存在有什么必要性(刪除使用過不再使用的變量,釋放內(nèi)存),具體的垃圾回收方法有哪幾種?各自的特點是什么?

垃圾收集器會定期(周期性)找出那些不在繼續(xù)使用的變量,然后釋放其內(nèi)存

垃圾回收的方法有兩種

1.標記清除法: 在函數(shù)聲明一個變量的時候,就將這個變量標記為“進入環(huán)境”。而當變量離開環(huán)境時,則將其標記為“離開環(huán)境”。 垃圾回收器在運行時會給存儲在內(nèi)存中的所有變量都加上標記, 然后會去掉環(huán)境中的變量以及被環(huán)境中變量引用的的變量的標記。 在此之后再被標記的變量將被視為準備刪除的變量,因為環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后,垃圾回收器完成內(nèi)存清除工作,銷毀那些帶標記的值并回收他們所占用的內(nèi)存空間

  function test(){
    var a = 10; // 被標記,進入環(huán)境
    var b = 20; // 被標記, 進入環(huán)境
 }

  test();  // 執(zhí)行完畢之后, a、b又被標記離開環(huán)境,被回收 

2.引用計數(shù)法
引用計數(shù)的含義是跟蹤記錄每個值被引用的次數(shù)。當生命了一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數(shù)就是1.如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1.相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)減1.當這個值的引用次數(shù)變成0時,則說明沒有辦法再范文這個值了,因而可以將其占用的內(nèi)存空間回收回來。這樣,當垃圾回收器下次再運行時,它就會釋放那些引用次數(shù)為0的值所占用的內(nèi)存。

當遇到循環(huán)引用的時候,函數(shù)的引用次數(shù)就不會為0,所以不會被垃圾回收器回收內(nèi)存,會造成內(nèi)存泄露

 function test(){
   var a = {}; // a的引用次數(shù)為0
   var b = a;  // a的引用次數(shù)加1, 為1
   var c = a; // a 的引用次數(shù)再加1, 為2
   var b = {}  // a的引用次數(shù)減1, 為1 
}

16 TCP與UDP區(qū)別總結(jié)

  1. TCP面向連接(如電話要先撥號建立連接); UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接
  2. TCP提供可靠的服務。也就是說,通過TCP連接傳送的數(shù)據(jù),無差錯,不丟失,不重復,且按需到達;UDP盡最大努力交付,即不保證可靠交付
  3. UDP具有較好的實時性,工作效率比TCP高,適用于對高速傳輸和實時性較高的通信或廣播通信。
  4. 每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
  5. TCP對系統(tǒng)資源要求較多,UDP對系統(tǒng)資源要求較少
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容