主頁 > 企業開發 > 記一次大廠的面試程序

記一次大廠的面試程序

2020-10-17 23:31:34 企業開發

前言

2019年6月中旬,實在厭倦了之前平平淡淡的作業和毫不起眼的薪資,不顧親人的反對,毅然決然地決定只身前往沿海城市,想著找到一份更加具有挑戰性的作業,來徹徹底底地重新打磨自己,同時去追求更好的薪資待遇,當然在此之前,自己每天下班后都會利用業余時間抓緊復習鞏固刷題等等,大概從3月份開始的吧,持續了3個多月,而后從6月中旬面試一直到6月底,中間大概兩個星期,其實我的學歷和背景并不突出,但是我個人感覺可能是因為自己簡歷做的稍微還行(后面我可能會單獨出一篇文章,來聊聊我做簡歷時的一點點心得),讓大廠的HR能夠多看幾眼,中間面過的公司包括喜馬拉雅、攜程、嗶哩嗶哩、流利說、蜻蜓FM、愛回收等,陸陸續續拿到4,5個Offer吧,如今已經轉正,所以在這里記錄下之前的部分面試題,和大家一起分享交流,

正文

1. 烈熊網路

這家公司其實我也沒有太了解過,是我前同事推薦的,說里面的薪資待遇不錯,然后我當時也有空閑時間,所以就去試試了,雖然公司名氣沒有上面提到的公司大,但是他的面試題我覺得還是挺有分量的,

1.1 請說出下面代碼的執行順序

async function async1() {
  console.log(1);
  const result = await async2();
  console.log(3);
}

async function async2() {
  console.log(2);
}

Promise.resolve().then(() => {
  console.log(4);
});

setTimeout(() => {
  console.log(5);
});

async1();
console.log(6);

我的回答是[1,2,6,4,3,5],這道題目主要考對JS宏任務微任務的理解程度,JS的事件回圈中每個宏任務稱為一個Tick(標記),在每個標記的末尾會追加一個微任務佇列,一個宏任務執行完后會執行所有的微任務,直到佇列清空,上題中我覺得稍微復雜點的在于async1函式,async1函式本身會回傳一個Promise,同時await后面緊跟著async2函式回傳的Promise,console.log(3)其實是在async2函式回傳的Promise的then陳述句中執行的,then陳述句本身也會回傳一個Promise然后追加到微任務佇列中,所以在微任務佇列中console.log(3)console.log(4)后面,不太清楚的同學可以網上查下資料或者關注我的公眾號「前端之境」,我們可以一起交流學習,

1.2 手動實作Promise,寫出偽代碼

幸運的是在面試前剛好查閱了下這部分的資料,所以回答程序中還算得心應手,主要是需要遵循Promise/A+規范:
(1) 一個promise必須具備三種狀態(pending|fulfilled(resolved)|rejected),當處于pending狀態時,可以轉移到fulfilled(resolved)狀態或rejected狀態,處于fulfilled(resolved)狀態或rejected狀態時,狀態不再可變;
(2) 一個promise必須有then方法,then方法必須接受兩個引數:

// onFulfilled在狀態由pending -> fulfilled(resolved) 時執行,引數為resolve()中傳遞的值
// onRejected在狀態由pending -> rejected 時執行,引數為reject()中傳遞的值
promise.then(onFulfilled,onRejected)

(3) then方法必須回傳一個promise:

promise2 = promise1.then(onFulfilled, onRejected);

實作代碼直接貼出來吧:

參考自:實作一個完美符合Promise/A+規范的Promise

function myPromise(constructor){
    let self=this;
    self.status="pending" //定義狀態改變前的初始狀態
    self.value=https://www.cnblogs.com/tangshiwei/p/undefined;//定義狀態為resolved的時候的狀態
    self.reason=undefined;//定義狀態為rejected的時候的狀態
    self.onFullfilledArray=[];
    self.onRejectedArray=[];
    function resolve(value){
       if(self.status==="pending"){
          self.value=https://www.cnblogs.com/tangshiwei/p/value;
          self.status="resolved";
          self.onFullfilledArray.forEach(function(f){
                f(self.value);
                //如果狀態從pending變為resolved,
                //那么就遍歷執行里面的異步方法
          });
        
       }
    }
    function reject(reason){
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
          self.onRejectedArray.forEach(function(f){
              f(self.reason);
             //如果狀態從pending變為rejected, 
             //那么就遍歷執行里面的異步方法
          })
       }
    }
    //捕獲構造例外
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}

