前端拾遗-JavaScript-手写题和基础

写在最前面

  • 本篇介绍出现频率较高的 JavaScript 手写相关的基础题目,JavaScript 基础很是重要。

JavaScript 基础手写题目

导航

  • 一、变量提高和函数提高
  • 二、闭包
  • 三、模糊搜索和防抖和节流
  • 四、call, apply, bind
  • 五、javascript 设计模式考察
  • 六、JavaScript 链式调用和异步处理
  • 七、JavaScript 算法-递归的思想

一、变量提高和 ES6

  • 说说箭头函数和普通函数的区别,能讲讲其中 this 的使用吗?
  • fn() 输出什么?,若是咱们使用 let 会发生什么,为何?
function fn() {
    console.log('a', a);
    var a = 1; // let a = 1;
    function a () {
        console.log('I am a function');
    }
}
fn() 
// ƒ a () {console.log('I am a function');}
复制代码

keywords: 变量提高函数提高 && let const 和 var 的区别 && 箭头函数和普通函数的区别javascript

二、闭包问题

  • 什么是闭包?使用场景
  • 闭包就是:可以读取其余函数内部变量的函数参考阮一峰老师的博客
  • 闭包的做用:能够读取函数内部的变量,和****
for(var i=0;i<10;i++){

    setTimeout(function(){

        console.log(i)//10个10

    },1000) 

 }
 // 输出的什么?Q&A 10 个 10

复制代码

若是想输出 0-9 能够怎么作?html

for(var i=0;i<10;i++){
    ((j)=>{

        setTimeout(function(){

            console.log(j)//10个10

        },1000) 
    })(i)
}
复制代码

三、使用 JavaScript 写一个模糊搜索

  • 咱们在考虑一个搜索框须要注意哪些部分?
function filterMap(arr: any[],value:string){
	return arr.filter(item => String(item).includes(value))
}
复制代码

什么是防抖

原理是利用闭包,外部使用内部的值。 防抖就是限制必定的时间才触发的函数,好比搜索框input,用户输入的时候不能一输入就触发函数,等必定的时间(好比 1s)才触发相关的函数。前端

什么是节流

原理同上,利用闭包。 节流就是在规定时间内只执行一次,好比页面 scrolljava

// 设置一个 timer = null, 传入 time 为
// setTimeout的函数的时间,返回一个函数,当 timer 存在的时候clearTimeout

// 防抖
function debounce(fn,time){
    let timer = null;
    return function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            fn.apply(this,arguments)
        },time)
    }
}


// 设置一个 canRun = true,传入 fn 和 time
// 一个闭包的函数,先设置 canRun = false,直接执行setTimeout在其中把 canRun 置为 true,如今又能够执行了

function throttle(fn,time){
    let canRun = true;
    return function(){
        if(!canRun){
            return
        }
        canRun = false;
        setTimeout(() => {
            fn.apply(this,arguments);
            canRun = true;
        },time)
    }
}
复制代码

四、call,bind,apply

  • 咱们通常使用什么判断 JS 对象类型(typeof类型检测,instanceof 原型检测,constructor 构造函数检测, Object.prototype.toString.call([]))?
  • 提到了call,call,bind,apply讲一下啊能简单的说一下他们的使用和区别吗?
    • call、apply、bind的做用是改变函数运行时this的指向。
    • 区别:bind返回对应函数, 便于稍后调用; apply, call则是当即调用;call 接收参数对象,apply 接收参数数组。
  • 手写一个 call,apply,和bind?
//call 重写
Function.prototype.MyCall = function(context, ...args){
    context = context || window; // 由于传递过来的context有多是null
    context.fn = this; // 让fn的上下文为context
    const result = context.fn(...args);
    delete context.fn;
    return result; // 由于有可能this函数会有返回值return
}

//apply 重写
Function.prototype.MyApply = function(context, arr: any[]){
    context = context || window; 
    context.fn = this; 
    const result = context.fn(...args);
    delete context.fn;
    return result; 
}

// bind 重写
Function.prototype.MyBind = function(context, ...args){
    var fn = this; 
    return function () {
        fn.call(context, ...args, ...arguments);
    }
}
复制代码
  • 理解上面的代码(MyCall)
    • 1:把传入的第一个参数做为 call 函数内部的一个临时对象 context;
    • 2:给 context 对象一个新属性 fn , 我称呼其为实际执行函数 context.fn ;让 this 关键字(仅仅是关键字,而不是this对象)指向这个属性 ,即 context.fn = this ; 注意 : 在这里的 this 对象指向的是调用call()函数的函数对象。
    • 3:传入 args 而后执行 context.fn,返回的结果保存在 result 中。
    • 4:执行完成后再把 context.fn 删除。返回执行 result

五、JavaScript 设计模式考察

先讲讲平时设计开发中经常使用的设计模式?react

  • 设计原理:单一责任原则(SRP),最少知识原则(LKP),开放-封闭原则(OCP)
  • 设计模式: 他的原则是“找出 程序中变化的地方,并将变化封装起来”,它的关键是意图,而不是结构。
  • 平时设计模式很是重要,这里只记录了两种考察得比较多设计模式,其余的还须要你们继续复习。

单例模式

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点
function SetManager(name) {
    this.manager = name;
}

SetManager.prototype.getName = function() {
    console.log(this.manager);
};

var SingletonSetManager = (function() {
    var manager = null;

    return function(name) {
        if (!manager) {
            manager = new SetManager(name);
        }

        return manager;
    } 
})();

SingletonSetManager('a').getName(); // a
SingletonSetManager('b').getName(); // a
SingletonSetManager('c').getName(); // a
复制代码

发布-订阅模式(观察者模式)

  • 定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,全部依赖于它的对象都将获得通知
