前端面试手撕编程之ES+算法

目录

ES6

防抖

节流

防抖、节流应用

改变this

call

[typeof this !== 'function'](#typeof this !== 'function')

[context = context || window](#context = context || window)

[context._this = this](#context._this = this)

[delete context._this](#delete context._this)

[bind: return _this.apply(context, [...arguments].slice(1));](#bind: return _this.apply(context, [...arguments].slice(1));)

深拷贝

[!arr|| arr == null || typeof arr != 'object'](#!arr|| arr == null || typeof arr != 'object')

[arr instanceof Array ? [] : {}](#arr instanceof Array ? [] : {})

for (const key in arr)

[result[key] = cloneDeep(arr[key])](#result[key] = cloneDeep(arr[key]))

setTimeout()

倒计时

setTimeout模拟实现setInterval

setInterval模拟实现setTimeout

[new :Fn=[...arguments].shift()](#new :Fn=[...arguments].shift())

*寄生组合式继承:call,create,constructor

[Object.defineProperty(obj, prop, descriptor)](#Object.defineProperty(obj, prop, descriptor))

Object.create

Object.freeze

[Object for of](#Object for of)

[*instance of](#*instance of)

算法

合法的URL

[千位分割:num.slice(render, len).match(/\d{3}/g).join(',')](#千位分割:num.slice(render, len).match(/\d{3}/g).join(','))

ES6

防抖

触发事件后在 n 秒内函数只能执行一次,如果在n 秒内又触发了 事件,会重计算函数执行时间。

javascript 复制代码
function debounce(fun,time) {
        let flag // 定义状态
        return function () {
            clearTimeout(flag)// 在执行之前 清除 定时器的 flag 不让他执行
            flag = setTimeout(() => {
                fun.call(this,arguments)//拿到正确的this对象,即事件发生的dom
            }, time)
        }
    }

节流

连续触发事件但是在n 秒中只执行一次函数。两种方式可以实现,分别是时间戳版和定时器版。

javascript 复制代码
function throttle(fun, time) {
        let flag // 定义一个空状态
        return function () { // 内部函数访问外部函数形成闭包
            if (!flag) { // 状态为空执行
                flag = setTimeout(() => {
                    fns.apply(this, arguments) // 改变this指向 把 event 事件对象传出去
                    flag = null // 状态为空
                }, time)
            }
        }
      }

防抖、节流应用

防止某一时间频繁触发

  • 防抖debounce:time内只执行一次
    • search搜索联想,用户在不断输入值 时,用防抖来节约请求资源
    • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • 节流throttle: 间隔time执行
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

改变this

call

typeof this !== 'function'
context = context || window
context._this = this
delete context._this
javascript 复制代码
    // 给function的原型上面添加一个 _call 方法
    Function.prototype._call = function (context) {
        //  判断调用者是否是一个函数  this 就是调用者
        if (typeof this !== 'function') {
           throw new TypeError('what is to be a function')
        }
        // 如果有 context 传参就是传参者 没有就是window
        context = context || window
        // 保存当前调用的函数
        context._this = this   
        // 截取传过来的参数
        /*
          arguments
                 a: 1
                 fn: ƒ fns()
        */
        // 通过 slice 来截取传过来的参数
        const local = [...arguments].slice(1)
        // 传入参数调用函数
        let result = context._this(...local)
        // 删属性
        delete context._this
        return result
    }

    let obj = { a: 1 }
    function fns(a, b) {
        console.log(a, b);
        console.log(this)
    }
    fns._call(obj, 23, 555)

bind: return _this.apply(context, [...arguments].slice(1));

深拷贝

!arr|| arr == null || typeof arr != 'object'

arr instanceof Array ? [] : {}

for (const key in arr)

result[key] = cloneDeep(arr[key])

javascript 复制代码
 function cloneDeep(arr = {}) {
        // 终止递归 
        if (!arr|| arr == null || typeof arr != 'object' ) return arr
        // 用 instanceof 判断原型链上是否有该类型的原型 是 Array => [] ! Arrays =>{}
        let result=arr instanceof Array ?  [] :  {}
        // forin 循环对象的key值
        for (const key in arr) {
            //  对象 key 赋值 result
            result[key] = cloneDeep(arr[key])
        }
        return result
   }

setTimeout()

倒计时

javascript 复制代码
​//返回值timeoutID是一个正整数,表示定时器的编号。
let timeoutID = scope.setTimeout(function[, delay]),//delay表示最小等待时间,真正等待时间取决于前面排队的消息
clearTimeout(timeoutID) //取消该定时器。
javascript 复制代码
function timedCount() {
			c -=1;
			if(c===0){
				clearTimeout(t);
				return;
			}
			t = setTimeout(function() {
				timedCount()
			}, 1000);
}
 

setTimeout模拟实现setInterval

javascript 复制代码
// 使用闭包实现
function mySetInterval(fn, t) {
  let timer = null;
  function interval() {
    fn();
    timer = setTimeout(interval, t);
  }
  interval();
  return {
    // cancel用来清除定时器
    cancel() {
      clearTimeout(timer);
    }
  };
}

setInterval模拟实现setTimeout

javascript 复制代码
function mySetTimeout(fn, time) {
  let timer = setInterval(() => {
    clearInterval(timer);
    fn();
  }, time);
}

// 使用
mySetTimeout(() => {
  console.log(1);
}, 2000);

new :Fn=[...arguments].shift()

"_new"函数,该函数会返回一个对象,

该对象的构造函数为函数参数 、原型对象为函数参数的原型,核心步骤有:

  1. 创建一个新对象
  2. 获取函数参数
  3. 将新对象的原型对象和函数参数的原型连接起来
  4. 将新对象和参数传给构造器执行
  5. 如果构造器返回的不是对象,那么就返回第一个新对象
javascript 复制代码
const _new = function() {
    const object1 = {}
    const Fn = [...arguments].shift()
    object1.__proto__ = Fn.prototype
    const object2 = Fn.apply(object1, arguments)
    return object2 instanceof Object ? object2 : object1
}

*寄生组合式继承:call,create,constructor

通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。要求如下:

  1. 给"Human"构造函数的原型上添加"getName"函数,该函数返回调用该函数对象的"name"属性

  2. 给"Chinese"构造函数的原型上添加"getAge"函数,该函数返回调用该函数对象的"age"属性

  3. 在"Human"构造函数的原型上添加"getName"函数

  4. 在"Chinese"构造函数中通过call函数借助"Human"的构造器来获得通用属性

  5. Object.create函数返回一个对象,该对象的__proto__属性为对象参数的原型。此时将"Chinese"构造函数的原型和通过Object.create返回的实例对象联系起来

  6. 最后修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身

  7. 在"Chinese"构造函数的原型上添加"getAge"函数

javascript 复制代码
function Human(name) {
    this.name = name
    this.kingdom = 'animal'
    this.color = ['yellow', 'white', 'brown', 'black']
}
Human.prototype.getName = function() {
    return this.name
}

function Chinese(name,age) {
    Human.call(this,name)//call函数借助"Human"的构造器来获得通用属性
    this.age = age
    this.color = 'yellow'
}

//返回的对象__proto__属性为对象参数的原型
Chinese.prototype = Object.create(Human.prototype)//使用现有的对象来作为新创建对象的原型
//修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身
Chinese.prototype.constructor = Chinese

Chinese.prototype.getAge = function() {
    return this.age
}

Object.defineProperty(obj, prop, descriptor)

Object.create

该函数创建一个新对象,使用现有的对象来提供新创建的对象的proto,核心步骤有:

  1. 创建一个临时函数
  2. 将该临时函数的原型指向对象参数
  3. 返回该临时对象的实例

Object.create法创建一个新对象,使用现有的对象来提供新创建的对象的proto。

javascript 复制代码
const _objectCreate = proto => {
    if(typeof proto !== 'object' || proto === null) return

    const fn = function() {}
    fn.prototype = proto

    return new fn()
}

Object.freeze

Object.freeze = writable: false + Object.seal = writable: false + Object.preventExtensions + configable: false

  • Symbol 类型作为 key 值的情况,也要冻结
  • 只冻结对象自有 的属性(使用 for ... in 会把原型链上的可枚举属性遍历出来)。
  • 注意不可扩展性(不能添加新属性,使用 Object.preventExtensions() 或 Object.seal() 实现,同时也相当于把原型链冻结)。

key:

  1. Object.getOwnPropertyNames/Symbol
  2. forEach
  3. Object.define Property:configurable,writable
  4. Object.preventExtensions(object)
javascript 复制代码
const _objectFreeze = object => {

    if(typeof object !== 'object' || object === null) {
        throw new TypeError(`the ${object} is not a object`)
    }

    const keys = Object.getOwnPropertyNames(object);
    const symbols = Object.getOwnPropertySymbols(object);
    
    [...keys, ...symbols].forEach(key => {
        Object.defineProperty(object, key, {
            configurable: false,
            writable: false,
        })
    })

    Object.preventExtensions(object)
}

Object for of

javascript 复制代码
// 创建一个构造函数
function MyObject() {
  // 为构造函数的实例对象添加两个属性
  this.prop1 = 'Value 1';
  this.prop2 = 'Value 2';
}

// 在原型上添加一个自定义的迭代器方法
MyObject.prototype[Symbol.iterator] = function () {
  // 获取实例对象的所有属性名并存储在数组 keys 中
  const keys = Object.keys(this);
  let index = 0;

  // 返回一个迭代器对象,包含 next 方法
  return {
    next: () => {
      // 如果还有属性未遍历完
      if (index < keys.length) {
        // 获取当前属性名 key,并递增索引 index
        const key = keys[index++];
        // 返回包含当前属性名和对应值的对象,并设置 done 为 false 表示未遍历完
        return { value: [key, this[key]], done: false };
      } else {
        // 如果所有属性都已遍历完,返回 done 为 true 表示遍历结束
        return { done: true };
      }
    },
  };
};

// 创建一个实例对象
const instance = new MyObject();

// 使用 for...of 遍历实例对象的属性
for (const [key, value] of instance) {
  // 打印属性名和对应值
  console.log(key, value);
}

*instance of

  1. 获取首个对象参数的原型对象
  2. 获取Fn函数的原型对象
  3. 进入死循环,当两个参数的原型对象相等时返回true
  4. 当两个参数的原型对象不相等时获取首个对象参数原型的原型并且循环该步骤直到null时返回false
javascript 复制代码
const _instanceof = (target, Fn) => {
    let proto = target.__proto__
    let prototype = Fn.prototype
    while(true) {
        if(proto === Fn.prototype) return true
        if(proto === null) return false
        proto = proto.__proto__
    }
}

 const _instanceof = (target, Fn) => {
     return Fn.prototype.isPrototypeOf(target);
          
}

算法

合法的URL

URL结构一般包括协议、主机名、主机端口、路径、请求信息、哈希

  1. 域名不区分大小写:"www"子域名(可选)、二级域名、"com"顶级域名
  2. 只能包含字母(a-z、A-Z)、数字(0-9)和连字符(-)(但-不能再首尾)
javascript 复制代码
https://www.bilibili.com/video/BV1F54y1N74E/?spm_id_from=333.337.search-card.all.click&vd_source=6fd32175adc98c97cd87300d3aed81ea
//开始:                     ^
//协议:                     http(s)?:\/\/
//域名:                     [a-zA-Z0-9]+-[a-zA-Z0-9]+|[a-zA-Z0-9]+
//顶级域名 如com cn,2-6位:   [a-zA-Z]{2,6}
//端口 数字:                (:\d+)?
//路径 任意字符 如 /login:   (\/.+)?
//哈希 ? 和 # ,如?age=1:    (\?.+)?(#.+)?
//结束:                      $
//     https://           www.bilibili                com    /video/BV1F54y1N74E  ?spm..            
/^(http(s)?:\/\/)?(([a-zA-Z0-9]+-[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)+([a-zA-Z]{2,6})(:\d+)?(\/.+)?(\?.+)?(#.+)?$/.test(url)

千位分割:num.slice(render, len).match(/\d{3}/g).join(',')

javascript 复制代码
  const format = (n) => {
        let num = n.toString() // 拿到传进来的 number 数字 进行 toString
        let len = num.length // 在拿到字符串的长度
        // 当传进来的结果小于 3 也就是 千位还把结果返回出去 小于3 不足以分割
        if (len < 3) {
            return num
        } else {
            let render = len % 3 //传入 number 的长度 是否能被 3 整除
            if (render > 0) { // 说明不是3的整数倍
                return num.slice(0, render) + ',' + num.slice(render, len).match(/\d{3}/g).join(',')
            } else {
                return num.slice(0, len).match(/\d{3}/g).join(',')
            }
        }
    }

    let str = format(298000)
    console.log(str)
相关推荐
爱吃生蚝的于勒3 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
binishuaio12 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE14 分钟前
【Java SE】StringBuffer
java·开发语言
就是有点傻18 分钟前
WPF中的依赖属性
开发语言·wpf
洋24027 分钟前
C语言常用标准库函数
c语言·开发语言
进击的六角龙28 分钟前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel
wrx繁星点点29 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
熊的猫37 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
NoneCoder1 小时前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发
苏三有春1 小时前
PyQt5实战——UTF-8编码器功能的实现(六)
开发语言·qt