前端 | js面試常問問題含詳細答案

總結一些前端js的知識,将筆記整理跟大家分享,有些知識會經常在前端面試的時候會問到,所以做個記錄,希望對大家有所幫助,如果有什麼問題,可以指出,會積極修正。
如果大家喜歡,可以點贊或留言我再繼續更新面試題~~~~,謝謝大家~~~

1、前++、後++、區别?
var i=2 ;
a = i++ //将i的值賦給a , 即a = i,之後再執行i = i + 1; 2
a = ++i //将i+1 的值賦給a,即a = i + 1 ,之後再執行i = i + 1; 3
console.log(a)

【總結】:

前++是先自加後計算、後++是後自加先計算

1:前置++ 是将自身+1 後的值賦給變量,同時自身加1;

2:後置++ 是将自身的值賦給變量,之後自身再加1;

2、JS 有哪些數據類型?

Js基本數據類型 undefined null boolean number string bigint
Js引用類型 object Array function Date Math RegExp
ES6基本數據類型 多了個symbol

3、js判斷類型?

1、typeof
檢測不出null 和 數組,結果都為object,所以typeof常用于檢測基本類型
2、instanceof
不能檢測出number、boolean、string、undefined、null、symbol類型,所以instancof常用于檢測複雜類型以及級成關系
3、constructor
null、undefined沒有construstor方法,因此constructor不能判斷undefined和null。但是contructor的指向是可以被改變,所以不安全
4、Object.prototype.toString.call
全類型都可以判斷

4、數據類型怎麼檢測?
1、typeof
例:console.log(typeof true) // boolean

2、instanceof
例:console.log([1,2] instanceof Array) // true

3、constructor
例: console.log([1, 2].constructor === Array) // ture

4、Object.prototype.toString.call
例:Object.prototype.toString.call([1, 2]) // [object Array]

判斷是否是數組
var vv = [1, 2];
console.log(vv instanceof Array)
console.log(vv.constructor === Array)
console.log(Array.isArray(vv))
console.log(Object.prototype.toString.call(vv) === '[object Array]')
console.log(vv.__proto__ === Array.prototype)

5、Js數組的方法

join()數組轉換成字符串
push()尾部添加
pop()尾部删除
shift() 頭部删除
unshift() 頭部添加
sort()排序
reverse()反轉
concat()鍊接兩個或多個數組
slice()
var arr=[1,2,3,4,5]
  console.log(arr.slice(1)) //[2,3,4,5]選擇序列号從1到最後的所有元素組成的新數組
  console.log(arr.slice(1,3)) //[2,3]不包含序列号,序号為3的元素
splice()
  splice(index,howmany,item1,...itemx)
  index參數:必須,整數,規定添加或删除的位置,使用負數,從數組尾部規定位置
  howmany參數:必須,要删除的數量,如果為0則不删除項目
  item1,...itemx參數:可選,向數組添加的新項目
  var arr=[1,2,3,4,5]
  console.log(arr.splice(2,1,"hello"));//[3]返回的新數組
  console.log(arr);//[1,2,"hello",4,5]
indexOf()和 lastIndexOf() (ES5新增)
forEach() (ES5新增)
map() (ES5新增)
filter() (ES5新增)
every() (ES5新增)
some() (ES5新增)
reduce()和 reduceRight() (ES5新增)

6、JS中的Array.splice()和Array.slice()方法有什麼區别?

話不多說,來看第一個例子:

var arr=[0,1,2,3,4,5,6,7,8,9];//設置一個數組
console.log(arr.slice(2,7));//2,3,4,5,6
console.log(arr.splice(2,7));//2,3,4,5,6,7,8
//由此我們簡單推測數量兩個函數參數的意義,
slice(start,end)第一個參數表示開始位置,第二個表示截取到的位置(不包含該位置)
splice(start,length)第一個參數開始位置,第二個參數截取長度

接着看第二個:

var x=y=[0,1,2,3,4,5,6,7,8,9]
console.log(x.slice(2,5));//2,3,4
console.log(x);[0,1,2,3,4,5,6,7,8,9]原數組并未改變
//接下來用同樣方式測試splice
console.log(y.splice(2,5));//2,3,4,5,6
console.log(y);//[0,1,7,8,9]顯示原數組中的數值被剔除掉了

slice和splice雖然都是對于數組對象進行截取,但是二者還是存在明顯區别,函數參數上slice和splice第一個參數都是截取開始位置,slice第二個參數是截取的結束位置(不包含),而splice第二個參數(表示這個從開始位置截取的長度),slice不會對原數組産生變化,而splice會直接剔除原數組中的截取數據!
slice不會改變原數組,splice會改變原數組

7、數值轉換

JSON.parse() 轉json對象
JSON.stringify() 轉json字符串
String(),toString() 轉字符串類型
Number parseInt()字符串轉數值類型
split 字符串轉數組
join 數組轉字符串

8、什麼是跨域,常見跨域

