一、面試基礎題
- 作用域和值類型引用類型的傳遞,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
//第1題 作用域
var num1 = 55;
var num2 = 66;//100
function f1(num, num1) {
// var num =55;
// var num1 = 66;
num = 100;//100
num1 = 100;//100
num2 = 100;//100
console.log(num);//100
console.log(num1);//100
console.log(num2);//100
}
// 55 66
f1(num1, num2);
console.log(num1);//55
console.log(num2);//100
console.log(num);// 報錯
//第2題 值類型和引用類型的傳遞
function Person(name, age, salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
function f1(person) {
//var person = p;
person.name = "ls";
person = new Person("aa", 18, 10);
}
var p = new Person("zs", 18, 1000);
console.log(p.name);//zs
f1(p);
console.log(p.name);//ls
</script>
</body>
</html>
- 封裝函數進行字符串駝峯命名的轉換,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
//已知有字符串foo='get-element-by-id',寫一個function將其轉化成駝峯表示法”getElementById”
//自定義函數
function toString(foo) {
// var foo = 'get-element-by-id';
//根據某個字符進行切割
var arr = foo.split('-');
//獲取每個元素中的第一個字符並轉換成大寫
console.log(arr[1].charAt(0).toUpperCase() + arr[1].substr(1, arr[1].length - 1));
for(var i = 1; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1)
}
//根據某個字符將數組轉成字符串
return arr.join('');
}
console.log(toString('get-element-by-id'));
</script>
</body>
</html>
- 冒泡排序的實現,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var arr = [32,4,67,82,21,11];
///輪數
for(var i = 0; i<arr.length-1;i++){
//次數
for(var j = 0;j<arr.length-1-i;j++){
//判斷前一個大於後一個數時進行交換
if(arr[j]>arr[j+1]){
//藉助第三方變量交換兩個變量的值
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
</script>
</body>
</html>
- 反轉數組,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
var arr = [1,2,3,4,5,6,7,8];
for(var i = 0;i<arr.length/2;i++){
//arr[0] arr[arr.length-1-0];
//arr[1] arr[arr.length-1-1];
//arr[2] arr[arr.length-1-2];
//arr[3] arr[arr.length-1-3];
//藉助第三方變量交換兩個變量的值
var temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
console.log(arr);
</script>
</body>
</html>
- 去掉數組中重複性的數據,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script type="text/javascript">
/*
* 1.創建一個新數組,把原數組中的第一個元素插入到新數組中
* 2.遍歷原數組中的每一個元素分別和新數組中的每一個元素進行比較
*/
//原數組
var arr = [8,11,20,5,20,8,0,2,4,0,8];
// 新數組
var t = [];//var t = [8,11];
t[0] = arr[0];
//arr中的每個元素
for(var i=0;i<arr.length;i++){
//t中的每個元素
for(var k = 0;k<t.length;k++){
//當原數組中的值和新數組中的值相同的時候,就沒有必要再繼續比較了,跳出內循環
if(t[k] == arr[i]){
break;
}
//拿原數組中的某個元素比較到新數組中的最後一個元素還沒有重複
if(k == t.length-1){
//將數據插入新數組
t.push(arr[i]);
}
}
}
console.log(t);
</script>
</body>
</html>
1
物理像素,有兩種方式,如下所示:
-
第一種方式,代碼如下所示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" /> <title></title> <style type="text/css"> *{ margin: 0; padding: 0; } #box{ width: 8rem; height: 8rem; border: 1px solid #000000; } </style> </head> <body> <div id="box"></div> </body> <script type="text/javascript"> var box = document.getElementById('box'); //獲取設備像素比 var dpr = window.devicePixelRatio; //比例 var scale = 1/dpr; //獲取屏幕寬度 375 var width = document.documentElement.clientWidth; //獲取meta標籤 var metaNode = document.querySelector('meta[name="viewport"]') metaNode.setAttribute('content','width=device-width,initial-scale='+ scale +',user-scalable=no') //元素比例乘回來 var htmlNode = document.querySelector('html'); htmlNode.style.fontSize = width/16*dpr + 'px'; </script> </html>
-
第二種方式,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
width: 200px;
height: 200px;
position: relative;
}
#box:after{
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #000000;
}
@media screen and ( -webkit-min-device-pixel-ratio:2 ) {
#box:after{
transform: scaleY(0.5);
}
}
@media screen and ( -webkit-min-device-pixel-ratio:3 ) {
#box:after{
transform: scaleY(0.333333333333);
}
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
flex
元素水平垂直居中,如下所示:
- 新版本的垂直居中,代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#wrap {
width: 500px;
height: 500px;
background: grey;
display: flex;
justify-content: center;
align-items: center;
}
#box{
width: 200px;
height: 200px;
background: deeppink;
}
</style>
</head>
<body>
<div id="wrap">
<div id="box"></div>
</div>
</body>
<script type="text/javascript">
window.onload = function () {
var box = document.getElementById('box');
}
</script>
</html>
-
老版本的垂直居中,代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> * { margin: 0; padding: 0; } #wrap { width: 500px; height: 500px; background: grey; display: -webkit-box; -webkit-box-pack: center; -webkit-box-align: center; } #box{ width: 200px; height: 200px; background: deeppink; } </style> </head> <body> <div id="wrap"> <div id="box"></div> </div> </body> <script type="text/javascript"> window.onload = function () { var box = document.getElementById('box'); } </script> </html>
css
實現三角形,代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box {
width: 0px;
height: 0px;
border: 100px solid transparent;
border-top-color: deeppink;
border-left-color: deeppink;
/*border-right-color: deeppink;*/
/*border-bottom-color: deeppink;*/
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
window.onload = function () {
var box = document.getElementById('box');
}
</script>
</html>
rem
適配,代碼如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
width: 8rem;
height: 8rem;
background: deeppink;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
//獲取屏幕寬度
var width = document.documentElement.clientWidth;
//獲取html
var htmlNode = document.querySelector('html');
//設置html字體大小
htmlNode.style.fontSize = width/16 + 'px';
</script>
</html>
- 背景圖片的距離,代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box{
width: 100px;
height: 200px;
background: pink;
padding: 100px;
border: 80px solid blue;
background-image: url("img/1.png");
background-repeat: no-repeat;
background-origin: content-box;
background-position: -50px 0;
}
/*答案:130px*/
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
- 對於函數節流與函數防抖的理解,如下所示:
- 函數節流,一個函數執行一次後,只有大於設定的執行週期後纔會執行第二次。有個需要頻繁觸發函數,出於優化性能的角度,在規定時間內,只讓函數觸發的第一次生效,後面不生效。具體來說,記錄上一次函數觸發的時間,記錄當前函數觸發的時間,同步時間。
- 防抖函數,一個需要頻繁觸發的函數,在規定時間內,只讓最後一次生效,前面的不生效。具體來說,記錄上一次的延時器,清除上一次的延時器,重新設置新的延時器。
- 對於跨域的理解,解決跨域的方法有哪些,如下所示:
- 同源策略,是瀏覽器安全策略,協議名、域名和端口號必須完全一致。
- 跨域,違背同源策略就會產生跨域
- 解決跨域,
jsonp
、cors
服務器代理 - 對於跨域的具體實現,創建
script
標籤,設置回調函數,數據請求回來會被觸發的函數,設置script
的src
屬性,設置請求地址,讓script
生效
- 對於
node.js
的事件輪詢機制的理解,可以藉助libuv
庫實現的,概括事件輪詢機制,分爲六個階段,如下所示:
timers
定時器階段,計時和執行到點的定時器回調函數pending callbacks
,某些操作系統的回調函數,比如TCP
錯誤類型idle,prepare
,準備工作poll
輪詢階段,輪詢隊列,如果輪詢隊列不爲空,依次同步取出輪詢隊列中第一個回調函數執行,直到輪詢隊列爲空或者達到系統最大的限制。如果輪詢隊列爲空,如果之前設置過setImmediate
函數,直接進入下一個check
階段。如果之前沒有設置過setImmediate
函數,在當前poll
階段等待,直到輪詢隊列添加回調函數,就去第一個情況執行。如果定時器到點了,也會去下一個階段。check
查階段,執行setImmediate
設置的回調函數close callbacks
關閉階段,執行close
事件回調函數process.nextTick
能在任意階段優先執行,process.nextTick() > setTimeout() > setImmediate()
- 對於從一個
url
地址到最終頁面渲染完成,發生了什麼,如下所示:
DNS
解析,將域名地址解析爲IP
地址,如下所示:- 瀏覽器
DNS
緩存 - 系統
DNS
緩存 - 路由器
DNS
緩存 - 網絡運營商
DNS
緩存 - 遞歸搜索,如
blog.baidu.com
,.com
域名下查找DNS
解析,.baidu
域名下查找DNS
解析,blog
域名下查找DNS
解析,出錯了
- 瀏覽器
TCP
連接,TCP
三次握手,如下所示:- 第一次握手,由瀏覽器發起,告訴服務器我要發送請求了
- 第二次握手,由服務器發起,告訴瀏覽器我準備接受了,你趕緊發送吧
- 第三次握手,由瀏覽器發送,告訴服務器,我馬上就發了,準備接受吧
- 發送請求,請求報文,
HTTP
協議的通信內容 - 接受響應,響應報文
- 渲染頁面,如下所示:
- 遇見
HTML
標記,瀏覽器調用HTML
解析器解析完成Token
並構建DOM
樹 - 遇見
style/link
標記,瀏覽器調用css
解析器,處理css
標記並構建CSSOM
樹 - 遇見
script
標記,調用javascript
解析器,處理script
代碼,綁定事件,修改DOM
樹/CSSOm
樹 - 將
DOM
樹和CSSOM
樹合併成一個渲染樹 - 根據渲染樹來計算佈局,計算每個節點的幾個信息,佈局
- 將各個節點顏色繪製到屏幕上,渲染
- 值得注意的是,這幾個步驟不一定按照順序執行,如果
DOM
樹或CSSOM
樹被修改了,可能會執行多次佈局和渲染,往往實際頁面中,這些步驟都會執行多次的
- 遇見
- 斷開連接,
TCP
四次揮手,如下所示:- 第一次揮手,由瀏覽器發起的,發送給服務器,我東西發送完了,請求報文,你準備關閉吧
- 第二次揮手,由服務器發起的,告訴瀏覽器,我東西接收完了,請求報文,我準備關閉了,你也準備吧
- 第三次揮手,有服務器發起,告訴瀏覽器,我東西發送完了,響應報文,你準備關閉吧
- 第四次揮手,由瀏覽器發起,告訴服務器,我東西接收完了,我準備關閉了,響應報文,你也準備吧
- 對於閉包的理解,如下所示:
- 閉包,密閉的容器,類似於
set
,map
容器,存儲數據的。閉包是一個對象,存放數據的格式:key: value
- 閉包的形成的條件,函數嵌套,內部函數引用外部函數的局部變量
- 閉包的優點,延長外部函數局部變量的生命週期
- 閉包的缺點,容易造成內存泄漏
- 注意的是,合理的使用閉包,用完閉包要及時清除(銷燬)
- 閉包的實例代碼,如下所示:
// 閉包的應用場景 // function fun() { // var count = 1; // return function () { // count++; // console.log(count); // } // } // // var fun2 = fun(); // fun2(); // 2 // fun2(); // 3 // /* 說說它們的輸出情況 */ function fun(n, o) { // var n = 1, o; console.log(o) return { fun: function (m) { // var m = 1; return fun(m, n) } } } var a = fun(0) a.fun(1) a.fun(2) a.fun(3) //undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3).fun(50).fun(22) //undefined,0,1,2,3,50 var c = fun(0).fun(1) c.fun(2) c.fun(3) //undefined,0,1,1
- 對於變量提升與執行上下文的理解,如下所示:
- 變量提升,
JS
引擎在代碼正式執行之前會做一個預處理的工作,收集變量和收集函數。依據是var
將var
後邊的變量定義但是不賦值var username = undefined; function() {}
提前定義該函數 - 執行上下文,
execute context EC
,代碼執行的環境,代碼正式執行之前會進入到執行環境,對於它的工作,如下所示:- 創建變量對象,變量,函數及函數的對象,全局是
window
,局部是抽象的但是確實存在 - 確認
this
的指向,全局this
指向window
,局部this
指向調用其的對象 - 創建作用域鏈,父級作用域鏈 + 當前的變量對象
- 擴展,
ECObj = { 變量對象:{變量,函數,函數的形參}, scopeChain:父級作用域鏈 + 當前的變量對象,this:{ window || 調用其的對象}}
- 創建變量對象,變量,函數及函數的對象,全局是
- 對於宏任務和微任務的理解,如下所示:
- 宏任務,分類:
setTimeout setInterval requrestAnimationFrame
,如下所示:- 宏任務所處的隊列就是宏任務隊列
- 第一個宏任務隊列中只有一個任務: 執行主線程的
js
代碼 - 宏任務隊列可以有多個
- 當宏任務隊列的中的任務全部執行完以後會查看是否有微任務隊列如果有先執行微任務隊列中的所有任務,如果沒有就查看是否有宏任務隊列
- 微任務,分類:
new Promise().then(回調) process.nextTick
,如下所示:- 微任務所處的隊列就是微任務隊列
- 只有一個微任務隊列
- 在上一個宏任務隊列執行完畢後如果有微任務隊列就會執行微任務隊列中的所有任務
- 宏任務和微任務的代碼,如下所示:
setTimeout(() => { console.log('setTimeout'); }, 0) new Promise((resolve, reject) =>{ for (var i = 0; i < 5; i++) { console.log(i); } resolve(); // 修改promise實例對象的狀態爲成功的狀態 }).then(() => { console.log('promise實例成功回調執行'); })
- 比較一下
React
與Vue
之間的相同點與不同點,如下所示:
- 相同點,如下所示:
- 都有組件化開發和
Virtual DOM
- 都支持
props
進行父子組件間數據通信 - 都支持數據驅動視圖, 不直接操作真實
DOM
, 更新狀態數據界面就自動更新 - 都支持服務器端渲染
- 都有支持
native
的方案,React
的React Native
,Vue
的Weex
- 都有組件化開發和
- 不同點,如下所示:
- 數據綁定:
vue
實現了數據的雙向綁定,react
數據流動是單向的 - 組件寫法不一樣,
React
推薦的做法是JSX
, 也就是把HTML
和CSS
全都寫進JavaScript
了,即'all in js';
Vue
推薦的做法是webpack+vue-loader
的單文件組件格式,即html,css,js
寫在同一個文件 state
對象在react
應用中不可變的,需要使用setState
方法更新狀態;在vue
中,state
對象不是必須的,數據由data
屬性在vue
對象中管理virtual DOM
不一樣,vue
會跟蹤每一個組件的依賴關係,不需要重新渲染整個組件樹.而對於React
而言,每當應用的狀態被改變時,全部組件都會重新渲染,所以react
中會需要shouldComponentUpdate
這個生命週期函數方法來進行控制React
嚴格上只針對MVC
的view
層,Vue
則是MVVM
模式
- 數據綁定:
- 對於
Redux
管理狀態的機制的理解,如下所示:
- 對
Redux
基本理解,如下所示:redux
是一個獨立專門用於做狀態管理的JS庫, 不是react
插件庫- 它可以用在
react
,angular
,vue
項目中, 但基本與react
配合使用 - 作用是集中式管理
react
應用中多個組件共享的狀態和從後臺獲取的數據
- 對
Redux
使用擴展,如下所示:- 使用
react-redux
簡化redux
的編碼 - 使用
redux-thunk
實現redux
的異步編程 - 使用
Redux DevTools
實現chrome
中redux
的調試
- 使用
- 對於
Vue
組件間通信方式的理解,如下所示:
- 通信種類,如下所示:
- 父組件向子組件通信
- 子組件向父組件通信
- 隔代組件間通信
- 兄弟組件間通信
- 實現通信方式,如下所示:
props
- vue自定義事件
- 消息訂閱與發佈
vuex
slot
- 方式1:
props
,如下所示:- 通過一般屬性實現父向子通信
- 通過函數屬性實現子向父通信
- 缺點是隔代組件和兄弟組件間通信比較麻煩
- 方式2:
vue
自定義事件,如下所示:vue
內置實現, 可以代替函數類型的props
,綁定監聽:<MyComp @eventName="callback"
,觸發(分發)事件:this.$emit("eventName", data)
- 缺點是隻適合於子向父通信
- 方式3: 消息訂閱與發佈,如下所示:
- 需要引入消息訂閱與發佈的實現庫, 如:
pubsub-js
,訂閱消息:PubSub.subscribe('msg', (msg, data)=>{})
,發佈消息:PubSub.publish(‘msg’, data)
- 優點是此方式可用於任意關係組件間通信
- 需要引入消息訂閱與發佈的實現庫, 如:
- 方式4:
vuex
,如下所示:vuex
是vue
官方提供的集中式管理vue
多組件共享狀態數據的vue
插件- 優點是對組件間關係沒有限制, 且相比於
pubsub
庫管理更集中, 更方便
- 方式5:
slot
,如下所示:- 專門用來實現父向子傳遞帶數據的標籤,子組件和父組件
- 注意的是,通信的標籤模板是在父組件中解析好後再傳遞給子組件的
- 對於
Vuex
管理狀態的機制的理解,如下所示:
Vuex
是一個專爲Vue.js
應用程序開發的狀態管理的vue
插件- 作用是集中式管理
vue
多個組件共享的狀態和從後臺獲取的數據
- 對於
Vue
的MVVM
實現原理的理解,如下所示:
Vue
作爲MVVM
模式的實現庫的2
種技術,模板解析和數據綁定- 模板解析,實現初始化顯示,解析大括號表達式和解析指令
- 數據綁定,實現更新顯示,通過數據劫持實現