一、如何正確判斷一個資料的基本型別?
1、typeof
typeof 對於原始型別(null undefined string number boolean symbol),除了null 都能顯示正確的型別
typeof null === 'object'
複製程式碼
typeof 對於物件來說,除了函式外其他都是object 型別
typeof [] === 'object'
typeof console.log === 'function'
複製程式碼
所以typeof 無法正確區分物件和null(陣列和null)
2、instanceof 判斷已知物件型別的方法
MDN 的解釋: instanceof
運算子用來測試一個物件在其原型鏈中是否存在一個建構函式的 prototype
屬性
判斷一個物件是否是資料型別的例項
因而可以衍生出 用constructor 來判斷型別({}).constructor == Object,但是constructor 是可以修改的
function Fn(){};
Fn.prototype=new Array();
var f=new Fn()
console.log(f.constructor===Fn); // false複製程式碼
,因而不準確。
3、通用型,借用Object.prototype.toString.call 來實現
var gettype=Object.prototype.toString
gettype.call('aaaa') 輸出 [object String]
gettype.call(2222) 輸出 [object Number]
gettype.call(true) 輸出 [object Boolean]
gettype.call(undefined) 輸出 [object Undefined]
複製程式碼
// 進行一層封裝
let Type = (function(){
let type = {};
let typeArr = ['String', 'Object', 'Number', 'Array', 'Undefined', 'Null', 'Symbol'];
for(let i = 0; i < typeArr.length; i++){
type['Is'+ typeArr[i]] = function(obj){
return Object.prototype.toString.call(obj) === '[object '+ typeArr[i] +']'
}
}
return type
})()
複製程式碼
eg:(第一個字母小寫,第二個字母大寫)
Type.IsFunction(function() {}) Type.IsObject({}) 這樣使用。複製程式碼
二、物件的深淺拷貝
什麼是淺拷貝?如何實現淺拷貝?什麼是深拷貝?如何實現深拷貝?
淺拷貝
利用Object.assign({}, obj) or 展開運算子 ... 來實現淺拷貝
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(a.age) // 1
console.log(b.age) // 2複製程式碼
let a = {
age: 1
}
let b = { ...a }
a.age = 2複製程式碼
深拷貝
利用JSON.parse(JSON.stringify(object)) 來解決,(基本夠用了)但這個有侷限性,
- 會忽略undefined
- 會忽略symbol
- 不能序列化函式
- 不能解決迴圈引用的物件
自個封裝 lodash 深拷貝函式
function deepClone(obj){
function isObject(o){
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
if(!isObject(obj)){
throw new Error('not object')
}
let isArray = Array.isArray(obj)
let newObj = isArray? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach((key)=>{
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return newObj
}
// eg
let obj = {
a: [1,2,3],
b:{
c:2,
d:3
}
}
let newObj = deepClone(obj)
newObj.b.c = 33
console.log(obj.b.c) // 2複製程式碼
三、call、apply 和 組合繼承
function Product(name, price){
this.name = name;
this.price = price;
}
function Food(name, price){
Product.call(this, name, price)
this.category = 'food'
}
let foot1 = new Food('chees', 5);
foot1 // 複製程式碼
通過Food 構造方法裡的call()
,成功使Food 擴充套件了name 以及price .(借用)
apply()
和 call()
都是為了改變某個函式執行時的上下文而存在的(就是為了改變函式內部的 this
指向)。然後,因為這兩個方法會立即呼叫,所以為了彌補它們的缺失,還有個方法 bind()
。
/*apply()方法 引數是已陣列的形式 add.apply(sub, [4,2]) */
function.apply(thisObj[, argArray])
/*call()方法 引數*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);複製程式碼
四、寄生組合繼承(經典例子):
function Parent(value){
this.val = value
}
Parent.prototype.getValue = function(){
console.log(this.val)
}
function Child(value){
Parent.call(this, name)
}
// Child.prototype = new Parent() 組合繼承
// 缺點就是在繼承父類函式的時候呼叫了父類建構函式,導致子類的原型上多了不需要的父類屬性,存在記憶體上的浪費
Child.prototype = Object.assign(Parent.prototype, {
constructor:{
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
複製程式碼
繼承實現的核心就是將父類的原型賦值給了子類,並且將建構函式設定為子類,這樣既解決了無用的父類屬性問題,還能正確的找到子類的建構函式。
es6 class 繼承
class Parent{
constructor(value){
this.val = value
}
getValue(){
console.log(this.val)
}
}
class Child extends Parent{
constructor(value){
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent //true複製程式碼
js 原型與原型鏈可以參考這個: https://juejin.im/post/5c72a1766fb9a049ea3993e6
五、防抖與節流
防抖即延時執行,指觸發事件後在規定時間內回撥函式只能執行一次,如果在規定時間內又觸發該事件,則會重新開始算規定時間。
應用場景: 輸入聯想功能,使用者不斷輸入時,用防抖來節約資源。
function callFn(content){
console.log('這是防抖回撥函式')
}
// 利用回撥函式 儲存定時器標識
function debounce(func ,delay = 500){
let timer = null
return function(args){
let that = this
let _args = args
if(timer) clearTimeout(timer)
timer = setTimeout(function(){
func.call(that, _args)
}, delay)
}
}
let currDebounce = debounce(callFn, 500) // 返回延時函式
// 使用
let evtFn= document.querySelector('body')
evtFn.addEventListener('click', function(e){
currDebounce (e.target.value)
})複製程式碼
節流
當持續觸發事件時,在規定時間段內只能呼叫一次,如再規定時間內又觸發該事件,則return
,什麼也不做。
應用場景,頻繁觸發事件,如滾動、resize 等。
// 定時器版
function throttle(func, delay = 500){
let timer = null;
return function(args){
let that = this;
let _args = args;
if(!timer){
timer = setTimeout(function(){
timer = null;
func.apply(that, _args)
}, delay)
}
}
}
// 時間戳版
function throttle(fun, delay = 500){
let previous = 0;
return function(args){
let now = Date.now();
let that = this;
let _args = args;
if(now - previous > delay){ // 如果時間差大於規定時間,則觸發
fun.apply(that, _args)
previous = now
}
}
}複製程式碼
垃圾回收主要分為:標記清除演算法(主流都是這個)與引用計數演算法。
將cookie設定成HttpOnly是為了防止XSS攻擊,竊取cookie內容,這樣就增加了cookie的安全性。
把cookie設定為secure,只保證 cookie 與伺服器之間的資料傳輸過程加密複製程式碼
六、渲染機制及重繪和迴流
瀏覽器的渲染機制一般分為以下幾個步驟:
-
- 處理 HTML 並構建 DOM 樹。
- 處理 CSS 構建 CSSOM 樹。
- 將 DOM 與 CSSOM 合併成一個渲染樹(Render Tree)。
- 根據渲染樹來佈局,計算每個節點的位置。
- 呼叫 GPU 繪製,合成圖層,顯示在螢幕上。
- 重繪是 當節點需要更改外觀而不會影響佈局的,比如改變 color、background-color、visibility, outline等就叫稱為重繪
- 迴流是 佈局或者幾何屬性需要改變 就稱為迴流。迴流是影響瀏覽器效能的關鍵因素,因為其變化涉及到部分頁面(或是整個頁面)的佈局更新。
迴流必定會發生重繪,重繪不一定會引發迴流。
瀏覽器優化:
現代瀏覽器大多都是通過佇列機制來批量更新佈局,瀏覽器會把修改操作放在佇列中,至少一個瀏覽器重新整理(即16.6ms)才會清空佇列,但當你獲取佈局資訊的時候,佇列中可能有會影響這些屬性或方法返回值的操作,即使沒有,瀏覽器也會強制清空佇列,觸發迴流與重繪來確保返回正確的值。
主要包括以下屬性或方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
width
、height
getComputedStyle()
getBoundingClientRect()
所以,我們應該避免頻繁的使用上述的屬性,他們都會強制渲染重新整理佇列。
減少重繪與迴流:
1、CSS
- 使用 transform 替代 top
- 使用 visibility 替換 display: none ,因為前者只會引起重繪,後者會引發迴流
- 避免使用table佈局,可能很小的一個小改動會造成整個
table
的重新佈局。 - 儘可能在DOM樹的最末端改變class,迴流是不可避免的,但可以減少其影響。儘可能在DOM樹的最末端改變class,可以限制了迴流的範圍,使其影響儘可能少的節點。
- 避免設定多層內聯樣式,CSS 選擇符從右往左匹配查詢,避免節點層級過多。
- CSS3 硬體加速(GPU加速),使用css3硬體加速,可以讓
transform
、opacity
、filters
這些動畫不會引起迴流重繪 。但是對於動畫的其它屬性,比如background-color
這些,還是會引起迴流重繪的,不過它還是可以提升這些動畫的效能。
避免頻繁操作樣式,最好一次性重寫
style
屬性,或者將樣式列表定義為class
並一次性更改class
屬性。-
避免頻繁操作DOM,建立一個
documentFragment
,在它上面應用所有DOM操作
,最後再把它新增到文件中。 -
避免頻繁讀取會引發迴流/重繪的屬性,如果確實需要多次使用,就用一個變數快取起來。
<script type="text/javascript">
var pNode,fragment = document.createDocumentFragment();
for(var i=0; i<20; i++){
pNode = document.createElement('p');
pNode.innerHTML = i;
fragment.appendChild(pNode);
}
document.body.appendChild(fragment);
</script>複製程式碼
documentFragment
節點不屬於文件樹,因此當把建立的節點新增到該物件時,並不會導致頁面的迴流。
七、前端安全性XSS 攻擊與CSRF攻擊
Cross-Site Scripting (跨站指令碼攻擊)簡稱XSS,是一種程式碼注入攻擊。攻擊者通過在目標網站上注入惡意指令碼,使之在使用者的瀏覽器上執行,進而實現攻擊。
XSS 常見的注入方法:
- 在 HTML 中內嵌的文字中,惡意內容以 script 標籤形成注入。
- 在內聯的 JavaScript 中,拼接的資料突破了原本的限制(字串,變數,方法名等)。
- 在標籤屬性中,惡意內容包含引號,從而突破屬性值的限制,注入其他屬性或者標籤。
- 在標籤的 href、src 等屬性中,包含
javascript:
(偽協議)等可執行程式碼。 - 在 onload、onerror、onclick 等事件中,注入不受控制程式碼。
- 在 style 屬性和標籤中,包含類似
background-image:url("javascript:...");
的程式碼(新版本瀏覽器已經可以防範)。 - 在 style 屬性和標籤中,包含類似
expression(...)
的 CSS 表示式程式碼(新版本瀏覽器已經可以防範)。
常用防範方法:
對輸入(和url引數)進行過濾,對輸出進行編碼,cookie 設定成 http-only
使用者輸入、url引數、post 請求引數、ajax
- httpOnly(服務端設定): 在 cookie 中設定 HttpOnly 屬性後,js指令碼將無法讀取到 cookie 資訊。
- 輸入過濾: 一般是用於對於輸入格式的檢查,例如:郵箱,電話號碼,使用者名稱,密碼……等,按照規定的格式輸入。不僅僅是前端負責,後端也要做相同的過濾檢查。因為攻擊者完全可以繞過正常的輸入流程,直接利用相關介面向伺服器傳送設定。
- 轉義 HTML: 如果拼接 HTML 是必要的,就需要對於引號,尖括號,斜槓進行轉義。
function escape(str) {
str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g, '&quto;')
str = str.replace(/'/g, ''')
str = str.replace(/`/g, '`')
str = str.replace(/\//g, '/')
return str
}複製程式碼
- 白名單: 對於顯示富文字來說,不能通過上面的辦法來轉義所有字元,因為這樣會把需要的格式也過濾掉。這種情況通常採用白名單過濾的辦法,當然也可以通過黑名單過濾,但是考慮到需要過濾的標籤和標籤屬性實在太多,更加推薦使用白名單的方式。
1、驗證碼;強制使用者必須與應用進行互動,才能完成最終請求。此種方式能很好的遏制 csrf,但是使用者體驗比較差。
2、Referer check;請求來源限制,此種方法成本最低,但是並不能保證 100% 有效,因為伺服器並不是什麼時候都能取到 Referer,而且低版本的瀏覽器存在偽造 Referer 的風險。
- 前端安全系列(一):如何防止XSS攻擊? (真的非常詳細!)
- 前端安全系列之二:如何防止CSRF攻擊?(真的非常詳細!+1)
八、Object.assign 模擬實現
if( typeof Object.assign2 !== 'function' ){
Object.defineProperty(Object, 'assign2', {
value: function(target){
if(!target){
throw new Error('cannot convert undefined or null to object')
}
var to = Object(target)
for(var index = 1; index < arguments.length;index++){
var nextSource = arguments[index]
if(nextSource){
for( var nextKey in nextSource ){
if(Object.prototype.hasOwnProperty.call(nextSource, nextKey)){
to[nextKey] = nextSource[nextKey]
}
}
}
}
return to;
},
writable: true,
configurable: true
})
}複製程式碼
// 測試用例
let a = {
name: "advanced",
age: 18
}
let b = {
name: "muyiy",
book: {
title: "You Don't Know JS",
price: "45"
}
}
let c = Object.assign2(a, b);
console.log(c);
// {
// name: "muyiy",
// age: 18,
// book: {title: "You Don't Know JS", price: "45"}
// }
console.log(a === c);
// true複製程式碼
九、vue 記錄
一般來說,vue 元件分成三類:
1、 由vue-router 產生的每個頁面,它本質上也是一個元件(.vue),主要承載當前頁面的html 結構,會包含資料獲取、資料整理、資料展示等業務操作,在實際專案開發中,我們寫的大部分程式碼都是這類元件,在協同開發時,每人維護自己的路由頁面,很少有交集。這類相對也是最好寫的,能完成需求就行。
2、 不包含業務,是個獨立、具體的功能基礎元件,比如模態框、日期選擇器。獨立元件的開發難度要高於第一類元件,它側重點是API的設計、相容性、效能、以及複雜的功能,也會包含非常多的技巧,比如在不依賴vuex 的情況下,各元件間的通訊。一個具有資料校驗功能的輸入框。
3、 業務元件,在業務中被多個頁面複用,會包含頁面元素,通用性要差一些,(依託於專案,可以使用專案中的技術棧,如vuex、axios等)。通用彈層
一個再複雜的元件,都是由三部分組成:prop、event、slot 。 Props 最好用物件的寫法,這樣可以針對每個屬性設定型別、預設值或自定義校驗屬性值。
十、JavaScript模組化方案
在es6之前,js 並沒有原生的模組。模組化方案的三個階段過程:
1、全域性變數+名稱空間 (window.XX ,以及自執行函式等操作)
2、AMD&commonjs 各類規範帶到前端
AMD模組類似:
define(function(require){
const bar = require('./bar')
return function(){}
})複製程式碼
commonJs 規範,它本不適合瀏覽器環境,但依賴現代打包工具進行轉換之後就可以在瀏覽器中執行。commonjs 規範格式更加簡潔,(nodejs 模組正在使用的就是commonjs 規範)
const bar = require('./bar')
module.exports = function(){
// ....
}複製程式碼
3、es6模組
模組化方案
import bar from './bar'
export default function(){
// ...
}複製程式碼
十一、webpack 與gulp 有什麼本質區別
gulp 是工具鏈,構建工具,可以配合各種外掛做js 壓縮,css 壓縮,less 壓縮,替代手工實現自動化工作。 1、 構建工具 2、自動化、3、提高效率
webpack 是檔案打包工具,把專案的各種js ,css 打包合併成一個或多個檔案,主要用於模組化打包。1、打包工具 2、模組化識別 3、編譯模組程式碼 4、程式碼拆分5、模組熱更新。
十二、常用es6 以及es6 規範
到時候移到掘金
https://blog.csdn.net/u010427666/article/details/54944986
十三、seo 相關知識以及如何優化
文件&文章連結
十四、內部原型繼承原理
需重新整理
https://blog.csdn.net/u010427666/article/details/53244698
十五、你需要知道的css
css 動畫、flex 佈局等
https://juejin.im/post/5c7646e2f265da2d8e70f681
十五、效能優化點
程式碼層面、網路層面、打包依賴等
https://segmentfault.com/a/1190000008273435
十六、瀏覽器快取機制(強快取&協商快取)
是否過期根據header中的Cache-Control和Expires來判斷,(快取過期後Etag 與last-modify 是由伺服器來判斷)
nginx 開啟強快取
location ~ ^/static/.*(jpg|jpeg|png|gif|ico|js|css|ttf)$ {
root /home/service/www/web;
expires 12h;
add_header Cache-Control "public";
}
複製程式碼
nginx 開啟gizp 壓縮(壓縮效率特高)
gzip on; #開啟gzip壓縮輸出
gzip_min_length 1k; #最小壓縮檔案大小
gzip_buffers 4 16k; #壓縮緩衝區
# gzip_http_version 1.0; #壓縮版本(預設1.1,前端如果是squid2.5請使用1.0)
gzip_comp_level 3; #壓縮等級
gzip_types text/plain application/javascript application/x-javascript text/css application/xml;
gzip_vary on;
gzip_disable "MSIE [1-7]\.";
複製程式碼
十七、陣列亂序
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
return Math.random() - 0.5;
});
複製程式碼
洗牌演算法:
function shuffle (arr) { var len = arr.length for (var i = 0;i < len - 1;i++) { var idx = Math.floor(Math.random() * (len - i)) var temp = arr[idx] arr[idx] = arr[len - i - 1] arr[len - i - 1] = temp } return arr}複製程式碼
十八、BFC(塊級格式化上下文)
產生BFC:
- 根元素或包含根元素的元素
- 浮動元素 float = left | right 或 inherit(≠ none)
- 絕對定位元素 position = absolute 或 fixed
- display = inline-block | flex | inline-flex | table-cell 或 table-caption
- overflow = hidden | auto 或 scroll (≠ visible)
BFC 作用:
1、 清除浮動,即在容器中建立BFC
2、導致外邊距摺疊塌陷
我們必須記住的是外邊距摺疊(Margin collapsing)只會發生在屬於同一BFC的塊級元素之間。如果它們屬於不同的 BFC,它們之間的外邊距則不會摺疊。所以通過建立一個不同的 BFC ,就可以避免外邊距摺疊。
https://segmentfault.com/a/1190000013647777
十九、單執行緒的JavaScript引擎是怎麼配合瀏覽器核心處理這些定時器和響應瀏覽器事件的呢?
1、單執行緒,多程式
https://www.cnblogs.com/joyco773/p/6038022.html
http://www.xuanfengge.com/js-realizes-precise-countdown.html
二十、websocket 實時推送
相關連結: https://juejin.im/post/5c20e5766fb9a049b13e387b
二十一、http2.0的新特性有哪些?(選項是多路複用、頭部壓縮、設定優先順序、服務端推送、二進位制傳輸)
相關連結: https://juejin.im/post/5c8f30606fb9a070ef60996d
別人優質彙總:
https://juejin.im/post/5c64d15d6fb9a049d37f9c20