前端手撕代码汇总

一、手写URL解析函数

function parseParams(url){
    // 正则中的+表示出现大于等于一次
    const pattern = /.+\?(.+)$/;
    const pararmStr = pattern.exec(url)[1];
    const paramArr = pararmStr.split('&');
    let paramObj = {};

    paramArr.forEach(param => {
        if(/=/.test(param)){
            let [key, val] = param.split('=');
            val = decodeURIComponent(val);
            val = /\d+$/.test(val) ? parseInt(val) : val;

            if(paramObj.hasOwnProperty(key)){
                paramObj[key] = [].concat(paramObj[key], val);
            }else {
                paramObj[key] = val;
            }
        }else {
            paramObj[param] = true;
        }
    })
    return paramArr;
}


const URL = 'https://cn.bing.com/search?q=bilinili&form=ANNTH1&refig=1e3c2833954e4c31ae5f43571a582b23';
console.log(parseParams(URL));

二、手写模板字符串

function render(template, data){
    const pattern = /\{\{\s*(\w+)\s*\}\}/;
    if(pattern.test(template)){
        const name = pattern.exec(template)[1];
        template = template.replace(pattern, data[name]);
        return render(template, data);
    }
    return template;
}

let template = '我是{{name}},职业{{job}},工资{{salary}}';
let person = { name: '阿巴', job: '前端', salary:30000};
console.log(render(template, person)); // 我是阿巴,职业前端,工资30000

三、手写new方法

function myNew(Fn, ...args){
    let newObj = {};
    if(Fn.prototype){
        newObj.__proto__ = Fn.prototype;
    }
    let result = Fn.apply(newObj, args);

    // 如果new的结果是对象则返回这个对象,否则直接返回这个新创建的对象
    if((typeof result === 'object' && result !== null) || typeof result === 'function'){
        return result;
    }
    return newObj;
}

四、手写Object.create

// proto 可以是object 或者function
Object.myCreate = function (proto, defineProperties){
    if((typeof proto === 'object' && proto !== null) || typeof proto === 'function'){
        let obj = {};

        // obj.__proto__ = proto;
        Object.setPrototypeOf(obj, proto);
        Object.defineProperty(obj, defineProperties);
        return obj;
    }else {
        throw new TypeError('类型错误');
    }
}

五、手写EventBus

class EventBus{
    constructor() {
        this.listeners = {};
    }

    on(event, listener){
        if(!this.listeners[event]){
            this.listeners[event] = [];
        }
        this.listeners[event].push(listener);
    }

    off(event, listener){
        if(this.listeners[event]){
            const index = this.listeners[event].indexOf(listener);
            if(index > -1){
                this.listeners[event].splice(index, 1);
            }
        }
    }

    emit(event, ...args){
        if(this.listeners[event]){
            this.listeners[event].forEach(listener => listener(...args));
        }
    }
}

const bus = new EventBus();

bus.on('login', function(name){
    console.log('12313', name);
})

bus.on('login', function(name){
    console.log('1231223', name);
})

bus.emit('login', 1222313);

六、手写forEach

Array.prototype._forEach = function (callbackFn, ins){
    if(typeof callbackFn !== 'function'){
        throw new TypeError(callbackFn + 'is not function');
    }

    const arr = this;
    const len = arr.length;

    for(let i = 0; i < len; i++){
        callbackFn.call(ins, arr[i], i, arr);
    }
}

七、手写map

Array.prototype._map = function (callbackFn, ins) {

  if(typeof callbackFn !== 'function'){
    throw new TypeError(callbackFn + 'is not function');
  }

  const arr = this;
  const len = arr.length;
  const result = new Array(len);

  for (let i = 0; i < len; i++) {
      result[i] = callbackFn.call(ins, arr[i], i, arr);
  }
  // 返回map结果
  return result;
};

const sss = [1,2,3,4]._map(function(item){
  return item * item;
});
console.log(sss);

八、手写filter

// Array.filter

Array.prototype._filter = function(callbackFn, ins){
  if (typeof callbackFn !== 'function') {
    throw new TypeError(`${callbackFn} is not a function`);
  }

  const arr = this;
  const len = arr.length;
  const temp = [];

  for(let i = 0; i < len; i++){
    const result = callbackFn.call(ins, arr[i], i, arr);
    // push方法 返回新数组的长度
    result && temp.push(arr[i]);
  }
  return temp;
}

const result = [1,2,3,4]._filter(item => item > 2);

console.log('result', result);

九、手写apply

