面試題精選及答案

面試題精選及答案

下是十道大廠一面的時候常見的面試題,如果全部理解並且弄透,在一面或者電話面的時候基本上能中1~2題。小夥伴可以先不急著看答案,先自己嘗試著思考一下和自己實現一下,然後再看答案。

第 1 題:http的狀態碼中,499是什麼?如何出現499,如何排查跟解決

499對應的是 “client has closed connection”,客戶端請求等待鏈接已經關閉,這很有可能是因為服務器端處理的時間過長,客戶端等得“不耐煩”了。還有一種原因是兩次提交post過快就會出現499。 解決方法:

  • 前端將timeout最大等待時間設置大一些
  • nginx上配置proxy_ignore_client_abort on;

提問解答與更多解析:github.com/airuikun/We…

第 2 題:如何遍歷一個dom樹

function traversal(node) {
 //對node的處理
 if (node && node.nodeType === 1) {
 console.log(node.tagName);
 }
 var i = 0,
 childNodes = node.childNodes,
 item;
 for (; i < childNodes.length; i++) {
 item = childNodes[i];
 if (item.nodeType === 1) {
 //遞歸先序遍歷子節點
 traversal(item);
 }
 }
}
複製代碼

提問解答與更多解析:github.com/airuikun/We…

第 3 題:new操作符都做了什麼

四大步驟:

1、創建一個空對象,並且 this 變量引用該對象,// lat target = {};

2、繼承了函數的原型。// target.proto = func.prototype;

3、屬性和方法被加入到 this 引用的對象中。並執行了該函數func// func.call(target);

4、新創建的對象由 this 所引用,並且最後隱式的返回 this 。// 如果func.call(target)返回的res是個對象或者function 就返回它

function new(func) {
	lat target = {};
	target.__proto__ = func.prototype;
	let res = func.call(target);
	if (typeof(res) == "object" || typeof(res) == "function") {
		return res;
	}
	return target;
}
複製代碼

提問解答與更多解析:github.com/airuikun/We…

第 4 題:手寫代碼,簡單實現call

Function.prototype.call2 = function(context) {
 var context = context || window; //因為傳進來的context有可能是null
 context.fn = this;
 var args = [];
 for (var i = 1; i < arguments.length; i++) {
 args.push("arguments[" + i + "]"); //不這麼做的話 字符串的引號會被自動去掉 變成了變量 導致報錯
 }
 args = args.join(",");
 var result = eval("context.fn(" + args + ")"); //相當於執行了context.fn(arguments[1], arguments[2]);
 delete context.fn;
 return result; //因為有可能this函數會有返回值return
}
複製代碼

提問解答與更多解析:github.com/airuikun/We…

第 5 題:手寫代碼,簡單實現apply

Function.prototype.apply2 = function(context, arr) {
 var context = context || window; //因為傳進來的context有可能是null
 context.fn = this;
 var args = [];
 var params = arr || [];
 for (var i = 0; i < params.length; i++) {
 args.push("params[" + i + "]"); //不這麼做的話 字符串的引號會被自動去掉 變成了變量 導致報錯
 }
 args = args.join(",");
 var result = eval("context.fn(" + args + ")"); //相當於執行了context.fn(arguments[1], arguments[2]);
 delete context.fn;
 return result; //因為有可能this函數會有返回值return
}
複製代碼
 

提問解答與更多解析:github.com/airuikun/We…

第 6 題:手寫代碼,簡單實現bind

Function.prototype.bind2 = function(context) {
 var _this = this;
 var argsParent = Array.prototype.slice.call(arguments, 1);
 return function() {
 var args = argsParent.concat(Array.prototype.slice.call(arguments)); //轉化成數組
 _this.apply(context, args);
 };
}
複製代碼

提問解答與更多解析:github.com/airuikun/We…

第 7 題:講解一下HTTPS的工作原理