myPromise.prototype.then=function(onFullfilled,onRejected){
    let self=this;
    let promise2;
    switch(self.status){
      case "pending":
        promise2 = new myPromise(function(resolve,reject){
             self.onFullfilledArray.push(function(){
                setTimeout(function(){
                  try{
	                 let temple=onFullfilled(self.value);
	                 resolvePromise(temple)
	                }catch(e){
	                   reject(e) //error catch
	                }
                })
             });
             self.onRejectedArray.push(function(){
                setTimeout(function(){
                   try{
	                   let temple=onRejected(self.reason);
	                   resolvePromise(temple)
	                 }catch(e){
	                   reject(e)// error catch
	               }
                })
             });
        })
      case "resolved":
        promise2=new myPromise(function(resolve,reject){
           setTimeout(function(){
               try{
	              let temple=onFullfilled(self.value);
	              //將上次一then里面的方法傳遞進下一個Promise狀態
	              resolvePromise(temple);
	            }catch(e){
                  reject(e);//error catch
               }
           })
        })
        break;
      case "rejected":
        promise2=new myPromise(function(resolve,reject){
           setTimeout(function(){
             try{
               let temple=onRejected(self.reason);
               //將then里面的方法傳遞到下一個Promise的狀態里
               resolvePromise(temple);   
             }catch(e){
               reject(e);
             }
           })
        })
        break;
      default:       
   }
   return promise2;
}

function resolvePromise(promise,x,resolve,reject){
  if(promise===x){
     throw new TypeError("type error")
  }
  let isUsed;
  if(x!==null&&(typeof x==="object"||typeof x==="function")){
      try{
        let then=x.then;
        if(typeof then==="function"){
           //是一個promise的情況
           then.call(x,function(y){
              if(isUsed)return;
              isUsed=true;
              resolvePromise(promise,y,resolve,reject);
           },function(e){
              if(isUsed)return;
              isUsed=true;
              reject(e);
           })
        }else{
           //僅僅是一個函式或者是物件
           resolve(x)
        }
      }catch(e){
         if(isUsed)return;
         isUsed=true;
         reject(e);
      }
  }else{
    //回傳的基本型別,直接resolve
    resolve(x)
  }
}

1.3 請說出以下列印結果

let a = {a: 10};
let b = {b: 10};
let obj = {
  a: 10
};
obj[b] = 20;
console.log(obj[a]);

我的回答是:20,這道題目主要考對JS資料型別的熟練度以及對ES6中屬性名運算式的理解,在上題中obj[b] = 20的賦值操作后,obj其實已經變成了{a: 10, [object Object]: 20},這是因為如果屬性名運算式是一個物件的話,那么默認情況下會自動將物件轉為字串[object Object],最后一步獲取obj[a]時,a本身也是一個物件,所以會被轉換為獲取obj['[object Object]']也就是上一步賦值的20,

1.4 說出幾種陣列去重的方式

這個其實網上已經有大把大把的實作方案了,我也就大概給出了以下幾種:

let originalArray = [1,2,3,4,5,3,2,4,1];

// 方式1
const result = Array.from(new Set(originalArray));
console.log(result); // -> [1, 2, 3, 4, 5]

// 方式2
const result = [];
const map = new Map();
for (let v of originalArray) {
    if (!map.has(v)) {
        map.set(v, true);
        result.push(v);
    }
}
console.log(result); // -> [1, 2, 3, 4, 5]

// 方式3
const result = [];
for (let v of originalArray) {
    if (!result.includes(v)) {
        result.push(v);
    }
}
console.log(result); // -> [1, 2, 3, 4, 5]

