當下,正面臨着近幾年來的最嚴重的互聯網寒冬,聽得最多的一句話即是:相見於江湖~🤣。縮減HC、裁人不絕於耳,你們都是人心惶惶,年前如此,年後想必確定又是一場更爲慘烈的江湖廝殺。但博主始終相信,寒冬之中,人才更是尤其珍貴。只要有過硬的操做和裝備,在逆風局下,一樣也能來一波收割翻盤。javascript
博主也是年前經歷了一番廝殺,最終拿到多家大廠的 offer。在閉關修煉的過程當中,本身整理出了一套面試祕籍供本身反覆研究,後來給了多位有須要的兄臺,均表示至關靠譜,理應在這寒冬之中回報於社會。因而決定花點精力整理成文,讓你們能比較系統的反覆學習,快速提高本身。css
面試當然有技巧,但毫不是僞造與吹流弊,經過一段短期沉下心來閉關修煉,出山收割,步入大廠,薪資翻番,豈不爽哉?🤓html
中篇已新鮮出爐,速速殺入前端
小菜雞博客求贊 🙂 blogvue
想必你們很厭煩筆試和考察知識點。由於其實在平時實戰中,講究的是開發效率,不多會去刻意記下一些細節和深挖知識點,腦海中都是一些分散的知識點,沒法系統性地關聯成網,一直處於似曾相識的狀態。不知道多少人和博主同樣,至今每次寫阻止冒泡都須要谷歌一番如何拼寫。🤪。java
以如此的狀態,定然是沒法在面試的戰場上縱橫的。其實面試就猶如考試,你們回想下高考以前所作的事,無非就是 理解 和 系統性關聯記憶。本祕籍的知識點較多,花點時間一個個理解並記憶後,天然也就融會貫通,無所畏懼。node
因爲本祕籍爲了便於記憶,快速達到應試狀態,相似於複習知識大綱。知識點會盡可能的精簡與提煉知識脈絡,並不去展開深刻細節,面面俱到。有興趣或者有疑問的童鞋能夠自行谷歌下對應知識點的詳細內容。😋react
頁面渲染時,dom 元素所採用的 佈局模型。可經過box-sizing
進行設置。根據計算寬高的區域可分爲:webpack
content-box
(W3C 標準盒模型)border-box
(IE 盒模型)padding-box
margin-box
(瀏覽器未實現)塊級格式化上下文,是一個獨立的渲染區域,讓處於 BFC 內部的元素與外部的元素相互隔離,使內外元素的定位不會相互影響。css3
IE下爲 Layout,可經過 zoom:1 觸發
觸發條件:
position: absolute/fixed
display: inline-block / table
float
元素ovevflow
!== visible
規則:
應用:
margin
重疊div
都位於同一個 BFC 區域之中)元素提高爲一個比較特殊的圖層,在三維空間中 (z軸) 高出普通元素一等。
觸發條件
html
)position
flex
transform
opacity
filter
will-change
-webkit-overflow-scrolling
層疊等級:層疊上下文在z軸上的排序
z-index
的優先級最高水平居中
text-align: center
margin: 0 auto
absolute + transform
flex + justify-content: center
垂直居中
line-height: height
absolute + transform
flex + align-items: center
table
水平垂直居中
absolute + transform
flex + justify-content + align-items
!important
> 行內樣式 > #id
> .class
> tag
> * > 繼承 > 默認:after / <br> : clear: both
link
功能較多,能夠定義 RSS,定義 Rel 等做用,而@import
只能用於加載 csslink
時,頁面會同步加載所引的 css,而@import
所引用的 css 會等到頁面加載完才被加載@import
須要 IE5 以上才能使用link
可使用 js 動態引入,@import
不行CSS預處理器的原理: 是將類 CSS 語言經過 Webpack 編譯 轉成瀏覽器可讀的真正 CSS。在這層編譯之上,即可以賦予 CSS 更多更強大的功能,經常使用功能:
面試中通常不會重點考察該點,通常介紹下本身在實戰項目中的經驗便可~
transition
: 過渡動畫
transition-property
: 屬性transition-duration
: 間隔transition-timing-function
: 曲線transition-delay
: 延遲transitionend
animation / keyframes
animation-name
: 動畫名稱,對應@keyframes
animation-duration
: 間隔animation-timing-function
: 曲線animation-delay
: 延遲animation-iteration-count
: 次數
infinite
: 循環動畫animation-direction
: 方向
alternate
: 反向播放animation-fill-mode
: 靜止模式
forwards
: 中止時,保留最後一幀backwards
: 中止時,回到第一幀both
: 同時運用 forwards / backwards
animationend
動畫屬性: 儘可能使用動畫屬性進行動畫,能擁有較好的性能表現
translate
scale
rotate
skew
opacity
color
一般,CSS 並非重點的考察領域,但這實際上是因爲如今國內業界對 CSS 的專一不夠致使的,真正精通並專一於 CSS 的團隊和人才並很少。所以若是能在 CSS 領域有本身的看法和經驗,反而會爲至關的加分和脫穎而出。
原型(prototype)
: 一個簡單的對象,用於實現對象的 屬性繼承。能夠簡單的理解成對象的爹。在 Firefox 和 Chrome 中,每一個JavaScript
對象中都包含一個__proto__
(非標準)的屬性指向它爹(該對象的原型),可obj.__proto__
進行訪問。
構造函數: 能夠經過new
來 新建一個對象 的函數。
實例: 經過構造函數和new
建立出來的對象,即是實例。 實例經過__proto__
指向原型,經過constructor
指向構造函數。
說了一大堆,你們可能有點懵逼,這裏來舉個栗子,以Object
爲例,咱們經常使用的Object
即是一個構造函數,所以咱們能夠經過它構建實例。
// 實例
const instance = new Object()
複製代碼
則此時, 實例爲instance
, 構造函數爲Object
,咱們知道,構造函數擁有一個prototype
的屬性指向原型,所以原型爲:
// 原型
const prototype = Object.prototype
複製代碼
這裏咱們能夠來看出三者的關係:
實例.__proto__ === 原型
原型.constructor === 構造函數
構造函數.prototype === 原型
// 這條線實際上是是基於原型進行獲取的,能夠理解成一條基於原型的映射線
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
實例.constructor === 構造函數
複製代碼
此處感謝 caihaihong 童鞋的指出。
放大來看,我畫了張圖供你們完全理解:
原型鏈是由原型對象組成,每一個對象都有 __proto__
屬性,指向了建立該對象的構造函數的原型,__proto__
將對象鏈接起來組成了原型鏈。是一個用來實現繼承和共享屬性的有限的對象鏈。
屬性查找機制: 當查找對象的屬性時,若是實例對象自身不存在該屬性,則沿着原型鏈往上一級查找,找到時則輸出,不存在時,則繼續沿着原型鏈往上一級查找,直至最頂級的原型對象Object.prototype
,如仍是沒找到,則輸出undefined
;
屬性修改機制: 只會修改實例對象自己的屬性,若是不存在,則進行添加該屬性,若是須要修改原型的屬性時,則能夠用: b.prototype.x = 2
;可是這樣會形成全部繼承於該對象的實例的屬性發生改變。
執行上下文能夠簡單理解爲一個對象:
它包含三個部分:
this
指向它的類型:
eval
執行上下文代碼執行過程:
push
到執行棧頂層pop
移除出執行棧,控制權交還全局上下文 (caller),繼續執行變量對象,是執行上下文中的一部分,能夠抽象爲一種 數據做用域,其實也能夠理解爲就是一個簡單的對象,它存儲着該執行上下文中的全部 變量和函數聲明(不包含函數表達式)。
活動對象 (AO): 當變量對象所處的上下文爲 active EC 時,稱爲活動對象。
執行上下文中還包含做用域鏈。理解做用域以前,先介紹下做用域。做用域其實可理解爲該上下文中聲明的 變量和聲明的做用範圍。可分爲 塊級做用域 和 函數做用域
特性:
let foo = function() { console.log(1) };
(function foo() {
foo = 10 // 因爲foo在函數中只爲可讀,所以賦值無效
console.log(foo)
}())
// 結果打印: ƒ foo() { foo = 10 ; console.log(foo) }
複製代碼
咱們知道,咱們能夠在執行上下文中訪問到父級甚至全局的變量,這即是做用域鏈的功勞。做用域鏈能夠理解爲一組對象列表,包含 父級和自身的變量對象,所以咱們便能經過做用域鏈訪問到父級裏聲明的變量或者函數。
[[scope]]
屬性: 指向父級變量對象和做用域鏈,也就是包含了父級的[[scope]]
和AO
如此 [[scopr]]
包含[[scope]]
,便自上而下造成一條 鏈式做用域。
閉包屬於一種特殊的做用域,稱爲 靜態做用域。它的定義能夠理解爲: 父函數被銷燬 的狀況下,返回出的子函數的[[scope]]
中仍然保留着父級的單變量對象和做用域鏈,所以能夠繼續訪問到父級的變量對象,這樣的函數稱爲閉包。
閉包會產生一個很經典的問題:
[[scope]]
都是同時指向父級,是徹底共享的。所以當父級的變量對象被修改時,全部子函數都受到影響。解決:
[[scope]]
向上查找setTimeout
包裹,經過第三個參數傳入<script>
引入<script>
<script defer>
: 延遲加載,元素解析完成後執行<script async>
: 異步加載,但執行時會阻塞元素渲染淺拷貝: 以賦值的形式拷貝引用對象,仍指向同一個地址,修改時原對象也會受到影響
Object.assign
深拷貝: 徹底拷貝一個新對象,修改時原對象再也不受到任何影響
JSON.parse(JSON.stringify(obj))
: 性能最快
undefined
、或symbol
時,沒法拷貝obj.__proto__ = Con.prototype
apply
能在實例的 原型對象鏈 中找到該構造函數的prototype
屬性所指向的 原型對象,就返回true
。即:
// __proto__: 表明原型對象鏈
instance.[__proto__...] === instance.constructor.prototype
// return true
複製代碼
當你發現任何代碼開始寫第二遍時,就要開始考慮如何複用。通常有如下的方式:
extend
mixin
apply/call
在 JS 中,繼承一般指的即是 原型鏈繼承,也就是經過指定原型,並能夠經過原型鏈繼承原型上的屬性或者方法。
var inherit = (function(c,p){
var F = function(){};
return function(c,p){
F.prototype = p.prototype;
c.prototype = new F();
c.uber = p.prototype;
c.prototype.constructor = c;
}
})();
複製代碼
class / extends
你們都知道 JS 中在使用運算符號或者對比符時,會自帶隱式轉換,規則以下:
valueOf
-> toString
boolean/null
-> 數字undefined
-> NaN
[1].toString() === '1'
{}.toString() === '[object object]'
NaN
!== NaN
、+undefined 爲 NaN
判斷 Target 的類型,單單用 typeof 並沒有法徹底知足,這其實並非 bug,本質緣由是 JS 的萬物皆對象的理論。所以要真正完美判斷時,咱們須要區分對待:
null
): 使用 String(null)
string / number / boolean / undefined
) + function
: 直接使用 typeof
便可Array / Date / RegExp Error
): 調用toString
後根據[object XXX]
進行判斷很穩的判斷封裝:
let class2type = {}
'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase())
function type(obj) {
if (obj == null) return String(obj)
return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}
複製代碼
模塊化開發在現代開發中已經是必不可少的一部分,它大大提升了項目的可維護、可拓展和可協做性。一般,咱們 在瀏覽器中使用 ES6 的模塊化支持,在 Node 中使用 commonjs 的模塊化支持。
分類:
import / export
require / module.exports / exports
require / defined
require
與import
的區別
require
支持 動態導入,import
不支持,正在提案 (babel 下可支持)require
是 同步 導入,import
屬於 異步 導入require
是 值拷貝,導出值變化不會影響導入值;import
指向 內存地址,導入值會隨導出值而變化防抖與節流函數是一種最經常使用的 高頻觸發優化方式,能對性能有較大的幫助。
function debounce(fn, wait, immediate) {
let timer = null
return function() {
let args = arguments
let context = this
if (immediate && !timer) {
fn.apply(context, args)
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
複製代碼
function throttle(fn, wait, immediate) {
let timer = null
let callNow = immediate
return function() {
let context = this,
args = arguments
if (callNow) {
fn.apply(context, args)
callNow = false
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
}
複製代碼
因爲 JS 的設計原理: 在函數中,能夠引用運行環境中的變量。所以就須要一個機制來讓咱們能夠在函數體內部獲取當前的運行環境,這即是this
。
所以要明白 this
指向,其實就是要搞清楚 函數的運行環境,說人話就是,誰調用了函數。例如:
obj.fn()
,即是 obj
調用了函數,既函數中的 this === obj
fn()
,這裏能夠當作 window.fn()
,所以 this === window
但這種機制並不徹底能知足咱們的業務需求,所以提供了三種方式能夠手動修改 this
的指向:
call: fn.call(target, 1, 2)
apply: fn.apply(target, [1, 2])
bind: fn.bind(target)(1,2)
因爲 Babel 的強大和普及,如今 ES6/ES7 基本上已是現代化開發的必備了。經過新的語法糖,能讓代碼總體更爲簡潔和易讀。
聲明
let / const
: 塊級做用域、不存在變量提高、暫時性死區、不容許重複聲明const
: 聲明常量,沒法修改解構賦值
class / extend
: 類聲明與繼承
Set / Map
: 新的數據結構
異步解決方案:
Promise
的使用與實現
generator
:
yield
: 暫停代碼next()
: 繼續執行代碼function* helloWorld() {
yield 'hello';
yield 'world';
return 'ending';
}
const generator = helloWorld();
generator.next() // { value: 'hello', done: false }
generator.next() // { value: 'world', done: false }
generator.next() // { value: 'ending', done: true }
generator.next() // { value: undefined, done: true }
複製代碼
await / async
: 是generator
的語法糖, babel中是基於promise
實現。async function getUserByAsync(){
let user = await fetchUser();
return user;
}
const user = await getUserByAsync()
console.log(user)
複製代碼
抽象語法樹 (Abstract Syntax Tree),是將代碼逐字母解析成 樹狀對象 的形式。這是語言之間的轉換、代碼語法檢查,代碼風格檢查,代碼格式化,代碼高亮,代碼錯誤提示,代碼自動補全等等的基礎。例如:
function square(n){
return n * n
}
複製代碼
經過解析轉化成的AST
以下圖:
在一個函數中,首先填充幾個參數,而後再返回一個新的函數的技術,稱爲函數的柯里化。一般可用於在不侵入函數的前提下,爲函數 預置通用參數,供屢次重複調用。
const add = function add(x) {
return function (y) {
return x + y
}
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21
複製代碼
map
: 遍歷數組,返回回調返回值組成的新數組
forEach
: 沒法break
,能夠用try/catch
中throw new Error
來中止
filter
: 過濾
some
: 有一項返回true
,則總體爲true
every
: 有一項返回false
,則總體爲false
join
: 經過指定鏈接符生成字符串
push / pop
: 末尾推入和彈出,改變原數組, 返回推入/彈出項
unshift / shift
: 頭部推入和彈出,改變原數組,返回操做項
sort(fn) / reverse
: 排序與反轉,改變原數組
concat
: 鏈接數組,不影響原數組, 淺拷貝
slice(start, end)
: 返回截斷後的新數組,不改變原數組
splice(start, number, value...)
: 返回刪除元素組成的數組,value 爲插入項,改變原數組
indexOf / lastIndexOf(value, fromIndex)
: 查找數組項,返回對應的下標
reduce / reduceRight(fn(prev, cur), defaultPrev)
: 兩兩執行,prev 爲上次化簡函數的return
值,cur 爲當前值(從第二項開始)
數組亂序:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
return Math.random() - 0.5;
});
複製代碼
Array.prototype.flat = function() {
return this.toString().split(',').map(item => +item )
}
複製代碼
不一樣標籤頁間的通信,本質原理就是去運用一些能夠 共享的中間介質,所以比較經常使用的有如下方法:
經過父頁面window.open()
和子頁面postMessage
window.open('about: blank')
和 tab.location.href = '*'
設置同域下共享的localStorage
與監聽window.onstorage
設置共享cookie
與不斷輪詢髒檢查(setInterval
)
藉助服務端或者中間層實現
事件循環是指: 執行一個宏任務,而後執行清空微任務列表,循環再執行宏任務,再清微任務列表
microtask(jobs)
: promise / ajax / Object.observe(該方法已廢棄)
macrotask(task)
: setTimout / script / IO / UI Rendering
當元素的樣式發生變化時,瀏覽器須要觸發更新,從新繪製元素。這個過程當中,有兩種類型的操做,即重繪與迴流。
重繪(repaint): 當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時因爲只須要UI層面的從新像素繪製,所以 損耗較少
迴流(reflow): 當元素的尺寸、結構或觸發某些屬性時,瀏覽器會從新渲染頁面,稱爲迴流。此時,瀏覽器須要從新通過計算,計算後還須要從新頁面佈局,所以是較重的操做。會觸發迴流的操做:
迴流一定觸發重繪,重繪不必定觸發迴流。重繪的開銷較小,迴流的代價較高。
css
table
佈局position
屬性爲absolute
或fixed
的元素上javascript
class
進行樣式修改dom
的增刪次數,可以使用 字符串 或者 documentFragment
一次性插入display: none
後修改咱們常常須要對業務中的一些數據進行存儲,一般能夠分爲 短暫性存儲 和 持久性儲存。
短暫性的時候,咱們只須要將數據存在內存中,只在運行時可用
持久性存儲,能夠分爲 瀏覽器端 與 服務器端
cookie
: 一般用於存儲用戶身份,登陸狀態等
localStorage / sessionStorage
: 長久儲存/窗口關閉刪除, 體積限制爲 4~5MindexDB
現代瀏覽器爲JavaScript
創造的 多線程環境。能夠新建並將部分任務分配到worker
線程並行運行,兩個線程可 獨立運行,互不干擾,可經過自帶的 消息機制 相互通訊。
基本用法:
// 建立 worker
const worker = new Worker('work.js');
// 向主進程推送消息
worker.postMessage('Hello World');
// 監聽主進程來的消息
worker.onmessage = function (event) {
console.log('Received message ' + event.data);
}
複製代碼
限制:
document
/ window
/ alert
/ confirm
垃圾回收: 將內存中再也不使用的數據進行清理,釋放出內存空間。V8 將內存分紅 新生代空間 和 老生代空間。
可用 chrome 中的 timeline 進行內存標記,可視化查看內存的變化狀況,找出異常點。
1.0 協議缺陷:
1.1 改進:
2.0:
https: 較爲安全的網絡傳輸協議
TCP:
緩存策略: 可分爲 強緩存 和 協商緩存
Cache-Control/Expires: 瀏覽器判斷緩存是否過時,未過時時,直接使用強緩存,Cache-Control的 max-age 優先級高於 Expires
當緩存已通過期時,使用協商緩存
Last-Modified 缺點:
Etag 的優先級高於 Last-Modified
二者詳細對好比下圖:
Websocket 是一個 持久化的協議, 基於 http , 服務端能夠 主動 push
兼容:
new WebSocket(url)
ws.onerror = fn
ws.onclose = fn
ws.onopen = fn
ws.onmessage = fn
ws.send()
創建鏈接前,客戶端和服務端須要經過握手來確認對方:
setTimeout / setInterval
隊列回調callback
setTimeout / setInterval
, 則返回 timer 階段setImmediate
,則前往 check 階段setImmediate
<script>
標籤不受跨域限制的特色,缺點是隻能支持 get 請求function jsonp(url, jsonpCallback, success) {
const script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
複製代碼
在下次dom
更新循環結束以後執行延遲迴調,可用於獲取更新後的dom
狀態
新版本中默認是microtasks
, v-on
中會使用macrotasks
macrotasks
任務的實現:
setImmediate / MessageChannel / setTimeout
_init_
initLifecycle/Event
,往vm
上掛載各類屬性callHook: beforeCreated
: 實例剛建立initInjection/initState
: 初始化注入和 data 響應性created
: 建立完成,屬性已經綁定, 但還未生成真實dom
$el / vm.$mount()
template
: 解析成render function
*.vue
文件: vue-loader
會將<template>
編譯成render function
beforeMount
: 模板編譯/掛載以前render function
,生成真實的dom
,並替換到dom tree
中mounted
: 組件已掛載update
:
diff
算法,比對改變是否須要觸發UI更新flushScheduleQueue
watcher.before
: 觸發beforeUpdate
鉤子 - watcher.run()
: 執行watcher
中的 notify
,通知全部依賴項更新UIupdated
鉤子: 組件已更新actived / deactivated(keep-alive)
: 不銷燬,緩存,組件激活與失活
destroy
:
beforeDestroy
: 銷燬開始remove()
: 刪除節點watcher.teardown()
: 清空依賴vm.$off()
: 解綁監聽destroyed
: 完成後觸發鉤子上面是vue
的聲明週期的簡單梳理,接下來咱們直接以代碼的形式來完成vue
的初始化
new Vue({})
// 初始化Vue實例
function _init() {
// 掛載屬性
initLifeCycle(vm)
// 初始化事件系統,鉤子函數等
initEvent(vm)
// 編譯slot、vnode
initRender(vm)
// 觸發鉤子
callHook(vm, 'beforeCreate')
// 添加inject功能
initInjection(vm)
// 完成數據響應性 props/data/watch/computed/methods
initState(vm)
// 添加 provide 功能
initProvide(vm)
// 觸發鉤子
callHook(vm, 'created')
// 掛載節點
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
// 掛載節點實現
function mountComponent(vm) {
// 獲取 render function
if (!this.options.render) {
// template to render
// Vue.compile = compileToFunctions
let { render } = compileToFunctions()
this.options.render = render
}
// 觸發鉤子
callHook('beforeMounte')
// 初始化觀察者
// render 渲染 vdom,
vdom = vm.render()
// update: 根據 diff 出的 patchs 掛載成真實的 dom
vm._update(vdom)
// 觸發鉤子
callHook(vm, 'mounted')
}
// 更新節點實現
funtion queueWatcher(watcher) {
nextTick(flushScheduleQueue)
}
// 清空隊列
function flushScheduleQueue() {
// 遍歷隊列中全部修改
for(){
// beforeUpdate
watcher.before()
// 依賴局部更新節點
watcher.update()
callHook('updated')
}
}
// 銷燬實例實現
Vue.prototype.$destory = function() {
// 觸發鉤子
callHook(vm, 'beforeDestory')
// 自身及子節點
remove()
// 刪除依賴
watcher.teardown()
// 刪除監聽
vm.$off()
// 觸發鉤子
callHook(vm, 'destoryed')
}
複製代碼
看完生命週期後,裏面的watcher
等內容實際上是數據響應中的一部分。數據響應的實現由兩部分構成: 觀察者( watcher ) 和 依賴收集器( Dep ),其核心是 defineProperty
這個方法,它能夠 重寫屬性的 get 與 set 方法,從而完成監聽數據的改變。
defineProperty
重寫每一個屬性的 get/set(defineReactive
)
get
: 收集依賴
Dep.depend()
watcher.addDep()
set
: 派發更新
Dep.notify()
watcher.update()
queenWatcher()
nextTick
flushScheduleQueue
watcher.run()
updateComponent()
你們能夠先看下面的數據相應的代碼實現後,理解後就比較容易看懂上面的簡單脈絡了。
let data = {a: 1}
// 數據響應性
observe(data)
// 初始化觀察者
new Watcher(data, 'name', updateComponent)
data.a = 2
// 簡單表示用於數據更新後的操做
function updateComponent() {
vm._update() // patchs
}
// 監視對象
function observe(obj) {
// 遍歷對象,使用 get/set 從新定義對象的每一個屬性值
Object.keys(obj).map(key => {
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, k, v) {
// 遞歸子屬性
if (type(v) == 'object') observe(v)
// 新建依賴收集器
let dep = new Dep()
// 定義get/set
Object.defineProperty(obj, k, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 當有獲取該屬性時,證實依賴於該對象,所以被添加進收集器中
if (Dep.target) {
dep.addSub(Dep.target)
}
return v
},
// 從新設置值時,觸發收集器的通知機制
set: function reactiveSetter(nV) {
v = nV
dep.nofify()
},
})
}
// 依賴收集器
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
notify() {
this.subs.map(sub => {
sub.update()
})
}
}
Dep.target = null
// 觀察者
class Watcher {
constructor(obj, key, cb) {
Dep.target = this
this.cb = cb
this.obj = obj
this.key = key
this.value = obj[key]
Dep.target = null
}
addDep(Dep) {
Dep.addSub(this)
}
update() {
this.value = this.obj[this.key]
this.cb(this.value)
}
before() {
callHook('beforeUpdate')
}
}
複製代碼
建立 dom 樹
樹的diff
,同層對比,輸出patchs(listDiff/diffChildren/diffProps)
tagName
與key
不變, 對比props
,繼續遞歸遍歷子樹
tagName
和key
值變化了,則直接替換成新節點渲染差別
patchs
, 把須要更改的節點取出來dom
// diff算法的實現
function diff(oldTree, newTree) {
// 差別收集
let pathchs = {}
dfs(oldTree, newTree, 0, pathchs)
return pathchs
}
function dfs(oldNode, newNode, index, pathchs) {
let curPathchs = []
if (newNode) {
// 當新舊節點的 tagName 和 key 值徹底一致時
if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
// 繼續比對屬性差別
let props = diffProps(oldNode.props, newNode.props)
curPathchs.push({ type: 'changeProps', props })
// 遞歸進入下一層級的比較
diffChildrens(oldNode.children, newNode.children, index, pathchs)
} else {
// 當 tagName 或者 key 修改了後,表示已是全新節點,無需再比
curPathchs.push({ type: 'replaceNode', node: newNode })
}
}
// 構建出整顆差別樹
if (curPathchs.length) {
if(pathchs[index]){
pathchs[index] = pathchs[index].concat(curPathchs)
} else {
pathchs[index] = curPathchs
}
}
}
// 屬性對比實現
function diffProps(oldProps, newProps) {
let propsPathchs = []
// 遍歷新舊屬性列表
// 查找刪除項
// 查找修改項
// 查找新增項
forin(olaProps, (k, v) => {
if (!newProps.hasOwnProperty(k)) {
propsPathchs.push({ type: 'remove', prop: k })
} else {
if (v !== newProps[k]) {
propsPathchs.push({ type: 'change', prop: k , value: newProps[k] })
}
}
})
forin(newProps, (k, v) => {
if (!oldProps.hasOwnProperty(k)) {
propsPathchs.push({ type: 'add', prop: k, value: v })
}
})
return propsPathchs
}
// 對比子級差別
function diffChildrens(oldChild, newChild, index, pathchs) {
// 標記子級的刪除/新增/移動
let { change, list } = diffList(oldChild, newChild, index, pathchs)
if (change.length) {
if (pathchs[index]) {
pathchs[index] = pathchs[index].concat(change)
} else {
pathchs[index] = change
}
}
// 根據 key 獲取本來匹配的節點,進一步遞歸從頭開始對比
oldChild.map((item, i) => {
let keyIndex = list.indexOf(item.key)
if (keyIndex) {
let node = newChild[keyIndex]
// 進一步遞歸對比
dfs(item, node, index, pathchs)
}
})
}
// 列表對比,主要也是根據 key 值查找匹配項
// 對比出新舊列表的新增/刪除/移動
function diffList(oldList, newList, index, pathchs) {
let change = []
let list = []
const newKeys = getKey(newList)
oldList.map(v => {
if (newKeys.indexOf(v.key) > -1) {
list.push(v.key)
} else {
list.push(null)
}
})
// 標記刪除
for (let i = list.length - 1; i>= 0; i--) {
if (!list[i]) {
list.splice(i, 1)
change.push({ type: 'remove', index: i })
}
}
// 標記新增和移動
newList.map((item, i) => {
const key = item.key
const index = list.indexOf(key)
if (index === -1 || key == null) {
// 新增
change.push({ type: 'add', node: item, index: i })
list.splice(i, 0, key)
} else {
// 移動
if (index !== i) {
change.push({
type: 'move',
form: index,
to: i,
})
move(list, index, i)
}
}
})
return { change, list }
}
複製代碼
let data = { a: 1 }
let reactiveData = new Proxy(data, {
get: function(target, name){
// ...
},
// ...
})
複製代碼
mode
hash
history
this.$router.push()
<router-link to=""></router-link>
<router-view></router-view>
state
: 狀態中心mutations
: 更改狀態actions
: 異步更改狀態getters
: 獲取狀態modules
: 將state
分紅多個modules
,便於管理其實算法方面在前端的實際項目中涉及得並很少,但仍是須要精通一些基礎性的算法,一些公司仍是會有這方面的需求和考覈,建議你們仍是須要稍微準備下,這屬於加分題。
function bubleSort(arr) {
var len = arr.length;
for (let outer = len ; outer >= 2; outer--) {
for(let inner = 0; inner <=outer - 1; inner++) {
if(arr[inner] > arr[inner + 1]) {
[arr[inner],arr[inner+1]] = [arr[inner+1],arr[inner]]
}
}
}
return arr;
}
複製代碼
function selectSort(arr) {
var len = arr.length;
for(let i = 0 ;i < len - 1; i++) {
for(let j = i ; j<len; j++) {
if(arr[j] < arr[i]) {
[arr[i],arr[j]] = [arr[j],arr[i]];
}
}
}
return arr
}
複製代碼
function insertSort(arr) {
for(let i = 1; i < arr.length; i++) { //外循環從1開始,默認arr[0]是有序段
for(let j = i; j > 0; j--) { //j = i,將arr[j]依次插入有序段中
if(arr[j] < arr[j-1]) {
[arr[j],arr[j-1]] = [arr[j-1],arr[j]];
} else {
break;
}
}
}
return arr;
}
複製代碼
function quickSort(arr) {
if(arr.length <= 1) {
return arr; //遞歸出口
}
var left = [],
right = [],
current = arr.splice(0,1);
for(let i = 0; i < arr.length; i++) {
if(arr[i] < current) {
left.push(arr[i]) //放在左邊
} else {
right.push(arr[i]) //放在右邊
}
}
return quickSort(left).concat(current,quickSort(right));
}
複製代碼
希爾排序:不定步數的插入排序,插入排序
口訣: 插冒歸基穩定,快選堆希不穩定
穩定性: 同大小狀況下是否可能會被交換位置, 虛擬dom的diff,不穩定性會致使從新渲染;
初始在第一級,到第一級有1種方法(s(1) = 1),到第二級也只有一種方法(s(2) = 1), 第三級(s(3) = s(1) + s(2))
function cStairs(n) {
if(n === 1 || n === 2) {
return 1;
} else {
return cStairs(n-1) + cStairs(n-2)
}
}
複製代碼
有n個硬幣,其中1個爲假幣,假幣重量較輕,你有一把天平,請問,至少須要稱多少次能保證必定找到假幣?
因爲精力時間及篇幅有限,這篇就先寫到這。你們慢慢來不急。。🤪。下篇打算準備如下內容,我也得補補課先:
因爲精力時間及篇幅有限,這篇就先寫到這。你們慢慢來不急。。🤪。調整下心態,繼續
在面試中,不少領域並無真正的答案,能回答到什麼樣的深度,仍是得靠本身真正的去使用和研究。知識面的廣度與深度應該並行,儘可能的拓張本身的領域,至少都有些基礎性的瞭解,在被問到的時候能夠同面試官嘮嗑兩句,而後在本身喜歡的領域,又有着足夠深刻的研究,讓面試官以爲你是這方面的專家。
知識大綱還在不斷的完善和修正,因爲也是精力時間有限,我會慢慢補充後面列出來的部分。固然,我也是在整理中不斷的學習,也但願你們能一塊兒參與進來,要補充或修正的地方麻煩趕忙提出。另外,剛新建了個公衆號,想做爲你們交流和分享的地方,有興趣想法的童鞋聯繫我哈~~😉
Tips: 聯繫我直接郵件 159042708@qq.com 或關注下面公衆號詳聊哈。
博主寫得很辛苦,感恩騙點star github。😚