由于浏覽器獲取數據遵循同源策略,所以當訪問非同源資源的時候,就需要跨域,常見的跨域方式有jsonp,a img src cors
同源策略:同協議,端口,域名的安全策略
jsonp原理
動态創建script标簽,利用callpack回調函數獲取值

function callbackFunction(){
alert("回滾");
}
var script=document.createElement("script");
script.src="http://frergeoip.net.json/?callback=callbackFunction";

CORS的原理:
當傳輸數據量比較大,get形式搞不定的時候,可以用到cors跨域,cors原理是定義一種跨域訪問的機制,可以讓ajax實現跨域訪問。Cors允許一個域上的網絡應用向另一個域提交跨域ajax請求。實現此功能非常簡單,隻需由服務器發送一個響應标頭即可。
Jsonp是get形式,承載的信息量有限,所以信息量較大的時cors是不二選擇。

9、http協議:

http協議是定義服務器端和客戶端之間文件傳輸的溝通方式
請求服務器上的資源,請求html css js 圖片文件等
請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:
GET (get) 請求獲取Request-URI所标識的資源 --獲取資源
POST (post) 在Request-URI所标識的資源後附加新的數據 ---傳輸資源
HEAD (head) 請求獲取由Request-URI所标識的資源的響應消息報頭 ---獲取報文首部
PUT (put) 請求服務器存儲一個資源,并用Request-URI作為其标識 ---更新資源
DELETE (delete) 請求服務器删除Request-URI所标識的資源 ---删除資源
TRACE (trace) 請求服務器回送收到的請求信息,主要用于測試或診斷
CONNECT(connect) 保留将來使用
OPTIONS(options) 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
常見狀态碼:
200 請求成功
301 資源(網頁等)被永久轉移到其他url
404 請求的資源不存在
500 内部服務器錯誤

10、HTTP狀态碼

100 Continue 繼續,一般在發送post請求時,已發送了http header之後服務端将返回此信息,表示确認,之後發送具體參數信息
200 OK 正常返回信息
201 Created 請求成功并且服務器創建了新的資源
202 Accepted 服務器已接受請求,但尚未處理
301 Moved Permanently 請求的網頁已永久移動到新位置。
302 Found 臨時性重定向。
303 See Other 臨時性重定向,且總是使用 GET 請求新的 URI。
304 Not Modified 自從上次請求後,請求的網頁未修改過。
400 Bad Request 服務器無法理解請求的格式,客戶端不應當嘗試再次使用相同的内容發起請求。
401 Unauthorized 請求未授權。
403 Forbidden 禁止訪問。
404 Not Found 找不到如何與 URI 相匹配的資源。
500 Internal Server Error 最常見的服務器端錯誤。
503 Service Unavailable 服務器端暫時無法處理請求(可能是過載或維護)。

11、說說你對閉包的理解?

使用閉包主要是為了設計私有的方法和變量。閉包的優點是可以避免全局變量的污染,缺點是閉包會常駐内存,會增大内存使用量,使用不當很容易造成内存洩露。
閉包有三個特性:
1.函數嵌套函數
2.函數内部可以引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收

哪些操作會造成閉包内存洩漏?

不正當地使用可能會造成内存洩露(不再用到的内存,沒有及時釋放,就叫做内存洩露)

内存洩漏指任何對象在您不再擁有或需要它之後仍然存在
setTimeout 的第⼀個參數使用字符串而非函數的話 ,會引發内存洩漏
閉包 、控制台日志 、循環 (在兩個對象彼此引用且彼此保留時 ,就會産生⼀個循環)

閉包用途
1緩存
設想我們有一個處理過程很耗時的函數對象,每次調用都會花費很長時間,那麼我們就需要将計算出來的值存儲起來,當調用這個函數的時候,首先在緩存中查找,如果找不到,則進行計算,然後更新緩存并返回值,如果找到了,直接返回查找到的值即可。閉包正是可以做到這一點,因為它不會釋放外部的引用,從而函數内部的值可以得以保留。
2 實現封裝
可以先來看一個關于封裝的例子,在person之外的地方無法訪問其内部的變量,而通過提供閉包的形式來訪問:

var person = function(){
//變量作用域為函數内部,外部無法訪問
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
print(person.name);//直接訪問,結果為undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName());

12、如何阻止事件冒泡?

ie:阻止冒泡ev.cancelBubble = true;
非IE ev.stopPropagation();

13、如何阻止默認事件?

(1)return false;(2) ev.preventDefault();

14、事件代理

事件代理是指,對父盒子綁定事件,然後子盒子觸發事件,由于産生事件冒泡,導緻父盒子事件也被觸發,此時,在父盒子的時間處理函數中,可以通過srcElement或者target屬性來獲取目标時間,并對其進行相應的處理

15、添加 删除 替換 插入到某個節點的方法?
1)創建新節點
createElement() //創建一個具體的元素
createTextNode() //創建一個文本節點

2)添加、移除、替換、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替換
insertBefore() //插入

3)查找
getElementsByTagName() //通過标簽名稱
getElementsByName() //通過元素的Name屬性的值
getElementById() //通過元素Id,唯一性

16、document load 和document ready的區别?

document.onload 是在結構和樣式,外部js以及圖片加載完才執行js
document.ready是dom樹創建完成就執行的方法,原生種沒有這個方法,jquery中有 $().ready(function)

17、Javascript的事件流模型都有什麼?

“事件捕捉”:是從上往下,window,document,document.documentelment(獲取的html) document,body 、……..目标元素

“事件冒泡”:是從下往上:反之

“DOM事件流”:三個階段:事件捕捉、目标階段、事件冒泡


Dom事件類:

Dom0 element.onclick=function(){}

DOM2 element.addEventlistener(‘click’,function(){},flase)

DOM3 element.addEventlistener(‘keyup’,function(){},flase)

Event.preventdefault() 阻止默認事件

Event.stoppropagation() 阻止冒泡

Event.currenTtarget()事件代理

Event.target 當前被點擊元素

18、Js事件循環機制/event loop?

用簡單的流程解釋時間循環機制
在了解什麼是事件循環之前我們需要先行了解 javascript是一個單線程語言 和 javascript的事件分類

1. javascript是一個單線程語言

什麼是單線程。舉個例子:這就好像食堂打飯,如果食堂開設多個窗口,那麼就可以有多個同學同時打飯。但如果食堂隻有一個窗口,那麼所有的同學都要在這一個窗口後面一個個排隊打飯。單線程,就屬于後者這個情況。同理,javascript中的所有任務都隻有一條線程在處理。

顯然,這種機制存在很大的弊端。如果我們有一個任務卡死了。那麼後面所有的任務都無法被執行。或者有某個任務耗時很長,那麼就會導緻後面所有的任務都被延遲執行。在這樣的環境下,javascript誕生了兩個任務種類:同步任務和異步任務

2. javascript的任務分類

接上文,javascript中的所有任務被分為同步任務和異步任務兩大類。

同步任務介紹:就是隻要被掃描到,就可以被主線程馬上執行的任務。(優先于所有異步任務)

異步任務介紹:即使被掃描到,也不會馬上執行,異步任務會被壓入異步任務隊列中,等待主線程中的任務全部清空了,再被召喚執行。

常見的異步任務有如下幾種

Promise.then() ---微任務

async/await ---Promise的語法糖 ---微任務

setTimeout() ---宏任務

setInterval() ---宏任務

...(還有更多,不常見的)

19、null和undefined的區别?

null是一個表示"無"的對象,轉為數值時為0;undefined是一個表示"無"的原始值,轉為數值時為NaN。
當聲明的變量還未被初始化時,變量的默認值為undefined。 null用來表示尚未存在的對象,常用來表示函數企圖返回一個不存在的對象。

20、call() 和 .apply() 的區别和作用?

相同點:兩個方法産生的作用是完全一樣的,都是改變this指向的

不同點:方法傳遞的參數不同

Object.call(this,obj1,obj2,obj3)

Object.apply(this,arguments)

Apply()接收兩個參數,一個是函數運行的作用域(this),另一個是參數數組。

Call()方法的第一個參數與apply()相同,但傳遞的參數必須列舉出來。

一、call,apply,bind的相同點:

都是改變this指向的;
第一個參數都是this要指向的對象;
都可以利用後續參數傳參;

二、call,apply,bind的區别:

call和bind的參數是依次傳參,一一對應的;
但apply隻有兩個參數,第二個參數為數組;
call和apply都是對函數進行直接調用,而bind方法返回的仍是一個函數;

21、mvc和mvvm模式原理:

22、JS為什麼要區分微任務和宏任務?

(1)js是單線程的,但是分同步異步
(2)微任務和宏任務皆為異步任務,它們都屬于一個隊列
(3)宏任務一般是:script,setTimeout,setInterval、setImmediate
(4)微任務:原生Promise
(5)遇到微任務,先執行微任務,執行完後如果沒有微任務,就執行下一個宏任務,如果有微任務,就按順序一個一個執行微任務

23、setTimeout和setInterval
//setTimeout是3秒後執行
setTimeout(function(){
alert(123)
},3000)

//setInterval是每隔三秒執行一次,不斷的重複執行
setInterval(function(){
alert(1121222)
},3000)
//兩個執行都是異步進行的

24、深拷貝淺拷貝

深拷貝和淺拷貝最根本的區别在于是否真正獲取一個對象的複制實體,而不是引用。

假設B複制了A,修改A的時候,看B是否發生變化:

如果B跟着也變了,說明是淺拷貝,拿人手短!(修改堆内存中的同一個值)

如果B沒有改變,說明是深拷貝,自食其力!(修改堆内存中的不同的值)
淺拷貝實現:

var a = [1, 2, 3, 4, 5];
var b = a;
a[0] = 2
console.log(a);//[2,2,3,4,5]
console.log(b);//[2,2,3,4,5] ////b會随着a的變化而變化

深拷貝實現:

var a = [{"name":"weifeng"},{"name":"boy"}];
var a_copy =[].concat(JSON.parse(JSON.stringify(a)));//深拷貝
a_copy[1].name = "girl"
console.log(a);//[{"name":"weifeng"},{"name":"boy"}]
console.log(a_copy );//[{"name":"weifeng"},{"name":"girl"}]

淺拷貝方法

1.Object.assign()方法
const obj2 = Object.assign({},obj1)

缺點:這裡就會發現使用Object.assign()隻能深拷貝一級屬性,二級以上的屬性(引用類型)就是淺拷貝了
2.擴展運算符...
const obj2 = {...obj1}

缺點:這個方法和上面使用Object.assign()方法使用的結果都是一樣的。隻能深拷貝一級屬性,二級以上屬性(引用類型)就是淺拷貝了
3.Array.prototype.concat()
4.Array.prototype.slice()

深拷貝方法

1.寫一個遞歸(推薦,比較完美的解決方案)
function deepClone(oldData) {
// Object 是對象或數組或null
if(typeof oldData === 'object' && oldData !== null){
let res = Array.isArray(oldData) ? [] : {};
for (let k in oldData) {
if (oldData.hasOwnProperty(k)) {
res[k] = deepClone(oldData[k])
}
}
return res;
}else {
return oldData;
}
}

2.JSON轉換方法JSON.parse(JSON.stringify())
const obj2 = JSON.parse(JSON.stringify(obj1))

缺點:你看到這裡就會發現數據類型是 function 和 undefined 情況下無法複制,其他的都可以進行深層次的拷貝
3.jQuery.extend()
const $ = require('jquery');
var obj = { a: { a: 'july', b: 10 }, b: 2 };
const obj1 = $.extend(true, {}, obj);
console.log(obj.a.a == obj1.a.a); // false

4.使用函數庫lodash
var _ = require('lodash');
var obj = { a: { a: 'july', b: 10 }, b: 2 };
var obj1 = _.cloneDeep(obj);
console.log(obj.a.a == obj1.a.a); //false

25、重排重繪

回流(重排):
當render tree中的一部分(或全部)因為元素的規模尺寸,布局,隐藏等改變而需要重新構建。這就稱為回流(reflow)。每個頁面至少需要一次回流,就是在頁面第一次加載的時候,這時候是一定會發生回流的,因為要構建render tree。在回流的時候,浏覽器會使渲染樹中受到影響的部分失效,并重新構造這部分渲染樹,完成回流後,浏覽器會重新繪制受影響的部分到屏幕中,該過程成為重繪。
重繪:
當render tree中的一些元素需要更新屬性,而這些屬性隻是影響元素的外觀,風格,而不會影響布局的,比如background-color。則就叫稱為重繪。
區别:
回流必将引起重繪,而重繪不一定會引起回流。比如:隻有顔色改變的時候就隻會發生重繪而不會引起回流
當頁面布局和幾何屬性改變時就需要回流
比如:添加或者删除可見的DOM元素,元素位置改變,元素尺寸改變——邊距、填充、邊框、寬度和高度,内容改變。

26、防抖和節流?

在前端開發的過程中,我們經常會需要綁定一些持續觸發的事件,如 resize、scroll、mousemove keyup 等等,但有些時候我們并不希望在事件持續觸發的過程中那麼頻繁地去執行函數。

通常這種情況下我們怎麼去解決的呢?一般來講,防抖和節流是比較好的解決方案。
1、防抖:
指觸發事件後在n秒後函數執行,如果在n秒内又觸發了事件,則會重新計算函數執行時間。應用場景(适合多次事件隻響應一次的情況):給按鈕加防抖函數防止表單多次提交;判斷scroll是否滑到底部;對于輸入框連續輸入進行AJAX驗證時,用函數防抖能有效減少請求次數。
現給一個場景:現監聽一個輸入框,文字變化後觸發change事件。若直接用keyup事件,則會頻繁觸發change事件。加了防抖後,用戶輸入結束或暫停時,才會觸發change事件。
所謂防抖,就是指觸發事件後在 n 秒内函數隻能執行一次,如果在 n 秒内又觸發了事件,則會重新計算函數執行時間。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="input1">
</body>
<script>
const input1 = document.getElementById('input1')
//1、不加防抖 ,會一直觸發change事件
input1.addEventListener('keyup', function(){
console.log(input1.value)
})

//2、簡單實現防抖
let timer = null
input1.addEventListener('keyup', function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
//模拟觸發change事件
console.log(input1.value)
//清空定時器
timer = null
}, 1000)
})