// 方式4
for (let i = 0; i < originalArray.length; i++) {
    for (let j = i + 1; j < originalArray.length; j++) {
        if (originalArray[i] === originalArray[j]) {
            originalArray.splice(j, 1);
            j--;
        }
    }
}
console.log(originalArray); // -> [1, 2, 3, 4, 5]

// 方式5
const obj = {};
const result = originalArray.filter(item => obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true));
console.log(result); // -> [1, 2, 3, 4, 5]

1.5 物件陣列如何去重?

這個題目不只一家公司問到了,開始的時候一臉懵逼,心里想著每個物件的記憶體地址本身就不一樣,去重的意義何在,非要去重的話,那只能通過JSON.stringify序列化成字串(這個方法有一定的缺陷)后進行對比,或者遞回的方式進行鍵-值對比,但是對于大型嵌套物件來說還是比較耗時的,所以還是沒有答好,后來面試官跟我說是根據每個物件的某一個具體屬性來進行去重,因為考慮到服務端回傳的資料中可能存在id重復的情況,需要前端進行過濾,如下:

const responseList = [
  { id: 1, a: 1 },
  { id: 2, a: 2 },
  { id: 3, a: 3 },
  { id: 1, a: 4 },
];
const result = responseList.reduce((acc, cur) => {
    const ids = acc.map(item => item.id);
    return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]

2. 攜程

當時是前一天進行了一次電面,然后第二天現場面,兩個面試官輪流問,大概持續了一個半小時吧,問的問題還是比較多的,有些問題時間久了還是不太記得了,多多見諒!

2.1 理解深拷貝和淺拷貝嗎?

淺拷貝是指創建一個物件,這個物件有著原始物件屬性值的一份精確拷貝,如果屬性是基本型別,那么拷貝的就是基本型別的值,如果屬性是參考型別,那么拷貝的就是記憶體地址,所以如果其中一個物件修改了某些屬性,那么另一個物件就會受到影響,
深拷貝是指從記憶體中完整地拷貝一個物件出來,并在堆記憶體中為其分配一個新的記憶體區域來存放,并且修改該物件的屬性不會影響到原來的物件,

2.2 深拷貝和淺拷貝的實作方式分別有哪些?

淺拷貝:(1) Object.assign的方式 (2) 通過物件擴展運算子 (3) 通過陣列的slice方法 (4) 通過陣列的concat方法,
深拷貝:(1) 通過JSON.stringify來序列化物件 (2) 手動實作遞回的方式,

2.3 大概說下實作無縫輪播的思路?

先簡單說了下實作輪播的思路,多張圖片從左至右依次排列,點擊左右側按鈕切換圖片的時候,讓圖片的父級容器的left偏移值增加或減少單張圖片的寬度大小,同時配合CSS3 transition過渡或者手寫一個影片函式,這樣可以實作一個比較平滑的影片效果,對于無縫輪播,我當時的思路是再拷貝一個圖片的父級容器出來,例如原來一個<ul><li></li><li></li></ul>對應兩張圖片,現在變為兩個ul對應4張圖片,同時ul的父容器監聽自身的scrollLeft,如果值已經大于等于一個ul的寬度,則立即將自身的scrollLeft值重置為0,這樣就又可以從起點開始輪播,實作無縫的效果,

2.3 說出以下代碼的執行結果

  var a = 10;
  var obj = {
      a: 20,
      say: function () {
          console.log(this.a);
      }
  };
  obj.say();

這個是被我簡化后的版本,具體題目記不太清了,反正就是考的this的指向問題,上題中答案為20,然后面試官繼續追問,如何才能列印出10,給出如下方式:

  // 方式1
  var a = 10;
  var obj = {
      a: 20,
      say: () => {  // 此處改為箭頭函式
          console.log(this.a);
      }
  };
  obj.say(); // -> 10
  
  // 方式2
  var a = 10;
  var obj = {
      a: 20,
      say: function () {
          console.log(this.a);
      }
  };
  obj.say.call(this); // 此處顯示系結this為全域window物件
  
  // 方式3
  var a = 10;
  var obj = {
      a: 20,
      say: function () {
          console.log(this.a);
      }
  };
  
  var say = obj.say; // 此處先創建一個臨時變數存放函式定義,然后單獨呼叫
  say();

2.4 Vue的生命周期有哪些?

創建:beforeCreate,created;
載入:beforeMount,mounted;
更新:beforeUpdate,updated;
銷毀:beforeDestroy,destroyed;

2.5 移動端如何設計一個比較友好的Header組件?

當時的思路是頭部(Header)一般分為左、中、右三個部分,分為三個區域來設計,中間為主標題,每個頁面的標題肯定不同,所以可以通過vue props的方式做成可配置對外進行暴露,左側大部分頁面可能都是回退按鈕,但是樣式和內容不盡相同,右側一般都是具有功能性的操作按鈕,所以左右兩側可以通過vue slot插槽的方式對外暴露以實作多樣化,同時也可以提供default slot默認插槽來統一頁面風格,

2.6 說出space-between和space-around的區別?

這個是flex布局的內容,其實就是一個邊距的區別,按水平布局來說,space-between在左右兩側沒有邊距,而space-around在左右兩側會留下邊距,垂直布局同理,如下圖所示:

2.7 你所知道的前端性能優化方案

這個其實方案還是比較多的,可以從DOM層面CSS樣式層面JS邏輯層面分別入手,大概給出以下幾種:
(1) 減少DOM的訪問次數,可以將DOM快取到變數中;
(2) 減少重繪回流,任何會導致重繪回流的操作都應減少執行,可將多次操作合并為一次
(3) 盡量采用事件委托的方式進行事件系結,避免大量系結導致記憶體占用過多;
(4) css層級盡量扁平化,避免過多的層級嵌套,盡量使用特定的選擇器來區分;
(5) 影片盡量使用CSS3影片屬性來實作,開啟GPU硬體加速;
(6) 圖片在加載前提前指定寬高或者脫離檔案流,可避免加載后的重新計算導致的頁面回流;
(7) css檔案在<head>標簽中引入,js檔案在<body>標簽中引入,優化關鍵渲染路徑
(8) 加速或者減少HTTP請求,使用CDN加載靜態資源,合理使用瀏覽器強快取協商快取,小圖片可以使用Base64來代替,合理使用瀏覽器的預取指令prefetch預加載指令preload
(9) 壓碩訓淆代碼洗掉無用代碼代碼拆分來減少檔案體積;
(10) 小圖片使用雪碧圖,圖片選擇合適的質量尺寸格式,避免流量浪費,

2.8 git多人協作時如何解決沖突

沖突主要是出現在多人在修改同一個檔案的同一部分內容時,對方當你之前push,然后你后push的時候git檢測到兩次提交內容不匹配,提示你Conflict,然后你pull下來的代碼會在沖突的地方使用=====隔開,此時你需要找到對應的開發人員商量代碼的取舍,切不可隨意修改并強制提交,解決沖突后再次push即可,

3. 喜馬拉雅

當時是兩輪技術面,一次電面,一次現場面,電面有部分題目還是答得很模糊,現場面自我感徑訓可以吧,

3.1 手動實作一個bind方法

代碼如下:

Function.prototype.bind = function(context, ...args1) {
    if (typeof this !== 'function') {
        throw new Error('not a function');
    }
    
    let fn = this;
    let resFn = function(...args2) {
        return fn.apply(this instanceof resFn ? this : context, args1.concat(args2));
    };
    const DumpFunction = function DumpFunction() {};
    DumpFunction.prototype = this.prototype;
    resFn.prototype = new DumpFunction();
    
    return resFn;
}

3.2 說說對React Hooks的理解

在React中我們一般有兩種方式來創建組件,類定義或者函式定義;在類定義中我們可以使用許多React的特性,比如state或者各種生命周期鉤子,但是在函式定義中卻無法使用,所以在React 16.8版本中新推出了React Hooks的功能,通過React Hooks我們就可以在函式定義中來使用類定義當中才能使用的特性,當然React Hooks的出現本身也是為了組件復用,以及相比于類定義當中的生命周期鉤子,React Hooks中提供的useEffect將多個生命周期鉤子進行結合,使得原先在類定義中分散的邏輯變得更加集中,方便維護和管理,

3.3 React Hooks當中的useEffect是如何區分生命周期鉤子的

useEffect可以看成是componentDidMountcomponentDidUpdatecomponentWillUnmount三者的結合,useEffect(callback, [source])接收兩個引數,呼叫方式如下:

 useEffect(() => {
   console.log('mounted');
   
   return () => {
       console.log('willUnmount');
   }
 }, [source]);

生命周期函式的呼叫主要是通過第二個引數[source]來進行控制,有如下幾種情況:
(1) [source]引數不傳時,則每次都會優先呼叫上次保存的函式中回傳的那個函式,然后再呼叫外部那個函式;
(2) [source]引數傳[]時,則外部的函式只會在初始化時呼叫一次,回傳的那個函式也只會最終在組件卸載時呼叫一次;
(3) [source]引數有值時,則只會監聽到陣列中的值發生變化后才優先呼叫回傳的那個函式,再呼叫外部的函式,

3.4 什么是高階組件(HOC)

高階組件(Higher Order Componennt)本身其實不是組件,而是一個函式,這個函式接收一個元組件作為引數,然后回傳一個新的增強組件,高階組件的出現本身也是為了邏輯復用,舉個例子:

  function withLoginAuth(WrappedComponent) {
      return class extends React.Component {
          
          constructor(props) {
              super(props);
              this.state = {
                isLogin: false
              };
          }
          
          async componentDidMount() {
              const isLogin = await getLoginStatus();
              this.setState({ isLogin });
          }
          
          render() {
            if (this.state.isLogin) {
                return <WrappedComponent {...this.props} />;
            }
            
            return (<div>您還未登錄...</div>);
          }
      }
  }

3.5 說出以下代碼的執行結果

parseInt('2017-07-01') // -> 2017
parseInt('2017abcdef') // -> 2017
parseInt('abcdef2017') // -> NaN

3.6 React實作的移動應用中,如果出現卡頓,有哪些可以考慮的優化方案

(1) 增加shouldComponentUpdate鉤子對新舊props進行比較,如果值相同則阻止更新,避免不必要的渲染,或者使用PureReactComponent替代Component,其內部已經封裝了shouldComponentUpdate的淺比較邏輯;
(2) 對于串列或其他結構相同的節點,為其中的每一項增加唯一key屬性,以方便React的diff演算法中對該節點的復用,減少節點的創建和洗掉操作;
(3) render函式中減少類似onClick={() => {doSomething()}}的寫法,每次呼叫render函式時均會創建一個新的函式,即使內容沒有發生任何變化,也會導致節點沒必要的重渲染,建議將函式保存在組件的成員物件中,這樣只會創建一次;
(4) 組件的props如果需要經過一系列運算后才能拿到最終結果,則可以考慮使用reselect庫對結果進行快取,如果props值未發生變化,則結果直接從快取中拿,避免高昂的運算代價;
(5) webpack-bundle-analyzer分析當前頁面的依賴包,是否存在不合理性,如果存在,找到優化點并進行優化,

3.7 (演算法題) 如何從10000個數中找到最大的10個數

這題沒答好,兩個字形容:稀爛!一碰到演算法題就容易緊張蒙圈,來個正解吧,

創建一個最小堆結構,初始值為10000個數的前10個,堆頂為10個數里的最小數,然后遍歷剩下的9990個數,如果數字小于堆頂的數,則直接丟棄,否則把堆頂的數洗掉,將遍歷的數插入堆中,堆結構進行自動調整,所以可以保證堆頂的數一定是10個數里最小的,遍歷完畢后,堆里的10個數就是這10000個數里面最大的10個,

4. 流利說

當時是提前有一次電面,然后過了幾天才去現場面,現場兩輪技術面,公司很注重底層原理,所以答得不是很好,

4.1 React實作一個防抖的模糊查詢輸入框

代碼如下:

  // 防抖函式
  function debounce(fn, wait, immediate) {
    let timer = null;
    
    return function (...args) {
        let context = this;
        
        if (immediate && !timer) {
            fn.apply(context, args);
        }
        
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(context, args);
        }, wait);
    }
  }
  
  class SearchInput extends React.Component {
  
      constructor(props) {
          super(props);
          this.state = {
              value: ''
          };
          this.handleChange = this.handleChange.bind(this);
          this.callAjax = debounce(this.callAjax, 500, true);
      }
      
      handleChange(e) {
          this.setState({
              value: e.target.value
          });
          this.callAjax();
      }
      
      callAjax() {
          // 此處根據輸入值呼叫服務端介面
          console.log(this.state.value);
      }
      
      render() {
          return (<input type="text" value=https://www.cnblogs.com/tangshiwei/p/{this.state.value} onChange={this.handleChange} />);
      }
      
  }

4.2 手動封裝一個請求函式,可以設定最大請求次數,請求成功則不再請求,請求失敗則繼續請求直到超過最大次數

代碼如下:

  function request(url, body, successCallback, errorCallback, maxCount = 3) {
      return fetch(url, body)
               .then(response => successCallback(response)
               .catch(err => {
                   if (maxCount <= 0) return errorCallback('請求超時');
                   return request(url, body, successCallback, errorCallback, --maxCount);
               });
  }
  
  // 呼叫
  request('https://some/path', { method: 'GET', headers: {} }, (response) => {
      console.log(response.json());
  }, (err) => console.error(err));

4.3 JS中=的區別

==表示抽象相等,兩邊值型別不同的時候,會先做隱式型別轉換,再對值進行比較;
===表示嚴格相等,不會做型別轉換,兩邊的型別不同一定不相等,

4.4 GET和POST的區別

(1) GET請求在瀏覽器回退和重繪時是無害的,而POST請求會告知用戶資料會被重新提交;
(2) GET請求可以收藏為書簽,POST請求不可以收藏為書簽;
(3) GET請求可以被快取,POST請求不可以被快取,除非在回應頭中包含合適的Cache-Control/Expires欄位,但是不建議快取POST請求,其不滿足冪等性,每次呼叫都會對服務器資源造成影響;
(4) GET請求一般不具有請求體,因此只能進行url編碼,而POST請求支持多種編碼方式,
(5) GET請求的引數可以被保留在瀏覽器的歷史中,POST請求不會被保留;
(6) GET請求因為是向URL添加資料,不同的瀏覽器廠商,代理服務器,web服務器都可能會有自己的長度限制,而POST請求無長度限制;
(7) GET請求只允許ASCII字符,POST請求無限制,支持二進制資料;
(8) GET請求的安全性較差,資料被暴露在瀏覽器的URL中,所以不能用來傳遞敏感資訊,POST請求的安全性較好,資料不會暴露在URL中;
(9) GET請求具有冪等性(多次請求不會對資源造成影響),POST請求不冪等;
(10) GET請求一般不具有請求體,請求中一般不包含100-continue 協議,所以只會發一次請求,而POST請求在發送資料到服務端之前允許雙方"握手",客戶端先發送Expect:100-continue訊息,詢問服務端是否愿意接收資料,接收到服務端正確的100-continue應答后才會將請求體發送給服務端,服務端再回應200回傳資料,

4.5 說下瀏覽器的快取機制

瀏覽器的快取機制可分為強快取協商快取,服務端可以在回應頭中增加Cache-Control/Expires來為當前資源設定快取有效期(Cache-Control的max-age的優先級高于Expires),瀏覽器再次發送請求時,會先判斷快取是否過期,如果未過期則命中強快取,直接使用瀏覽器的本地快取資源,如果已過期則使用協商快取,協商快取大致有以下兩種方案:
(1) 唯一標識:Etag(服務端回應攜帶) & If-None-Match(客戶端請求攜帶)
(2) 最后修改時間: Last-Modified(服務端回應攜帶) & If-Modified-Since (客戶端請求攜帶) ,其優先級低于Etag
服務端判斷值是否一致,如果一致,則直接回傳304通知瀏覽器使用本地快取,如果不一致則回傳新的資源,

5. 嗶哩嗶哩

現場兩輪技術面,問了很多考驗基礎知識的題目,整體來說回答的還算比較滿意吧,

5.1 CSS3中transition和animation的屬性分別有哪些

transition 過渡影片:
(1) transition-property:屬性名稱
(2) transition-duration: 間隔時間
(3) transition-timing-function: 影片曲線
(4) transition-delay: 延遲

animation 關鍵幀影片:
(1) animation-name:影片名稱
(2) animation-duration: 間隔時間
(3) animation-timing-function: 影片曲線
(4) animation-delay: 延遲
(5) animation-iteration-count:影片次數
(6) animation-direction: 方向
(7) animation-fill-mode: 禁止模式

5.2 盒模型

指的是頁面在渲染時,DOM元素所采用的布局模型,一個元素占用的空間大小由幾個部分組成,內容(content)、內邊距(padding),邊框(border)和外邊距(margin),可以通過box-sizing來進行設定,其中IE盒模型的content包含了padding和border,這是區別于W3C標準盒模型的地方,

5.3 選擇器優先級

!important > 行內樣式 > id選擇器 > class選擇器 > 標簽選擇器 > * > 繼承 > 默認

5.4 forEach,map和filter的區別

forEach遍歷陣列,引數為一個回呼函式,回呼函式接收三個引數,當前元素,元素索引,整個陣列;
mapforEach類似,遍歷陣列,但其回呼函式的回傳值會組成一個新陣列,新陣列的索引結構和原陣列一致,原陣列不變;
filter會回傳原陣列的一個子集,回呼函式用于邏輯判斷,回傳true則將當前元素添加到回傳陣列中,否則排除當前元素,原陣列不變,

5.5 實作函式柯里化

代碼如下:

const curry = (fn, ...args1) => (...args2) => (
 arg => arg.length === fn.length ? fn(...arg) : curry(fn, ...arg)
)([...args1, ...args2]);

// 呼叫
const foo = (a, b, c) => a * b * c;
curry(foo)(2, 3, 4); // -> 24
curry(foo, 2)(3, 4); // -> 24
curry(foo, 2, 3)(4); // -> 24
curry(foo, 2, 3, 4)(); // -> 24

5.6 跨標簽頁的通訊方式有哪些

(1) BroadCast Channel
(2) Service Worker
(3) LocalStorage + window.onstorage監聽
(4) Shared Worker + 定時器輪詢(setInterval)
(5) IndexedDB + 定時器輪詢(setInterval)
(6) cookie + 定時器輪詢(setInterval)
(7) window.open + window.postMessage
(8) Websocket

5.7 實作一個函式判斷資料型別

代碼如下:

function getType(obj) {
   if (obj === null) return String(obj);
   return typeof obj === 'object' 
   ? Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
   : typeof obj;
}

// 呼叫
getType(null); // -> null
getType(undefined); // -> undefined
getType({}); // -> object
getType([]); // -> array
getType(123); // -> number
getType(true); // -> boolean
getType('123'); // -> string
getType(/123/); // -> regexp
getType(new Date()); // -> date

總結

有些面試題實在是想不起來了,上面的題目其實大部分還是比較基礎的,問到的頻率也比較高,這里只是做一個簡單的分享,希望對大家多多少少有點幫助,也希望能和大家一起交流學習,如果有疑惑歡迎留言討論,

交流

今天先分享到這里,筆者剛新開公眾號,如果大家有興趣和筆者一起學習,相互討論技術,可以關注咱們的公眾號,一起見證公眾號的成長,

文章已同步更新至Github博客,若覺文章尚可,歡迎前往star!

你的一個點贊,值得讓我付出更多的努力!

逆境中成長,只有不斷地學習,才能成為更好的自己,與君共勉!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/177009.html

標籤:JavaScript

上一篇:JavaScript 語法和資料型別

下一篇:Vue學習系列(四)——理解生命周期和鉤子

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more