前端手写题之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前端学习#