HTTPS在傳輸數據之前需要客戶端(瀏覽器)與服務端(網站)之間進行一次握手,在握手過程中將確立雙方加密傳輸數據的密碼信息。TLS/SSL協議不僅僅是一套加密傳輸的協議,更是一件經過藝術家精心設計的藝術品,TLS/SSL中使用了非對稱加密,對稱加密以及HASH算法。握手過程的簡單描述如下:

  • 瀏覽器將自己支持的一套加密規則發送給網站。
  • 網站從中選出一組加密算法與HASH算法,並將自己的身份信息以證書的形式發回給瀏覽器。證書裡面包含了網站地址,加密公鑰,以及證書的頒發機構等信息。
  • 獲得網站證書之後瀏覽器要做以下工作:
  • a) 驗證證書的合法性(頒發證書的機構是否合法,證書中包含的網站地址是否與正在訪問的地址一致等),如果證書受信任,則瀏覽器欄裡面會顯示一個小鎖頭,否則會給出證書不受信的提示。
  • 如果證書受信任,或者是用戶接受了不受信的證書,瀏覽器會生成一串隨機數的密碼,並用證書中提供的公鑰加密。
  • 使用約定好的HASH計算握手消息,並使用生成的隨機數對消息進行加密,最後將之前生成的所有信息發送給網站。
  • 網站接收瀏覽器發來的數據之後要做以下的操作:
  • a) 使用自己的私鑰將信息解密取出密碼,使用密碼解密瀏覽器發來的握手消息,並驗證HASH是否與瀏覽器發來的一致。
  • b) 使用密碼加密一段握手消息,發送給瀏覽器。
  • 瀏覽器解密並計算握手消息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之後所有的通信數據將由之前瀏覽器生成的隨機密碼並利用對稱加密算法進行加密。

提問解答與更多解析:github.com/airuikun/We…

第 8 題:講解一下https對稱加密和非對稱加密。

對稱加密: 發送方和接收方需要持有同一把密鑰,發送消息和接收消息均使用該密鑰。相對於非對稱加密,對稱加密具有更高的加解密速度,但雙方都需要事先知道密鑰,密鑰在傳輸過程中可能會被竊取,因此安全性沒有非對稱加密高。

非對稱加密: 接收方在發送消息前需要事先生成公鑰和私鑰,然後將公鑰發送給發送方。發送放收到公鑰後,將待發送數據用公鑰加密,發送給接收方。接收到收到數據後,用私鑰解密。 在這個過程中,公鑰負責加密,私鑰負責解密,數據在傳輸過程中即使被截獲,攻擊者由於沒有私鑰,因此也無法破解。 非對稱加密算法的加解密速度低於對稱加密算法,但是安全性更高。

提問解答與更多解析:github.com/airuikun/We…

第 9 題: 簡單實現項目代碼按需加載,例如import { Button } from 'antd',打包的時候只打包button

原理很簡單,就是將

import { Select, Pagination, Button } from 'xxx-ui';
複製代碼

通過babel轉化成

import Button from `xxx-ui/src/components/ui-base/Button/Button`;
import Pagination from `xxx-ui/src/components/ui-base/Pagination/Pagination`;
import Select from `xxx-ui/src/components/ui-base/Select/Select`;
複製代碼

自定義拓展一個babel插件,代碼如下:

visitor: {
	ImportDeclaration (path, { opts }) {
	 const specifiers = path.node.specifiers;
	 const source = path.node.source;
 // 判斷傳入的配置參數是否是數組形式
	 if (Array.isArray(opts)) {
	 opts.forEach(opt => {
	 assert(opt.libraryName, 'libraryName should be provided');
	 });
	 if (!opts.find(opt => opt.libraryName === source.value)) return;
	 } else {
	 assert(opts.libraryName, 'libraryName should be provided');
	 if (opts.libraryName !== source.value) return;
	 }
	 const opt = Array.isArray(opts) ? opts.find(opt => opt.libraryName === source.value) : opts;
	 opt.camel2UnderlineComponentName = typeof opt.camel2UnderlineComponentName === 'undefined'
	 ? false
	 : opt.camel2UnderlineComponentName;
	 opt.camel2DashComponentName = typeof opt.camel2DashComponentName === 'undefined'
	 ? false
	 : opt.camel2DashComponentName;
	 if (!types.isImportDefaultSpecifier(specifiers[0]) && !types.isImportNamespaceSpecifier(specifiers[0])) {
	 // 遍歷specifiers生成轉換後的ImportDeclaration節點數組
 		const declarations = specifiers.map((specifier) => {
	 // 轉換組件名稱
 const transformedSourceName = opt.camel2UnderlineComponentName
 	? camel2Underline(specifier.imported.name)
 	: opt.camel2DashComponentName
 		 ? camel2Dash(specifier.imported.name)
 		 : specifier.imported.name;
 		 // 利用自定義的customSourceFunc生成絕對路徑,然後創建新的ImportDeclaration節點
 return types.ImportDeclaration([types.ImportDefaultSpecifier(specifier.local)],
 	types.StringLiteral(opt.customSourceFunc(transformedSourceName)));
 });
 // 將當前節點替換成新建的ImportDeclaration節點組
 		path.replaceWithMultiple(declarations);
 	}
 }
}
複製代碼

提問解答與更多解析:github.com/airuikun/We…

第 10 題:簡單手寫實現promise

 // 簡易版本的promise 
 // 第一步: 列出三大塊 this.then resolve/reject fn(resolve,reject)
 // 第二步: this.then負責註冊所有的函數 resolve/reject負責執行所有的函數 
 // 第三步: 在resolve/reject裡面要加上setTimeout 防止還沒進行then註冊 就直接執行resolve了
 // 第四步: resolve/reject裡面要返回this 這樣就可以鏈式調用了
 // 第五步: 三個狀態的管理 pending fulfilled rejected
 
 // *****promise的鏈式調用 在then裡面return一個promise 這樣才能then裡面加上異步函數
 // 加上了catch
 function PromiseM(fn) {
 var value = null;
 var callbacks = [];
 //加入狀態 為了解決在Promise異步操作成功之後調用的then註冊的回調不會執行的問題
 var state = 'pending';
 var _this = this;
 //註冊所有的回調函數
 this.then = function (fulfilled, rejected) {
 //如果想鏈式promise 那就要在這邊return一個new Promise
 return new PromiseM(function (resolv, rejec) {
 //異常處理
 try {
 if (state == 'pending') {
 callbacks.push(fulfilled);
 //實現鏈式調用
 return;
 }
 if (state == 'fulfilled') {
 var data = fulfilled(value);
 //為了能讓兩個promise連接起來
 resolv(data);
 return;
 }
 if (state == 'rejected') {
 var data = rejected(value);
 //為了能讓兩個promise連接起來
 resolv(data);
 return;
 }
 } catch (e) {
 _this.catch(e);
 }
 });
 }
 //執行所有的回調函數
 function resolve(valueNew) {
 value = valueNew;
 state = 'fulfilled';
 execute();
 }
 //執行所有的回調函數
 function reject(valueNew) {
 value = valueNew;
 state = 'rejected';
 execute();
 }
 function execute() {
 //加入延時機制 防止promise裡面有同步函數 導致resolve先執行 then還沒註冊上函數
 setTimeout(function () {
 callbacks.forEach(function (cb) {
 value = cb(value);
 });
 }, 0);
 }
 this.catch = function (e) {
 console.log(JSON.stringify(e));
 }
 //經典 實現異步回調
 fn(resolve, reject);
 }

作者:第一名的小蝌蚪

鏈接:https://juejin.im/post/5ca9de22e51d452b5372ed90

來源:掘金

著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。


分享到:


相關文章: