前端手写题之JavaScript 原生方法重写

本人github链接 求follow

前端手写题集锦 use js 记录大厂面试常考手写题,github仓库地址:https://github.com/Sunny-117/Front-end-handwritten-question。伴随秋招陆续更新,求star

前端手写题系列文章:

前端手写题之正则表达式

前端手写题之 树 场景题 js

前端手写题之JavaScript 原生方法重写

flat

function flatDeep(arr, d = 1) {
    return d > 0 ? arr.reduce((a, b) => a.concat(Array.isArray(b) ?
        flatDeep(b, d - 1) : b), []) : arr.slice();
}


arr.toString().split(',').map(item => {
    return Number(item)
})


Array.prototype.flatten = function () {
  return this.reduce((a, b) => {
    return a.concat(Array.isArray(b) ? b.flatten() : b);
  }, []);
};

forEach

// 无返回值,调用callback
Array.prototype.myForEach = function (callback) {
  if (typeof callback !== "function") {
    throw new TypeError(callback + " is not a function");
  }
  for (let i = 0; i < this.length; i++) {
    callback(this[i], i, this);
  }
};

map

Array.prototype.map = function (callback) {
  return this.reduce((prev, curr, i) => {
    prev.push(callback(curr, i, this))
    return prev
  }, [])
}


//返回一个新的数组
Array.prototype.myMap = function (callback) {
  const res = [];
  for (let i = 0; i < this.length; i++) {
    res.push(callback(this[i], i, this));
  }
  return res;
};

filter

Array.prototype.myFilter = function (callback) {
  const res = [];
  for (let i = 0; i < this.length; i++) {
    callback(this[i], i, this) && res.push(this[i]);
  }
  return res;
};

reduce

Array.prototype.myReduce = function (callback, ...args) {
  let start = 0,
    pre;
  if (args.length) {
    //有参数的话pre等于参数第0项
    pre = args[0];
  } else {
    //没参数的话,默认从数组0项开始
    pre = this[0];
    start = 1;
  }
  for (let i = start; i < this.length; i++) {
    pre = callback(pre, this[i], i, this);
  }
  return pre;
};

fill

Array.prototype.myFill = function (initValue, start = 0, end) {
  end = end < 0 ? this.length + end : end;
  for (let i = start; i < end; i++) {
    this[i] = initValue;
  }
  return this;
};
let arr = [3, 3, 21, 3, 14, 12, 4, 1, 2];
console.log(arr.fill(1, 3, 5));

includes

Array.prototype.myIncludes = function (value, start = 0) {
  start = start < 0 ? this.length + start : start;
  for (let i = start; i < this.length; i++) {
    if (this[i] == value || (Number.isNaN(value) && Number.isNaN(this[i])))
      return true;
  }
  return false;
};

push

Array.prototype.myPush = function (...arg) {
  for (let i = 0; i < arg.length; i++) {
    this[this.length] = arg[i];
  }	
  return this.length;
};

slice

Array.prototype.mySlice = function (start, end) {
    // start>=0 min(start,len)  // start<0 max(start+len,0)
    let len = this.length;
    let l = start === undefined ? 0 : start < 0 ? Math.max(start + len, 0) : Math.min(start, len);
    let r = end === undefined ? len : end < 0 ? Math.max(end + len, 0) : Math.min(end, len);
    const res = [];
    while (l < r) {
        res.push(this[l++]);
    }
    return res;
};
const arr = [1, 2, 3, 4, 5]
const res = arr.mySlice(-3)
console.log(res);

unshift

Array.prototype.myUnshift = function (...items) {
    this.reverse().push(...items.reverse())
    this.reverse()
    return this.length
}

getLevel

Array.prototype.getLevel = function () {
    let max = 1
    for (const a of this) {
        if (a instanceof Array) {
            const depth = a.getLevel() + 1
            if (max < depth) max = depth
        }
    }
    return max
};
const a1 = [1, 2, [1], [1, [2, [3]]]];
console.log(a1.getLevel()); //4

copy

Array.prototype.copy = function () {
    return [...this, ...this]
}
console.log([1, 2, 3, 4, 5].copy());

lodash的get