//3、将防抖函數這個工具進行封裝
function debounce(fn, delay = 50){
//timer是閉包中的,不能被别人修改
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
input1.addEventListener('keyup', debounce(function(){
console.log(input1.value)
}, 600))
</script>
</html>

則封裝後的防抖函數為:

function debounce(fn, delay = 50){
let timer = null //timer是閉包中的,不能被别人修改
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}

2、節流:
連續發送的事件在n秒内隻執行一次函數。應用場景(适合大量事件按時間做平均分配觸發):DOM元素拖拽;Canvas畫筆功能。
現給一個場景:拖拽一個元素,要随時拿到該元素被拖拽的位置。若直接用drag事件,則會頻繁觸發,很容易導緻卡頓。加了節流後,無論拖拽速度多快,都會每隔固定時間觸發一次。
所謂節流,就是指連續觸發事件但是在 n 秒中隻執行一次函數。節流會稀釋函數的執行頻率。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#div1{
border: 1px solid #ccc;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div id = "div1" draggable="true">可拖拽</div>
<script>
const div1 = document.getElementById('div1')

//1、簡單實現節流
let timer = null
div1.addEventListener('drag', function(e){
if(timer){
return
}
timer = setTimeout(() => {
console.log(e.offsetX, e.offsetY)
timer = null //定時器執行了,才讓timer為空
}, 1000)
})

//2、将節流函數這個工具進行封裝
function throttle(fn, delay = 100){
let timer = null
return function(){
if(timer){
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
},delay)
}
}
div1.addEventListener('drag', throttle(function(e){ //形參e會傳給throttle函數運行後返回的函數
console.log(e.offsetX, e.offsetY)
},200))
</script>
</body>
</html>

則封裝後的節流函數為:

function throttle(fn, delay = 100){
let timer = null
return function(){
if(timer){
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
},delay)
}
}

27、一個頁面從輸入 URL 到頁面加載顯示完成,這個過程中都發生了什麼?

分為4個步驟:
(1),當發送一個URL請求時,不管這個URL是Web頁面的URL還是Web頁面上每個資源的URL,浏覽器都會開啟一個線程來處理這個請求,同時在遠程DNS服務器上啟動一個DNS查詢。這能使浏覽器獲得請求對應的IP地址。
(2), 浏覽器與遠程Web服務器通過TCP三次握手協商來建立一個TCP/IP連接。該握手包括一個同步報文,一個同步-應答報文和一個應答報文,這三個報文在 浏覽器和服務器之間傳遞。該握手首先由客戶端嘗試建立起通信,而後服務器應答并接受客戶端的請求,最後由客戶端發出該請求已經被接受的報文。
(3),一旦TCP/IP連接建立,浏覽器會通過該連接向遠程服務器發送HTTP的GET請求。遠程服務器找到資源并使用HTTP響應返回該資源,值為200的HTTP響應狀态表示一個正确的響應。
(4),此時,Web服務器提供資源服務,客戶端開始下載資源。
請求返回後,便進入了我們關注的前端模塊
簡單來說,浏覽器會解析HTML生成DOM Tree,其次會根據CSS生成CSS Rule Tree,而javascript又可以根據DOM API操作DOM
詳情:從輸入 URL 到浏覽器接收的過程中發生了什麼事情?

28、說說TCP傳輸的三次握手策略

為了準确無誤地把數據送達目标處,TCP協議采用了三次握手策略。用TCP協議把數據包送出去後,TCP不會對傳送 後的情況置之不理,它一定會向對方确認是否成功送達。握手過程中使用了TCP的标志:SYN和ACK。
發送端首先發送一個帶SYN标志的數據包給對方。接收端收到後,回傳一個帶有SYN/ACK标志的數據包以示傳達确認信息。最後,發送端再回傳一個帶ACK标志的數據包,代表“握手”結束
若在握手過程中某個階段莫名中斷,TCP協議會再次以相同的順序發送相同的數據包。

29、說說你對語義化的理解?

1,去掉或者丢失樣式的時候能夠讓頁面呈現出清晰的結構
2,有利于SEO:和搜索引擎建立良好溝通,有助于爬蟲抓取更多的有效信息:爬蟲依賴于标簽來确定上下文和各個關鍵字的權重;
3,方便其他設備解析(如屏幕閱讀器、盲人閱讀器、移動設備)以意義的方式來渲染網頁;
4,便于團隊開發和維護,語義化更具可讀性,是下一步吧網頁的重要動向,遵循W3C标準的團隊都遵循這個标準,可以減少差異化

30、你如何對網站的文件和資源進行優化?

期待的解決方案包括:
文件合并
文件最小化/文件壓縮
使用 CDN 托管
緩存的使用(多個域名來提供緩存)
其他

31、請說出三種減少頁面加載時間的方法?

1、壓縮css、js文件
2、合并js、css文件,減少http請求
3、外部js、css文件放在最底下
4、減少dom操作,盡可能用變量替代不必要的dom操作

32、js延遲加載的方式有哪些?

defer和async、動态創建DOM方式(創建script,插入到DOM中,加載完畢後callBack)、按需異步載入js

33、你有哪些性能優化的方法?

(詳情請看雅虎14條性能優化原則)。
(1) 減少http請求次數:CSS Sprites, JS、CSS源碼壓縮、圖片大小控制合适;網頁Gzip,CDN托管,data緩存 ,圖片服務器。
(2) 前端模闆 JS+數據,減少由于HTML标簽導緻的帶寬浪費,前端用變量保存AJAX請求結果,每次操作本地變量,不用請求,減少請求次數
(3) 用innerHTML代替DOM操作,減少DOM操作次數,優化javascript性能。
(4) 當需要設置的樣式很多時設置className而不是直接操作style。
(5) 少用全局變量、緩存DOM節點查找的結果。減少IO讀取操作。
(6) 避免使用CSS Expression(css表達式)又稱Dynamic properties(動态屬性)。
(7) 圖片預加載,将樣式表放在頂部,将腳本放在底部 加上時間戳。

