手寫路徑導航javascript
new
操做符new
操做符作了這些事:css
[[Prototype]]
(也就是__proto__
)連接。this
指向新建立的對象。。new
建立的每一個對象將最終被[[Prototype]]
連接到這個函數的prototype
對象上。Object
(包含Functoin, Array, Date, RegExg, Error
),那麼new
表達式中的函數調用將返回該對象引用。function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
複製代碼
JSON.stringify
JSON.stringify(value[, replacer [, space]])
:html
Boolean | Number| String
類型會自動轉換成對應的原始值。undefined
、任意函數以及symbol
,會被忽略(出如今非數組對象的屬性值中時),或者被轉換成 null
(出如今數組中時)。function jsonStringify(obj) {
lettype = typeof obj;
if (type !== "object" || type === null) {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"';
}
return String(obj);
} else {
let json = []
arr = (obj && obj.constructor === Array);
for (let k in obj) {
let v = obj[k];
lettype = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"';
} elseif (type === "object") {
v = jsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
複製代碼
JSON.parse
JSON.parse(text[, reviver])
前端
用來解析JSON字符串,構造由字符串描述的JavaScript值或對象。提供可選的reviver函數用以在返回以前對所獲得的對象執行變換(操做)。vue
function jsonParse(opt) {
returneval('(' + opt + ')');
}
jsonParse(jsonStringify({x : 5}))
// Object { x: 5}
jsonParse(jsonStringify([1, "false", false]))
// [1, "false", falsr]
jsonParse(jsonStringify({b: undefined}))
// Object { b: "undefined"}
複製代碼
避免在沒必要要的狀況下使用
eval
,eval() 是一個危險的函數, 他執行的代碼擁有着執行者的權利。若是你用 eval()運行的字符串代碼被惡意方(不懷好意的人)操控修改,您最終可能會在您的網頁/擴展程序的權限下,在用戶計算機上運行惡意代碼。java
它會執行JS代碼,有XSS漏洞。react
若是你只想記這個方法,就得對參數json作校驗。webpack
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
if (
rx_one.test(
json
.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
var obj = eval("(" +json + ")");
}
複製代碼
來源 神奇的eval()與new Function()git
核心:Function
與eval
有相同的字符串參數特性。
var func = new Function(arg1, arg2, ..., functionBody);
在轉換JSON的實際應用中,只須要這麼作。
var jsonStr = '{ "age": 20, "name": "jack" }'
var json = (new Function('return ' + jsonStr))();
複製代碼
eval
與 Function
都有着動態編譯js代碼的做用,可是在實際的編程中並不推薦使用。
這裏是面向面試編程,寫這兩種就夠了。至於第三,第四種,涉及到繁瑣的遞歸和狀態機相關原理,具體能夠看:
call
或 apply
call
語法:
fun.call(thisArg, arg1, arg2, ...)
,調用一個函數, 其具備一個指定的this值和分別地提供的參數(參數的列表)。
apply
語法:
func.apply(thisArg, [argsArray])
,調用一個函數,以及做爲一個數組(或相似數組對象)提供的參數。
Function.call
按套路實現call
核心:
this
到函數並傳入給定參數執行函數爲啥說是套路實現呢?由於真實面試中,面試官很喜歡讓你逐步地往深考慮,這時候你能夠反套路他,先寫個簡單版的:
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
複製代碼
當面試官有進一步的發問,或者此時你能夠僞裝思考一下。而後寫出如下版本:
Function.prototype.call2 = function(content = window) {
content.fn = this;
let args = [...arguments].slice(1);
let result = content.fn(...args);
delect content.fn;
return result;
}
var foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
複製代碼
Function.apply
的模擬實現apply()
的實現和call()
相似,只是參數形式不一樣。直接貼代碼吧:
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判斷是否有第二個參數
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn()
return result
}
複製代碼
Function.bind()
bind()
方法:
會建立一個新函數。當這個新函數被調用時,bind() 的第一個參數將做爲它運行時的 this,以後的一序列參數將會在傳遞的實參前傳入做爲它的參數。(來自於 MDN )
此外,bind
實現須要考慮實例化後對原型鏈的影響。
Function.prototype.bind2 = function(content) {
if(typeof this != "function") {
throw Error("not a function")
}
// 若沒問參數類型則從這開始寫
let fn = this;
let args = [...arguments].slice(1);
let resFn = function() {
return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
}
functiontmp() {}
tmp.prototype = this.prototype;
resFn.prototype = new tmp();
return resFn;
}
複製代碼
寄生組合式繼承
通常只建議寫這種,由於其它方式的繼承會在一次實例中調用兩次父類的構造函數或有其它缺點。
核心實現是:用一個 F
空的構造函數去取代執行了 Parent
這個構造函數。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log('parent name:', this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
function create(proto) {
functionF(){}
F.prototype = proto;
return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
console.log('child name:', this.name);
}
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.sayName(); // parent name: father
var child = new Child('son', 'father');
複製代碼
在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數且返回結果的新函數的技術。
函數柯里化的主要做用和特色就是參數複用、提早返回和延遲執行。
functioncurry() {
var args = Array.prototype.slice.call(arguments);
var fn = function() {
var newArgs = args.concat(Array.prototype.slice.call(arguments));
return multi.apply(this, newArgs);
}
fn.toString = function() {
return args.reduce(function(a, b) {
return a * b;
})
}
return fn;
}
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);
複製代碼
ES6
騷寫法const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
複製代碼
Promise
(中高級必考)咱們來過一遍Promise/A+
規範:
pending| fulfilled(resolved) | rejected
pending
狀態的時候,能夠轉移到fulfilled(resolved)
或者rejected
狀態fulfilled(resolved)
狀態或者rejected
狀態的時候,就不可變。then
異步執行方法,then
接受兩個參數且必須返回一個promise:// onFulfilled 用來接收promise成功的值
// onRejected 用來接收promise失敗的緣由
promise1=promise.then(onFulfilled, onRejected);
複製代碼
Promise
的流程圖分析Promise
用法:
var promise = new Promise((resolve,reject) => {
if (操做成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
複製代碼
function myPromise(constructor){
let self=this;
self.status="pending" //定義狀態改變前的初始狀態
self.value=undefined;//定義狀態爲resolved的時候的狀態
self.reason=undefined;//定義狀態爲rejected的時候的狀態
function resolve(value){
//兩個==="pending",保證了狀態的改變是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//兩個==="pending",保證了狀態的改變是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕獲構造異常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
複製代碼
同時,須要在myPromise
的原型上定義鏈式調用的then
方法:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case"resolved":
onFullfilled(self.value);
break;
case"rejected":
onRejected(self.reason);
break;
default:
}
}
複製代碼
測試一下:
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//輸出1
複製代碼
直接貼出來吧,這個版本還算好理解
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let that = this; // 緩存當前promise實例對象
that.status = PENDING; // 初始狀態
that.value = undefined; // fulfilled狀態時 返回的信息
that.reason = undefined; // rejected狀態時 拒絕的緣由
that.onFulfilledCallbacks = []; // 存儲fulfilled狀態對應的onFulfilled函數
that.onRejectedCallbacks = []; // 存儲rejected狀態對應的onRejected函數
function resolve(value) { // value成功態時接收的終值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 實踐中要確保 onFulfilled 和 onRejected 方法異步執行,且應該在 then 方法被調用的那一輪事件循環以後的新執行棧中執行。
setTimeout(() => {
// 調用resolve 回調對應onFulfilled函數
if (that.status === PENDING) {
// 只能由pending狀態 => fulfilled狀態 (避免調用屢次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) { // reason失敗態時接收的拒因
setTimeout(() => {
// 調用reject 回調對應onRejected函數
if (that.status === PENDING) {
// 只能由pending狀態 => rejected狀態 (避免調用屢次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
// 捕獲在excutor執行器中拋出的異常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 處理參數默認值 保證參數後續可以繼續執行
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功態
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一個onFulfilled的返回值
} catch(e) {
reject(e); // 捕獲前面onFulfilled中拋出的異常 then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失敗態
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待態
// 當異步調用resolve/rejected時 將onFulfilled/onRejected收集暫存到集合中
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
};
複製代碼
emmm,我仍是乖乖地寫回進階版吧。
Debouncing
)和節流(Throttling
)
scroll
事件自己會觸發頁面的從新渲染,同時scroll
事件的handler
又會被高頻度的觸發, 所以事件的handler
內部不該該有複雜操做,例如DOM
操做就不該該放在事件處理中。 針對此類高頻度觸發事件問題(例如頁面scroll
,屏幕resize
,監聽用戶輸入等),有兩種經常使用的解決方法,防抖和節流。
Debouncing
)實現典型例子:限制 鼠標連擊 觸發。
一個比較好的解釋是:
當一次事件發生後,事件處理器要等必定閾值的時間,若是這段時間過去後 再也沒有 事件發生,就處理最後一次發生的事件。假設還差
0.01
秒就到達指定時間,這時又來了一個事件,那麼以前的等待做廢,須要從新再等待指定時間。
// 防抖動函數
function debounce(fn,wait=50,immediate) {
let timer;
returnfunction() {
if(immediate) {
fn.apply(this,arguments)
}
if(timer) clearTimeout(timer)
timer = setTimeout(()=> {
fn.apply(this,arguments)
},wait)
}
}
複製代碼
Throttling
)實現能夠理解爲事件在一個管道中傳輸,加上這個節流閥之後,事件的流速就會減慢。實際上這個函數的做用就是如此,它能夠將一個函數的調用頻率限制在必定閾值內,例如 1s,那麼 1s 內這個函數必定不會被調用兩次
function throttle(fn, wait) {
let prev = new Date();
returnfunction() {
const args = arguments;
const now = new Date();
if (now - prev > wait) {
fn.apply(this, args);
prev = new Date();
}
}
複製代碼
經過第三個參數來切換模式。
const throttle = function(fn, delay, isDebounce) {
let timer
let lastCall = 0
returnfunction (...args) {
if (isDebounce) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn(...args)
}, delay)
} else {
const now = new Date().getTime()
if (now - lastCall < delay) return
lastCall = now
fn(...args)
}
}
}
複製代碼
有個最著名的乞丐版實現,在《你不知道的JavaScript(上)》裏也有說起:
var newObj = JSON.parse( JSON.stringify( someObj ) );
複製代碼
### 10.2 面試夠用版
function deepCopy(obj){
//判斷是不是簡單數據類型,
if(typeof obj == "object"){
//複雜數據類型
var result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
//簡單數據類型 直接 == 賦值
var result = obj;
}
return result;
}
複製代碼
關於深拷貝的討論每天有,這裏就貼兩種吧,畢竟我...
instanceOf
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) returnfalseif(proto === prototype) returntrue
proto = proto.__proto__;
}
}
複製代碼
(兩側定寬,中間自適應)
採用了 absolute,致使父元素脫離了文檔流,那全部的子元素也須要脫離文檔流。若是頁面複雜,那開發的難度可想而知
利用浮動 當中間內容高於兩側時,兩側高度不會隨中間內容變高而變高
彈性盒子佈局
利用負邊距和浮動,實現起來比較複雜
利用網格佈局
.container { display: grid; grid-template-columns: 100px auto 200px; } 複製代碼
[...new Set(arr]
複製代碼
var arr = [1,2,1,2,3,5,4,5,3,4,4,4,4],
init=[]
var result = arr.sort().reduce((init, current)=>{
console.log(init,current)
if(init.length===0 || init[init.length-1]!==current){
init.push(current);
}
return init;
}, []);
console.log(result);//1,2,3,4,5複製代碼
複製代碼
var deBounce=function(fn,wait=300){
let timer
returnfunction(){
if(timer){
clearTimeOut(timer)
}
timer=setTimeOut(()=>{
fn.apply(this,arguments)
},wait)
}
}
var throttle=function(fn,wait=300){
let prev=+newDate();
returnfunction(){
const args=argument,
now=+newDate();
if(now>last+wait){
last=now;
fn.apply(this,args)
}
}
}
複製代碼
複製代碼
//0 pending , 1 resolve,2 reject functionPromise(fn) {
...
this._state = 0// 狀態標記
doResolve(fn, this)
}
functiondoResolve(fn, self) {
var done = false// 保證只執行一個監聽try {
fn(function(value) {
if (done) return
done = true
resolve(self, value)
},
function(reason) {
if (done) return;
done = true
reject(self, value)
})
} catch(err) {
if (done) return
done = true
reject(self, err)
}
}
functionresolve(self, newValue) {
try {
self._state = 1;
...
}
catch(err) {
reject(self, err)
}
}
functionreject(self, newValue) {
self._state = 2;
...
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
}
複製代碼
複製代碼
functioncommafy(num) {
return num && num
.toString()
.replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
return $1 + ",";
});
}
console.log(commafy(1312567.903000))
複製代碼
複製代碼
javascript是單線程語言,任務設計成了兩類,同步任務和異步任務 同步和異步任務分別進入不一樣的執行「場所」,同步進入主線程,異步進入Event Table並註冊函數。當指定的事情完成時,Event Table會將這個函數移入Event Queue。主線程內的任務執行完畢爲空,回去了Event Queue讀取對應的函數,進入主線程。 上述過程會不斷重複,也就是常說的Event Loop(事件循環)。 可是,JS異步還有一個機制,就是遇到宏任務,先執行宏任務,將宏任務放入event queue,而後再執行微任務,將微任務放入eventqueue,可是,這兩個queue不是一個queue。當你往外拿的時候先從微任務裏拿這個回調函數,而後再從宏任務的queue拿宏任務的回調函數 宏任務通常包括:總體代碼script,setTimeout,setInterval。 微任務:Promise,process.nextTick
functioncreate(){
//1.建立一個空對象let obj={}
//2.獲取構造函數let Con=[].shift.call(arguments)
//3.設置空對象的原型
obj._proto_=Con.prototype
//4.綁定this並執行構造函數,給新對象添加屬性和方法let result=Con.apply(obj,arguments)
//5.確保返回值爲對象return result instanceofObject?result:obj
}
複製代碼
複製代碼
/* 封裝ajax函數
* @param {string}opt.type http鏈接的方式,包括POST和GET兩種方式
* @param {string}opt.url 發送請求的url
* @param {boolean}opt.async 是否爲異步請求,true爲異步的,false爲同步的
* @param {object}opt.data 發送的參數,格式爲對象類型
* @param {function}opt.success ajax發送並接收成功調用的回調函數
*/functionmyAjax(opt){
opt = opt || {};
opt.method = opt.method.toUpperCase() || 'POST';
opt.url = opt.url || '';
opt.async = opt.async || true;
opt.data = opt.data || null;
opt.success = opt.success || function () {}
let xmlHttp = null;
if (XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}else{
xmlHttp =new ActiveXObject('Microsoft.XMLHTTP')
}
let params;
for (var key in opt.data){
params.push(key + '=' + opt.data[key]);
}
let postData = params.join('&');
if (opt.method.toUpperCase() === 'POST') {
xmlHttp.open(opt.method, opt.url, opt.async);
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
xmlHttp.send(postData);
}elseif (opt.method.toUpperCase() === 'GET') {
xmlHttp.open(opt.method, opt.url + '?' + postData, opt.async);
xmlHttp.send(null);
}
xmlHttp.onreadystatechange= function () {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
opt.success(xmlHttp.responseText);//若是是json數據能夠在這使用opt.success(JSON.parse( xmlHttp.responseText))
}
};
}
複製代碼
複製代碼
var url = "http://www.taobao.com/index.php?key0=0&key1=1&key2=2";
functionparseQueryString(url){
var str = url.split("?")[1], //經過?獲得一個數組,取?後面的參數
items = str.split("&"); //分割成數組var arr,name,value;
for(var i=0; i<items.length; i++){
arr = items[i].split("="); //["key0", "0"]
name = arr[0];
value = arr[1];
this[name] = value;
}
}
var obj = new parseQueryString(url);
alert(obj.key2)
複製代碼
複製代碼
HTTP協議(超文本傳輸協議) 主要特色
HTTP之請求消息Request
HTTP之響應消息Response
HTTP響應也由四個部分組成,分別是:狀態行、消息報頭、空行和響應正文。
SYN (同步序列編號)ACK(確認字符)
這是由於服務端在LISTEN狀態下,收到創建鏈接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。而關閉鏈接時,當收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,己方也未必所有數據都發送給對方了,因此己方能夠當即close,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACK和FIN通常都會分開發送。
當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣作:
其實 VNode 是對真實 DOM 的一種抽象描述,它的核心定義無非就幾個關鍵屬性,標籤名、數據、子節點、鍵值等,其它屬性都是都是用來擴展 VNode 的靈活性以及實現一些特殊 feature 的。因爲 VNode 只是用來映射到真實 DOM 的渲染,不須要包含操做 DOM 的方法,所以它是很是輕量和簡單的。 Virtual DOM 除了它的數據結構的定義,映射到真實的 DOM 實際上要經歷 VNode 的 create、diff、patch 等過程。那麼在 Vue.js 中,VNode 的 create 是經過以前提到的 createElement 方法建立的,咱們接下來分析這部分的實現。
Object.defineProperty(obj, prop, descriptor)
obj 是要在其上定義屬性的對象;prop 是要定義或修改的屬性的名稱;descriptor 是將被定義或修改的屬性描述符。 比較核心的是 descriptor,它有不少可選鍵值,具體的能夠去參閱它的文檔。這裏咱們最關心的是 get 和 set,get 是一個給屬性提供的 getter 方法,當咱們訪問了該屬性的時候會觸發 getter 方法;set 是一個給屬性提供的 setter 方法,當咱們對該屬性作修改的時候會觸發 setter 方法。一旦對象擁有了 getter 和 setter,咱們能夠簡單地把這個對象稱爲響應式對象
對象遞歸調用
數組變異方法的解決方法:代理原型/實例方法
observe
observe 方法的做用就是給非 VNode 的對象類型數據添加一個 Observer,若是已經添加過則直接返回,不然在知足必定條件下去實例化一個 Observer 對象實例。
observe 的功能就是用來監測數據的變化.
Observer 是一個類,它的做用是給對象的屬性添加 getter 和 setter,用於依賴收集和派發更新:
依賴收集和派發更新
收集依賴的目的是爲了當這些響應式數據發生變化,觸發它們的 setter 的時候,能知道應該通知哪些訂閱者去作相應的邏輯處理,咱們把這個過程叫派發更新,其實 Watcher 和 Dep 就是一個很是經典的觀察者設計模式的實現
派發更新就是數據發生變化的時候,觸發 setter 邏輯,把在依賴過程當中訂閱的的全部觀察者,也就是 watcher,都觸發它們的 update 過程,這個過程又利用了隊列作了進一步優化,在 nextTick 後執行全部 watcher 的 run,最後執行它們的回調函數
vue編譯Compile的過程主要分如下幾步 parse(生成AST)=> optimize(優化靜態節點) => generate(生成render function)
// 解析模板字符串生成 ASTconst ast = parse(template.trim(), options) //優化語法樹 optimize(ast, options) //生成代碼const code = generate(ast, options) 複製代碼
預防:
使用XSS Filter
<script>
中,能夠進行JS編碼。使用 HttpOnly Cookie 將重要的cookie標記爲httponly,這樣的話當瀏覽器向Web服務器發起請求的時就會帶上cookie字段,可是在js腳本中卻不能訪問這個cookie,這樣就避免了XSS攻擊利用JavaScript的document.cookie獲取cookie。
CSRF:跨站請求僞造,也稱 XSRF,是一種挾制用戶在當前已登陸的Web應用程序上執行非本意的操做的攻擊方法。與 XSS 相比,XSS利用的是用戶對指定網站的信任,CSRF利用的是網站對用戶網頁瀏覽器的信任。
沒有套路,只是面試前攢人品,有錯誤請指出,謝謝。
要求:垂直兩欄,左邊固定右邊自適應。
查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
要求:垂直三欄佈局,左右兩欄寬度固定,中間自適應
查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
和三欄佈局要求相同,不過中間列要寫在前面保證優先渲染。
查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
實現一個三角形
常見題目,經過 border
實現 查看代碼
使用 css 實現一個寬高自適應的正方形
查看代碼 <metacharset="utf-8">
實現一個 1/4 圓、任意弧度的扇形
有多種實現方法,這裏選幾種簡單方法(我看得懂的)實現。 查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
實現子元素的水平垂直居中
查看代碼
要求:清除浮動
能夠經過 clear:both
或 BFC 實現 查看代碼
使用 CSS 寫一個彈出框效果
查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
要求:一個
div
內部放不少水平div
,並能夠橫向滾動。
查看代碼 <htmllang="en"><metacharset="UTF-8"><metaname="viewport"content="width=div, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge">
CSS 部分完,總結,Flex 無敵。
Function.prototype.bind = function(context, ...bindArgs) {
// func 爲調用 bind 的原函數const func = this;
context = context || window;
if (typeof func !== 'function') {
thrownewTypeError('Bind must be called on a function');
}
// bind 返回一個綁定 this 的函數returnfunction(...callArgs) {
let args = bindArgs.concat(callArgs);
if (thisinstanceof func) {
// 意味着是經過 new 調用的 而 new 的優先級高於 bindreturnnew func(...args);
}
return func.call(context, ...args);
}
}
// 經過隱式綁定實現Function.prototype.call = function(context, ...args) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
thrownewTypeError('call must be called on a function');
}
let res = context.func(...args);
delete context.func;
return res;
}
Function.prototype.apply = function(context, args) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
thrownewTypeError('apply must be called on a function');
}
let res = context.func(...args);
delete context.func;
return res;
}
複製代碼
複製代碼
// 參考 You Dont Know JavaScript 上卷// 基類functionBase() {
}
// 派生類functionDerived() {
Base.call(this);
}
// 將派生類的原型的原型鏈掛在基類的原型上Object.setPrototypeOf(Derived.prototype, Base.prototype);
複製代碼
複製代碼
// 手動實現一個 new 關鍵字的功能的函數 _new(fun, args) --> new fun(args)function_new(fun, ...args) {
if (typeof fun !== 'function') {
returnnewError('參數必須是一個函數');
}
let obj = Object.create(fun.prototype);
let res = fun.call(obj, ...args);
if (res !== null && (typeof res === 'object' || typeof res === 'string')) {
return res;
}
return obj;
}
複製代碼
複製代碼
// a instanceof bfunction_instanceof(a, b) {
while (a) {
if (a.__proto__ === b.prototype) returntrue;
a = a.__proto__;
}
returnfalse;
}
複製代碼
複製代碼
// foo 函數將會被調用 傳入後臺返回的數據functionfoo(data) {
console.log('經過jsonp獲取後臺數據:', data);
document.getElementById('data').innerHTML = data;
}
/**
* 經過手動建立一個 script 標籤發送一個 get 請求
* 並利用瀏覽器對 <script> 不進行跨域限制的特性繞過跨域問題
*/
(functionjsonp() {
let head = document.getElementsByTagName('head')[0]; // 獲取head元素 把js放裏面let js = document.createElement('script');
js.src = 'http://domain:port/testJSONP?a=1&b=2&callback=foo'; // 設置請求地址
head.appendChild(js); // 這一步會發送請求
})();
// 後臺代碼// 由於是經過 script 標籤調用的 後臺返回的至關於一個 js 文件// 根據前端傳入的 callback 的函數名直接調用該函數// 返回的是 'foo(3)'functiontestJSONP(callback, a, b) {
return`${callback} + (${a + b})`;
}
複製代碼
複製代碼
感受這個有點無聊了…… 查看代碼 // Asynchronous Javascript And XMLfunctionajax(options) { // 選項var method = options.method || 'GET', params = options.params, data = options.data, url = options.url + (params ? '?' + Object.keys(params).map(key => key + '=' + params[key]).join('&') : ''), async = options.async === false ? false : true, success = options.success, headers = options.headers;
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onstatechange = function() {
/**
readyState:
0: 請求未初始化
1: 服務器鏈接已創建
2: 請求已接收
3: 請求處理中
4: 請求已完成,且響應已就緒
status: HTTP 狀態碼
**/if (request.readyState === 4 && request.status === 200) {
success && success(request.responseText);
}
}
request.open(method, url, async);
if (headers) {
Object.keys(headers).forEach(key => request.setRequestHeader(key, headers[key]));
}
method === 'GET' ? request.send() : request.send(request.data);
}
// e.g.
ajax({
method: 'GET',
url: '...',
success: function(res) {
console.log('success', res);
},
async: true,
params: {
p: 'test',
t: 666
},
headers: {
'Content-Type': 'application/json'
}
})
複製代碼
複製代碼
functionreduce(arr, callback, initial) {
let i = 0;
let acc = initial === undefined ? arr[i++] : initial;
for (; i < arr.length; i++) {
acc = callback(acc, arr[i], i, arr);
}
return acc;
}
複製代碼
複製代碼
要求是 yield
後面只能是 Promise
或 Thunk
函數,詳見 es6.ruanyifeng.com/#docs/gener…
functionrun(gen) {
let g = gen();
functionnext(data) {
let result = g.next(data);
if (result.done) return result.value;
if (result.value instanceofPromise) {
result.value.then(data => next(data));
} else {
result.value(next);
}
}
return next();
}
// ======== e.g. ==========functionfunc(data, cb) {
console.log(data);
cb();
}
function *gen() {
let a = yieldPromise.resolve(1);
console.log(a);
let b = yieldPromise.resolve(2);
console.log(b);
yield func.bind(null, a + b);
}
run(gen);
/**
output:
1
2
3
**/複製代碼
複製代碼
老生常談了,感受不必寫太複雜
/**
* 節流函數 限制函數在指定時間段只能被調用一次
* 用法 好比防止用戶連續執行一個耗時操做 對操做按鈕點擊函數進行節流處理
*/functionthrottle(func, wait) {
let timer = null;
returnfunction(...args) {
if (!timer) {
func(...args);
timer = setTimeout(() => {
timer = null;
}, wait);
}
}
}
複製代碼
複製代碼
/**
* 函數調用後不會被當即執行 以後連續 wait 時間段沒有調用纔會執行
* 用法 如處理用戶輸入
*/functiondebounce(func, wait) {
let timer = null;
returnfunction(...args) {
if (timer) clearTimeout(timer); // 若是在定時器未執行期間又被調用 該定時器將被清除 並從新等待 wait 秒
timer = setTimeout(() => {
func(...args);
}, wait);
}
}
複製代碼
複製代碼
簡單實現,基本功能都有了。
const PENDING = 1;
const FULFILLED = 2;
const REJECTED = 3;
functionMyPromise(executor) {
let self = this;
this.resolveQueue = [];
this.rejectQueue = [];
this.state = PENDING;
this.val = undefined;
functionresolve(val) {
if (self.state === PENDING) {
setTimeout(() => {
self.state = FULFILLED;
self.val = val;
self.resolveQueue.forEach(cb => cb(val));
});
}
}
functionreject(err) {
if (self.state === PENDING) {
setTimeout(() => {
self.state = REJECTED;
self.val = err;
self.rejectQueue.forEach(cb => cb(err));
});
}
}
try {
// 回調是異步執行 函數是同步執行
executor(resolve, reject);
} catch(err) {
reject(err);
}
}
MyPromise.prototype.then = function(onResolve, onReject) {
let self = this;
// 不傳值的話默認是一個返回原值的函數
onResolve = typeof onResolve === 'function' ? onResolve : (v => v);
onReject = typeof onReject === 'function' ? onReject : (e => { throw e });
if (self.state === FULFILLED) {
returnnew MyPromise(function(resolve, reject) {
setTimeout(() => {
try {
let x = onResolve(self.val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
if (self.state === REJECTED) {
returnnew MyPromise(function(resolve, reject) {
setTimeout(() => {
try {
let x = onReject(self.val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
if (self.state === PENDING) {
returnnew MyPromise(function(resolve, reject) {
self.resolveQueue.push((val) => {
try {
let x = onResolve(val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
self.rejectQueue.push((val) => {
try {
let x = onReject(val);
if (x instanceof MyPromise) {
x.then(resolve);
} else {
resolve(x);
}
} catch(e) {
reject(e);
}
});
});
}
}
MyPromise.prototype.catch = function(onReject) {
returnthis.then(null, onReject);
}
MyPromise.all = function(promises) {
returnnew MyPromise(function(resolve, reject) {
let cnt = 0;
let result = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
result[i] = res;
if (++cnt === promises.length) resolve(result);
}, err => {
reject(err);
})
}
});
}
MyPromise.race = function(promises) {
returnnew MyPromise(function(resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
}
MyPromise.resolve = function(val) {
returnnew MyPromise(function(resolve, reject) {
resolve(val);
});
}
MyPromise.reject = function(err) {
returnnew MyPromise(function(resolve, reject) {
reject(err);
})
}
複製代碼
複製代碼
實現原理就是監聽 url 的哈希值變化了
<!DOCTYPE html><html><head><title>hash 路由</title></head><body><header><ahref="#home">首頁</a><ahref="#center">我的中心頁</a><ahref="#help">幫助頁</a></header><sectionid="content"></section><script>window.addEventListener('hashchange', (e) => {
let content = document.getElementById('content');
content.innerText = location.hash;
})
</script></body></html>複製代碼
複製代碼
<!DOCTYPE html><html><head><title>history 路由</title></head><body><header><aonclick="changeRoute(this)"data-path="home">首頁</a><aonclick="changeRoute(this)"data-path="center">我的中心頁</a><aonclick="changeRoute(this)"data-path="help">幫助頁</a></header><sectionid="content"></section><script>functionchangeRoute(route) {
let path = route.dataset.path;
/**
* window.history.pushState(state, title, url)
* state:一個與添加的記錄相關聯的狀態對象,主要用於popstate事件。該事件觸發時,該對象會傳入回調函數。
* 也就是說,瀏覽器會將這個對象序列化之後保留在本地,從新載入這個頁面的時候,能夠拿到這個對象。
* 若是不須要這個對象,此處能夠填 null。
* title:新頁面的標題。可是,如今全部瀏覽器都忽視這個參數,因此這裏能夠填空字符串。
* url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。
*/
changePage(path);
history.pushState({ content: path }, null, path);
}
/**
* 調用 history.pushState() 或者 history.replaceState() 不會觸發 popstate 事件。
* 點擊後退、前進按鈕、或者在 js 中調用 history.back()、history.forward()、history.go() 方法會觸發
*/window.addEventListener('popstate', (e) => {
let content = e.state && e.state.content;
changePage(content);
});
functionchangePage(pageContent) {
let content = document.getElementById('content');
content.innerText = pageContent;
}
</script></body></html>複製代碼
複製代碼
還有一些稍複雜的能夠寫,有時間再補。