const obj = {
    a: {
        b: 123
    },
    arr: [
        {
            demo: 'demo'
        }
    ]
}
function getKey(obj, str) {
    str = str.split('.')
    let len = str.length;
    for (let i = 0; i < len; i++) {
        if (str[i] && str[i].indexOf('[') !== -1) {
            let index = str[i].match(/\[(\d+)\]/)[1]
            let name = str[i].split('[')[0]
            if (name in obj) {
                obj = obj[name][index]
            } else {
                return undefined
            }
        } else if (str[i] in obj && obj[str[i]]) {
            obj = obj[str[i]]
        } else {
            return undefined
        }

    }
    return obj
}
console.log(getKey(obj, 'a.b'));
console.log(getKey(obj, 'arr[0].demo'));

set,map

class MySet {
  constructor(iterator = []) {//不传默认空数组
    if (typeof iterator[Symbol.iterator] !== "function") {
      throw new TypeError(`你提供的${iterator}不是一个可迭代的对象`)
    }
    this._datas = [];
    for (const item of iterator) {
      this.add(item);
    }
  }

  get size() {
    return this._datas.length;
  }

  add(data) {
    if (!this.has(data)) {// 不包含data,才加入
      this._datas.push(data);
    }
  }

  has(data) {// 是否有data
    for (const item of this._datas) {
      if (this.isEqual(data, item)) {// isEqual判断两个数据是否相等
        return true;
      }
    }
    return false;
  }
  delete(data) {
    for (let i = 0; i < this._datas.length; i++) {
      const element = this._datas[i];
      if (this.isEqual(element, data)) {
        //删除
        this._datas.splice(i, 1);
        return true;
      }
    }
    return false;
  }

  clear() {
    this._datas.length = 0;
  }
  *[Symbol.iterator]() {// 遍历效果
    for (const item of this._datas) {
      yield item;
    }
  }
  forEach(callback) {
    for (const item of this._datas) {
      callback(item, item, this);
    }
  }
  /**
     * 判断两个数据是否相等
     * @param {*} data1 
     * @param {*} data2 
     */
  isEqual(data1, data2) {
    if (data1 === 0 && data2 === 0) {
      return true;
    }
    return Object.is(data1, data2);
  }
}


class MyMap {
  constructor(iterable = []) {
    //验证是否是可迭代的对象
    if (typeof iterable[Symbol.iterator] !== "function") {
      throw new TypeError(`你提供的${iterable}不是一个可迭代的对象`)
    }
    this._datas = [];
    for (const item of iterable) {
      // item 也得是一个可迭代对象
      if (typeof item[Symbol.iterator] !== "function") {
        throw new TypeError(`你提供的${item}不是一个可迭代的对象`);
      }
      const iterator = item[Symbol.iterator]();
      //不一定是数组,所以用这种方式
      const key = iterator.next().value;
      const value = iterator.next().value;
      this.set(key, value);
    }

  }

  set(key, value) {
    const obj = this._getObj(key);
    if (obj) {//已经有了就是要修改
      //修改
      obj.value = value;
    }
    else {//没有的话添加
      this._datas.push({
        key,
        value
      })
    }
  }

  get(key) {
    const item = this._getObj(key);
    if (item) {
      return item.value;
    }
    return undefined;// 找不到
  }

  get size() {
    return this._datas.length;
  }

  delete(key) {
    for (let i = 0; i < this._datas.length; i++) {
      const element = this._datas[i];
      if (this.isEqual(element.key, key)) {
        this._datas.splice(i, 1);
        return true;
      }
    }
    return false;
  }

  clear() {
    this._datas.length = 0;
  }

  /**
     * 根据key值从内部数组中,找到对应的数组项
     * @param {*} key 
     */
  _getObj(key) {
    for (const item of this._datas) {
      if (this.isEqual(item.key, key)) {
        return item;
      }
    }
  }

  has(key) {
    return this._getObj(key) !== undefined;
  }

  /**
     * 判断两个数据是否相等
     * @param {*} data1 
     * @param {*} data2 
     */
  isEqual(data1, data2) {
    if (data1 === 0 && data2 === 0) {
      return true;
    }
    return Object.is(data1, data2);
  }