34、如何進行網站性能優化?

content 方面

減少 HTTP 請求:合并文件 、 CSS 精靈 、 inline Image
減少 DNS 查詢: DNS 緩存 、将資源分布到恰當數量的主機名
減少 DOM 元素數量

Server 方面

使用 CDN
配置 ETag
對組件使用 Gzip 壓縮

Cookie 方面

減⼩ cookie 大⼩

css 方面

将樣式表放到頁面頂部
不使用 CSS 表達式
使用 <link> 不使用 @import

Javascript 方面

将腳本放到頁面底部
将 javascript 和 css 從外部引⼊
壓縮 javascript 和 css
删除不需要的腳本
減少 DOM 訪問

圖片⽅面

優化圖片:根據實際顔色需要選擇色深 、壓縮
優化 css 精靈
不要在 HTML 中拉伸圖片

35、異步加載和延遲加載

1.異步加載的方案: 動态插入script标簽
2.通過ajax去獲取js代碼,然後通過eval執行
3.script标簽上添加defer或者async屬性
4.創建并插入iframe,讓它異步執行js
5.延遲加載:有些 js 代碼并不是頁面初始化的時候就立刻需要的,而稍後的某些情況才需要的。

36、GET和POST的區别,何時使用POST?

GET:一般用于信息獲取,使用URL傳遞參數,對所發送信息的數量也有限制,一般在2000個字符
POST:一般用于修改服務器上的資源,對所發送的信息沒有限制。
GET方式需要使用Request.QueryString來取得變量的值,而POST方式通過Request.Form來獲取變量的值,也就是說Get是通過地址欄來傳值,而Post是通過提交表單來傳值。
然而,在以下情況中,請使用 POST 請求:
無法使用緩存文件(更新服務器上的文件或數據庫)
向服務器發送大量數據(POST 沒有數據量限制)
發送包含未知字符的用戶輸入時,POST 比 GET 更穩定也更可靠

37、平時如何管理你的項目?

先期團隊必須确定好全局樣式(globe.css),編碼模式(utf-8) 等;
編寫習慣必須一緻(例如都是采用繼承式的寫法,單樣式都寫成一行);
标注樣式編寫人,各模塊都及時标注(标注關鍵樣式調用的地方);
頁面進行标注(例如 頁面 模塊 開始和結束);
CSS跟HTML 分文件夾并行存放,命名都得統一(例如style.css);
JS 分文件夾存放 命名以該JS功能為準的英文翻譯。

38、你如何優化自己的代碼?

代碼重用
避免全局變量(命名空間,封閉空間,模塊化mvc…)
拆分函數避免函數過于臃腫
注釋

39、什麼是 FOUC(無樣式内容閃爍)?你如何來避免 FOUC?
FOUC - Flash Of Unstyled Content 文檔樣式閃爍
<style type="text/css" media="all">@import "../fouc.css";</style>

而引用CSS文件的@import就是造成這個問題的罪魁禍首。IE會先加載整個HTML文檔的DOM,然後再去導入外部的CSS文件,因此,在頁面DOM加載完成到CSS導入完成中間會有一段時間頁面上的内容是沒有樣式的,這段時間的長短跟網速,電腦速度都有關系。

解決方法簡單的出奇,隻要在<head>之間加入一個<link>或者<script>元素就可以了。

40、網站重構的理解?

網站重構:在不改變外部行為的前提下,簡化結構、添加可讀性,而在網站前端保持一緻的行為。也就是說是在不改變UI的情況下,對網站進行優化,在擴展的同時保持一緻的UI。
對于傳統的網站來說重構通常是:
表格(table)布局改為DIV+CSS
使網站前端兼容于現代浏覽器(針對于不合規範的CSS、如對IE6有效的)
對于移動平台的優化
針對于SEO進行優化
深層次的網站重構應該考慮的方面
減少代碼間的耦合
讓代碼保持彈性
嚴格按規範編寫代碼
設計可擴展的API
代替舊有的框架、語言(如VB)
增強用戶體驗
通常來說對于速度的優化也包含在重構中
壓縮JS、CSS、image等前端資源(通常是由服務器來解決)
程序的性能優化(如數據讀寫)
采用CDN來加速資源加載
對于JS DOM的優化
HTTP服務器的文件緩存

41、什麼叫優雅降級和漸進增強?

