按部就班,越日後越難!!!
若是給一個變量賦值一個對象,那麼二者的值會是同一個引用,其中一方改變,另外一方也會相應改變。針對引用類型咱們須要實現數據的拷貝。前端
...
實現const copy = {...{x:1}}
Object.assign
實現const copy = Object.assign({}, {x:1})
slice
實現let arr = [1, 3, { x: 1 }]; let copy = arr.slice();
一般淺拷貝就能解決大部分問題,可是隻解決了第一層的問題,若是接下去的值中還有對象的話,那麼咱們須要使用深拷貝。vue
缺點:vuex
const obj = {a: 1, b: {x: 3}} const copy = JSON.parse(JSON.stringify(obj))
乞丐版的遞歸,針對經常使用的JS類型(基礎類型、數組、對象),雖然解決了大部分JSON.parse(JSON.stringify(oldObj))
的問題,但依然沒法解決循環引用的問題。數組
function deepClone(obj) { let copy = obj instanceof Array ? [] : {} for (let i in obj) { if (obj.hasOwnProperty(i)) { copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i] } } return copy }
參考vuex的deepCopy源碼,解決循環引用promise
function deepCopy (obj, cache = []) { // typeof [] => 'object' // typeof {} => 'object' if (obj === null || typeof obj !== 'object') { return obj } // 若是傳入的對象與緩存的相等, 則遞歸結束, 這樣防止循環 /** * 相似下面這種 * var a = {b:1} * a.c = a * 資料: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value */ const hit = cache.filter(c => c.original === obj)[0] if (hit) { return hit.copy } const copy = Array.isArray(obj) ? [] : {} // 將copy首先放入cache, 由於咱們須要在遞歸deepCopy的時候引用它 cache.push({ original: obj, copy }) Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key], cache) }) return copy }
固然: cache 可使用 new WeakMap()
代替緩存
Map
和 Set
相關,固然你能夠再添加 Date
之類的補充WeakMap
代替 []
function clone(target, map = new WeakMap()) { // 克隆原始類型 if (!isObject(target)) { return target; } // 初始化 const type = getType(target); let cloneTarget; if (deepTag.includes(type)) { cloneTarget = getInit(target, type); } // 防止循環引用 if (map.get(target)) { return map.get(target); } map.set(target, cloneTarget); // 克隆set if (type === setTag) { target.forEach(value => { cloneTarget.add(clone(value,map)); }); return cloneTarget; } // 克隆map if (type === mapTag) { target.forEach((value, key) => { cloneTarget.set(key, clone(value,map)); }); return cloneTarget; } // 克隆對象和數組 const keys = type === arrayTag ? undefined : Object.keys(target); forEach(keys || target, (value, key) => { if (keys) { key = value; } cloneTarget[key] = clone(target[key], map); }); return cloneTarget; }
兼容對象、數組、Symbol、正則、Error、Date、基礎類型。閉包
const mapTag = '[object Map]'; const setTag = '[object Set]'; const arrayTag = '[object Array]'; const objectTag = '[object Object]'; const argsTag = '[object Arguments]'; const boolTag = '[object Boolean]'; const dateTag = '[object Date]'; const numberTag = '[object Number]'; const stringTag = '[object String]'; const symbolTag = '[object Symbol]'; const errorTag = '[object Error]'; const regexpTag = '[object RegExp]'; const funcTag = '[object Function]'; const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]; function forEach(array, iteratee) { let index = -1; const length = array.length; while (++index < length) { iteratee(array[index], index); } return array; } function isObject(target) { const type = typeof target; return target !== null && (type === 'object' || type === 'function'); } function getType(target) { return Object.prototype.toString.call(target); } function getInit(target) { const Ctor = target.constructor; return new Ctor(); } function cloneSymbol(targe) { return Object(Symbol.prototype.valueOf.call(targe)); } function cloneReg(targe) { const reFlags = /\w*$/; const result = new targe.constructor(targe.source, reFlags.exec(targe)); result.lastIndex = targe.lastIndex; return result; } function cloneFunction(func) { const bodyReg = /(?<={)(.|\n)+(?=})/m; const paramReg = /(?<=\().+(?=\)\s+{)/; const funcString = func.toString(); if (func.prototype) { const param = paramReg.exec(funcString); const body = bodyReg.exec(funcString); if (body) { if (param) { const paramArr = param[0].split(','); return new Function(...paramArr, body[0]); } else { return new Function(body[0]); } } else { return null; } } else { return eval(funcString); } } function cloneOtherType(targe, type) { const Ctor = targe.constructor; switch (type) { case boolTag: case numberTag: case stringTag: case errorTag: case dateTag: return new Ctor(targe); case regexpTag: return cloneReg(targe); case symbolTag: return cloneSymbol(targe); case funcTag: return cloneFunction(targe); default: return null; } } function clone(target, map = new WeakMap()) { // 克隆原始類型 if (!isObject(target)) { return target; } // 初始化 const type = getType(target); let cloneTarget; if (deepTag.includes(type)) { cloneTarget = getInit(target, type); } else { return cloneOtherType(target, type); } // 防止循環引用 if (map.get(target)) { return map.get(target); } map.set(target, cloneTarget); // 克隆set if (type === setTag) { target.forEach(value => { cloneTarget.add(clone(value, map)); }); return cloneTarget; } // 克隆map if (type === mapTag) { target.forEach((value, key) => { cloneTarget.set(key, clone(value, map)); }); return cloneTarget; } // 克隆對象和數組 const keys = type === arrayTag ? undefined : Object.keys(target); forEach(keys || target, (value, key) => { if (keys) { key = value; } cloneTarget[key] = clone(target[key], map); }); return cloneTarget; }
經過閉包調用 call || apply 能夠實現 bind 函數。app
Function.prototype.myapply = function (context, ...preArgs) { return (...args) => this.call(context, ...preArgs, ...args) }
Function.prototype.mybind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } let _this = this let arg = [...arguments].slice(1) return function F() { // 處理函數使用new的狀況 if (this instanceof F) { return new _this(...arg, ...arguments) } else { return _this.apply(context, arg.concat(...arguments)) } } }
實現 bind
須要調用 apply || call,那麼如何實現?less
思路:將要改變this指向的方法掛到目標this上執行並返回異步
Function.prototype.myapply = function (context) { if (typeof this !== 'function') { throw new TypeError('not funciton') } context = context || window context.fn = this let result if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result }
滾動事件、resize事件、input事件等: 須要作個複雜計算或者實現一個按鈕的防二次點擊操做。這些需求均可以經過函數防抖動來實現。
缺點:這個防抖只能在最後調用。通常的防抖會有immediate選項,表示是否當即調用。
const debounce = (func, wait = 50) => { // 緩存一個定時器id let timer = 0 // 這裏返回的函數是每次用戶實際調用的防抖函數 // 若是已經設定過定時器了就清空上一次的定時器 // 開始一個新的定時器,延遲執行用戶傳入的方法 return function(...args) { if (timer) clearTimeout(timer) timer = setTimeout(() => { func.apply(this, args) }, wait) } }
// 這個是用來獲取當前時間戳的 function now() { return +new Date() } /** * 防抖函數,返回函數連續調用時,空閒時間必須大於或等於 wait,func 纔會執行 * * @param {function} func 回調函數 * @param {number} wait 表示時間窗口的間隔 * @param {boolean} immediate 設置爲ture時,是否當即調用函數 * @return {function} 返回客戶調用函數 */ function debounce (func, wait = 50, immediate = true) { let timer, context, args // 延遲執行函數 const later = () => setTimeout(() => { // 延遲函數執行完畢,清空緩存的定時器序號 timer = null // 延遲執行的狀況下,函數會在延遲函數中執行 // 使用到以前緩存的參數和上下文 if (!immediate) { func.apply(context, args) context = args = null } }, wait) // 這裏返回的函數是每次實際調用的函數 return function(...params) { // 若是沒有建立延遲執行函數(later),就建立一個 if (!timer) { timer = later() // 若是是當即執行,調用函數 // 不然緩存參數和調用上下文 if (immediate) { func.apply(this, params) } else { context = this args = params } // 若是已有延遲執行函數(later),調用的時候清除原來的並從新設定一個 // 這樣作延遲函數會從新計時 } else { clearTimeout(timer) timer = later() } } }
節流的本質和防抖差很少。
缺點:這個節流函數缺乏首尾調用的開關
/** * 函數節流方法 * @param Function fn 延時調用函數 * @param Number delay 延遲多長時間 * @param Number atleast 至少多長時間觸發一次 * @return Function 延遲執行的方法 */ var throttle = function (fn, delay, atleast) { var timer = null; var previous = null; return function () { var now = Date.now(); if ( !previous ) previous = now; if ( now - previous > atleast ) { fn(); // 重置上一次開始時間爲本次結束時間 previous = now; } else { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } } };
lodash
經典的節流函數/** * underscore 節流函數,返回函數連續調用時,func 執行頻率限定爲 次 / wait * * @param function func 回調函數 * @param number wait 表示時間窗口的間隔 * @param object options 若是想忽略開始函數的的調用,傳入{leading: false}。 * 若是想忽略結尾函數的調用,傳入{trailing: false} * 二者不能共存,不然函數不能執行 * @return function 返回客戶調用函數 */ const throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 以前的時間戳 var previous = 0; // 若是 options 沒傳則設爲空對象 if (!options) options = {}; // 定時器回調函數 var later = function() { // 若是設置了 leading,就將 previous 設爲 0 // 用於下面函數的第一個 if 判斷 previous = options.leading === false ? 0 : Date.now(); // 置空一是爲了防止內存泄漏,二是爲了下面的定時器判斷 timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { // 得到當前時間戳 var now = Date.now(); // 首次進入前者確定爲 true // 若是須要第一次不執行函數 // 就將上次時間戳設爲當前的 // 這樣在接下來計算 remaining 的值時會大於0 if (!previous && options.leading === false) previous = now; // 計算剩餘時間 var remaining = wait - (now - previous); context = this; args = arguments; // 若是當前調用已經大於上次調用時間 + wait // 或者用戶手動調了時間 // 若是設置了 trailing,只會進入這個條件 // 若是沒有設置 leading,那麼第一次會進入這個條件 // 還有一點,你可能會以爲開啓了定時器那麼應該不會進入這個 if 條件了 // 其實仍是會進入的,由於定時器的延時 // 並非準確的時間,極可能你設置了2秒 // 可是他須要2.2秒才觸發,這時候就會進入這個條件 if (remaining <= 0 || remaining > wait) { // 若是存在定時器就清理掉不然會調用二次回調 if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { // 判斷是否設置了定時器和 trailing // 沒有的話就開啓一個定時器 // 而且不能不能同時設置 leading 和 trailing timeout = setTimeout(later, remaining); } return result; }; };
柯里化:
實現柯里化
/** * 實現要點:柯里化函數接收到足夠參數後,就會執行原函數,那麼咱們如何去肯定什麼時候達到足夠的參數呢? * 柯里化函數須要記住你已經給過他的參數,若是沒給的話,則默認爲一個空數組。 * 接下來每次調用的時候,須要檢查參數是否給夠,若是夠了,則執行fn,沒有的話則返回一個新的 curry 函數,將現有的參數塞給他。 * */ // 待柯里化處理的函數 let sum = (a, b, c, d) => { return a + b + c + d } // 柯里化函數,返回一個被處理過的函數 let curry = (fn, ...arr) => { // arr 記錄已有參數 return (...args) => { // args 接收新參數 if (fn.length <= (...arr,...args)) { // 參數夠時,觸發執行 return fn(...arr, ...args) } else { // 繼續添加參數 return curry(fn, [...arr, ...args]) } } } var sumPlus = curry(sum) sumPlus(1)(2)(3)(4) sumPlus(1, 2)(3)(4) sumPlus(1, 2, 3)(4)
/** * 固然了,柯里化函數的主要做用仍是延遲執行,執行的觸發條件不必定是參數個數相等,也能夠是其餘的條件。 * 例如參數個爲0的狀況,那麼咱們須要對上面curry函數稍微作修改 */ // 待柯里化處理的函數 let sum = arr => { return arr.reduce((a, b) => { return a + b }) } let curry = (fn, ...arr) => { // arr 記錄已有參數 return (...args) => { // args 接收新參數 if (args.length === 0) { // 參數爲空時,觸發執行 return fn(...arr, ...args) } else { // 繼續添加參數 return curry(fn, ...arr, ...args) } } } var sumPlus = curry(sum) sumPlus(1)(2)(3)(4)() sumPlus(1, 2)(3)(4)() sumPlus(1, 2, 3)(4)()
const flattenDeep = (arr) => Array.isArray(arr) ? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , []) : [arr] flattenDeep([1, [[2], [3, [4]], 5]])
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);
instanceOf
的內部機制是經過判斷對象的原型鏈中是否是能找到該類型的 prototype
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__; } }
Promise 有3個狀態 pending
、 resolve
和 reject
。由於狀態的的肯定,因此Promise的結果是可靠的。
Promise 還能解決回調地獄的問題。
實現了 Promise
的主要功能,缺乏異步處理等其餘狀況
class Promise { constructor (fn) { // 三個狀態 this.state = 'pending' this.value = undefined this.reason = undefined let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled' this.value = value } } let reject = value => { if (this.state === 'pending') { this.state = 'rejected' this.reason = value } } // 自動執行函數 try { fn(resolve, reject) } catch (e) { reject(e) } } // then then(onFulfilled, onRejected) { switch (this.state) { case 'fulfilled': onFulfilled(this.value) break case 'rejected': onRejected(this.value) break default: } } }
// 三種狀態 const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "rejected"; // promise 接收一個函數參數,該函數會當即執行 function MyPromise(fn) { let _this = this; _this.currentState = PENDING; _this.value = undefined; // 用於保存 then 中的回調,只有當 promise // 狀態爲 pending 時纔會緩存,而且每一個實例至多緩存一個 _this.resolvedCallbacks = []; _this.rejectedCallbacks = []; _this.resolve = function (value) { if (value instanceof MyPromise) { // 若是 value 是個 Promise,遞歸執行 return value.then(_this.resolve, _this.reject) } setTimeout(() => { // 異步執行,保證執行順序 if (_this.currentState === PENDING) { _this.currentState = RESOLVED; _this.value = value; _this.resolvedCallbacks.forEach(cb => cb()); } }) }; _this.reject = function (reason) { setTimeout(() => { // 異步執行,保證執行順序 if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.rejectedCallbacks.forEach(cb => cb()); } }) } // 用於解決如下問題 // new Promise(() => throw Error('error)) try { fn(_this.resolve, _this.reject); } catch (e) { _this.reject(e); } } MyPromise.prototype.then = function (onResolved, onRejected) { var self = this; // 規範 2.2.7,then 必須返回一個新的 promise var promise2; // 規範 2.2.onResolved 和 onRejected 都爲可選參數 // 若是類型不是函數須要忽略,同時也實現了透傳 // Promise.resolve(4).then().then((value) => console.log(value)) onResolved = typeof onResolved === 'function' ? onResolved : v => v; onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r}; if (self.currentState === RESOLVED) { return (promise2 = new MyPromise(function (resolve, reject) { // 規範 2.2.4,保證 onFulfilled,onRjected 異步執行 // 因此用了 setTimeout 包裹下 setTimeout(function () { try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === REJECTED) { return (promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { // 異步執行onRejected try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === PENDING) { return (promise2 = new MyPromise(function (resolve, reject) { self.resolvedCallbacks.push(function () { // 考慮到可能會有報錯,因此使用 try/catch 包裹 try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); self.rejectedCallbacks.push(function () { try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); })); } }; // 規範 2.3 function resolutionProcedure(promise2, x, resolve, reject) { // 規範 2.3.1,x 不能和 promise2 相同,避免循環引用 if (promise2 === x) { return reject(new TypeError("Error")); } // 規範 2.3.2 // 若是 x 爲 Promise,狀態爲 pending 須要繼續等待不然執行 if (x instanceof MyPromise) { if (x.currentState === PENDING) { x.then(function (value) { // 再次調用該函數是爲了確認 x resolve 的 // 參數是什麼類型,若是是基本類型就再次 resolve // 把值傳給下個 then resolutionProcedure(promise2, value, resolve, reject); }, reject); } else { x.then(resolve, reject); } return; } // 規範 2.3.3.3.3 // reject 或者 resolve 其中一個執行過得話,忽略其餘的 let called = false; // 規範 2.3.3,判斷 x 是否爲對象或者函數 if (x !== null && (typeof x === "object" || typeof x === "function")) { // 規範 2.3.3.2,若是不能取出 then,就 reject try { // 規範 2.3.3.1 let then = x.then; // 若是 then 是函數,調用 x.then if (typeof then === "function") { // 規範 2.3.3.3 then.call( x, y => { if (called) return; called = true; // 規範 2.3.3.3.1 resolutionProcedure(promise2, y, resolve, reject); }, e => { if (called) return; called = true; reject(e); } ); } else { // 規範 2.3.3.4 resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { // 規範 2.3.4,x 爲基本類型 resolve(x); } }
// 三種狀態 const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "rejected"; function MyPromise(fn) { let _this = this; _this.currentState = PENDING; _this.value = undefined; _this.resolvedCallbacks = []; _this.rejectedCallbacks = []; _this.resolve = function (value) { if (value instanceof MyPromise) { return value.then(_this.resolve, _this.reject) } setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = RESOLVED; _this.value = value; _this.resolvedCallbacks.forEach(cb => cb()); } }) }; _this.reject = function (reason) { setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.rejectedCallbacks.forEach(cb => cb()); } }) } try { fn(_this.resolve, _this.reject); } catch (e) { _this.reject(e); } } MyPromise.prototype.then = function (onResolved, onRejected) { var self = this; var promise2; onResolved = typeof onResolved === 'function' ? onResolved : v => v; onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r}; if (self.currentState === RESOLVED) { return (promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === REJECTED) { return (promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }); })); } if (self.currentState === PENDING) { return (promise2 = new MyPromise(function (resolve, reject) { self.resolvedCallbacks.push(function () { try { var x = onResolved(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); self.rejectedCallbacks.push(function () { try { var x = onRejected(self.value); resolutionProcedure(promise2, x, resolve, reject); } catch (r) { reject(r); } }); })); } }; // 規範 2.3 function resolutionProcedure(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError("Error")); } if (x instanceof MyPromise) { if (x.currentState === PENDING) { x.then(function (value) { resolutionProcedure(promise2, value, resolve, reject); }, reject); } else { x.then(resolve, reject); } return; } let called = false; if (x !== null && (typeof x === "object" || typeof x === "function")) { try { let then = x.then; if (typeof then === "function") { then.call( x, y => { if (called) return; called = true; resolutionProcedure(promise2, y, resolve, reject); }, e => { if (called) return; called = true; reject(e); } ); } else { resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { resolve(x); } } // catch方法 MyPromise.prototype.catch = function (rejectFn) { return this.then(undefined, rejectFn) } //finally方法 MyPromise.prototype.finally = function (callback) { return this.then( value => MyPromise.resolve(callback()).then(() => value), reason => MyPromise.resolve(callback()).then(() => { throw reason }) ) } /* 靜態方法添加 */ // resolve方法 MyPromise.resolve = function(val){ return new MyPromise((resolve,reject)=>{ resolve(val) }); } //reject方法 MyPromise.reject = function(val){ return new MyPromise((resolve,reject)=>{ reject(val) }); } //race方法 MyPromise.race = function(promises){ return new MyPromise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(resolve, reject) }; }) } //all方法(獲取全部的promise,都執行then,把結果放到數組,一塊兒返回) MyPromise.all = function(promises){ let arr = []; let i = 0; function processData(index,data){ arr[index] = data; i++; if(i == promises.length){ resolve(arr); }; }; return new Promise((resolve,reject)=>{ for(let i=0;i<promises.length;i++){ promises[i].then(data=>{ processData(i,data); },reject); }; }); }
實現了主要功能,但不處理異常場景,也不實現 remove
class EventEmitter { constructor () { // 存儲事件 this.events = this.events || new Map() } // 監聽事件 addListener (type, fn) { if (!this.events.get(type)) { this.events.set(type, fn) } } // 觸發事件 emit (type) { let handle = this.events.get(type) handle.apply(this, [...arguments].slice(1)) } }
class EventEmitter{ constructor(){ if(this._events === undefined){ this._events = Object.create(null);//定義事件對象 this._eventsCount = 0; } } emit(type,...args){ const events=this._events; const handler=events[type]; //判斷相應type的執行函數是否爲一個函數仍是一個數組 if(typeof handler==='function'){ Reflect.apply(handler,this,args); }else{ const len=handler.length; for(var i=0;li<len;i++){ Reflect.apply(handler[i],this,args); } } return true; } on(type,listener,prepend){ var m; var events; var existing; events=this._events; //添加事件的 if(events.newListener!==undefined){ this.emit('namelessListener',type,listener); events=target._events; } existing=events[type]; //判斷相應的type的方法是否存在 if(existing===undefined){ //若是相應的type的方法不存在,這新增一個相應type的事件 existing=events[type]=listener; ++this._eventsCount; }else{ //若是存在相應的type的方法,判斷相應的type的方法是一個數組仍是僅僅只是一個方法 //若是僅僅是 if(typeof existing==='function'){ //若是僅僅是一個方法,則添加 existing=events[type]=prepend?[listener,existing]:[existing,listener]; }else if(prepend){ existing.unshift(listener); }else{ existing.push(listener); } } //鏈式調用 return this; } removeListener(type,listener){ var list,events,position,i,originalListener; events=this._events; list=events[type]; //若是相應的事件對象的屬性值是一個函數,也就是說事件只被一個函數監聽 if(list===listener){ if(--this._eventsCount===0){ this._events=Object.create(null); }else{ delete events[type]; //若是存在對移除事件removeListener的監聽函數,則觸發removeListener if(events.removeListener) this.emit('removeListener',type,listener); } }else if(typeof list!=='function'){ //若是相應的事件對象屬性值是一個函數數組 //遍歷這個數組,找出listener對應的那個函數,在數組中的位置 for(i=list.length-1;i>=0;i--){ if(list[i]===listener){ position=i; break; } } //沒有找到這個函數,則返回不作任何改動的對象 if(position){ return this; } //若是數組的第一個函數纔是所須要刪除的對應listener函數,則直接移除 if(position===0){ list.shift(); }else{ list.splice(position,1); } if(list.length===1) events[type]=list[0]; if(events.removeListener!==undefined) this.emit('removeListener',type,listener); } return this; } }