  *[Symbol.iterator]() {//迭代器创建函数本身就是生成器函数  *
    for (const item of this._datas) {
      yield [item.key, item.value];
    }
  }

  forEach(callback) {
    for (const item of this._datas) {
      callback(item.value, item.key, this);
    }
  }
}

实现填充字符串

String.prototype.zpadStart = function (targetLength, padString) {
    let string = this
    while (string.length < targetLength) {
        string = padString + string
    }
    return string
}
const res = 'abc'.padStart(8, "0");

plainObj

function isPlainObject(obj) {
  if (typeof obj !== "object") {
    return false;
  }
  return Object.getPrototypeOf(obj) === Object.prototype;
}

Object.assign

Object.assign2 = function(target, ...source) {
  let ret = Object(target) 
  source.forEach(function(obj) {
    if (obj) {
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          ret[key] = obj[key]
        }
      }
    }
  })
  return ret
}
const obj1 = {a: 1}
const obj2 = {b: 2}
const res = Object.assign2(obj1, obj2)
console.log(obj1);

Object.is

Object.is = function (x, y) {
  if (x === y) {
    // 当前情况下,只有一种情况是特殊的,即 +0 -0
    // 如果 x !== 0,则返回true
    // 如果 x === 0,则需要判断+0和-0,则可以直接使用 1/+0 === Infinity 和 1/-0 === -Infinity来进行判断
    return x !== 0 || 1 / x === 1 / y;
  }
  // x !== y 的情况下,只需要判断是否为NaN,如果x!==x,则说明x是NaN,同理y也一样
  // x和y同时为NaN时,返回true
  return x !== x && y !== y;
};

Object.create()

Object.create2 = function(proto, propertyObject = undefined) {
    function F() {}
    F.prototype = proto
    const obj = new F()
    if (propertyObject != undefined) {
        Object.defineProperties(obj, propertyObject)
    }
    if (proto === null) {
        // 创建一个没有原型对象的对象,Object.create(null)
        obj.__proto__ = null
    }
    return obj
}

JSON.stringify

实现一个函数toJSON,将传入的数据转换为JSON格式的字符串

function toJSON(data){

}

// test
toJSON(""); // -> ""
toJSON("abc"); // -> "abc"
toJSON(123); // -> 123
toJSON({a:1, b:2}); // -> {"a":1, "b":2}
toJSON(["1", 3, {name:"monica", age:18}]); //-> ["1", 3, {"name":"monica", "age":18}]
var specialTypes = ["function", "symbol", "undefined"];

function isArrayItemToNull(item) {//数组的元素是不是转换成null
  const itemType = typeof item;
  return (
    specialTypes.includes(itemType) || (isNaN(item) && itemType === "number")//判断NaN
  );
}

function isDropProp(data) {
  return specialTypes.includes(typeof data);
}

function hanldeObject(data) {
  // 是不是null
  if (data === null) {
    return "null";
  }
  data = data.valueOf();
  if (typeof data !== "object") {
    // 说明data已经是原始类型
    return toJSON(data);
  }
  // 是数组的情况
  if (Array.isArray(data)) {
    return `[${data
      .map((it) => (isArrayItemToNull(it) ? "null" : toJSON(it)))
      .join(", ")}]`;
  }
  // 是普通对象的情况
  const result = Object.entries(data)
  .flatMap(([k, v]) => (isDropProp(v) ? [] : `"${k}": ${toJSON(v)}`))
  .join(",");
  return `{${result}}`;
}

/**
 * 将传入的数据转换为 JSON 格式的字符串
 * @param {any} data 要转换的数据
 * @returns {String|undefined} 返回转换后的 JSON 字符串
 */
function toJSON(data) {
  const type = typeof data; // 拿到data的数据类型
  switch (type) {
    case "boolean":
    case "number":
      return "" + data;
    case "bigint":
      throw new TypeError("Do not know how to serialize a BigInt");
    case "string":
      return `"${data}"`;
    case "function":
    case "undefined":
    case "symbol":
      return;
    case "object":
      return hanldeObject(data);
  }
}