優雅降級:Web站點在所有新式浏覽器中都能正常工作,如果用戶使用的是老式浏覽器,則代碼會檢查以确認它們是否能正常工作。由于IE獨特的盒模型布局問題,針對不同版本的IE的hack實踐過優雅降級了,為那些無法支持功能的浏覽器增加候選方案,使之在舊式浏覽器上以某種形式降級體驗卻不至于完全失效.
漸進增強:從被所有浏覽器支持的基本功能開始,逐步地添加那些隻有新式浏覽器才支持的功能,向頁面增加無害于基礎浏覽器的額外樣

42、棧和隊列的區别?

棧的插入和删除操作都是在一端進行的,而隊列的操作卻是在兩端進行的。
隊列先進先出,棧先進後出。
棧隻允許在表尾一端進行插入和删除,而隊列隻允許在表尾一端進行插入,在表頭一端進行删除

43、棧和堆的區别?

棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。
堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。
堆(數據結構):堆可以被看成是一棵樹,如:堆排序;
棧(數據結構):一種先進後出的數據結構。

44、對前端界面工程師這個職位是怎麼樣理解的?它的前景會怎麼樣?

前端是最貼近用戶的程序員,比後端、數據庫、産品經理、運營、安全都近。
1、實現界面交互
2、提升用戶體驗
3、有了Node.js,前端可以實現服務端的一些事情
前端是最貼近用戶的程序員,前端的能力就是能讓産品從 90分進化到 100 分,甚至更好,
參與項目,快速高質量完成實現效果圖,精确到1px;
與團隊成員,UI設計,産品經理的溝通;
做好的頁面結構,頁面重構和用戶體驗;
處理hack,兼容、寫出優美的代碼格式;
針對服務器的優化、擁抱最新前端技術。

45、浏覽器緩存

一、為什麼需要緩存

在前端開發中,我們主要追求的是性能和用戶體驗。對于一個網站查看性能最簡單的方式就是打開網站的速度。而一個好的緩存策略可以大大提升網站的性能。使得已經下載後的資源被重複利用。減少客戶端和服務器之間的請求次數,減少帶寬,減少網絡負荷。

二、什麼是緩存

對于web緩存,主要是針對一些web資源(html、 js、圖片、數據等),就是介于web服務器和浏覽器之間的文件數據副本。當我們第一次打開某一個網頁時,浏覽器向服務器發起請求,請求所需要的資源。如果我們使用了web緩存,當我們下一次再次訪問該網站頁面的時候,我們可以根據一些緩存策略,來決定是否直接使用緩存中的一些資源,還是再次向服務端發起請求,從而避免再次向服務器發起請求,減少客戶端和服務器之間通信的時延。

三、緩存的作用

減少網絡帶寬的消耗
降低服務器壓力
減少網絡延時,加快頁面打開速度。

浏覽器緩存處理

1.強緩存(本地緩存)
浏覽器本地在有效時間内強制緩存

使用強緩存策略時,如果緩存資源有效,則直接使用緩存資源,不必再向服務器發起請求。
強緩存策略可以通過兩種方式來設置,分别是 http 頭信息中的 Expires 屬性和 Cache-Control 屬性

2.弱緩存(協商緩存)
和服務器溝通,由服務器告知浏覽器是否使用緩存

如果命中強制緩存,我們無需發起新的請求,直接使用緩存内容,如果沒有命中強制緩存,設置了協商緩存,這個時候協商緩存就會發揮作用了。
上面已經說到了,命中協商緩存的條件有兩個:max-age=xxx 過期了值為 no-store
使用協商緩存策略時,會先向服務器發送一個請求,如果資源沒有發生修改,則返回一個304狀态,讓浏覽器使用本地的緩存副本。如果資源發生了修改,則返回修改後的資源。
弱緩存也可以通過兩種方式來設置,分别是http頭信息中的Etag 和 Last-Modified 屬性。

http标準其實是并沒有強緩存、弱緩存名詞,隻是為了方便理解而構建出來的名詞而已

在http裡所謂的緩存,本質上隻是浏覽器和業務,根據不同的報文字段,做出不同的緩存動作而已,四種緩存字段如下
http緩存字段
1.Cache-Control
2.Expires
3.ETag / If-None-Match
4.Last-Modified / If-Modified-Since

1.通過響應頭設置catch-control和max-age,指定該請求需要在浏覽器緩存多久,在有效時間内重複請求,則無需再次訪問服務器,直接從浏覽器訪問結果
模拟3秒請求數據
async cacheControlTest(){
//模拟3秒請求數據
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 3000);
})
ctx.set('Cache-Control','public','max-age=10')//設置Cache-Control,内容是max-age=10 10秒
我們期望10秒内不會再重複訪問服務端
10秒内都不會訪問真實的數據,而是訪問disk cache一毫秒,十秒後再重新訪問真實的數據
}

2.Expires則是通過響應頭設置Expires字段,指定緩存過期時間,在過期時間之前重複請求,則無需再次訪問服務器,直接從浏覽器獲取結果,
async cacheControlTest(){
//模拟3秒請求數據
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 3000);
})
ctx.set('Expires',new Date(Date.now() * 10 * 1000).toUTCString())//在返回頭設置Expires,過期時間設置為10秒後
我們期望10秒内不會再重複訪問服務端
10秒内都不會訪問真實的數據,而是訪問disk cache一毫秒,十秒後再重新訪問真實的數據
}