// 观察者
var observer = {
    // 订阅集合
    subscribes: [],

    // 订阅
    subscribe: function(type, fn) {
        if (!this.subscribes[type]) {
            this.subscribes[type] = [];
        }
        
        // 收集订阅者的处理
        typeof fn === 'function' && this.subscribes[type].push(fn);
    },

    // 发布 可能会携带一些信息发布出去
    publish: function() {
        var type = [].shift.call(arguments),
            fns = this.subscribes[type];
        
        // 不存在的订阅类型,以及订阅时未传入处理回调的
        if (!fns || !fns.length) {
            return;
        }
        
        // 挨个处理调用
        for (var i = 0; i < fns.length; ++i) {
            fns[i].apply(this, arguments);
        }
    },
    
    // 删除订阅
    remove: function(type, fn) {
        // 删除所有
        if (typeof type === 'undefined') {
            this.subscribes = [];
            return;
        }

        var fns = this.subscribes[type];

        // 不存在的订阅类型,以及订阅时未传入处理回调的
        if (!fns || !fns.length) {
            return;
        }

        if (typeof fn === 'undefined') {
            fns.length = 0;
            return;
        }

        // 挨个处理删除
        for (var i = 0; i < fns.length; ++i) {
            if (fns[i] === fn) {
                fns.splice(i, 1);
            }
        }
    }
};
复制代码

使用 emit,on 来现实这个类面试

class Event {
    constructor() {
        this.eventTypeObj = {}
        this.cacheObj = {}
    }
    on(eventType, fn) {
        if (!this.eventTypeObj[eventType]) {
            // 按照不一样的订阅事件类型,存储不一样的订阅回调
            this.eventTypeObj[eventType] = []
        }
        this.eventTypeObj[eventType].push(fn)

        // 若是是先发布,则在订阅者订阅后,则根据发布后缓存的事件类型和参数,执行订阅者的回调
        if (this.cacheObj[eventType]) {
            var cacheList = this.cacheObj[eventType]
            for (var i = 0; i < cacheList.length; i++) {
                cacheList[i]()
            }
        }
    }
    emit() {
        // 能够理解为arguments借用shift方法
        var eventType = Array.prototype.shift.call(arguments)
        var args = arguments
        var that = this

        function cache() {
            if (that.eventTypeObj[eventType]) {
                var eventList = that.eventTypeObj[eventType]
                for (var i = 0; i < eventList.length; i++) {
                    eventList[i].apply(eventList[i], args)
                }
            }
        }
        if (!this.cacheObj[eventType]) {
            this.cacheObj[eventType] = []
        }

        // 若是先订阅,则直接订阅后发布
        cache(args)

        // 若是先发布后订阅,则把发布的事件类型与参数保存起来,等到有订阅后执行订阅
        this.cacheObj[eventType].push(cache)
    }
}
复制代码

六、JavaScript 链式调用和异步处理

  • 实现一个 lazyMan
    • LazyMan("Hank").eat("dinner").sleepFirst(5).sleep(2).eat("supper"); 打印以下
// Hank 
 // ...(延迟5s)
 // dinner
 // ... (延迟2s)
 // supper
复制代码

》 实现如上函数算法

  • 重点在于 next,咱们把执行的函数放进 queue 中,在运行的时候 next(), 取出数组中的函数执行。
function _LazyMan(name){
    this.nama = name;
    this.queue = [];
    this.queue.push(() => {
        console.log("Hi! This is " + name + "!");
        this.next();
    })
    setTimeout(()=>{
        this.next()
    },0)
  }
  
_LazyMan.prototype.eat = function(name){
this.queue.push(() =>{
    console.log("Eat " + name + "~");
    this.next()
})
return this;
}

_LazyMan.prototype.next = function(){
var fn = this.queue.shift();
fn && fn();
}

_LazyMan.prototype.sleep = function(time){
this.queue.push(() =>{
    setTimeout(() => {
        console.log("Wake up after " + time + "s!");
        
        this.next()
    },time * 1000)
})
return this;
}

_LazyMan.prototype.sleepFirst = function(time){
this.queue.unshift(() =>{
    setTimeout(() => {
        console.log("Wake up after " + time + "s!");
        
        this.next()
    },time * 1000)
})
return this;
}

function LazyMan(name){
return new _LazyMan(name)
}

复制代码

补充: 若是咱们使用 promise 怎么实现如上的 lazyMan?设计模式

算法-递归

  • 一、实现一个有缓存的斐波拉契数列
var cache = { };
var count = 0;
function fib(n){
    count++;
    if(n === 1 || n === 2){
        return 1;
    }
    if(cache[n]){
        return cache[n];
    }else{
        var ret = fib(n - 1) + fib(n - 2);
        cache[n] = ret;
        return ret;
    }
}
复制代码
  • 这个的时间复杂度是多少 O(2^n) 指数增加的复杂度很是高了?若是不用递归,还有什么其余方法吗?
    • 迭代的思想, 用空间换时间:
function fibonacci(n) {
    var one = 1;
    var two = 1;
    for(var i = 3; i <= n; i++) {
       var three = one + two;
        one = two;
        two = three;

    }
    if (n==1||n==2) {
        return one;
    }
    return three;
}
复制代码
  • 二、棋盘问题(一个机器人位于一个 m x n 网格的左上角 (起始点在图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角.跨域

  • 递归的思想数组

function robotPath(m,n) {
        if(m == 1 && n == 1) return 1;
        if(m == 1) return robotPath(m, n - 1);
        if(n == 1) return robotPath(m - 1, n);
        return robotPath(m-1, n) + robotPath(m, n - 1);
    }
复制代码

待续后续补充 和更多 js 常见算法 promise 手写相关的知识

外链

相关文章
相关标签/搜索