HTTP
- [緩存相關]
- 講一講強緩存和協(xié)議緩存儡羔?
- 有了【Last-Modified毛俏,If-Modified-Since】為何還要有【ETag奔则、If-None-Match】
- 有關 HTTP 緩存的首部字段說一下
- [TCP/IP 相關]
- HTTP 和 TCP 的區(qū)別
- TCP 三次握手和四次揮手技即?以其存在意義。
- 在一次傳輸中它是如何保證每個數(shù)據(jù)包之間的順序的绳瘟?
- [HTTP 相關]
- HTTP 請求的什么時候用的對稱加密什么時候非對稱加密
- HTTP 中的 keep-alive 有了解嗎雕憔?
- HTTP2 特性
- HTTP/2 對比 HTTP1.1
- HTTP/2 都有哪些特性?頭部壓縮的原理糖声?
- HTTP/2 是怎么解決隊頭阻塞的
- HTTP/2 是如何壓縮頭部的
- HTTPS 握手
- 具體說一下 HTTP/2 中的多路復用
- GET 和 POST 的區(qū)別
- GET 就一定是冪等的嗎斤彼?
- 狀態(tài)碼。302.304.301.401.403 的區(qū)別蘸泻?
- 狀態(tài)碼畅卓。204 和 304 分別有什么作用?
- HTTP 和 HTTPS 握手差異蟋恬?
- 為什么說 HTTPS 比 HTTP 安全呢
- 簡單講了一下非對稱加密的握手過程
- 證書簽名過程和如何防止被串改
- TCP/IP 網絡分層模型是怎樣分層的
- OSI 網絡分層模型是怎樣分層的
- TCP 和 UDP 區(qū)別
- HTTP/0.9、HTTP/1.0趁冈、HTTP/1.1歼争、HTTP/2、HTTP/3 各版本之間的區(qū)別渗勘?
- [綜合相關]
- CSRF 跨站請求偽造和 XSS 跨站腳本攻擊是什么沐绒?
- 如果讓你去實現(xiàn)一個 CSRF 攻擊你會怎做?
- 如果使用 jsonp 的話會有什么安全問題嗎旺坠?
- 你是如何解決跨域的乔遮?都有幾種?
- 為什么說 GET 會留下歷史記錄取刃?
- 從“在瀏覽器輸入域名”到“頁面靜態(tài)資源完全加載”的整個流程
- 假設有兩個子項目蹋肮,他們需要共用同一個用戶體系如何保證關掉頁面之后打開另一個項目用戶還是登錄狀態(tài)?
JS
- Promise 實現(xiàn)
- Generator 實現(xiàn)
- async璧疗、await 實現(xiàn)
- 垃圾回收中的堆和棧的區(qū)別
- 0.1 + 0.2 != 0.3 背后的原理坯辩?
- 手寫 vue2/3 具體實現(xiàn)
- 手寫繼承
- requestAnimationFrame 屬于宏任務還是微任務
- 動態(tài)表單實現(xiàn)
- null 為什么被 typeof 錯誤的判斷為了'object'
- 洋蔥模型
- 實現(xiàn)對象深拷貝
- 實現(xiàn)數(shù)組去重
- 實現(xiàn)一個 apply/call/bind 方法
- 實現(xiàn)一個函數(shù),URL 參數(shù)解析為對象
- 實現(xiàn)一個 trim
- 實現(xiàn)柯理化函數(shù)
- 實現(xiàn) loadsh 的 get 方法 (_.get('a.b.c'))
- 查找字符串中的最長無重復子串
- 兩個數(shù)組的并集與交集
- 排序算法實現(xiàn)
- 數(shù)組扁平化
Vue
- 雙向綁定原理
- nextTick 原理
- nextTick 中的 waiting 是什么時候變?yōu)?true 的呢
- 虛擬 DOM
- Object.defineProperty()有什么缺點崩侠?Vue3 為什么用 Proxy
- Vue3 有哪些新的 API 或者有做哪些優(yōu)化漆魔?
- 說一下 Vue 的 diff 算法
- diff 算法的缺點
Devops
- webpack 的構建流程、自定義 loader,插件原理改抡、HMR 的實現(xiàn)矢炼、打包原理,常用的插件以及性能優(yōu)化
- 單元測試做過嗎阿纤?你是怎么做的?
- 對 tree-shaking 的了解(雖然生產模式下默認開啟句灌,但是由于經過 babel 編譯全部模塊被封裝成 IIFE IIFE 存在副作用無法被 tree-shaking 掉 需要配置 { module: false }和 sideEffects: false rollup 和 webpack 的 shaking 程度不同,以一個 Class 為例子)
- webpack-dev-server 原理和如何處理跨域
書單
- 《 網絡是怎樣連接的 》
- 《 圖解 HTTP 》
- 《 圖解 TCP/IP 》
- 《 極客時間- 透視 HTTP 協(xié)議 》
- 《 TCP/IP 詳解(第一卷)》
- 《 瀏覽器原理 》
- 《 DevOps 實戰(zhàn)筆記 》
- 《 Nginx 核心知識 100 講 》
- 《 學習 Javascipt 數(shù)據(jù)結構與算法 》
- 《 JavaScript 版 數(shù)據(jù)結構與算法 》
- 《 極客時間- 數(shù)據(jù)結構與算法之美 》
- 《 極客時間- 算法面試通關 》
代碼實現(xiàn)
- 手寫節(jié)流防抖
/**
* 節(jié)流
* @param {Func} callback 需要節(jié)流的函數(shù)
* @param {Number} wait 等待毫秒數(shù)
* 執(zhí)行一次后阵赠,一段時間內不再執(zhí)行
*/
function throttle(func, wait) {
let timer = null;
return function (...args) {
if (timer === null) {
timer = setTimeout(() => {
func.apply(this, args);
timer = null;
}, wait);
}
};
}
/**
* 防抖
* @param {Func} callback 需要防抖的函數(shù)
* @param {Number} wait 等待毫秒數(shù)
* 一段時間內涯塔,取最后一次執(zhí)行
*/
function debounce(func, wait) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
- 圣杯與雙飛翼布局
/** 圣杯 **/
<div class="wrapper1">
<div class="main">
<p>bilibili</p>
</div>
<div class="left"></div>
<div class="right"></div>
</div>
<style>
.wrapper1 {
padding: 0 60px 0 30px;
}
.wrapper1 .main {
float: left;
width: 100%;
height: 300px;
background: red;
}
.wrapper1 .left {
float: left;
width: 30px;
margin-left: -100%;
background: blue;
height: 100px;
position: relative;
right: 30px;
}
.wrapper1 .right {
float: left;
width: 60px;
margin-left: -60px;
background: yellow;
height: 200px;
position: relative;
left: 60px;
}
</style>
/** 雙飛翼 **/
<div class="wrapper2">
<div class="container">
<div class="main">
<p>bilibili</p>
</div>
</div>
<div class="left"></div>
<div class="right"></div>
</div>
<style>
.wrapper2 {
min-width: 630px;
}
.wrapper2 .container {
float: left;
width: 100%;
}
.wrapper2 .container .main {
height: 300px;
background: red;
margin: 0 600px 0 30px;
}
.wrapper2 .left {
float: left;
width: 30px;
background: blue;
height: 100px;
margin-left: -100%;
}
.wrapper2 .right {
float: left;
width: 600px;
background: yellow;
height: 200px;
margin-left: -600px;
}
</style>
- 數(shù)組長度為 5 且元素的隨機數(shù)在 2-32 間不重復的值
// 生成 [n,m] 的隨機整數(shù)
const creatRandomNum = (min,max) => parseInt(Math.random()*(max-min+1)+min);
function arr5(arr=[]) {
if(arr.length >= 5){return arr};
arr = [...new Set([...arr, creatRandomNum(2,32)])];
return arr5(arr);
}
4.寫一個獲取當前 url 查詢字符串中的參數(shù)的方法
const getUrlQuery = (url = location.href) => {
try {
return [...new URL(url).searchParams].reduce(
(pre, [key, value]) => Object.assign(pre, { [key]: value }),
{},
);
} catch {
throw new Error(`url格式不正確。url: ${url}`);
}
};
- html5 中的 form 怎么關閉自動完成清蚀?
// 在 input 標簽中匕荸,可以設置 autocomplete="off" 來關閉自動填充。
<form action="demo_form.html" method="get" autocomplete="off">
First name:<input type="text" name="fname" /><br />
E-mail: <input type="email" name="email" /><br />
<input type="submit" />
</form>
1.script 同步下載榛搔,下載成功繼續(xù)阻塞 DOM 的渲染,立即執(zhí)行东揣。
2. async異步下載践惑,下載成功后阻塞 DOM 的渲染,立即執(zhí)行嘶卧。
3. defer異步加載尔觉,下載成功后等待文檔加載,文檔加載完成后執(zhí)行芥吟。
4.async侦铜、defer這兩個屬性無法應用于內聯(lián)script。
image
- 實現(xiàn)一個 call / apply / bind / new
// call
Function.prototype.call2 = function (target, ...args) {
target._func = this;
const ret = target._func(...args);
delete target._func;
return ret;
};
// apply
Function.prototype.apply2 = function (target, args=[]) {
target._func = this;
const ret = target._func(...args);
delete target._func;
return ret;
};
// bind
Function.prototype._bind = function (target, ...res) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const that = this;
const _func = function(...args) {
// 判斷當前函數(shù)是直接訪問還是通過new進行構造
return that.apply(this instanceof _func ? this : target, res.concat(args));
}
// 添加一層原型钟鸵,防止修改_func.prototype時影響到this.prototype
_func.prototype = Object.create(this.prototype);
return _func;
}
// new
function _new(target, ...args) {
const obj = Object.create(target.prototype);
const ret = target.apply(obj, args);
return typeof ret === 'object' ? ret : obj;
}
8.Object.create 的模擬實現(xiàn)
Object.create = function(target) {
function _func () {};
_func.prototype = target;
return new _func();
}
- instanceof 模擬實現(xiàn)
function _instanceof(left, right) {
let proto = left.__proto__;
let prototype = right.prototype;
while (true) {
if (proto === null) return false;
if (proto === prototype) return true;
proto = proto.__proto__;
}
}
10.解釋一個為什么 10.toFixed(10)會報錯钉稍?
/**
在我們的直覺上,10.toFixed(10) 是把整數(shù)的 10 轉為浮點數(shù)并且保留 10 位小數(shù)部分棺耍。
但實際上會出錯贡未,是因為 JS 的解釋器對 . 操作符產生了歧義。
在 JS 中 . 可以表示小數(shù)和從對象中取值蒙袍。在這個例子中俊卤, 由于 10 是整數(shù),
所以在 10. 默認是小數(shù)點左敌,因此會報錯瘾蛋。
**/
// 解決的辦法有下面幾種:
(10).toFixed(10) 個人喜歡這種,看起來舒服一點
10..toFixed(10)
11.將 Object 與 Map 互轉
var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));// Map { foo: "bar", baz: 42 }
var obj2 = Object.fromEntries(map);// {foo: "bar", baz: 42}
12.寄生組合式繼承
// 通過構造函數(shù)來繼承實例屬性矫限,通過原型鏈的混成形式來繼承原型方法
function clonePrototype(Super,Suber){
var prototype = Object.create(Super.prototype);
prototype.constructor = Suber;
Suber.prototype = prototype;
}
function Super(name){
this.name = name
this.like = ['sing','dance','rap','basketball']
}
Super.prototype.sayName=function(){
console.log(this.name)
}
function Suber(name,age){
Super.call(this,name) // 繼承屬性
this.age=age
}
clonePrototype(Super,Suber);
Suber.prototype.sayAge=function(){
console.log(this.age)
}
var sub1 = new Suber('sss',18);
console.log(sub1.name); // -> sss
sub1.sayName(); // -> sss
sub1.sayAge(); // -> 18
13.如何讓 (a == 1 && a == 2 && a == 3) 的值為 true哺哼?
var a = {
valueOf: (function () {
let i = 1;
//閉包的特性之一:i 不會被回收
return function () {
return i++;
}
})()
}
console.log(a == 1 && a == 2 && a == 3); // true
- JS 模塊化
AMD: require.js 為代表佩抹,依賴前置,一律先加載再使用取董。
CMD: sea.js 為代表棍苹,依賴就近原則。
UMD: 兼容AMD和commonJS規(guī)范的同時茵汰,還兼容全局引用的方式枢里。
ES6 import/export
15.你從未注意的隱藏危險: target = "_blank" 和 "opener"
// 最佳實踐
<a target="_blank"
rel="noopener noreferrer nofollow">
Enter an "evil" website
</a>
16.階乘
function factorial(num, sum = 1) {
if(num <= 1)return sum;
sum *= num;
return factorial(--num, sum); // 尾遞歸
}
factorial(3) // -> 6
17.柯里化
const curry = (fn , args = []) => {
return (...rets) => {
const allArgs = args.concat(rets);
if(allArgs.length < fn.length) {
return curry(fn, allArgs);
} else {
return fn.apply(this, allArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
實現(xiàn)一個 Promise 《圖解 Promise 實現(xiàn)原理》
// Promise 簡單的實現(xiàn)
function APromise(fn) {
const that = this;
that.status = "pending"; // Promise初始狀態(tài)為pending
that.data = undefined; // Promise的值
that.resCbs = []; // APromise resolve回調函數(shù)集合
that.rejCbs = []; // APromise reject回調函數(shù)集合
function resolve(data) {
if (that.status === "pending") {
that.status = "resolved";
that.data = data;
setTimeout(() => {
that.resCbs.forEach((cb) => cb(data));
});
}
}
function reject(data) {
if (that.status === "pending") {
that.status = "rejected";
that.data = data;
setTimeout(() => {
that.rejCbs.forEach((cb) => cb(data));
});
}
}
try {
fn(resolve, reject); // 執(zhí)行傳進來的函數(shù),傳入resolve, reject參數(shù)
} catch (e) {
reject(e);
}
}
APromise.prototype.then = function (onRes = (v) => v, onRej = (j) => j) {
const that = this;
if (that.status === "resolved") {
// 這里promise的狀態(tài)已經確定是resolved蹂午,所以調用onResolved
return new APromise((resolve, reject) => {
setTimeout(() => {
try {
// ret是onRes的返回值
const ret = onRes(that.data);
if (ret instanceof APromise) {
// 如果ret是一個promise栏豺,則取其值作為新的promise的結果
ret.then(resolve, reject);
} else {
// 否則,以它的返回值作為新的promise的結果
resolve(ret);
}
} catch (e) {
// 如果出錯豆胸,以捕獲到的錯誤作為promise2的結果
reject(e);
}
});
});
}
// 這里的邏輯跟前面一樣奥洼,不再贅述
if (that.status === "rejected") {
return new APromise((resolve, reject) => {
setTimeout(() => {
try {
const ret = onRej(that.data);
if (ret instanceof APromise) {
ret.then(resolve, reject);
} else {
reject(ret);
}
} catch (e) {
reject(e);
}
});
});
}
if (that.status === "pending") {
// 如果當前的Promise還處于pending狀態(tài),則不能確定調用
// onResolved還是onRejecte晚胡,只能等到Promise狀態(tài)確定后灵奖,
// 才能確定如何處理
return new APromise((resolve, reject) => {
that.resCbs.push(() => {
setTimeout(() => {
try {
const ret = onRes(that.data);
if (ret instanceof APromise) {
ret.then(resolve, reject);
} else {
resolve(ret);
}
} catch (e) {
reject(e);
}
});
});
that.rejCbs.push(() => {
setTimeout(() => {
try {
const ret = onRej(that.data);
if (ret instanceof APromise) {
ret.then(resolve, reject);
} else {
reject(ret);
}
} catch (e) {
reject(e);
}
});
});
});
}
};
// 順便實現(xiàn)一下catch方法
APromise.prototype.catch = function (onRej) {
return this.then(null, onRej);
};
const p = new APromise(function (resolve, reject) {
setTimeout(function () {
resolve(1);
}, 2000);
});
p.then(function (v) {
console.log(v);
return 2;
})
.then(function (v) {
console.log(v);
return new APromise(function (resolve, reject) {
setTimeout(function () {
resolve(3);
}, 3000);
});
})
.then(function (v) {
console.log(v);
});
/**
*實現(xiàn)promise中的all方法
*/
Promise.prototype.all = function (array) {
judgeType(array);
let count = 0;
const total = array.length;
const ret = [];
return new Promise((resolve, reject) => {
array.forEach((element) => {
element.then((res) => {
ret.push(res);
count++;
if (count === total) {
resolve(ret);
}
});
element.catch((err) => {
reject(err);
});
});
});
};
/**
*類型的判斷
*/
function judgeType(array) {
if (array instanceof Array) {
array.forEach((item) => {
if (!(item instanceof Promise)) {
throw "該參數(shù)的每一項必須是Promise的實例";
}
});
} else {
throw "必須是數(shù)組哦";
}
}
var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
all = function (promises) {
const total = promises.length;
const ret = [];
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((item) => {
item.then((res) => {
ret.push(res);
count++;
if (count === total) {
return resolve(ret);
}
});
item.catch((err) => {
return reject(err);
});
});
});
};
all([p1, p2, p3]).then(function (results) {
//then方法不會被執(zhí)行
console.log(results);
}).catch(function (e){
//catch方法將會被執(zhí)行,輸出結果為:2
console.log(2);
});
/**
*實現(xiàn)promise中的race方法
*/
Promise.prototype.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((item) => {
Promise.resolve(item).then(
(res) => {
return resolve(res);
},
(err) => {
return reject(err);
}
);
});
});
};
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "500");
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, "600");
});
race([p1, p2]).then(function (result) {
console.log(result); // '2018' 因為2018更快
});
/**
*實現(xiàn)promise中的finally方法
*/
Promise.prototype.finally = function (onFinally) {
return this.then(
/* onFulfilled */
(res) => Promise.resolve(onFinally()).then(() => res),
/* onRejected */
(err) =>
Promise.resolve(onFinally()).then(() => {
throw err;
})
);
};
// test
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
//Promise 完整的實現(xiàn)
class Promise {
callbacks = [];
state = 'pending';//增加狀態(tài)
value = null;//保存結果
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
catch(onError) {
return this.then(null, onError);
}
finally(onDone) {
if (typeof onDone !== 'function') return this.then();
let Promise = this.constructor;
return this.then(
value => Promise.resolve(onDone()).then(() => value),
reason => Promise.resolve(onDone()).then(() => { throw reason })
);
}
static resolve(value) {
if (value && value instanceof Promise) {
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => {
then(resolve);
});
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
}
static reject(value) {
if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise((resolve, reject) => {
then(reject);
});
} else {
return new Promise((resolve, reject) => reject(value));
}
}
static all(promises) {
return new Promise((resolve, reject) => {
let fulfilledCount = 0
const itemNum = promises.length
const rets = Array.from({ length: itemNum })
promises.forEach((promise, index) => {
Promise.resolve(promise).then(result => {
fulfilledCount++;
rets[index] = result;
if (fulfilledCount === itemNum) {
resolve(rets);
}
}, reason => reject(reason));
})
})
}
static race(promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(function (value) {
return resolve(value)
}, function (reason) {
return reject(reason)
})
}
})
}
_handle(callback) {
if (this.state === 'pending') {
this.callbacks.push(callback);
return;
}
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {//如果then中沒有傳遞任何東西
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
let ret;
try {
ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
} catch (error) {
ret = error;
cb = callback.reject
} finally {
cb(ret);
}
}
_resolve(value) {
if(this.state !== 'pending') return
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';//改變狀態(tài)
this.value = value;//保存結果
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
if(this.state !== 'pending') return
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}
- 深拷貝
// 小細節(jié) -> 需要考慮一下循環(huán)引用的情況 -> WeakMap
function deepCopy(obj){
let result;
if(typeof obj == "object"){
//復雜數(shù)據(jù)類型
result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
result = obj;
}
return result;
}
function deepCopy2(target, weakMaps = new WeakMap()) {
if (typeof target !== "object" || target === null) {
return target;
}
if (weakMaps.has(target)) return weakMaps.get(target);
const res = Array.isArray(target) ? [] : {};
weakMaps.set(target, res);
for (let key in target) {
res[key] = deepCopy(target[key], weakMaps);
}
return res;
}
21.瀏覽器進程與線程
https://imweb.io/topic/58e3bfa845e5c13468f567d5
22.設計一個簡單的任務隊列估盘,要求分別在 1,3,4 秒后打印出”1“瓷患,”2“,”3“
/**
* 題目
*/
new Quene()
.task(1000, () => {
console.log(1);
})
.task(2000, () => {
console.log(2);
})
.task(1000, () => {
console.log(3);
})
.start();
function Quene() { ... }
/**
* 解題1 promise
*/
class Quene {
constructor() {
this.tasks = [];
}
task(wait, fn) {
this.tasks.push({
wait,
fn,
});
return this;
}
async start() {
for (let task of this.tasks) {
const { wait, fn } = task;
await new Promise((res, rej) => {
setTimeout(() => {
fn();
res();
}, wait);
});
}
}
}
/**
* 解題2 遞歸
*/
class Quene {
constructor() {
this.tasks = [];
}
task(wait, fn) {
this.tasks.push({ wait, fn });
return this;
}
start() {
const firstTask = this.tasks.shift();
if (firstTask) {
setTimeout(() => {
firstTask.fn();
this.start();
}, firstTask.wait);
}
}
}
/**
* 解題3 閉包
*/
class Queue {
constructor() {
this.tasks = [];
}
task(wait, fn) {
this.tasks.push({
wait,
fn,
});
return this;
}
start() {
let int = 0;
this.tasks.forEach(({ wait, fn }) => {
setTimeout(() => {
fn();
}, (int += wait));
});
}
}
- 用 setTimeout 實現(xiàn) setInterval遣妥,闡述實現(xiàn)的效果與 setInterval 的差異
function mySetInterval(fn, wait) {
mySetInterval.timer = setTimeout(() => {
fn();
mySetInterval(fn, wait);
}, wait)
}
mySetInterval.clear = function() {
clearTimeout(mySetInterval.timer)
}
mySetInterval(() => {
console.log(11111)
}, 1000)
setTimeout(() => {
// 5s 后清理
mySetInterval.clear()
}, 5000)
- vue3 中的數(shù)據(jù)偵測
const rawToReactive = new WeakMap();
const reactiveToRaw = new WeakMap();
/**
* utils
* */
function isObject(val) {
return typeof val === "object";
}
function hasOwn(val, key) {
const hasOwnProperty = Object.prototype.hasOwnProperty;
return hasOwnProperty.call(val, key);
}
/**
* traps
* */
// get
function createGetter() {
return function(target,key,receiver) {
const res = Reflect.get(target,key,receiver);
console.log('get', key);
return isObject(res) ? reactive(res) : res;
}
}
// set
function set(target,key,value,receiver) {
const hadKey = hasOwn(target, key);
const oldValue = target[key];
value = reactiveToRaw.get(value) || value;
const res = Reflect.set(target, key, value, receiver)
if(!hadKey || value !== oldValue) {
console.log('tigger...');
}
return res;
}
// handle
const mutableHandlers = {
get: createGetter(),
set: set
};
// create reactive object
function createReactiveObject(target, toProxy, toRaw, baseHandlers) {
let observed = toProxy.get(target);
// target 生成過 observed,返回 observed
if(observed !== void 0) {
return observed;
};
// target 是 observed, 返回 target
if(toRaw.has(target)) {
return target;
}
observed = new Proxy(target, baseHandlers);
toProxy.set(target, observed);
toRaw.set(observed, target);
return observed;
}
// enter
function reactive(target) {
return createReactiveObject(target, rawToReactive, reactiveToRaw, mutableHandlers)
}
- Vue 的響應式原理中 Object.defineProperty 有什么缺陷沙咏?為什么在 Vue3.0 采用了 Proxy,拋棄了 Object.defineProperty?
- 優(yōu)缺點
- Object.defineProperty 無法監(jiān)控到數(shù)組下標的變化故河,導致通過數(shù)組下標添加元素,不能實時響應鱼的;
- Object.defineProperty 只能劫持對象的屬性,從而需要對每個對象凑阶,每個屬性進行遍歷,如果宙橱,屬性值是對象蘸拔,還需要深度遍歷环葵。Proxy 可以劫持整個對象,并返回一個新的對象张遭。
- Proxy 不僅可以代理對象,還可以代理數(shù)組菊卷。還可以代理動態(tài)增加的屬性。
- 實現(xiàn) async await
function co(genFun) {
return new Promise((resolve, reject) => {
const gen = genFun();
function next(...params){
const {value, done} = gen.next(...params);
if(done){
return resolve(value)
} else {
value.then((...args)=>next(...args));
}
};
next();
})
};
// test
const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000));
function* testG() {
// await被編譯成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
co(testG).then(res=>{console.log('res', res)});
// logs
data: data
data2: data
res success
- LazyMan
// 實現(xiàn)LazyMan
LazyMan('jack').sleep(3000).eat('籃球').eat('rap').eat('唱歉甚、跳')
// hi,I'm jack
// 阻塞3s
// 籃球
// rap
// 唱、跳
LazyMan('x1').eat('高').sleep(5000).eat('富').eat('帥')
// hi,I'm x1
// 高
// 阻塞5s
// 富
// 帥
LazyMan('lurenjia').sleepFirst(3000).eat('吹').eat('牛')
// 阻塞3s
// hi,I'm lurenjia
// 吹
// 牛
class CraeteLazyMan {
constructor(name) {
this.queue = [];
const nameTask = () => {
console.log(`ADI-LOG => name`, name);
this.task();
};
this.queue.push(nameTask);
setTimeout(() => {
this.task();
}, 0);
}
task() {
const fun = this.queue.shift();
fun && fun();
}
sleep(time) {
const _task = () => {
setTimeout(() => {
this.task();
}, time);
};
this.queue.push(_task);
return this;
}
sleepFirst(time) {
const _task = () => {
setTimeout(() => {
this.task();
}, time);
};
this.queue.unshift(_task);
return this;
}
eat(str) {
const _task = () => {
console.log(`ADI-LOG => eat`, str);
this.task();
};
this.queue.push(_task);
return this;
}
}
function LazyMan(name = "") {
return new CraeteLazyMan(name);
}