代碼算法之類
46、數組去重indexOf
var arr=[1,1,2,2,3,4,5,7,8,9,6,4,6,2,]
var arr2=[]
for(var i=0;i<arr.length;i++){
if(arr2.indexOf(arr[i])<0){
arr2.push(arr[i])
}
}
console.log(arr2)

47、es6方法數組去重
let a = [1,5,6,3,8,0,5,7,0,4,2,7,5,4,5,9,22]
let b=new Set([...a])
console.log(b);
b=[...b];
console.log(b)

48、冒泡排序
var arr=[1,3,4,6,8,0,2,5,7,4,9,2];
var temp=0;
for (var i=0;i<arr.length;i++) {
for(var j=0;j<arr.length-i;j++){
if(arr[j]<arr[j+1]){
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
console.log(arr)

49、獲取url中的參數
//測試地址:http://www.runobb.com/jqur/dfev.html?name=xiaohong&age=22
function showWindowHref(){
var sHref=window.location.href;
var args=sHref.split('?');
if(args[1]==sHref){
return '';
}
var aa=args[1].split('&');
var obj={}
for (var i=0;i<aa.length;i++) {
var bb=aa[i].split('=')
obj[bb[0]]=bb[1]
}
return obj;
}

50、降維數組
//利用[].concat.apply實現降維
var arr=[[1,2],[3,4]];
function Jw(obj){
console.log(Array.prototype.concat.apply([],obj))
return Array.prototype.concat.apply([],obj);
}
Jw(arr);
//如果concat方法的參數是一個元素,該元素會直接插入新數組中;如果參數是一個數組,該數組的各個元素将被插入到新數組中
function reduceDimension(arr){
let ret = [];
for(let i=0;i<arr.length;i++){
ret=ret.concat(arr[i])
}
console.log(ret)
return ret;
}
reduceDimension(arr)
//遞歸
function reduceDimension(arr){
let ret = [];
let toArr = function(arr){
arr.forEach(function(item){
item instanceof Array ? toArr(item) : ret.push(item);
//Array.isArray(item) ? toArr(item) : ret.push(item);
});
}
toArr(arr);
console.log(ret)
return ret;
}
reduceDimension([1, 2, [3, 4, [5, 6]]])
//flat
let list = [1, 2, 3, [4, [5]]];
let res = list.flat(Infinity)
console.log(res) // [1, 2, 3, 4, 5]

51、js判斷一個字符串中出現次數最多的字符,統計這個次數
var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
if(!json[str.charAt(i)]){
json[str.charAt(i)] = 1;
}else{
json[str.charAt(i)]++;
}
};
var iMax = 0;
var iIndex = '';
for(var i in json){
if(json[i]>iMax){
iMax = json[i];
iIndex = i;
}
}
console.log('出現次數最多的是:'+iIndex+'出現'+iMax+'次'); //出現次數最多的是:a出現 9次

let string = 'kapilalipak';
const table={};
for(let char of string) {
table[char]=table[char]+1 || 1;
}
// 輸出
console.log(table)// {k: 2, a: 3, p: 2, i: 2, l: 2}

52、寫一個function,清除字符串前後的空格。(兼容所有浏覽器)
function trim(str) {
if (str & typeof str === "string") {
return str.replace(/(^s*)|(s*)$/g,""); //去除前後空白符
}
}

53、如何用jquery禁用浏覽器的前後進退按鈕?
<script type="text/javascript" language="javascript">
$(document).ready(function() {
window.history.forward(1);
//OR
window.history.forward(-1);
});
</script>

54、獲取頁面中所有的checkbox怎麼做?(不使用第三方框架)
var inputs = document.getElementsByTagName("input");//獲取所有的input标簽對象
var checkboxArray = [];//初始化空數組,用來存放checkbox對象。
for(var i=0;i<inputs.length;i++){
var obj = inputs[i];
if(obj.type=='checkbox'){
checkboxArray.push(obj);
}
}

55、程序中捕獲異常的方法?
try{

}catch(e){

}finally{

}

56、js排序

升序降序排序函數sortNumber

const arr1 = [6,1,2,3,4];
function sortNumber(a,b){
return b-a;
}
arr1.sort(sortNumber);
console.log(arr1)
// [6, 4, 3, 2, 1]

按照flag排序,為true的在前面顯示

const arr2 = [
{ id: 10, flag: true },
{ id: 5, flag: false },
{ id: 6, flag: true },
{ id: 9, flag: false }
];
const r = arr2.sort((a, b) => b.flag - a.flag);
console.log(r);
// [
// { id: 10, flag: true },
// { id: 6, flag: true },
// { id: 5, flag: false },
// { id: 9, flag: false }
// ]

喜歡的小夥伴點個贊吧,感覺對身邊人有幫助的,麻煩動動手指,分享一下。感謝各位花時間閱讀完,同時也感謝各位的點贊和分享。

添加新評論

暱稱
郵箱
網站