[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hookscss
[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIhtml
[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程前端
subject:目標對象
observer:觀察者對象
pattern:模式
notify:通知
Publisher:發佈者
Subscriber:訂閱者
directive:指令
compile:編譯
Recursive:遞歸 adj
recursion:遞歸 n
factorial:階乘
複製代碼
arr.splice(start, count, addElement1, addElement2, ...);
start:開始位置,從0開始( 若是是負數,表示從倒數位置開始刪除 )
count:刪除的個數
addElement1...:被添加的元素
vue
做用:splice 刪除( 原數組 )的一部分紅員,並能夠在刪除的位置( 添加 )新的數組成員
返回值:被刪除的元素組成的數組,注意是一個數組
注意:splice 改變原數組
特別:若是隻有一個參數( 第一個參數 ),則變相至關於把數組拆分紅兩個數組,一個是返回值數組,一個是改變後的原數組
react
for...in 和 for...of
(1)
for...in:能夠用於數組 或 對象
for...of:只能用於數組,由於對象沒有部署 iterator 接口
(2)
1. 數組
- for(let i of arr) ----------- i 表示值
- for(let i in arr) ----------- i 表示下標
2. 對象
- 對象只能用for in循環,不能用for of循環
- 由於對象沒有部署iterator接口,不用使用for of循環
- for (let i in obj) ----------- i 表示key
複製代碼
keys
,values
, entries
Array.prototype.keys(),Array.prototype.values(),Array.prototype.entries() - 返回 Iterator 遍歷器對象
如何優雅的遍歷對象
const arr = [{name: '123'},2,3,4]
const obj = {name: '111', age: 222}
對象
// (注意Object.keys() values() entries() 能夠用於對象,也能夠用於數組)
for(let [key, value] of Object.entries(obj)) { // object || array
console.log(key, value)
// name 111
// age 222
// Object.entries(obj) 返回一個數組,每一個成員也是一個數組,由( 鍵,值 )組成的數組
}
// 數組
// for(let [key, value] of arr.entries()) { // 返回 iterator 對象接口,能夠用for...of遍歷
// console.log(key, value)
// }
複製代碼
Node.childNodes是NodeList集合, Element.children是HTMLCollection集合
Node.childNodes包括當前節點的全部子節點,包括元素節點,文本節點,註釋節點
Element.children包括當前元素節點的全部子元素節點,只包括元素節點
<div id='content'>
<div>111</div>
<div>
<div>222</div>
<div>333</div>
</div>
<p1>444</p1>
</div>
<script>
const contentHTMLCollection = document.getElementById('content') ---------- HTMLCollection
console.log(content.children)
// HTMLCollection(3) [div, div, p1]
const contentNodeList = document.querySelector('div') --------------------- NodeList
console.log(contentNodeList.childNodes)
// NodeList(7) [text, div, text, div, text, p1, text]
</script>
複製代碼
尾調用自身,尾遞歸
尾調用 和 非尾調用
尾調用
function a() {
return b()
}
非尾調用
function a() {
return b() + 1
// 非尾調用
// 由於調用b()時,a函數並未執行完,未出棧 => b執行完後 + 1 ,這時a函數才執行完畢
}
複製代碼
----
尾調用優化
----
// (1) 正常版 - 階乘函數
function factorial(number) {
if(number === 1 ) return number;
return number * factorial(number - 1)
}
const res = factorial(3)
console.log(res)
// 調用棧
// 1 ------------------------------ 3 * factorial(2)
// factorial(3)
// 2 ------------------------------ 3 * factorial(2)
// factorial(2)
// factorial(3)
// 3 ------------------------------ 3 * 2 * factorial(1)
// factorial(1)
// factorial(2)
// factorial(3)
// 4 ------------------------------ 3 * 2 * 1
// factorial(2)
// factorial(3)
// 5 ------------------------------ 6
// factorial(3)
// (2) 尾遞歸版 - 階乘函數
function factorial(number, multiply) {
if (number === 1) return multiply;
return factorial(number-1, number * multiply)
}
const res = factorial(4, 1)
console.log(res)
// factorial(4, 1)
// factorial(3, 4*1)
// factorial(2, 3 * (4 * 1))
// factorial(1, 2 * (3 * 4 * 1))
// return 1 * 3 * 4 * 1
// 12
// 每次都是尾遞歸,因此執行上線問棧中始終只有一個factorial()
// 即下一個 factorial 調用時, 外層的 factorial 已經執行完畢出棧了
// (3) 優化尾遞歸版 - 階乘函數
function factorialTemp(multiply, number) {
if (number === 1) return multiply;
return factorialTemp(number * multiply, number-1)
}
function partial(fn) { // --------------------------------------- 偏函數
let params = Array.prototype.slice.call(arguments, 1) // ------ 固定部分參數
return function closure() {
params = params.concat([...arguments])
if (params.length < fn.length) { // 參數小於fn形參個數,就繼續收集參數
return closure
}
return fn.apply(this, params) // 不然,證實參數收集完畢,執行fn
}
}
const factorial = partial(factorialTemp, 1) // 固定參數1
const res = factorial(4)
console.log(res)
複製代碼
對程序中的某個對象進行觀察,並在其發生改變時獲得通知webpack
存在( 觀察者對象 ) 和 ( 目標對象 )兩種角色git
目標對象:subjectes6
觀察者對象:observerweb
Subject 目標對象:維護一個觀察者實例組成的數組,而且具備( 添加,刪除,通知 )操做該數組的各類方法 |
Observer 觀察者對象:僅僅只須要維護收到通知後( 更新 )操做的方法 |
說明:
(1) Subject構造函數
- Subject構造函數的實例( 目標對象 ),維護一個( 觀察者實例對象 ) 組成的數組
- Subject構造函數的實例( 目標對象 ),擁有( 添加,刪除,發佈通知) 等操做觀察者數組的方法
(2) Observer構造函數
- Observer構造函數的實例( 觀察者對象 ),僅僅只須要維護收到通知後,更新的方法
---------
代碼:
function Subject() { // 目標對象的構造函數
this.observes = [] // 觀察者對象實例組成的數組
}
Subject.prototype = { // 原型上掛載操做數組的方法
add(...params) { // --------------------------------- 添加觀察者
// 對象方法的縮寫,添加觀察者對象
// params是ES6中的( rest參數數組 ),用來代替 arguments 對象,能夠接收多個參數
// this在調用時肯定指向,Subject構造函數的實例在調用,因此實例具備observes屬性
this.observes = this.observes.concat(params)
},
delete(obj) { // ------------------------------------ 刪除觀察者
const cashObservers = this.observes // ------------ 緩存能提升性能,由於下面有屢次用到
cashObservers.forEach((item, index) => {
if (item === obj) cashObservers.splice(index, 1)
// 這裏是三等判斷,由於 obj 和 item 的指針都執行同一個堆內存地址
// 舉例
// const a = {name: 'woow_wu7'}
// const b = [a, 1, 2]
// b[0] === a
// 上面的結果是true - 由於b[0]和a指向了同一個堆內存地址
// 注意:cashObservers.splice(index, 1)
// 刪除cashObservers至關於刪除this.observes
// 由於:this.observes賦值給了cashObservers,而且this.observes是複合類型的數據,因此兩個變量指針一致
})
},
notify() { // --------------------------------------- 通知觀察者,並執行觀察者各自的更新函數
if(this.observes.length) this.observes.forEach(item => item.update())
}
}
Subject.prototype.constructor = Subject // ------------- 改變prototype的同時修改constructor,防止引用出錯
function Observer(fn) { // ----------------------------- 觀察者對象僅僅維護更新函數
this.update = fn
}
const obsObj1 = new Observer(() => console.log(111111))
const obsObj2 = new Observer(() => console.log(222222))
const subObj = new Subject()
subObj.add(obsObj1, obsObj2)
subObj.notify()
subObj.delete(obsObj1)
subObj.notify()
複製代碼
-----------
觀察者模式 - es6語法實現
class Subject {
constructor() {
this.observers = []
}
add = (...rest) => {
this.observers = this.observers.concat(rest)
}
delete = (obj) => {
const cachObservers = this.observers
cachObservers.forEach((item, index) => {
if (item === obj) cachObservers.splice(index, 1)
})
}
notify = () => {
this.observers.forEach(item => {
item.update()
});
}
}
class Observer {
constructor(fn) {
this.update = fn
}
}
const objserverIns = new Observer(() => console.log(1111))
const objserverIns2 = new Observer(() => console.log(2222))
const subjectIns = new Subject()
subjectIns.add(objserverIns, objserverIns2)
subjectIns.notify()
複製代碼
發佈訂閱模式 - 代碼實現
var pubsub = {}; // 中介對象
(function(pubsub) {
var topics = {}
// topics對象,存放每一個事件對應的( 訂閱者對象 )組成的( 數組 )
// key: 事件名
// eventName:[{functionName: fn.name, fn: fn}]
pubsub.subscribe = function(eventName, fn) {
// ------------------------------------------------------------ 訂閱
if (!topics[eventName]) topics[eventName] = [] // 不存在,新建
topics[eventName].push({
functionName: fn.name, // 函數名做爲標識,注意函數名不能相同
fn, // 更新函數
})
}
pubsub.publish = function(eventName, params) {
// ---------------- --------------------------------------------- 發佈
if (!topics[eventName].length) return; // 若是空數組,即沒有該事件對象的訂閱者對象組成的數組,跳出整個函數
topics[eventName].forEach(item => {
item.fn(params) // 數組不爲空,就循環執行該數組全部訂閱者對象綁定更新函數
})
}
pubsub.unSubscribe = function(eventName, fn) {
// -------------------------------------------------------------- 取消訂閱
if (!topics[eventName]) {
return
}
topics[eventName].forEach((item, index) => {
if (item.functionName = fn.name) {
// 若是:事件名 和 函數名 相同
// 就:刪除這個事件對應的數組中的訂閱者對象,該對象包含函數名,fn兩個屬性
topics[eventName].splice(index)
}
})
}
})(pubsub)
function closoleLog(args) { // 訂閱者收到通知後的更新函數
console.log(args)
}
function closoleLog2(args) {
console.log(args)
}
pubsub.subscribe('go', closoleLog) // 訂閱
pubsub.subscribe('go', closoleLog2)
pubsub.publish('go', 'home') // 發佈
pubsub.unSubscribe('go', closoleLog) // 取消發佈
console.log(pubsub)
複製代碼
class PubSub {
constructor() {
this.topics = {}
}
subscribe(eventName, fn) {
if (!this.topics[eventName]) {
this.topics[eventName] = []
}
this.topics[eventName].push({
fname: fn.name,
fn
})
}
publish(eventName, params) {
if (!this.topics[eventName].length) {
return
}
this.topics[eventName].forEach((item, index) => {
item.fn(params)
})
}
unSubscribe(eventName, fname) {
if (!this.topics[eventName]) {
return
}
this.topics[eventName].forEach((item, index) => {
if (item.fname === fname) {
this.topics[eventName].splice(index, 1)
}
})
}
}
const pubsub = new PubSub()
function goFn1(params) {
console.log('goFn1', params)
}
function goFn2(params) {
console.log('goFn2', params)
}
pubsub.subscribe('go', goFn1)
pubsub.subscribe('go', goFn2)
pubsub.publish('go', '1') // 發佈
pubsub.unSubscribe('go', goFn2.name) // 取消訂閱
pubsub.publish('go', '2')
複製代碼
前置知識:
1. Element.children
- 返回一個相似數組的對象(HTMLCollection實例)
- 包括當前元素節點的全部子元素
- 若是當前元素沒有子元素,則返回的對象包含0個成員
2. Node.childNodes
- 返回一個相似數組的對象(NodeList集合),成員包括當前節點的全部子節點
- NodeList是一個動態集合
3. Node.childNodes 和 Element.children 的區別
- Element.children只包含元素類型的子節點,不包含其餘類型的子節點
- Node.childNodes包含元素節點,文本節點,註釋節點
複製代碼
代碼
-------------
<!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>
<div id="app">
<div v-text="name"></div>
<input type="text" v-model="name">
</div>
<script>
// 監聽類
// 主要做用是:將改變以後的data中的 ( 最新數據 ) 更新到 ( ui視圖 ) 中
class Watcher {
constructor(directiveName, el, vm, exp, attr) {
this.name = directiveName // 指令的名字,好比 'v-text','v-model'
this.el = el // 每一個具體的DOM節點
this.vm = vm // MyVue實例對象
this.exp = exp // el中的directiveName屬性對應的屬性值
this.attr = attr // el的屬性,須要需改的屬性
this._update() // 注意這裏在實例化Watcher時,會執行_update()方法
}
_update() {
this.el[this.attr] = this.vm.$data[this.exp]
// 將MyVue實例的data屬性的最新值更新到ui視圖中
}
}
class MyVue {
constructor(options) {
const {el, data} = this.options = options // 參數結構賦值
this.$el = document.querySelector(el) // 獲取el選擇器的元素節點
this.$data = data // data數據
this._directive = {}
// key:data對象中的 key,遞歸循環data從而獲取每一層的屬性做爲key
// value:數組,用來存放Watcher實例
this._observes(this.$data)
// 1. 遞歸循環,獲取data每一層的key
// 2. 把_directive對象,用每一個key作( 鍵 ),用一個Watcher實例組成的數組作( 值 )
// 3. 監聽data中每一個key對應的value是否變化
// 變化就執行 => _directive對象中key對應的數組中的Watcher實例對象的_update()方法
this._compile(this.$el)
// 1. 遞歸循環,獲取全部$el對象的元素節點的 全部子元素節點 Element.children
// 2. 若是元素節點有 v-text 指令,就把Watcher實例push進_directive對象該指令值對應的數組
// 由於執行了new Watcher(),因此constructor中調用了_update,因此會執行一次
// 即push實例的同時,也會執行_update(),已是會把data的數據寫入到對應的指令所在的元素節點中
// 3. 若是元素節點有 v-model指令,同理
// 不一樣的是:須要監聽( input事件 ),鍵最新的input框的value值,賦值給data
// data改變,會被Object.defineProperty監聽,從而執行更新函數
// 更新函數負責把最新的data中的數據更新到頁面中
}
_observes(data) {
for(let [key, value] of Object.entries(data)) {
if (data.hasOwnProperty(key)) {
this._directive[key] = []
}
if (typeof value === 'object') { // 數組 或 對象
this._observes(value) // 遞歸調用
}
const _dir = this._directive[key]
Object.defineProperty(this.$data, key, { // ------------- 監聽屬性變化
enumerable: true,
configurable: true,
get() {
return value
},
set(newValue) {
if (newValue !== value) { // ------------------------ 屬性變化後執行因此訂閱者對象的更新函數
value = newValue
_dir.forEach(item => item._update())
}
}
})
}
}
_compile(el) {
for(let [key, value] of Object.entries(el.children)) {
if (value.length) {
_compile(value)
}
if (value.hasAttribute('v-text')) {
const attrValue = value.getAttribute('v-text')
this._directive[attrValue].push(new Watcher('input', value, this, attrValue, 'innerHTML'))
}
if(value.hasAttribute('v-model') && (value.tagName === 'INPUT' || value.tagName === 'TEXTAREA')) {
const attrValue = value.getAttribute('v-model')
this._directive[attrValue].push(new Watcher('v-model', value, this, attrValue, 'value'))
let that = this
value.addEventListener('input', function() { // input事件監聽
that.$data[attrValue] = value.value
// 獲取最新的input框值,賦值給data => Object.defineProperty監聽到data變化 => 執行更新函數更新視圖
})
}
}
}
}
// 實例化MyVue
new MyVue({
el: '#app',
data: {
name: 'woow_wu7'
}
})
</script>
</body>
</html>
複製代碼
詳細 juejin.im/post/5cc577…
精簡 juejin.im/post/5bb1bb…
實現vue數據雙向綁定 juejin.im/post/5bce9a…
segmentfault.com/a/119000001…
juejin.im/post/5c10ed…
個人簡書:www.jianshu.com/p/bdb03feab…canvas