手写前端面试题(一)

目录

手写eventEmitter类,包括on()、off()、once()、emit()方法

手写Promise封装Ajax

手写bind、apply、call

手写instanceof

手写Promise

手写Promise.all

手写Promise.race

手写Promise.allSettled

数组拍平

快速排序

手写深拷贝

防抖节流


手写前端面试题(二)_yubaibai2233的博客-CSDN博客目录手写compose函数手写compose函数接收若干个函数作为参数,每个函数执行后的输出作为下一个函数的输入。执行方向是自右向左的,初始函数的参数在最右边。 function compose(...fns) { return function(initialValue) { return fns.reverse().reduce((accumulator, fn) => {https://blog.csdn.net/yubaibai2233/article/details/123458749

手写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
                }
            }
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值