国家为什么不禁止外包,合肥网站开发 合肥网站优化,wordpress汉化包安装,微信公众平台申请入口手动实现map方法#xff08;面试#xff1a;用友、猿辅导、字节#xff09;
回调函数接受三个参数。分别为#xff1a;数组元素#xff0c;元素索引#xff0c;原数组本身。map方法执行的时候#xff0c;会自动跳过未被赋值或者被删除的索引。map方法返回一个新数组面试用友、猿辅导、字节
回调函数接受三个参数。分别为数组元素元素索引原数组本身。map方法执行的时候会自动跳过未被赋值或者被删除的索引。map方法返回一个新数组而且不会改变原数组。当然你想改变也是可以的通过回调函数的第三个参数即可改变原数组。
// thisArg参数就是用来改变回调函数内部this的
Array.prototype.myMap function (fn, thisArg) {// // 首先检查传递的参数是否正确。if (typeof fn ! function) {throw new TypeError(fn is not a function);}// 每次调用此函数时我们都会创建一个 res 数组, 因为我们不想改变原始数组。let res [];for (let i 0; i this.length; i) {// 简单处理空项this[i] ? res.push(fn.call(thisArg, this[i], i, this)) : res.push(this[i]);}return res;
};//测试
const obj {name: ha,age: 12
}const arr [1, 3, , 4];
// 原生map
const newArr arr.map(function (ele, index, arr) {console.log(this);return ele 2;}, obj);console.log(newArr);// 用reduce实现map方法字节
// 方法1
Array.prototype.myMap function (fn, thisArg) {if (this null) {throw new TypeError(this is null or not defined);}if (typeof fn ! function) {throw new TypeError(fn is not a function);}return this.reduce((acc, cur, index, array) {const res fn.call(thisArg, cur, index, array);acc.push(res);return acc;}, []);
};// 方法2
Array.prototype.myMap function(fn, thisArg){if (this null) {throw new TypeError(this is null or not defined);}if (typeof fn ! function) {throw new TypeError(fn is not a function);}var res [];this.reduce(function(pre, cur, index, arr){return res.push(fn.call(thisArg, cur, index, arr));}, []);return res;
}var arr [2,3,1,5];
arr.myMap(function(item,index,arr){console.log(item,index,arr);
})
参考链接参考链接
实现reduce方法
Array.prototype.myReduce function(fn, initValue) {// 边界条件判断if(typeof fn ! function) {console.error(this is not a function);}// 初始值let preValue, curValue, curIndex;if(typeof initValue undefined) {preValue this[0];curValue this[1];curIndex 1;} else {preValue initValue;curValue this[0];curIndex 0; }// 遍历for (let i 0; i this.length; i) {preValue fn(preValue, this[i], i, this)}return preValue;
}reduce手写方法链接
实现promise.all美团一面
function promiseAll(promises) {return new Promise((resolve, reject) {// 边界条件判断if(!Array.isArray(promises)){throw new TypeError(argument must be a array);}// 成功的数量var resolvedCounter 0;// 保存的结果var resolvedResult [];for (let i 0; i promises.length; i) {Promise.resolve(promises[i]).then(value {resolvedCounter;resolvedResult[i] value;// 当所有的promise都成功之后if (resolvedCounter promises.length) {resolve(resolvedResult)}}, error{reject(error)})}})
}Promise.all方法接受一个数组作为参数p1、p2、p3都是 Promise 实例如果不是就会先调用下面讲到的Promise.resolve方法将参数转为 Promise 实例再进一步处理。Promise.all方法的参数可以不是数组但必须具有 Iterator 接口且返回的每个成员都是 Promise 实例。错误处理有时候我们使用Promise.all()执行很多个网络请求可能有一个请求出错但我们并不希望其他的网络请求也返回reject要错都错这样显然是不合理的。如何做才能做到promise.all中即使一个promise程序rejectpromise.all依然能把其他数据正确返回呢?方法1当promise捕获到error 的时候代码吃掉这个异常返回resolve约定特殊格式表示这个调用成功了
实现promise.race58同城一面
Promise.race function (promises) {return new Promise((resolve, reject) {promises.forEach(promise {promise.then(resolve, reject)})})
}// 改进方法
Promise._race promises new Promise((resolve, reject) {promises.forEach(promise {promise.then(resolve, reject)})
})模拟实现一个 Promise.finally
Promise.prototype.finally function (callback) {let P this.constructor;return this.then(value P.resolve(callback()).then(() value),reason P.resolve(callback()).then(() { throw reason }));
};链接
防抖面试
// 防抖
//参数func需要防抖的函数
//参数delayTime延时时长单位ms
function debounce(func, delayTime) {//用闭包路缓存延时器idlet timer;return function (...args) {if (timer) {clearTimeout(timer); //清除-替换把前浪拍死在沙滩上} timer setTimeout(() { // 延迟函数就是要晚于上面匿名函数func.apply(this, args); // 执行函数}, delayTime);}
}// 测试
const task () { console.log(run task) }
const debounceTask debounce(task, 1000)
window.addEventListener(scroll, debounceTask)节流面试
// 节流函数
function throttle(fn, delay) {let timer null;return function(...args) {if(timer) {return;}timer setTimeout(() {fn.apply(this, args);timer null;}, delay)}
}// 测试
function print(e) {console.log(123, this, e)
}
input.addEventListener(input, throttle(print, 1000));new面试中问到
function mynew(Func, ...args) {// 1.创建一个新对象const obj {};// 2.新对象原型指向构造函数原型对象obj.__proto__ Func.prototype;// 3.将构建函数的this指向新对象 (让函数的 this 指向这个对象执行构造函数的代码为这个新对象添加属性)let result Func.apply(obj, args);// 4.根据返回值判断return result instanceof Object ? result : obj;
}// 测试
function Person(name, age) {this.name name;this.age age;
}
Person.prototype.say function () {console.log(this.name)
}let p mynew(Person, huihui, 123)
console.log(p) // Person {name: huihui, age: 123}
p.say() // huihui注意
类型说明不 return 和 return 值类型结果就是输出 Person { name: ‘wang’ }这是正常的。return 引用类型输出了 { age: 18 }也就是我们return的引用类型此时我们若创建原型方法也不会挂到实例上调用时会报错TypeError。
function Person(name) {this.name name;// return 1; // 情况1return 值类型结果为{name: wang}// return { age: 18 }; // 情况2return 引用类型结果为{ age: 18 }
}
const p new Person(wang)
console.log(p)事件总线 | 发布订阅模式快手、滴滴
✅参考链接 手写js发布订阅模式以及观察者模式
// 发布订阅模式
class EventEmitter {constructor() {// 事件对象存放订阅的名字和事件this.events {};}// 订阅事件的方法on(eventName, callback) {// 判断事件名是否是 string 类型if (typeof eventName ! string) {throw TypeError(传入的事件名数据类型需为string类型)}// 判断事件函数是否是 function 类型if (typeof eventCallback ! function) {throw TypeError(传入的回调函数数据类型需为function类型)}if (!this.events[eventName]) {// 注意时数据一个名字可以订阅多个事件函数this.events[eventName] [callback]} else {// 存在则push到指定数组的尾部保存this.events[eventName].push(callback)}}// 触发事件的方法emit(eventName) {// 遍历执行所有订阅的事件this.events[eventName] this.events[eventName].forEach(cb cb());}// 移除订阅事件off(eventName, callback) {if (!this.events[eventName]) {return new Error(事件无效);}if (this.events[eventName]) {this.events[eventName] this.events[eventName].filter(cb cb ! callback);}}// 只执行一次订阅的事件然后移除once(eventName, callback) {// 绑定的时fn, 执行的时候会触发fn函数let fn () {callback(); // fn函数中调用原有的callback// 当第一次emit触发事件后在执行这一步的时候就通过 off 来移除这个事件函数 这样这个函数只会执行一次this.off(eventName, fn);}this.on(eventName, fn);}
}// 测试
let em new EventEmitter();
let workday 0;
em.on(work, function() {workday;console.log(work everyday);
});em.once(love, function() {console.log(just love you);
});function makeMoney() {console.log(make one million money);
}
em.on(money,makeMoney);let time setInterval(() {em.emit(work);em.off(money,makeMoney);em.emit(money);em.emit(love);if (workday 5) {console.log(have a rest)clearInterval(time);}
}, 1000);柯里化知乎面试二面
柯里化currying 指的是将一个多参数的函数拆分成一系列函数每个拆分后的函数都只接受一个参数。柯里化是一种函数的转换它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。柯里化不会调用函数。它只是对函数进行转换。视频讲解人类高质量JS函数柯里化
// 函数求和
function sumFn(...rest) {return rest.reduce((a, b) a b);
}
// 柯里化函数
var currying function (func) {// 保存所有传递的参数const args [];return function result(...rest) {// 最后一步没有传递参数如下例子if(rest.length 0) {return func(...args);} else {// 中间过程将参数push到argsargs.push(...rest);return result; // 链式调用}}
}// 测试
currying(sumFn)(1)(2)(3)(4)(); // 10
currying(sumFn)(1, 2, 3)(4)(); // 10// es6 实现
function curry(fn, ...args) {return fn.length args.length ? fn(...args) : curry.bind(null, fn, ...args);
}// 第二种方式
function curry(func) { return function curried(...args) { if (args.length func.length) {return func.apply(this, args); } else {return function(...args2) {return curried.apply(this, args.concat(args2)); } } };
}// test
function sum(a, b, c) { return a b c;}
let curriedSum curry(sum);
alert( curriedSum(1, 2, 3) ); // 6仍然可以被正常调用
alert( curriedSum(1)(2,3) ); // 6对第一个参数的柯里化
alert( curriedSum(1)(2)(3) ); // 6全柯里化一文带你搞懂JavaScript Currying柯里化函数
深拷贝deepCopy面试
function deepCopy(obj, cache new WeakMap()) {// 数据类型校验if (!obj instanceof Object) return obj;// 防止循环引用if (cache.get(obj)) return cache.get(obj);// 支持函数if (obj instanceof Function) {return function () {obj.apply(this, arguments);}}// 支持日期if (obj instanceof Date) return new Date(obj);// 支持正则对象if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);// 还可以增加其他对象比如Map, Set等根据情况判断增加即可面试点到为止就可以了// 数组是 key 为数字的特殊对象const res Array.isArray(obj) ? [] : {};// 缓存 copy 的对象用于处理循环引用的情况cache.set(obj, res);Object.keys(obj).forEach(key {if (obj[key] instanceof Object) {res[key] deepCopy(obj[key], cache);} else {res[key] obj[key];}});return res;
}// 测试
const source {name: Jack,meta: {age: 12,birth: new Date(1997-10-10),ary: [1, 2, { a: 1 }],say() {console.log(Hello);}}
}
source.source source
const newObj deepCopy(source)
console.log(newObj.meta.ary[2] source.meta.ary[2]);附加JSON.stringify深拷贝的缺点
如果obj里有RegExp(正则表达式的缩写)、Error对象则序列化的结果将只得到空对象如果obj里面有时间对象时间将只是字符串的形式而不是对象的形式;如果obj里有函数undefined则序列化的结果会把函数或 undefined丢失如果obj里有NaN、Infinity和-Infinity则序列化的结果会变成null;**如果对象中存在循环引用的情况也无法正确实现深拷贝思路**我们设置一个数组或者哈希表存储已拷贝过的对象当检测到当前对象已存在于哈希表中时取出该值并返回即可如上。// 例如: a:{b:{c:{d: null}}}, da, a 的深拷贝对象是 copy, 则 weakmap 里保存一条 a-copy 记录当递归拷贝到d, 发现d指向a而a已经存在于weakmap则让新d指向copy
var test {a: new RegExp(\\w),b: new Date(1536627600000),c: undefined,d: function() {},e: NaN};
console.log(JSON.parse(JSON.stringify(test)));
// 结果
// {
// a: {},
// b: 2018-09-11T01:00:00.000Z,
// e: null
// }链接链接
instanceof虾皮
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
// 方法1 只关心这个就行
function isInstanceOf(instance, klass) {let proto instance.__proto__;let prototype klass.prototype;while (true) {if (proto null) return false;if (proto prototype) return true;proto proto.__proto__;}
}// 测试
class Parent {}
class Child extends Parent {}
const child new Child()
console.log(isInstanceOf(child, Parent), isInstanceOf(child, Child), isInstanceOf(child, Array))
// true true false// 方法2
function myInstanceof(left, right) {// 获取对象的原型let proto Object.getPrototypeOf(left)// 获取构造函数的 prototype 对象let prototype right.prototype; // 判断构造函数的 prototype 对象是否在对象的原型链上while (true) {if (!proto) return false;if (proto prototype) return true;// 如果没有找到就继续从其原型上找Object.getPrototypeOf方法用来获取指定对象的原型proto Object.getPrototypeOf(proto);}
}Object.getPrototypeOf() 静态方法返回指定对象的原型即内部 [[Prototype]] 属性的值
const prototype1 {};
const object1 Object.create(prototype1);console.log(Object.getPrototypeOf(object1) prototype1);
// Expected output: trueObject.create() 静态方法以一个现有对象作为原型创建一个新对象。
const person {isHuman: false,printIntroduction: function () {console.log(My name is ${this.name}. Am I human? ${this.isHuman});},
};const me Object.create(person);me.name Matthew; // name is a property set on me, but not on person
me.isHuman true; // Inherited properties can be overwrittenme.printIntroduction();
// Expected output: My name is Matthew. Am I human? trueinstanceof mdn介绍
实现 jsonp
// 实现 jsonp
// 动态的加载js文件
function addScript(src) {const script document.createElement(script);script.src src;script.type text/javascript;document.body.appendChild(script);
}
addScript(http://xxx.xxx.com/xxx.js?callbackhandleRes);
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) {console.log(res);
}
// 接口返回的数据格式
handleRes({a: 1, b: 2});实现prototype继承
// 实现prototype继承
// 所谓的原型链继承就是让新实例的原型等于父类的实例//父方法
function SupperFunction(flag1){this.flag1 flag1;
}//子方法
function SubFunction(flag2){this.flag2 flag2;
}//父实例
var superInstance new SupperFunction(true);//子继承父
SubFunction.prototype superInstance;//子实例
var subInstance new SubFunction(false);
//子调用自己和父的属性
subInstance.flag1; // true
subInstance.flag2; // false实现控制并发请求个数
js封装一个请求函数可以5个并发请求等其中一个结束后面的补上直到结束。链接参考链接链接
实现indexOf
实现一个字符串匹配算法从长度为 n 的字符串 S 中查找是否存在字符串 TT 的长度是 m若存在返回所在位置。
const find (S, T) {if (S.length T.length) return -1;for (let i 0; i S.length; i) {if (S.slice(i, i T.length) T) return i}return -1;
}
// 待定
const find (S,T) S.indexOf(T)手写call、apply、bind
call()、apply()、bind()这两个方法的作用可以简单归纳为改变this指向从而让我们的this指向不在是谁调用了函数就指向谁。 每个JavaScript函数都是Function对象Function对象是构造函数而它的原型对象是Function.prototype这个原型对象上有很多属性可以使用比如说call就是从这个原型对象上来的。如果我们要模仿必须在这个原型对象上添加和call一样的属性或者说方法。 // 三者的使用
var obj {x: 81,
};var foo {getX: function() {return this.x;}
}console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81参考链接✅手写 实现call、apply和bind方法 超详细✅手写bind视频讲解
// call方法实现
Function.prototype.myCall function(context) {// 判断调用对象if(typeof this ! function) {console.error(type error);}// 判断call方法是否有传值如果是null或者是undefined指向全局变量windowcontext context || window;// 获取除了this指向对象以外的参数, 空数组slice后返回的仍然是空数组let args [...arguments].slice(1);let result null;// 获取调用call的函数用this可以获取context.fn this; // this指向的是使用call方法的函数(Function的实例即下面测试例子中的bar方法)result context.fn(...args); //隐式绑定,当前函数的this指向了context.// 将属性删除delete context.fn;return result;
}//测试代码
var foo {name: Selina
}
var name Chirs;
function bar(job, age) {console.log(this.name);console.log(job, age);
}
bar.myCall(foo, programmer, 20);
// Selina
// programmer 20
bar.myCall(null, teacher, 25);
// undefined
// teacher 25call 和 apply 的区别是什么哪个性能更好一些
call 比 apply 的性能好, 我的理解是内部少了一次将 apply 第二个参数解构的操作
// apply的实现
Function.prototype.myApply function (context) {if (!context) {//context为null或者是undefined时,设置默认值context typeof window undefined ? global : window;}context.fn this;let result null;if(arguments[1]) {// 第二个参数有值的话result context.fn(...arguments[1]);} else {result context.fn();}// 删除属性delete context.fn;return result;
}// 测试代码
var foo {name: Selina
}
var name Chirs;
function bar(job, age) {console.log(this.name);console.log(job, age);
}
bar.myApply(foo, [programmer, 20]);
// Selina programmer 20
bar.myApply(null, [teacher, 25]);
// Chirs teacher 25// bind方法
Function.prototype.myBind function (context) {// 判断调用对象是否为函数if (typeof this ! function) {throw new TypeError(Error);}// 获取参数const args [...arguments].slice(1), fn this;return function Fn() {// 根据调用方式传入不同绑定值return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments)); }
}// 解析
// 1、bind会返回一个函数
// 2、注意点函数返回一个函数很容易造成this的丢失
// 3、bind的实现里面用到了上面已经实现的apply方法所以这里直接复用
// 4、bind可以和new进行配合使用new的过程会使this失效
// 5、三目运算符就是判断是否使用了new
// 6、this instanceof Fn的目的判断new出来的实例是不是返回函数Fn的实例es5 实现继承
待定异步并发数限制
在这里插入代码片
异步串行 | 异步并行// 字节面试题实现一个异步加法
在这里插入代码片
vue reactive
手写promise一般情况下不会考因为太费时间
视频讲解史上最最最详细的手写Promise教程
class MyPromise {static PENDING pending;static FULFILLED fulfilled;static REJECTED rejected;constructor(func) {this.status MyPromise.PENDING; // 状态this.result null; // 参数this.resolveCallbacks [];this.rejectCallbacks [];// 异常校验为了兼容下面的代码throw new Error(抛出失败);try {func(this.resolve.bind(this), this.reject.bind(this));} catch (error) {this.reject(error);}}resolve(result) {// resolve和reject函数是在函数的末尾执行的所以加一层setTimeoutsetTimeout(() {if(this.status MyPromise.PENDING) {this.status MyPromise.FULFILLED;this.result result;this.resolveCallbacks.forEach(callback {callback(result);});}})}reject(result) {// resolve和reject函数是在函数的末尾执行的所以加一层setTimeoutsetTimeout(() {if(this.status MyPromise.PENDING) {this.status MyPromise.REJECTED;this.result result;this.rejectCallbacks.forEach(callback {callback(result);});}})}// then函数有两个函数参数then(onSuccess, onError) {// 外层return promise的目的是为了完成链式调用return new MyPromise((resolve, reject) {// then方法中的参数必须是函数如果不是函数就忽略onSuccess typeof onSuccess function ? onSuccess : () {};onError typeof onError function ? onError : () {};// 如果then里面的状态为pending, 必须等resolve执行完之后在执行then, 所以需要创建数组保留then里面的函数if(this.status MyPromise.PENDING) {this.resolveCallbacks.push(onSuccess);this.rejectCallbacks.push(onError);}// 如果then方法执行的是成功的函数if(this.status MyPromise.FULFILLED) {// 包裹setTimeout解决异步问题then放阿飞执行是微任务setTimeout(() {onSuccess(this.result);});}// 如果then方法执行的是失败的函数if(this.status MyPromise.REJECTED) {// 同上setTimeout(() {onSuccess(this.result);});} }) }
}// 测试
console.log(第一步);
let promise1 new MyPromise((resolve, reject) {console.log(第二步);setTimeout(() {resolve(这次一定);reject(下次一定);console.log(第四步);});// resolve(这次一定);// throw new Error(抛出失败);
});
promise1.then(result {console.log(result)},err {console.log(err.message)},
);
console.log(第三步);数组扁平化
// 方案 1
function test(arr []) {return arr.flat(Infinity);
}
test([1, 2, [3, 4, [5, 6]], 7])// 方案 2
function reduceFlat(ary []) {return ary.reduce((res, item) res.concat(Array.isArray(item) ? reduceFlat(item) : item), [])
}// 测试
const source [1, 2, [3, 4, [5, 6]], 7]
console.log(reduceFlat(source))对象扁平化
// 需求
var output {a: {b: {c: {dd: abcdd}},d: {xx: adxx},e: ae}
}// 要求转换成如下对象
var entry {a.b.c.dd: abcdd,a.d.xx: adxx,a.e: ae
}// 实现方案
function objectFlat(obj {}) {const res {};function flat(item, preKey ) {Object.entries(item).forEach(([key, val]) {const newKey preKey ? ${preKey}.${key} : key;if (val typeof val object) {flat(val, newKey);} else {res[newKey] val;}})}flat(obj);return res;
}// 测试
const source { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source)); // {a.b.c: 1, a.b.d: 2, a.e: 3, f.g: 2}对象扁平化反转
图片懒加载
图片懒加载手写见上面链接原理图片的加载是由src引起的当对src赋值时浏览器就会请求图片资源 。根据这个原理我们使用HTML5 的data-src 属性来储存图片的路径在需要加载图片的时候将data-src中图片的路径赋值给src这样就实现了图片的按需加载即懒加载。
预加载预加载指的是将所需的资源提前请求加载到本地这样后面在需要用到时就直接从缓存取资源。 通过预加载能够减少用户的等待时间提高用户的体验。预加载的最常用的方式是使用 js 中的image对象通过为image对象来设置 src 属性来实现图片的预加载。预加载则会增加服务器前端压力。
使用 setTimeout 实现 setInterval待定
// 使用 setTimeout 实现 setInterval
function mySetInterval(fn, timeout) {// 控制器控制定时器是否继续执行var timer {flag: true};// 设置递归函数模拟定时器执行。function interval() {if (timer.flag) {fn();setTimeout(interval, timeout);}}// 启动定时器setTimeout(interval, timeout);// 返回控制器return timer;
}// 方法2
function mySetInterval() {mySetInterval.timer setTimeout(() {arguments[0]()mySetInterval(...arguments)}, arguments[1])
}mySetInterval.clear function() {clearTimeout(mySetInterval.timer)
}mySetInterval(() {console.log(11111)
}, 1000)setTimeout(() {// 5s 后清理mySetInterval.clear()
}, 5000)参考链接
参考链接前端面试出场率奇高的18个手写代码
其他手写代码
1、手写获取数组的重复元素要求尽可能用多种方法实现小米 3、用 promise 封装实现 readfile 和 writefile 的同步请求百度4、 手写 Promise字节、百度、深信服、小红书 9、手写虚拟 dom 转换成真实 dom字节 10、手写 assign要考虑全面包括 symbol 也要考虑在内猿辅导 11、手写 ES6 的模板字符串百度 13、手写斐波那锲数列知乎