簡單記錄一些之前遇到的題目验懊。
- 1.寫出trottle和debonce函數(shù),
- 2.寫出Number.MAX_VALUE相加函數(shù)
- 3.原生DOM寫出拖拽效果
- 4.dfs算法
- 5.原生js寫出eventEmitter
- 實現(xiàn)一個promise, (promise.all, Promise.race)仍源。
- 實現(xiàn)一個reapeat 方法。
- 快速排序
-9 DeepClone
- 快速排序
Function.prototype.mybind = function(context) {
return () => this.call(context);
}
寫一個通用的事件偵聽器函數(shù)
// event(事件)工具集,來源:github.com/markyun
markyun.Event = {
// 視能力分別使用dom0||dom2||IE方式 來綁定事件
// 參數(shù): 操作的元素,事件名稱 ,事件處理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件類型、需要執(zhí)行的函數(shù)曲管、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, function() {
handler.call(element);
});
} else {
element['on' + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因為IE不支持事件捕獲)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默認行為
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 獲取事件目標
getTarget : function(event) {
return event.target || event.srcElement;
}
三位加逗號
'1234567'.replace(/\B(?=(?:\d{3})+\b)/g, ',');
// 1234567 => '1,234,567'
// 處理數(shù)字
let str1 = 2123456789;
let str2 = 2123456789.12;
console.log(str1.toLocaleString()); // 2,123,456,789
// 處理字符串
let str1 = '2123456789';
let str2 = '2123456789.12';
// 利用正向預查 匹配 開頭一個數(shù)字\d 后面匹配這個數(shù)字后面必須是三個數(shù)字為一組為結尾或小數(shù)為結尾
function thousandth(str) {
let reg = /\d(?=(?:\d{3})+(?:\.\d+|$))/g;
return str.replace(reg, '$&,');
}
console.log(thousandth(str1)); // 2,123,456,789
// 寫出trottle和debonce函數(shù);
// 乞丐版
var throttle = function(delay, action) {
var last = 0硕糊;
return function(){
var curr = +new Date()
if (curr - last > delay){
action.apply(this, arguments)
last = curr
}
}
}
// throttle 也接收兩個參數(shù): 一個實際要執(zhí)行的函數(shù) fn院水,一個執(zhí)行間隔閾值 threshhold。
function throttle(fn, threshhold) {
var last, timer;
threshhold || (threshhold = 300);
return function() {
var context = this;
var args = arguments;
var now = +new Date();
if(last && now < last + threshhold) {
clearTimeout(timer);
timer = setTimeout(()=> {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
}
function debounce(fn, delay){
var timer;
// delay || (delay = 500);
return function(){
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay)
}
}
document.addEventListener('mousemove', debounce(function(e){
console.log('move');
}, 1000));
// 寫出Number.MAX_VALUE相加函數(shù)
var BigInt = function (str) {
// your code here
};
BigInt.prototype.plus = function (bigint) {
// your code here
return this.toString(result);
};
BigInt.prototype.toString = function (result) {
// your code here
};
var bigint1 = new BigInt('1234232453525454546445451434342153453454545454545454');
var bigint2 = new BigInt('1234232453525454546445451434342153453454545454545454');
console.log(bigint1.plus(bigint2));
// 一種解法:
var BigInt = function (str) {
// your code here
var strArr = str.split("")
strArr.plus = this.plus
strArr.toString = this.toString
return strArr;
};
BigInt.prototype.plus = function (bigint) {
// your code here
const arr = []
const distance = bigint.length - this.length
//補位
if(distance > 0){
for(let i = 0; i < distance; i++){
this.unshift('0');
}
}else{
for(let i = 0; i < -distance; i++){
bigint.unshift('0');
}
}
// > 10 進位為1 否則為0
let step = 0;
let result = bigint.map((item, index) => {
let temp = (+item) + (+this[index]) + step;
if(temp > 10){
step = 1;
return (temp + '')[0]
}else{
step = 0;
return temp
}
})
return this.toString(result);
};
BigInt.prototype.toString = function (result) {
// your code here
return result.join("");
};
var bigint1 = new BigInt('1234232453525454546445451434342153453454545454545454');
var bigint2 = new BigInt('1234232453525454546445451434342153453454545454545454');
console.log(bigint1.plus(bigint2));
// 24684648106104108108108198108102868684210681068108108108108108108108
// 原生DOM寫出拖拽效果
// 基本思路如下:
拖拽狀態(tài) = 0鼠標在元素上按下的時候{
拖拽狀態(tài) = 1
記錄下鼠標的x和y坐標
記錄下元素的x和y坐標
}
鼠標在元素上移動的時候{
如果拖拽狀態(tài)是0就什么也不做简十。
如果拖拽狀態(tài)是1檬某,那么
元素y = 現(xiàn)在鼠標y - 原來鼠標y + 原來元素y
元素x = 現(xiàn)在鼠標x - 原來鼠標x + 原來元素x
}
鼠標在任何時候放開的時候{
拖拽狀態(tài) = 0
}
<div class="calculator" id="drag">**********</div>
window.onload = function() {
//拖拽功能(主要是觸發(fā)三個事件:onmousedown\onmousemove\onmouseup)
var drag = document.getElementById('drag');
//點擊某物體時,用drag對象即可螟蝙,move和up是全局區(qū)域橙喘,也就是整個文檔通用,應該使用document對象而不是drag對象(否則胶逢,采用drag對象時物體只能往右方或下方移動)
drag.onmousedown = function(e) {
var e = e || window.event; //兼容ie瀏覽器
var diffX = e.clientX - drag.offsetLeft; //鼠標點擊物體那一刻相對于物體左側邊框的距離=點擊時的位置相對于瀏覽器最左邊的距離-物體左邊框相對于瀏覽器最左邊的距離
var diffY = e.clientY - drag.offsetTop;
/*低版本ie bug:物體被拖出瀏覽器可是窗口外部時厅瞎,還會出現(xiàn)滾動條,
解決方法是采用ie瀏覽器獨有的2個方法setCapture()\releaseCapture(),這兩個方法初坠,
可以讓鼠標滑動到瀏覽器外部也可以捕獲到事件和簸,而我們的bug就是當鼠標移出瀏覽器的時候,
限制超過的功能就失效了碟刺。用這個方法锁保,即可解決這個問題。注:這兩個方法用于onmousedown和onmouseup中*/
if(typeof drag.setCapture!='undefined'){
drag.setCapture();
}
document.onmousemove = function(e) {
var e = e || window.event; //兼容ie瀏覽器
var left=e.clientX-diffX;
var top=e.clientY-diffY;
//控制拖拽物體的范圍只能在瀏覽器視窗內,不允許出現(xiàn)滾動條
if(left<0){
left=0;
}else if(left >window.innerWidth-drag.offsetWidth){
left = window.innerWidth-drag.offsetWidth;
}
if(top<0){
top=0;
}else if(top >window.innerHeight-drag.offsetHeight){
top = window.innerHeight-drag.offsetHeight;
}
//移動時重新得到物體的距離爽柒,解決拖動時出現(xiàn)晃動的現(xiàn)象
drag.style.left = left+ 'px';
drag.style.top = top + 'px';
};
document.onmouseup = function(e) { //當鼠標彈起來的時候不再移動
this.onmousemove = null;
this.onmouseup = null; //預防鼠標彈起來后還會循環(huán)(即預防鼠標放上去的時候還會移動)
//修復低版本ie bug
if(typeof drag.releaseCapture!='undefined'){
drag.releaseCapture();
}
};
};
};
// dfs算法
function dfs(tree, name){
// 請在這里實現(xiàn)
let i = 0;
let targetObj;
const keys = Object.keys(tree.children || {});
while( i < keys.length) {
if(tree.children[keys[i]].name === name) {
targetObj = tree.children[keys[i]];
console.log(targetObj);
break;
}
dfs(tree.children[keys[i]], name);
i += 1;
}
console.log(targetObj);
return targetObj ;
}
var tree = {
name : '中國',
children : [
{
name : '北京',
children : [
{
name : '朝陽群眾'
},
{
name : '海淀區(qū)'
},
{
name : '昌平區(qū)'
}
]
},
{
name : '浙江省',
children : [
{
name : '杭州市',
code : 0571,
},
{
name : '嘉興市'
},
{
name : '紹興市'
},
{
name : '寧波市'
}
]
}
]
};
var node = dfs(tree, '杭州市');
console.log(node); // { name: '杭州市', code: 0571 }
// 原生js寫出eventEmitter
document.getElementById('submit').onclick = function() {
/*
var emit = new EventEmitter();
emit.on('click', function() {
console.log('clcik');
}, 2);
emit.trigger('click');
emit.trigger('click');
emit.trigger('click');
*/
var Target = function(){};
EventEmitter.inherit(Target);
var test = new Target();
test.on('click', function() {
console.log('clcik');
}, 2);
test.trigger('click');
test.trigger('click');
};
;(function (window, undefined) {
'use strict';
/*構造函數(shù)*/
var EventEmitter = function() {
this.events = {};//保存事務吴菠,存儲結構為{'eventName1':[{listener:function觸發(fā)的函數(shù), time:觸發(fā)的次數(shù)}], 'eventName2':[],}
};
EventEmitter.prototype.once = function(evt, listener) {
return this.addListener(evt, listener, 0);
};
/*獲取所有的事務*/
EventEmitter.prototype.getEvents = function() {
return this.events || (this.events = {});
}
/*獲取某個實踐的所有觸發(fā)函數(shù)*/
EventEmitter.prototype.getListeners = function(evt) {
var events = this.getEvents();
return events[evt] || (events[evt] = []);
};
/**
注冊實踐觸發(fā)函數(shù)
evet:事件名稱
listener:事件監(jiān)聽函數(shù)
time:可選,選擇可以觸發(fā)的次數(shù)浩村,-1表示無數(shù)次做葵,默認為-1
**/
EventEmitter.prototype.on = function(evt, listener, time) {
time = typeof(time) == 'number' ? time : -1;
time = time >= -1 ? time : -1;
var listeners = this.getListeners(evt);
var listenerWrapper = {
listener:listener,
time:time,
};
listeners.push(listenerWrapper);
return this;
};
/*addListener 和on 同義 */
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
/*移除事件的所有監(jiān)聽函數(shù)*/
EventEmitter.prototype.off = function(evt) {
var events = this.getEvents();
events[evt] = [];
};
EventEmitter.prototype.removeEvent = EventEmitter.prototype.off;
/**
會刪除同一事件中的所有l(wèi)istener
**/
EventEmitter.prototype.removeListener = function(evt, listener) {
var listeners = this.getListeners(evt);
for(var i=0; i<listeners.length; i++) {
if(listeners[i].listener == listener) {
delete listeners[i];
}
}
};
/**
觸發(fā)事件
**/
EventEmitter.prototype.trigger = function(evt, args) {
var listeners = this.getListeners(evt);
for(var i=0; i<listeners.length; i++){
var listener = listeners[i];
if(listener.time != -1) {
listener.time--;
}
if (listener.time == 0) {
this.removeListener(evt, listener.listener);//可以同步或異步執(zhí)行
}
listener.listener.apply(this, args || []);
}
};
EventEmitter.prototype.fire = EventEmitter.prototype.trigger;
/**
觸發(fā)事件
**/
EventEmitter.prototype.emit = function(evt) {
var args = Array.prototype.slice.call(arguments, 1);
return this.trigger(evt, args);
};
EventEmitter.inherit = function(target) {
if(typeof(target.prototype) == 'undefined') {
throw 'target:' + target + 'must have prototype';
}
var souPto = EventEmitter.prototype;
var tarPto = target.prototype;
for(var key in souPto) {
tarPto[key] = souPto[key];
}
return target;
};
window.EventEmitter = EventEmitter;
})(window);
實現(xiàn)一個promise, (promise.all, Promise.race)。
//完整版心墅,代碼后面附有實例酿矢,方便檢驗
(function(window,undefined){
// resolve 和 reject 最終都會調用該函數(shù)
var final = function(status,value){
var promise = this, fn, st;
if(promise._status !== 'PENDING') return;
// 所以的執(zhí)行都是異步調用,保證then是先執(zhí)行的
setTimeout(function(){
promise._status = status;
st = promise._status === 'FULFILLED'
queue = promise[st ? '_resolves' : '_rejects'];
while(fn = queue.shift()) {
value = fn.call(promise, value) || value;
}
promise[st ? '_value' : '_reason'] = value;
promise['_resolves'] = promise['_rejects'] = undefined;
});
}
//參數(shù)是一個函數(shù)怎燥,內部提供兩個函數(shù)作為該函數(shù)的參數(shù),分別是resolve 和 reject
var Promise = function(resolver){
if (!(typeof resolver === 'function' ))
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
//如果不是promise實例瘫筐,就new一個
if(!(this instanceof Promise)) return new Promise(resolver);
var promise = this;
promise._value;
promise._reason;
promise._status = 'PENDING';
//存儲狀態(tài)
promise._resolves = [];
promise._rejects = [];
//
var resolve = function(value) {
//由於apply參數(shù)是數(shù)組
final.apply(promise,['FULFILLED'].concat([value]));
}
var reject = function(reason){
final.apply(promise,['REJECTED'].concat([reason]));
}
resolver(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
var promise = this;
// 每次返回一個promise,保證是可thenable的
return new Promise(function(resolve,reject){
function handle(value) {
// 這一步很關鍵铐姚,只有這樣才可以將值傳遞給下一個resolve
var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value;
//判斷是不是promise 對象
if (ret && typeof ret ['then'] == 'function') {
ret.then(function(value) {
resolve(value);
}, function(reason) {
reject(reason);
});
} else {
resolve(ret);
}
}
function errback(reason){
reason = typeof onRejected === 'function' && onRejected(reason) || reason;
reject(reason);
}
if(promise._status === 'PENDING'){
promise._resolves.push(handle);
promise._rejects.push(errback);
}else if(promise._status === FULFILLED){ // 狀態(tài)改變后的then操作策肝,立刻執(zhí)行
callback(promise._value);
}else if(promise._status === REJECTED){
errback(promise._reason);
}
});
}
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected)
}
Promise.prototype.delay = function(ms,value){
return this.then(function(ori){
return Promise.delay(ms,value || ori);
})
}
Promise.delay = function(ms,value){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve(value);
console.log('1');
},ms);
})
}
Promise.resolve = function(arg){
return new Promise(function(resolve,reject){
resolve(arg)
})
}
Promise.reject = function(arg){
return Promise(function(resolve,reject){
reject(arg)
})
}
Promise.all = function(promises){
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
return Promise(function(resolve,reject){
var i = 0,
result = [],
len = promises.length,
count = len
//這里與race中的函數(shù)相比,多了一層嵌套隐绵,要傳入index
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function rejecter(reason){
reject(reason);
}
function resolveAll(index,value){
result[index] = value;
if( --count == 0){
resolve(result)
}
}
for (; i < len; i++) {
promises[i].then(resolver(i),rejecter);
}
});
}
Promise.race = function(promises){
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to race.');
}
return Promise(function(resolve,reject){
var i = 0,
len = promises.length;
function resolver(value) {
resolve(value);
}
function rejecter(reason){
reject(reason);
}
for (; i < len; i++) {
promises[i].then(resolver,rejecter);
}
});
}
window.Promise = Promise;
})(window);
/**********************************************************************/
//實例
var getData100 = function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('100ms');
},1000);
});
}
var getData200 = function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('200ms');
},2000);
});
}
var getData300 = function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
reject('reject');
},3000);
});
}
getData100().then(function(data){
console.log(data); // 100ms
return getData200();
}).then(function(data){
console.log(data); // 200ms
return getData300();
}).then(function(data){
console.log(data); // 100ms
}, function(data){
console.log(data);
});
Promise.all([getData100(), getData200()]).then(function(data){
console.log(data); // 100ms
});
Promise.race([getData100(), getData200(), getData300()]).then(function(data){
console.log(data); // 100ms
});
- 快速排序
// 簡陋版本
function quickSort(arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr[pivotIndex];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if(i!== pivotIndex){
if (arr[i] > pivot) {
right.push(arr[i]);
} else {
left.push(arr[i]);
}
}
}
return quickSort(left).concat([pivot],quickSort(right));
}
進階版:
const sort = (function() {
// 默認狀態(tài)下的比較函數(shù)
function compare(a, b) {
if (a === b) {
return 0;
}
return a < b ? -1 : 1;
}
// 原地交換函數(shù)之众,而非用臨時數(shù)組
function swap(array, a, b) {
[array[a], array[b]] = [array[b], array[a]];
}
// 分治函數(shù)
function partition(array, left, right) {
// 用index取中間值而非splice
const pivot = array[Math.floor((right + left) / 2)];
let i = left;
let j = right;
while (i <= j) {
while (compare(array[i], pivot) === -1) {
i++;
}
while (compare(array[j], pivot) === 1) {
j--;
}
if (i <= j) {
swap(array, i, j);
i++;
j--;
}
}
return i;
}
// 快排函數(shù)
function quick(array, left, right) {
let index;
if (array.length > 1) {
index = partition(array, left, right);
if (left < index - 1) {
quick(array, left, index - 1);
}
if (index < right) {
quick(array, index, right);
}
}
return array;
}
return function quickSort(array) {
return quick(array, 0, array.length - 1);
};
})();
const arr = [2,5,3,7,6,1]
sort(arr)
function deepClone(o1, o2) {
for (let k in o2) {
if (typeof o2[k] === 'object') {
o1[k] = {};
deepClone(o1[k], o2[k]);
} else {
o1[k] = o2[k];
}
}
}
// 測試用例
let obj = {
a: 1,
b: [1, 2, 3],
c: {}
};
let emptyObj = Object.create(null);
deepClone(emptyObj, obj);
console.log(emptyObj.a == obj.a);
console.log(emptyObj.b == obj.b);...
let arr = [[1, 2], 3, [[[4], 5]]]; // 數(shù)組展平
function flatten(arr) {
return [].concat(
...arr.map(x => Array.isArray(x) ? flatten(x) : x)
)
}
function CodingMan(name) { // 主要考察的是 面向對象以及JS運行機制(同步 異步 任務隊列 事件循環(huán))
function Man(name) {
setTimeout(() => { // 異步
console.log(`Hi! This is ${name}`);
}, 0);
}
Man.prototype.sleep = function(time) {
let curTime = new Date();
let delay = time * 1000;
setTimeout(() => { // 異步
while (new Date() - curTime < delay) {} // 阻塞當前主線程
console.log(`Wake up after ${time}`);
}, 0);
return this;
}
Man.prototype.sleepFirst = function(time) {
let curTime = new Date();
let delay = time * 1000;
while (new Date() - curTime < delay) {} // 阻塞當前主線程
console.log(`Wake up after ${time}`);
return this;
}
Man.prototype.eat = function(food) {
setTimeout(() => { // 異步
console.log(`Eat ${food}~~`);
}, 0)
return this;
}
return new Man(name);
}
// CodingMan('Peter');
// CodingMan('Peter').sleep(3).eat('dinner');
// CodingMan('Peter').eat('dinner').eat('supper');
// CodingMan('Peter').sleepFirst(5).eat('supper');
有興趣的朋友, 可以補充答案到評論區(qū)氢橙。