目录
手写eventEmitter类,包括on()、off()、once()、emit()方法
手写eventEmitter类,包括on()、off()、once()、emit()方法
1.on(event, fn) 监听event 事件,事件触发时调用fn函数;
2.once(event, fn) 为指定事件注册一个单次监听器,单次监听器最多只触发一次,触发后立即解除监听器;
3.emit(event, arg1, arg2, arg3, …): 触发event事件,并把参数arg1, arg2, arg3… 传给事件处理函数;
4.off (event, fn) 停止监听某个事件
class EventEmitter{
constructor(){
this._events={}
}
on(event,callback){
let callbacks = this._events[event] || []
callbacks.push(callback);
this._events[event] = callbacks;
return this
}
off(event,callback){
let callbacks = this._events[event]
this._events[event] = callbacks && callbacks.filter(function(fn){
return fn !== callback;
})
return this
}
emit(eventName,...args) {
const callbacks = this._events[eventName]
callbacks.map(cb => {
cb(...args)
})
// callbacks.forEach(fn => fn.apply(this,args))
return this;
}
once(event,callback){
let wrap = (...args) => {
callback.apply(this, args) //apply第二个参数是数组
this.off(event, wrap)
}
this.on(event, wrap )
return this
}
}
手写Promise封装Ajax
// Promise封装Ajax
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.apiopen.top/getJoke");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
}
else {
reject(xhr.status)
}
}
}
})
p.then(
res => {
console.log(res)
},
err => {
console.log(err)
}
)
手写bind、apply、call
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔。
apply 的所有参数都必须放在一个数组里面传进去。
bind 除了返回是函数以外,它的参数和 call 一样。
// bind
Function.prototype.bindNew = function (context, ...args) {
return (...newArgs) => {
this.apply(context, [...args, ...newArgs])
}
}
const test = {
name: "fy",
showName: function (last) {
console.log(this.name + " is " + last);
},
};
test.showName("handsome"); // fy is handsome
test.showName.bind({ name: "Mr.fy" })("handsome");
test.showName.bindNew({ name: "Mr.fy" })("handsome");
// apply
Function.prototype.applyNew = function (context, args) {
context.fn = this
res = context.fn(...args)
return res
}
test.showName.applyNew({ name: "Mr.fy" }, ["handsome"])
// call
Function.prototype.callNew = function (context, ...args) {
context.fn = this
res = context.fn(...args)
// delete context.fn; // 为什么
return res
}
test.showName.callNew({ name: "Mr.fy" }, "handsome")
手写instanceof
1. 每个函数function都有一个`prototype`,即`显式`原型(属性)
2. 每个实例对象都有一个[`__ proto __`],可称为`隐式`原型(属性)
3. 对象的隐式原型的值为其对应构造函数的显式原型的值
function myinstanceof(left,right){
let prototype = right.prototype
left = left.__proto__
while(1){
if(!left){
return false
}
if(left === prototype){
return true
}
left = left.__proto__
}
}
console.log(myinstanceof([], Array));
console.log(myinstanceof([], Map));
手写Promise构造函数和then(异步版,then无链式)
BAT前端经典面试问题:史上最最最详细的手写Promise教程-阿里云开发者社区
class myPromise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放法数组
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦resolve执行,调用成功数组的函数
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦reject执行,调用失败数组的函数
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.reason);
};
// 当状态state为pending时
if (this.state === 'pending') {
// onFulfilled传入到成功数组
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
// onRejected传入到失败数组
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason);
})
}
}
}
手写Promise.all
数组中的可能是Promise,也可能是常量!
function myPromiseAll(lists) {
let resArray = []
return new Promise((resolve, reject) => {
lists.forEach(element => {
// element.then( // 可能是常量,没有then
Promise.resolve(element).then(
res => {
resArray.push(res)
if (resArray.length === lists.length) {
resolve(resArray)
}
}
)
});
})
}
var p1 = Promise.reject(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
myPromiseAll([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
// Promise.all([p1, p2, p3]).then(values => {
// console.log(values); // [3, 1337, "foo"]
// });
手写Promise.race
race
函数返回一个 Promise
,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
function myPromiseRace(lists){
return new Promise((resolve,reject)=>{
lists.forEach(element=>{
element.then(
res => resolve(res),
err => reject(err)
)
})
})
}
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, "two");
});
// Promise.race([p1, p2]).then(
// function(value) {
// console.log(value); // "two" 两个都完成,但 p2 更快
// },
// function(reason){
// console.log("失败",reason)
// }
// );
myPromiseRace([p1,p2]).then(
function(value) {
console.log(value); // "two" 两个都完成,但 p2 更快
},
function(reason){
console.log("失败",reason)
}
);
手写Promise.allSettled
增加status
function myPromiseAllSettled(lists) {
let resArray = []
return new Promise((resolve, reject) => {
lists.forEach(element => {
// element.then( // 可能是常量,没有then
Promise.resolve(element).then(
res => {
resArray.push({ status: 'fullfilled', value: res })
if (resArray.length === lists.length) {
resolve(resArray)
}
},
err => {
resArray.push({ status: 'rejected', reason: err })
if (resArray.length === lists.length) {
resolve(resArray)
}
}
)
});
})
}
var p1 = Promise.reject(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
myPromiseAllSettled([p1, p2, p3]).then(values => {
console.log(values);
});
// Promise.allSettled([p1, p2, p3]).then(values => {
// console.log(values);
// });
数组拍平
function flatten(arr) {
let res = []
arr.forEach(element => {
if (Array.isArray(element)) {
res = res.concat(flatten(element)) // 一定要接住concat返回值!
}
else {
res.push(element)
}
});
return res;
}
console.log(flatten([1, [1, 2, [2, 4]], 3, 5])); // [1, 1, 2, 2, 4, 3, 5]
console.log([1, [1, 2, [2, 4]], 3, 5].flat(2)); // [1, 1, 2, 2, 4, 3,5]
快速排序
// 快排
function partition(nums,low,high){
let pivot = nums[low];
let left = low;
let right = high;
while(left<right){
while(nums[right]>=pivot&&left<right){
right--;
}
nums[left]=nums[right];
while(nums[left]<=pivot&&left<right){
left++;
}
nums[right]=nums[left];
}
nums[left] = pivot
return left
}
function quickSort(nums,low,high){
if(low<high){
let loc = partition(nums,low,high)
quickSort(nums,low,loc-1)
quickSort(nums,loc+1,high)
}
}
var nums=[3,2,6,5,7,1]
quickSort(nums,0,nums.length-1)
console.log(nums)
手写深拷贝
function deepClone(obj) {
var clone = obj instanceof Array ? [] : {} // 先判断是对象还是数组
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 判断是否是对象上的属性,而不是原型上的属性
// obj[key] 是否是对象(包括数组),如果是对象,递归遍历
clone[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key]
}
}
return clone
}
console.log(typeof [] === 'object') // typeof
let obj = { name: 'jack', birth: { year: '1997', month: '10' } }
let cloneObj = deepClone(obj)
cloneObj.name = 'hi'
console.log(cloneObj)
console.log(obj)
防抖节流
let num = 1;
let content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
// content.onmousemove = debounce(count,1000);;
content.onmousemove = throttle(count,1000);;
// 防抖,就是指触发事件后在 n 秒内函数只能执行一次,
// 如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(fn,delay){
let timer = null
return function(){
let context = this
let args = arguments // debounce函数最终返回的函数依旧能接受到 e 参数
clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(context,args)
},delay)
}
}
// 节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
// 对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。
// 时间戳版:
function throttle(fn,delay){
let pre = Date.now()
return function(){
let context = this
let args = arguments
let now = Date.now()
if(now-pre>delay){
fn.apply(context,args)
pre = now
}
}
}