// test
console.log(
  toJSON({
    a: undefined,
    b: Symbol("1"),
    d: () => {},
    c: "abc",
    e: {
      a: 1,
      b: [{ name: "monica", age: 18 }, 44, {}],
    },
  })
);
// console.log(toJSON(""));
// toJSON(""); // -> ""
// toJSON("abc"); // -> "abc"
// toJSON(123); // -> 123
// toJSON({ a: 1, b: 2 }); // -> {"a":1, "b":2}
// toJSON(["1", 3, { name: "monica", age: 18 }]); //-> ["1", 3, {"name":"monica",

JSON.parse

var json = '{"a":"1", "b":2}'; var obj = eval("(" + json + ")"); // obj 就是 json 反序列化之后得到的对象
var json = '{"name":"小姐姐", "age":20}'; var obj = (new Function('return ' + json))();

call apply bind

Function.prototype.call2 = function(context, ...args) {
  context = (context === undefined || context === null) ? window : context
  context.__fn = this
  let result = context.__fn(...args)
  delete context.__fn
  return result
}
Function.prototype.apply2 = function(context, args) {
  context = (context === undefined || context === null) ? window : context
  context.__fn = this
  let result = context.__fn(...args)
  delete context.__fn
  return result
}
Function.prototype.bind2 = function(context, ...args1) {
  context = (context === undefined || context === null) ? window : context
  let _this = this
  return function(...args2) {
    context.__fn = _this
    let result = context.__fn(...[...args1, ...args2])
    delete context.__fn
    return result
  }
}

promisify

手动实现一个promisify函数的意思是:我们把一个异步请求的函数,封装成一个可以具有 then方法的函数,并且在then方法中返回异步方法执行结果的这么一个函数

// 首先定一个需要进行 promisify 的函数
function asyncFn(a, b, callback) {
        // 异步操作,使用 setTimeout 模拟
        console.log('异步请求参数', a, b)
        setTimeout(function() {
                callback('异步请求结果')
        }, 3000)
}

// 我们希望调用的方式是
const proxy = promisify(asyncFn)
proxy(11,22).then(res => {
        // 此处输出异步函数执行结果
        console.log(res)
})

// 定义一个方法, 需要针对异步方法做封装,所以需要一个入参,既需要promisify的原异步方法
function promisify(func) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      const callback = (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data)
        }
      };
      func.call(this, ...args, callback);
    });
  }
}

instanceof

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

function myInstanceof(left, right) {
  let proto = Object.getPrototypeOf(left), // 获取对象的原型
      prototype = right.prototype; // 获取构造函数的 prototype 对象

  // 判断构造函数的 prototype 对象是否在对象的原型链上
  while (true) {
    if (!proto) return false;
    if (proto === prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}
function Person() { };
var p = new Person();
console.log(myInstanceof(p, Object));
// console.log(p instanceof Person);//true

trim

return str.replace(/(^\s+)|(\s+$)/g,'')//将前空格和后空格替换为空

new

(1)首先创建了一个新的空对象 (2)设置原型,将对象的原型设置为函数的 prototype 对象。 (3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性) (4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

function myNew(constructorFn, ...args) {
    let newObj = {}
    newObj.__proto__ = constructorFn.prototype;
    // newObj = Object.create(constructor.prototype);
    let result = constructorFn.apply(newObj, args)
    return result instanceof Object ? result : newObj
}
function Animal(name) {
    this.name = name;
}
let animal = myNew(Animal, 'dog')
console.log(animal);

repeat

输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。

不用循环

String.prototype.repeat = function (n) {// 我写的
    let str = this;
    let res = ''
    while (n) {
        res += str;
        n--
    }
    return res
}
console.log('abc'.repeat(3));

function repeat(src, n) {
  return new Array(n + 1).join(src);
}
console.log(repeat("abc", 4));



方法3:递归
function repeat(src, n) {
  return n > 0 ? src.concat(repeat(src, --n)) : "";
}

#前端面经##互联网求职##前端面试##web前端学习#
全部评论
正头疼呢,感谢感谢
点赞
送花
回复
分享
发布于 2022-10-24 12:03 陕西

相关推荐

点赞 评论 收藏
转发
4 35 评论
分享
牛客网
牛客企业服务