Function.prototype._apply = function(target, args){
  if(typeof this !== 'function'){
    throw Error('');
  }
  target = target || window;
  const key = new Symbol('fn');
  target[key] = this;
  const res = target[key](...args);
  delete target[key];
  return res;
}

十、手写call

Function.prototype._call = function(target, ...args){
  if(typeof this !== 'function'){
    throw Error('');
  }
  target =  target || window;
  let key = Symbol('fn');
  target[key] = this;
  const res = target[key](...args);
  delete target[key];
  return res;
}

const B = {
  name: 'B',
  say(prefix, age) {
    console.log(`${prefix},my name is ${this.name},i am ${age} year old`)
  }
}

const A = {
  name:'小A'
}

console.log(B.say._call(A, 'prefix', 18));

十一、手写bind

Function.prototype._bind = function(target, ...args){
  if(typeof this !== 'function'){
    throw new Error('type Error');
  }
  context = target || window;

  const _this = this;
  return function fn(...innerArgs){
    if(this instanceof fn){
      return new _this(...args, ...innerArgs);
    }else{
      return _this.apply(context, [...args, ...innerArgs]);
    }
  }
}

function foo(x, y, z) {
  this.name = "张三";
  console.log(this.num, x + y + z);
  console.log(this.name);
}
var obj = {
 num: 666
}
var foo2 = foo._bind(obj, 1,2,3);
console.log(new foo2);

十二、手写instance of

// 自己实现instanceof操作符

/**
 * instanceof 用于查找左边变量原型链上是否有右边变量;
 */

// 左侧 传入的是原型对象,是一个对象
// 右侧 传入的是构造函数或者是Class声明的类

function new_instanceof(left, right){

  // let leftValue = left.__proto__
  let leftValue = Object.getPrototypeOf(left);
  const rightValue = right.prototype;

  while(leftValue !== null){
    if(leftValue === rightValue){
      return true;
    }
    leftValue = Object.getPrototypeOf(leftValue);
  }
  return false;
}

class Student{
  constructor(name){
    this.name = name;
  }
}

const sss = new Student('123');

console.log(new_instanceof(sss, Student));

十三、手写浅拷贝

// 浅拷贝只能拷贝一层对象
function shallowCopy(params){
  if(!params || typeof params !== 'object'){
    return params;
  }
  let newObject = Array.isArray(params) ? [] : {};
  for(let key in params){
    if(params.hasOwnProperty(key)){
      newObject[key] = params[key];
    }
  }
  return newObject;
}

十四、手写深拷贝

// 深拷贝解决循环引用爆栈的问题
function deepClone(source, map = new Map()){
  // 为空
  if(source == null) return source;
  // 为日期类型
  if(source instanceof Date) return new Date(source);
  // 为Reg类型
  if(source instanceof RegExp) return new RegExp(source);
  // 如果是函数的话也不需要深拷贝
  if(typeof source !== 'object') return source;
  
  if(map.has(source)) return map.get(source);

  let newObj = Array.isArray(source) ? []: {};
  map.set(source, newObj);
  for(let key in source){
    if(source.hasOwnProperty(key)){
      newObj[key] = deepClone(source[key], map);
    }
  }
  return newObj;
}

const obj1 = {
  name: 123,
  num: 111,
  arr: [1, 2, 3],
  child: {
    arr: [1,2,3,4]
  },
  next: 'obj1',
}

obj1.next = obj1;

const obj2 = deepClone(obj1);
console.log(obj1);
console.log(obj2);
console.log(obj1['child'] === obj2['child']);

十五、手写洋葱模型

function fn1(x){
    return x + 1;
}

function fn2(x){
    return x + 2;
}

function fn3(x){
    return x + 3;
}

function fn4(x){
    return x+ 4;
}

function compose(...fn){
    if(!fn.length){
        return v => v;
    }
    if(fn.length === 1){
        return fn[0];
    }
    return fn.reduce((pre, curr) =>
        (...args) => {
            return pre(curr(...args));
        }
    )
}

const a = compose(fn1, fn2, fn3, fn4);

console.log(a(3))

十六、手写柯里化

function toCurry(func, ...args){
    let len = func.length;
    return function (...params){
        let _args = [...args, ...params];
        if(_args.length < len){
            return toCurry(func, ..._args);
        }else {
            return func.apply(this, _args);
        }
    }
}

const add = function (a, b, c){
    return a + b + c;
}

const a = toCurry(add, 1, 2);
console.log(a(4))

十七、手写防抖函数

<html>

<body>
    <div>
        <label for="input1">labe1</label>
        <input id="input1" oninput=input(value) />
        <label for="input2">labe2</label>
        <input id="input2"/>
        <button id="button">click</button>
    </div>
