一、this 的指向html
1)、由 new 調用?綁定到新建立的對象。前端
2)、 由 call 或者 apply(或者 bind)調用?綁定到指定的對象。vue
3)、 由上下文對象調用?綁定到那個上下文對象。node
4)、 默認:在嚴格模式下綁定到 undefined,不然綁定到全局對象。 webpack
二、new關鍵字的過程nginx
function Student(name, age) { this.name = name; this.age = age; } var first = new Student('John', 26);
建立了一個新對象 —— first 對象git
this 綁定到 first 對象。全部 this 引用指向 first。github
添加__proto__。first.__proto__ 如今指向 Studnet.prototype。web
全部事情完成後,咱們將新的 first 對象返回出來賦值給新的 first 變量。面試
三、call、apply 和 bind 的區別
共同點:都是改變代碼運行時的上下文
不一樣點:apply 接收一個包含多個參數的數組,而 call 與 bind 接收的是若干個參數的列表,另外 bind 會建立一個被初始化參數改造的新函數
// 一個簡單的實現 Function.prototype.bind = function(ctx) { var fn = this; return function() { fn.apply(ctx, arguments); } // ...... }
四、獲取對象屬性的方法
Object.getOwnPropertyNames vs Object.keys
var a = {}; Object.defineProperties(a, { one: {enumerable: true, value: 'one'}, two: {enumerable: false, value: 'two'}, }); // 只能獲取 enumerable 爲 true 便可枚舉的屬性 // ["one"] Object.keys(a); // 可不可枚舉都能獲得 // ["one", "two"] Object.getOwnPropertyNames(a);
五、閉包
定義:是一個由函數和其詞法做用域綁定在一塊兒所造成的組合結構
特徵:函數能夠記住並訪問其詞法做用域,即時函數在當前的詞法做用域外執行也能夠
栗子:
function Eat() { var desc = ' is eating'; function eat(animal) { console.log(animal.name + desc); } return eat; } var eat = Eat(); //全局變量 var desc = '正在吃...'; var dog = {name: 'dog'}; // 閉包裏的詞法做用域(又叫靜態做用域)是在函數建立時就和函數綁定了 // 知道輸出結果了吧! eat(dog); // 還不明白,看這個 var x = 1; function foo() { console.log(x); } function bar(fn) { var x = 2; fn(); } //輸出? bar(foo);
應用:模塊加載器
var MyModules = (function Manager(){ var modules = {}; function define(name, deps, impl) { for(var i=0; i<deps.length; i++) { deps[i] = modules[deps[i]]; } modules[name] = impl.apply( impl, deps ); } function get(name) { return modules[name]; } return { define: define, get: get }; })();
六、怎麼理解JS模塊化?有沒有使用過webpack?
優勢:提升代碼的可維護性、可重用性,避免污染命名空間
七、怎麼彌補 CSS in JS 不支持嵌套、keyframes、媒體查詢等特性的缺陷 ?
使用 styled-components,它的實現原理是使用 ES6的模板字符串特性,解決了在 JSX 中編寫 CSS 的各類問題,因爲它最終是在 head 裏插入 style,因此它支持全部 CSS 特性,同時又能獲取 JS 強大的控制力,具體詳情戳這裏styled-components 背後的魔法
八、requestAnimationFrame 優化原理
requestAnimationFrame 會自動匹配 W3C 所建議的刷新頻率,既不會由於頻率過高,增長開銷,也不會頻率過低,形成動畫丟幀卡頓。
優勢:
requestAnimationFrame
將不會進行重繪或迴流,這固然就意味着更少的CPU、GPU和內存使用量九、怎麼突破 localStorage 的容量限制
十、Event Loop 原理
當 js 代碼執行時會將不一樣的變量存到堆(heap)和棧(stack)中。其中,堆中存放着一些對象,而棧中則存放着一些基礎類型變量以及對象的指針。
而執行棧是指當一系列方法被依次調用的時候,由於js是單線程的,同一時間只能執行一個方法,因而這些方法被排隊在一個單獨的地方。這個地方被稱爲執行棧。
當一個腳本第一次執行的時候,js引擎會解析這段代碼,並將其中的同步代碼按照執行順序加入執行棧中,而後從頭開始執行。當這個執行環境中的代碼 執行完畢並返回結果後,js會退出這個執行環境並把這個執行環境銷燬,回到上一個方法的執行環境,這個過程反覆進行,直到執行棧中的代碼所有執行完畢。
當一個異步事件返回結果後,js會將這個事件加入與當前執行棧不一樣的另外一個隊列,咱們稱之爲事件隊列。被放入事件隊列不會馬上執行其回調,而是等待當前執行棧中的全部任務都執行完畢, 主線程處於閒置狀態時,主線程會去查找事件隊列是否有任務。若是有,那麼主線程會從中取出排在第一位的事件,並把這個事件對應的回調放入執行棧中,而後執行其中的同步代碼...,如此反覆,這樣就造成了一個無限的循環。這就是這個過程被稱爲「事件循環(Event Loop)」的緣由。
這個過程需記住噹噹前執行棧執行完畢時會馬上先處理全部微任務隊列中的事件,而後再去宏任務隊列中取出一個事件。同一次事件循環中,微任務永遠在宏任務以前執行。
如下事件屬於宏任務:
setInterval()
setTimeout()
如下事件屬於微任務
new Promise()
new MutaionObserver()
那麼,下面這段代碼的結果是 ?
1 setTimeout(function () { 2 console.log(1); 3 }); 4 5 new Promise(function(resolve,reject){ 6 console.log(2) 7 resolve(3) 8 }).then(function(val){ 9 console.log(val); 10 })
// 2 3 1
詳細規則爲:
當一個程序有:setTimeout, setInterval ,setImmediate, I/O, UI渲染,Promise ,process.nextTick, Object.observe, MutationObserver的時候:
1.先執行 macrotasks:I/O -》 UI渲染-》requestAnimationFrame
2.再執行 microtasks :process.nextTick -》 Promise -》MutationObserver ->Object.observe
3.再把setTimeout setInterval setImmediate【三個貨不討喜】 塞入一個新的macrotasks,依次:setTimeout ,setInterval --》setImmediate
1 setImmediate(function(){ 2 console.log(1); 3 },0); 4 setTimeout(function(){ 5 console.log(2); 6 },0); 7 new Promise(function(resolve){ 8 console.log(3); 9 resolve(); 10 console.log(4); 11 }).then(function(){ 12 console.log(5); 13 }); 14 console.log(6); 15 process.nextTick(function(){ 16 console.log(7); 17 }); 18 console.log(8); 19 20 // 結果是:3 4 6 8 7 5 2 1
十一、Vue.js 中 nextTick 的實現原理
當觀察到數據變化時,Vue 將開啓一個隊列,並緩衝在同一事件循環中發生的全部數據改變。若是同一個 watcher 被屢次觸發,只會一次推入到隊列中。這種在緩衝時去除重複數據對於避免沒必要要的計算和 DOM 操做上很是重要。而後,在下一個的事件循環「tick」中,Vue 刷新隊列並執行實際(已去重的)工做。Vue 在內部嘗試對異步隊列使用原生的 Promise.then 和 MutationObserver,若是執行環境不支持,會採用 setTimeout(fn, 0) 代替。
Vue源碼詳解之nextTick:MutationObserver只是浮雲,microtask纔是核心! · Issue #6 · Ma63d/vue-analysis
十二、Vue 實現原理
vue
將data
初始化爲一個Observer
並對對象中的每一個值,重寫了其中的get
、set
,data
中的每一個key
,都有一個獨立的依賴收集器。get
中,向依賴收集器添加了監聽Watcher
,將收集器的目標指向了當前Watcher
data
值發生變動時,觸發set
,觸發了依賴收集器中的全部監聽的更新,來觸發Watcher.update
1 const Observer = function(data) { 2 // 循環修改成每一個屬性添加get set 3 for (let key in data) { 4 defineReactive(data, key); 5 } 6 } 7 8 const defineReactive = function(obj, key) { 9 // 局部變量dep,用於get set內部調用 10 const dep = new Dep(); 11 // 獲取當前值 12 let val = obj[key]; 13 Object.defineProperty(obj, key, { 14 // 設置當前描述屬性爲可被循環 15 enumerable: true, 16 // 設置當前描述屬性可被修改 17 configurable: true, 18 get() { 19 console.log('in get'); 20 // 調用依賴收集器中的addSub,用於收集當前屬性與Watcher中的依賴關係 21 dep.depend(); 22 return val; 23 }, 24 set(newVal) { 25 if (newVal === val) { 26 return; 27 } 28 val = newVal; 29 // 當值發生變動時,通知依賴收集器,更新每一個須要更新的Watcher, 30 // 這裏每一個須要更新經過什麼判定?dep.subs 31 dep.notify(); 32 } 33 }); 34 } 35 36 const observe = function(data) { 37 return new Observer(data); 38 } 39 40 const Vue = function(options) { 41 const self = this; 42 // 將data賦值給this._data,源碼這部分用的Proxy因此咱們用最簡單的方式臨時實現 43 if (options && typeof options.data === 'function') { 44 this._data = options.data.apply(this); 45 } 46 // 掛載函數 47 this.mount = function() { 48 new Watcher(self, self.render); 49 } 50 // 渲染函數 51 this.render = function() { 52 with(self) { 53 _data.text; 54 } 55 } 56 // 監聽this._data 57 observe(this._data); 58 } 59 60 const Watcher = function(vm, fn) { 61 const self = this; 62 this.vm = vm; 63 // 將當前Dep.target指向本身 64 Dep.target = this; 65 // 向Dep方法添加當前Wathcer 66 this.addDep = function(dep) { 67 dep.addSub(self); 68 } 69 // 更新方法,用於觸發vm._render 70 this.update = function() { 71 console.log('in watcher update'); 72 fn(); 73 } 74 // 這裏會首次調用vm._render,從而觸發text的get 75 // 從而將當前的Wathcer與Dep關聯起來 76 this.value = fn(); 77 // 這裏清空了Dep.target,爲了防止notify觸發時,不停的綁定Watcher與Dep, 78 // 形成代碼死循環 79 Dep.target = null; 80 } 81 82 const Dep = function() { 83 const self = this; 84 // 收集目標 85 this.target = null; 86 // 存儲收集器中須要通知的Watcher 87 this.subs = []; 88 // 當有目標時,綁定Dep與Wathcer的關係 89 this.depend = function() { 90 if (Dep.target) { 91 // 這裏其實能夠直接寫self.addSub(Dep.target), 92 // 沒有這麼寫由於想還原源碼的過程。 93 Dep.target.addDep(self); 94 } 95 } 96 // 爲當前收集器添加Watcher 97 this.addSub = function(watcher) { 98 self.subs.push(watcher); 99 } 100 // 通知收集器中所的全部Wathcer,調用其update方法 101 this.notify = function() { 102 for (let i = 0; i < self.subs.length; i += 1) { 103 self.subs[i].update(); 104 } 105 } 106 } 107 108 const vue = new Vue({ 109 data() { 110 return { 111 text: 'hello world' 112 }; 113 } 114 }) 115 116 vue.mount(); // in get 117 vue._data.text = '123'; // in watcher update /n in get
1三、 跨域解決方案
一、 經過jsonp跨域
二、 document.domain + iframe跨域
三、 location.hash + iframe
四、 window.name + iframe跨域
五、 postMessage跨域
六、 跨域資源共享(CORS)
七、 nginx代理跨域
八、 nodejs中間件代理跨域
九、 WebSocket協議跨域
1六、前端優化的方法
參考:
http://www.ayqy.net/blog/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96%EF%BC%9A%E9%9B%85%E8%99%8E35%E6%9D%A1/