好程式設計師web前端培訓分享JS面試題總結一

好程式設計師發表於2020-07-23

  好程式設計師 web 前端培訓分享 JS 面試題總結一,準備參加面試的小夥伴們一起看一看吧,希望本篇文章能夠對從事 web 前端工作的小夥伴們有所幫助。

  一、說說你對閉包的認識

  ( ) 什麼是閉包

  一句話解釋:

  能夠讀取其他函式內部變數的函式。

  稍全面的回答:

  在js 中變數的作用域屬於函式作用域 , 在函式執行完後 , 作用域就會被清理 , 記憶體也會隨之被回收 , 但是由於閉包函式是建立在函式內部的子函式 , 由於其可訪問上級作用域 , 即使上級函式執行完 , 作用域也不會隨之銷燬 , 這時的子函式 ( 也就是閉包 ), 便擁有了訪問上級作用域中變數的許可權 , 即使上級函式執行完後作用域內的值也不會被銷燬。

  這裡涉及到對函式作用域的認識: js 變數分為全域性變數和區域性變數 ; 函式內部可以直接讀取全域性變數 , 而在函式外部自然無法讀取函式內的區域性變數

  ( ) 閉包解決了什麼問題

  1. 可以讀取函式內部的變數

  2. 讓這些變數的值始終保持在記憶體中。不會在函式呼叫後被清除

  可以透過下面的程式碼來幫助理解上面所說的:

  function addCounter() {

  let counter = 0

  const myFunction = function () {

  counter = counter + 1

  return counter

  }

  return myFunction

  }

  const increment = addCounter()

  const c1 = increment()

  const c2 = increment()

  const c3 = increment()

  console.log('increment:', c1, c2, c3);

  // increment: 1 2 3

  在這段程式碼中increment 實際上就是閉包函式 myFunction, 它一共執行了三次,第一次的值是 1 ,第二次的值是 2 ,第三次的值是 3 。這證明了,函式 addCounter 中的區域性變數 counter 一直儲存在記憶體中,並沒有在 addCounter 呼叫後被自動清除。

  ( ) 閉包的應用場景

  在開發中, 其實我們隨處可見閉包的身影 , 大部分前端 JavaScript 程式碼都是“事件驅動”的 , 即一個事件繫結的回撥方法 ; 傳送 ajax 請求成功 | 失敗的回撥 ;setTimeout 的延時回撥 ; 或者一個函式內部返回另一個匿名函式 , 這些都是閉包的應用。

  下面是具體應用的栗子:

  1. 老掉牙的取正確值問題

  for (var i = 0; i < 10; i++) {

  setTimeout(function () {

  console.log(i) //10 10

  }, 1000)

  }

  怎麼取到每一次迴圈的正確值呢? 閉包這樣用 :

  for (var i = 0; i < 10; i++) {

  ((j) => {

  setTimeout(function () {

  console.log(j) //1-10

  }, 1000)

  })(i)

  }

  宣告瞭10 個自執行函式,儲存當時的值到內部

  2. 使用閉包模擬私有變數

  私有變數在java 裡使用 private 宣告就可以了 , 但是在 js 中還沒有,但是我們可以使用閉包模擬實現。

  var counter = (function () {

  var privateCounter = 0;

  function changeBy(val) {

  privateCounter += val

  }

  return {

  increment: function () {

  changeBy(1)

  },

  decrement: function () {

  changeBy(-1)

  },

  value: function () {

  return privateCounter

  }

  }

  })();

  counter.value() //0

  counter.increment() //1

  counter.increment() //2

  counter.decrement() //1

  匿名函式已經定義就立即執行, 建立出一個詞法環境包含 counter.increment counter.decrement counter.value 三個方法 , 還包含了兩個私有項 :privateCounter 變數和 changeBy 函式。這兩個私有項無法在匿名函式外部直接訪問,必須透過匿名包裝器返回的物件的三個公共函式訪問。

  ( ) 閉包的缺點

  1. 由於閉包會是的函式中的變數都被儲存到記憶體中 , 濫用閉包很容易造成記憶體消耗過大 , 導致網頁效能問題。解決方法是在退出函式之前,將不再使用的區域性變數全部刪除。

  2. 閉包可以使得函式內部的值可以在函式外部進行修改。所有,如果你把父函式當作物件 (object) 使用,把閉包當作它的公用方法 (Public Method) ,把內部變數當作它的私有屬性 (private value) ,這時一定要小心,不要隨便改變父函式內部變數的值。

  二、跨域問題有哪些處理方式

  跨域解決方案

  1. 透過 jsonp 跨域

  2. 跨域資源共享 (CORS)

  3. nodejs 中介軟體代理跨域

  4. nginx 反向代理中設定 proxy_cookie_domain

  Ⅰ . 透過 jsonp 跨域

  通常為了減輕web 伺服器的負載,我們把 js css img 等靜態資源分離到另一臺 獨立域名的伺服器上,在 html 頁面中再透過相應的標籤從不同域名下載入靜態資源,而被瀏覽器允許,基於此原理,我們可以透過動態建立 script ,再請求一個帶參網址實現跨域通訊。

  1. 原生實現

  <script>

  var script = document.createElement('script');

  script.type = 'text/javascript';

  // 傳參一個回撥函式名給後端,方便後端返回時執行這個在前端定義的回撥函式

  script.src = '

  document.head.appendChild(script);

  // 回撥執行函式

  function jsonCallback(res) {

  alert(JSON.stringify(res));

  }

  </script>

  伺服器端返回如下( 返回即執行全域性函式 )

  jsonCallback({"status": 0, "user": "admin"})

  2. jquery 方式實現

  $.ajax({

  url: '

  type: 'get',

  dataType: 'jsonp', // 請求方式為 jsonp

  jsonpCallback: "handleCallback", // 自定義回撥函式名

  data: {}

  });

  Ⅱ . 跨域資源共享 (CORS)

  CORS 是一個 W3C 標準,全稱是 " 跨域資源共享 "(Cross-origin resource sharing) 跨域資源共享 CORS 詳解。看名字就知道這是處理跨域問題的標準做法。 CORS 有兩種請求,簡單請求和非簡單請求。

  · 簡單請求

  只要同時滿足以下兩大條件, 就屬於簡單請求 :

  1. 請求方法是以下三種方法之一 :

  · HEAD

  · GET

  · POST

  2. HTTP 請求頭的資訊不超出以下幾種欄位:

  · Accept

  · Accept-Language

  · Content-Language

  · Last-Event-ID

  · Content-Type :只限於三個值 application/x-www-form-urlencoded multipart/form-data text/plain

  如果是簡單請求, 後端處理即可 , 前端什麼也不用幹 ; 這裡注意的是如果前端要帶 cookie, 前端也需要單獨設定

  · 原生 ajax ( 前端 )

  var xhr = new XMLHttpRequest();

  // 前端設定是否帶 cookie

  xhr.withCredentials = true;

  ...

  · jquery ( 前端 )

  $.ajax({

  ...

  xhrFields: {

  withCredentials: true // 前端設定是否帶 cookie

  },

  crossDomain: true, // 會讓請求頭中包含跨域的額外資訊,但不會含 cookie

  ...

  });

  · vue 中使用 axios ( 前端 )

  axios.defaults.withCredentials = true

  · 後端 node

  可以藉助koa2-cors 快速實現

  const path = require('path')

  const Koa = require('koa')

  const koaStatic = require('koa-static')

  const bodyParser = require('koa-bodyparser')

  const router = require('./router')

  const cors = require('koa2-cors')

  const app = new Koa()

  const port = 9871

  ...

  // 處理 cors

  app.use(cors({

  origin: function (ctx) {

  return '

  },

  credentials: true,

  allowMethods: ['GET', 'POST', 'DELETE'],

  allowHeaders: ['t', 'Content-Type']

  }))

  // 路由

  app.use(router.routes()).use(router.allowedMethods())

  // 監聽埠

  ...

  Ⅲ .nodejs 中介軟體代理跨域

  跨域原理: 同源策略是瀏覽器的安全策略 , 不是 HTTP 協議的一部分。伺服器端呼叫 HTTP 介面只是使用 HTTP 協議, 不會執行 js 指令碼 , 不需要檢驗同源策略 , 也就不存在跨域問題。

  實現思路:透過起一個代理伺服器, 實現資料的轉發,也可以透過設定cookieDomainRewrite 引數修改響應頭 cookie 中域名 , 實現當前域下 cookie 的寫入

  · 在 vue 框架下實現跨域

  利用node + webpack + webpack-dev-server 代理介面跨域。在開發環境下,由於 vue 渲染服務和介面代理服務都是 webpack-dev-server 同一個,所以頁面與代理介面之間不再跨域,無須設定 headers 跨域資訊了。後臺可以不做任何處理。

  webpack.config.js 部分配置

  module.exports = {

  entry: {},

  module: {},

  ...

  devServer: {

  historyApiFallback: true,

  proxy: [{

  context: '/login',

  target: ' // 代理跨域目標介面

  changeOrigin: true,

  secure: false, // 當代理某些 https 服務報錯時用

  cookieDomainRewrite: ' // 可以為 false ,表示不修改

  }],

  noInfo: true

  }

  }

  Ⅳ .nginx 反向代理中設定

  和使用node 中介軟體跨域原理相似。前端和後端都不需要寫額外的程式碼來處理, 只需要配置一下 Ngnix

  server{

  # 監聽 9099

  listen 9099;

  # 域名是 localhost

  server_name localhost;

  # 凡是 localhost:9099/api 這個樣子的,都轉發到真正的服務端地址

  location ^~ /api {

  proxy_pass ;

  }

  }

  對於跨域還有挺多方式可以實現, 這裡就不一一列舉了。

  三、for...in for...of 的區別

  1. for...of ES6 新引入的特性,修復了 ES5 引入的 for...in 的不足

  2. for...in 迴圈出的是 key for...of 迴圈出的是 value

  3. for...of 不能迴圈普通的物件,需要透過和 Object.keys() 搭配使用

  4. 推薦在迴圈物件屬性的時候,使用 for...in, 在遍歷陣列的時候的時候使用 for...of

  四、new 一個物件,這個過程中發生了什麼

  var obj = new Object("name","sansan");

  1. 建立一個新物件,如: var obj = {};

  2. 新物件的 _proto_ 屬性指向建構函式的原型物件。

  3. 將建構函式的作用域賦值給新物件。 ( 也所以 this 物件指向新物件 )

  4. 執行建構函式內部的程式碼,將屬性新增給 obj 中的 this 物件。

  5. 返回新物件 obj

  五、js 的防抖和節流是什麼

  · 防抖 : 在事件被觸發 n 秒後再執行回撥,如果在這 n 秒內又被觸發,則重新計時。

  使用場景:

  1. 給按鈕加函式防抖防止表單多次提交。

  2. 對於輸入框連續輸入進行 AJAX 驗證時,用函式防抖能有效減少請求次數。

  簡單的防抖(debounce) 程式碼 :

  function debounce(fn, wait) {

  var timeout = null;

  return function () {

  if (timeout !== null) clearTimeout(timeout)

  timeout = setTimeout(fn, wait)

  }

  }

  // 處理函式

  function handle() {

  console.log(Math.random())

  }

  // 滾動事件

  window.addEventListener('scroll', debounce(handle, 2000));

  · 節流 : 就是指連續觸發事件但是在 n 秒中只執行一次函式。節流會稀釋函式的執行頻率。

  function throttle(func, delay) {

  var prev = Date.now();

  return function () {

  var context = this;

  var args = arguments;

  var now = Date.now();

  if (now - prev >= delay) {

  func.apply(context, args);

  prev = Date.now();

  }

  }

  }

  function handle() {

  console.log(Math.random());

  }

  window.addEventListener('scroll', throttle(handle, 2000));

  區別:

  函式節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函式,而函式防抖只是在最後一次事件後才觸發一次函式。 比如在頁面的無限載入場景下,我們需要使用者在滾動頁面時,每隔一段時間發一次Ajax 請求,而不是在使用者停下滾動頁面操作時才去請求資料。這樣的場景,就適合用節流技術來實現。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2706481/,如需轉載,請註明出處,否則將追究法律責任。

相關文章