</body>
<script>
const debounce = function (fn, wait){
    let time;
    return function (...args){
        if(time){
            clearTimeout(time);
        }
        time = setTimeout(() => {
            fn.apply(this, args);
        }, wait);
    }
}

// 立即执行防抖函数
const debounce2 = function (fn, wait, immediate){
    let time;
    return function (...args){
        const _this = this;
        if(time) {
            clearTimeout(time);
        }
        if(immediate){
            const action = !time;
            time = setTimeout(() => {
                fn.apply(_this, args);
                time = null;
            }, wait);
            if(action){
                fn.apply(_this, args);
            }
        }else {
            time = setTimeout(() => {
                fn.apply(_this, args);
            }, wait);
        }
    }
}


// 节流函数立即执行版
function throttle(fn, wait){
    let flag = true;
    let timer = null;
    return function (...args){
        if(flag){
            fn.apply(this, args);
            flag = false;
            timer = setTimeout(() => {
                flag = true;
            }, wait);
        }
    }
}

function throttle2(fn, wait){
    let time = null;

    return function (){
        if(time){
            return;
        }
        time = setTimeout(() => {
            fn.apply(this, arguments);
            time = null;
        }, wait);
    }
}

const input = debounce(function (v){
    console.log('v', v);
}, 300);

const input2 = debounce2(function (args){
    console.log('args------', args);
}, 300, true);

const inputDom = document.getElementById('input2');
inputDom.addEventListener('input', event => input2(event))

const button = document.getElementById('button');
button.addEventListener('click', e => {input2(e)})


</script>
</html>

发布订阅模型

class EventBus{
    constructor() {
        this.tasks = {};
    }

    on(event, fn){
        if(!this.tasks[event]){
            this.tasks[event] = [];
        }
        this.tasks[event].push(fn);
    }

    off(event, fn){
        const tasks = this.tasks[event];
        if(!Array.isArray(tasks)){
            return;
        }
        // 利用 filter 删除队列中的指定函数
        this.tasks[event] = tasks.filter((cb) => fn !== cb);
    }

    emit(event, ...args){
        if(!this.tasks[event]){
            return;
        }
        this.tasks[event].forEach(fn => fn(...args));
    }

    /**
     * 注册一个只能执行一次的事件
     * @param 事件类型
     * @param 回调函数
     */
    once(event, fn){
        if(!this.tasks[event]){
            this.tasks[event] =[];
        }
        const that = this;

        function _once(...args){
            fn(...args);
            that.off(event, _once);
        }
        this.tasks[event].push(_once);
    }
}

const bus = new EventBus();

bus.once('login', function(name){
    console.log('hello', name);
})

bus.once('login22', function(name){
    console.log('hello', name);
})

bus.emit('login', '第一次登录');
bus.emit('login', '再次触发无效');
bus.emit('login2', '第二次登录');

十九、下划线转为驼峰命名


function toHump(name){
  return name.replace(/\_(\w)/g, function (all, letter){
    return letter.toUpperCase();
  });
}

function toLine(name){
  return name.replace(/([A-Z])/g, '_$1').toLowerCase();
}

let a = 'a_b2_345_c2345';
console.log(toHump(a));

let b = 'aBdaNf';
console.log(toLine(b));


function replaceProperty(obj){
  if(typeof obj === 'object' && obj !== null){
    let cp = Array.isArray(obj) ? [] : {};
    for(let key in obj){
      if(Object.prototype.hasOwnProperty.call(obj, key)){
        cp[toHump(key)] = replaceProperty(obj[key]);
      }
    }
    return cp;
  }else {
    return obj;
  }
}

let obj = {
  a_aa_aa: 1,
  b_bb_bb: {c_cc_cc: 2},
  c_cc_cc: [{c_cc_cc: 2},{c_cc_cc: 2}]
}
console.log(replaceProperty(obj))

二十、手写千分位

function FormatWithRegex(number){
    let str = number.toString();
    let pattern1 = /\d{1,3}(?=(\d{3})+$)/g;
    let pattern2 = /\d{1,3}(?=(\d{3})+(\.))/g;
    return str.includes('.') ? str.replace(pattern2, (match) => {
        return match + ',';
    }) : str.replace(pattern1, (match) => {
        return match + ',';
    })
}
const a = FormatWithRegex(12312124545.3333);//'12,312,124,545.31313'
const b = FormatWithRegex(-12121245.42135);//'123,121,245.45'

console.log(a);
console.log(b);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值