1.用es5实现es6中类的继承
function Animal(){
this.name="动物";
this.sex="男孩";
}
Animal.prototype.eat = ()=>{console.log("吃食物")}
function Dog(){
Animal.call(this);
this.name="狗狗";
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = ()=>{console.log("狗在叫")}
/*
Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;这两句代码其实可以使用 Dog.prototype.__proto__ = Animal.prototype代替.
*/
结果:
2.你知道多少种数组去重的方法?
方法1:
function listUnique(list){
let arr = [];
list.forEach((item)=>{
if(!arr.includes(item)){
arr.push(item);
}
})
return arr;
}
方法2:
function listUnique(list){
let obj = {},arr = [];
list.forEach((item)=>{
if(!obj[item]){
obj[item] = true;
arr.push(item);
}
})
return arr;
}
方法3:
function listUnique(list){
let set = new Set(list); //Set与数组类似,但是它存储的值是唯一的
return [...set];
}
方法4:(如果题目设定数组参数全是数字,从低到高按序排列没有空值,可以采用双指针去重)
function deDuplecate(list){
let slow = 0;
let fast = 1;
const result = [];
while(list[fast] != null){
if(list[fast] > list[slow]){
result.push(list[slow]);
slow = fast;
}
fast++;
}
result.push(list[slow]);
return result;
}
3.实现script的延迟加载有多少种方法
1.使用动态创建script标签的方式实现延迟加载
function lazyLoad(src){ //需要延迟加载的时候传入要加载的js路径
let script = document.createElement("SCRIPT");
script.src = src;
document.body.appendChild(script);
}
2.操作dom的方式实现延迟加载
<script src="" id="sc"></script>
<script>
setTimeout(()=>{
document.getElementById("sc").src="xxx.js";
},3000)
</script>
3.async属性
HTML5为<script>标签定义了async属性,不让页面等待脚本下载和执行,从而异步加载页面其他内容.
<script src="test1.js" async></script>
4.call函数实现
Function.prototype._call = function (obj, ...rest) {
if (typeof obj === "object" && obj !== null) {
obj.fun = this;
obj.fun(...rest);
delete obj.fun;
} else {
this(...rest);
}
}
function test(a, b, c) {
console.log(a, b, c, this.name);
}
test._call({ name: "kay" }, 1, 2, 3);
5.apply函数实现
Function.prototype._apply = function (obj, arr = []) {
if (typeof obj === "object" && obj !== null) {
obj.fun = this;
obj.fun(...arr);
delete obj.fun;
} else {
this(...arr);
}
}
function test(a, b, c) {
console.log(a, b, c, this.name);
}
test._apply({ name: "kay" }, [1, 3, 5]);
6.bind函数实现
Function.prototype._bind = function (obj, ...rest) {
if (typeof obj === "object" && obj !== null) {
const obj_copy = { ...obj, fun: this };
return function (...rest2) {
return obj_copy.fun(...rest, ...rest2);
}
} else {
const fun = this;
return function (...rest2) {
return fun(...rest, ...rest2);
}
}
}
function fun1(a, b) {
return this.x + a + b;
}
const fun2 = fun1._bind({ x: 1 }, 2);
console.log(fun2(3));
7.你能写出几种节流函数
//时间戳版本(立即执行)
function throttle(fun, delay = 250) {
let old_time = 0;
return function (...args) {
const now = new Date().getTime();
if (now - old_time >= delay) {
fun(...args);
old_time = now;
}
}
}
//定时器版本(延迟执行)
function throttle(fun, delay = 250) {
let running = false;
return function (...args) {
if (running) {
return false;
}
running = true;
let timer = setTimeout(() => {
clearTimeout(timer);
fun(...args);
running = false;
}, delay)
}
}
验证方法:
window.onload = function () {
window.onresize = throttle(function () {
console.log(123);
}, 1000)
}
8.实现防抖函数
//延迟执行
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
fn(...args);
}, delay)
}
}
//和上面函数相比多了一个初次进入函数时会立即执行
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
if (timer === null) { //第一次执行
fn(...args);
}
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
fn(...args);
}, delay)
}
}
验证方法:
9.模拟实现new(创建实例)
/**
* new主要做三件事情
*
* 1.创建一个空对象
* 2.构造函数的this指向该空对象
* 3.执行构造函数
* 4.返回该对象
*/
function _new(constructor) {
let obj = {};
return function (...args) {
constructor.apply(obj, args);
obj.__proto__ = constructor.prototype;
return obj;
}
}
验证方法:
function test(a, b) {
this.name = `测试:${a},${b}`;
}
test.prototype.init = function () { console.log("测试原型对象上面的方法") }
const obj = _new(test)(1, 2);//创建的新对象
console.log(obj);
obj.init();
10.你知道多少种浅拷贝和深拷贝的方法
/**
* 使用函数的嵌套实现深拷贝
*/
function deepCopy(data) {
if (Array.isArray(data)) { // 对数组的处理
let arr = [];
data.forEach((item) => {
arr.push(deepCopy(item));
})
return arr;
} else if (Object.prototype.toString.call(data) === "[object Object]" && data !== null) { //对对象的处理
let obj = Object.create(null);
for (let key in data) {
obj[key] = deepCopy(data[key]);
}
return obj;
} else { //其他类型数据处理
return data;
}
}
/*
* 利用json方法实现深拷贝,但是缺点是不能拷贝函数
*/
function deepCopy(data) {
try {
return JSON.parse(JSON.stringify(data));
} catch (error) {
return data;
}
}
/**
* 使用es6解析结构实现浅拷贝
*/
function lightCopy(data) {
if (Array.isArray(data)) {
return [...data];
} else if (Object.prototype.toString === "[object Object]" && data !== null) {
return { ...data };
} else {
return data;
}
}
/**
* 使用Object.assign实现浅拷贝
*/
function lightCopy(data) {
if (typeof data !== "object" || typeof data === null) {
return data;
}
return Object.assign(data);
}
11.实现instanceof
/**
* @param {*对象} left
* @param {*构造函数} right
*/
function _instanceof(left, right) {
let _left = left.__proto__;
const _right = right.prototype;
while (true) {
if (_left === _right) {
return true;
} else if (_left === null || _left === undefined) {
return false;
} else {
_left = _left.__proto__;
}
}
}
console.log(_instanceof({}, Object));
console.log(_instanceof([], Array));
console.log(_instanceof(new Number(123), Number));
console.log(_instanceof(new String("String"), String)); //instanceof只能验证对象是否是特定类的一个实例.例如 var test = "string";_instanceof(test,String)结果为false
console.log(_instanceof([], Object));
12.实现Object.create
Object._create = function (obj) {
function F() { }
F.prototype = obj;
return new F();
}
const obj = Object._create(null);
console.log(obj);
13.实现Array.isArray
Array._isArray = function (origin) {
return Object.prototype.toString.call(origin) === "[object Array]";
}
console.log(Array._isArray({}));
14.实现reduce
Array.prototype._reduce = function(fn,data){
const array = this;
let start_index = 0;
if(data === null || data === undefined){
data = array[0];
start_index = 1;
}
for(let i = start_index;i<array.length;i++){
data = fn(data,array[i],i,array);
}
return data;
}
const arr = [1,2,3,4,5,6];
let obj = {};
const result = arr._reduce((prev,current,index,arr)=>{
prev[index] = current;
return prev;
},obj)
console.log(result);
15.获取对象上所有的可枚举的属性(不包括原型链上的属性)
Object._getOwnPropertyNames = function(data){
if(Object.prototype.toString.call(data)!== "[object Object]"){
return null;
}
let arr = [];
for(let key in data){
if(data.hasOwnProperty(key)){
arr.push(key);
}
}
return arr;
}
let obj = {
a:1,
b:2,
c:function(){}
}
console.log(Object._getOwnPropertyNames(obj));
16.请分别使用Object.defineProperty和Proxy实现双向数据绑定
<!--html部分-->
<input type="text" id="inputText"/>
<span id="showText"></span>
//使用Object.defineProperty实现
const data = {};
const data_bak = {
text:""
}
const inputText = document.getElementById("inputText");
const showText = document.getElementById("showText");
Object.defineProperty(data,"text",{
set(value){
inputText.value = value;
showText.innerText = value;
data_bak.text = value;
},
get(){
return data_bak.text;
}
})
inputText.oninput = function(e){
data.text = e.target.value;
}
//使用Proxy实现
const data = {
text:""
};
const inputText = document.getElementById("inputText");
const showText = document.getElementById("showText");
const proxyItem = new Proxy(data,{
set(target,key,value){
inputText.value = value;
showText.innerText = value;
target[key] = value;
},
get(target,key){
return target[key];
}
})
inputText.oninput = function(e){
proxyItem.text = e.target.value;
}
17.实现forEach
Array.prototype._forEach = function(fn){
const array = this;
for(let i=0;i<array.length;i++){
fn(array[i],i,array);
}
}
const arr = [{value:1},{value:2},{value:3},{value:4}];
arr._forEach((item,index)=>{
item.value++;
})
console.log(arr);
18.模拟实现async await
function async(fn){
return new Promise((resolve,reject)=>{
const gennerator = fn();
function next(data){
const result = gennerator.next(data);
if(result.done){ //运行完成了
resolve(result.value);
}else{
if(result.value instanceof Promise){
result.value.then((value)=>{
next(value)
}).catch((e)=>{
reject(e);
})
}else{
next(result.value);
}
}
}
next();
})
}
验证方法
function delay(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(100);
},1000)
})
}
function* getData(){
let data = yield delay();
data++;
data = data + (yield 100);
data = data + (yield delay());
return data;
}
async(getData).then((value)=>{
console.log(value); //结果为301
})
19.题目如下
题目:有如下的求和函数
function sum(a,b,c,d,e){
return a+b+c+d+e;
}
请用函数柯里化的方式编写一个增强函数currying,如果当传入增强后的函数的参数个数等于sum的参数个数时才执行sum函数
例如:
const newFn = currying(sum);
console.log(newFn(1,2,3,4)); //结果为[Function]
console.log(newFn(1)(2)(3,4,5)); //结果为15
console.log(newFn(1)(2)(3)(4)(5)); //结果为15
/**
* 函数珂里化
*/
function currying(fn){
const length = fn.length;//调用函数的length属性可以得到该函数拥有几个参数
return function child(...args){
if(args.length>=length){
return fn(...args);
}else{
return (...new_args)=>{
return child(...args,...new_args);
}
}
}
}
20.箭头函数能new吗?为什么?
箭头函数不能使用new创建实例.new操作符其实做了四件事.1.创建一个空对象,将this指向该对象2.执行构造函数3.将空对象的__proto__指向构造函数的原型对象4.最后返回该对象.箭头函数为什么不能new呢?首先它没有自己的this指向,其次箭头函数没有原型对象.
21.有形似如下树形结构任意层级的数据static,请编写一个函数findName通过传入数据源static和type_id返回相应的type_name.例如:
const static = {
type_id:1,
children:[
{
type_id:2,
type_name:"2号",
children:[{
type_id:5,
type_name:"5号",
children:[
{type_id:6, type_name:"6号",},
{type_id:88, type_name:"88号",}
]
}]
},
{
type_id:3,
type_name:"3号",
}, {
type_id:4,
type_name:"4号",
children:[{
type_id:7,
type_name:"7号",
},{
type_id:8,
type_name:"8号",
}]
}
]
}
console.log(findName(static,88)); //返回88号
console.log(findName(static,6)); //返回6号
console.log(findName(static,5)); //返回5号
console.log(findName(static,2)); //返回2号
console.log(findName(static,100)); //返回null
参考编码:
const toString = Object.prototype.toString;
const static = {
type_id:1,
children:[
{
type_id:2,
type_name:"2号",
children:[{
type_id:5,
type_name:"5号",
children:[
{type_id:6, type_name:"6号",},
{type_id:88, type_name:"88号",}
]
}]
},
{
type_id:3,
type_name:"3号",
}, {
type_id:4,
type_name:"4号",
children:[{
type_id:7,
type_name:"7号",
},{
type_id:8,
type_name:"8号",
}]
}
]
}
function findName(data,id){
if(typeof data !== "object" || data === null){
return false;
}
if(toString.call(data) === "[object Object]"){
data = [data];
}
const result = clac(data,id,[]);
if(result){
let value;
Array.from(Array(result.length)).forEach((v,index)=>{
if(index == 0){
value = data[result[index]];
}else{
value = value.children[result[index]];
}
})
if(value){
return value.type_name;
}
}else{
return null;
}
}
function clac(data,id,array){
for(let i = 0;i<data.length;i++){
if(data[i].type_id === id){
array.push(i);
return array;
}else if(toString.call(data[i].children) === "[object Array]"){//还有下一级
const result = clac(data[i].children,id,[...array,i]);
if(result !== undefined){
return result;
}
}
}
}
console.log(findName(static,88));
console.log(findName(static,6));
console.log(findName(static,5));
console.log(findName(static,2));
console.log(findName(static,4));
console.log(findName(static,3));
console.log(findName(static,100));
运行结果:
22.请实现迭代器函数Iterator,如下所示:
const it = Iterator([1,2,3,4]);
console.log(it.next()); //输出{ done: false, value: 1 }
console.log(it.next()); //输出{ done: false, value: 2 }
console.log(it.next()); //输出{ done: false, value: 3 }
console.log(it.next()); //输出{ done: false, value: 4 }
console.log(it.next()); //输出{ done: true, value: undefined }
Iterator函数代码实现:
function Iterator(array){
let i = 0;
return {
next:function(){
return {
done:i<array.length?false:true,
value:array[i++]
}
}
}
}
23.请实现一个函数getDays,通过传入年份和月份作为函数的参数,返回该月的天数.
(提示:闰年的二月有29天,平年的二月只有28天.如果一个年份能被4整除但不能被100整除或者能被400整除则该年份为闰年)
例如:
实现代码:
function getDays(year, month) {
//根据年和月得到当月的天数
month = parseInt(month);
if (!month || month <= 0 || month > 12) {
return null;
}
if (month === 2) {
// 2月份的时候根据闰年和平年计算
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 == 0) {
//闰年
return 29;
} else {
return 28;
}
} else {
const result = month % 2;
if (month <= 7) {
return result > 0 ? 31 : 30;
} else {
return result > 0 ? 30 : 31;
}
}
}
24.请编写函数moneyFilter,传入金额,将其转化为千分位格式.
例如: moneyFilter("1234567899") ,运行结果为 "1,234,567,899"
const money = '1234567899';
function moneyFilter() {
const reg = /\d{1,3}(?=(\d{3})+$)/g;
return money.replace(reg, (v1) => {
return `${v1},`;
});
}
console.log(moneyFilter(money));
25.模拟实现es6中的rest: fun(a,b,...rest)
例如代码如下,对于任意函数test,请编写高阶函数wrapRest实现test函数中通过rest获取剩余参数.
function test(a,b,rest){
console.log(a,b,rest);
}
function wrapRest(fn){
...
}
const restFn = wrapRest(test);
restFn(1,2,3,4,5); //输出结果: 1 2 [3, 4, 5]
restFn(1); //输出结果: 1 undefined []
restFn(1,2); //输出结果: 1 2 []
实现代码:
function wrapRest(fn){
const len = fn.length; //获取参数的个数
const start_index = len - 1;
return function(){
const prev_array = [];
for(let i = 0;i < start_index; i++){
prev_array.push(arguments[i]);
}
const next_array = Array.prototype.slice.call(arguments,start_index);
prev_array.push(next_array);
return fn.apply(this,prev_array);
}
}
26.洗牌算法.请编写一个函数sample,传入一个任意数组和个数n,每次返回数组中n个随机元素.
例如:sample([1,2,3,4,5,6,7,8,9], 5) //每次运行函数返回数组中5个随机的数字,比如[7, 8, 5, 1, 9]
实现代码:
//获取随机数
function randomValue(max, min) {
if (min == null) {
min = 0;
}
return parseInt(Math.random() * (max - min + 1)) + min;
}
//获取随机数组
function sample(array, n) {
if (n == null) {
return array(randomValue(array.length));
}
const new_array = Array.isArray(array)
? array.slice()
: [];
const len = new_array.length;
const count = Math.max(Math.min(n, len),0);
for (i = 0; i < count; i++) {
const idx = randomValue(i, len - 1);
let tmp = new_array[i];
new_array[i] = new_array[idx];
new_array[idx] = tmp;
}
return new_array.slice(0,n);
}
console.log(sample([1,2,3,4,5,6,7,8,9], 5)); //测试代码
27.数组平摊.请编写函数flatten,传入任意数组array和是否浅度展开标识shallow,将数组平摊输出.
例如:
flatten([1, [2, 3], [4, 5, [6, [[7], 8, [9, [10, 11]]]]]]) //输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
flatten([1, [2, 3], [4, 5, [6, [[7], 8, [9, [10, 11]]]]]],true) //输出: [1, 2, 3, 4, 5, Array(2)]
实现代码:
//shallow为true,浅展开.false,深度展开
function flatten(array, shallow) {
function circulate(data, flag) {
let arr = [];
data.forEach((item) => {
if (Array.isArray(item) && !flag) {
const result = circulate(item, shallow);
arr = arr.concat(result);
} else {
arr.push(item);
}
});
return arr;
}
return circulate(array, false);
}
console.log(flatten([1, [2, 3], [4, 5, [6, [[7], 8, [9, [10, 11]]]]]])); //测试代码
28.函数组合.题目如下,存在任意有返回值的函数A,B,C.请编写函数compose,生成的新函数fun传入参数时,函数将按照由右往左的顺序依次运行.传入的参数将作为A函数的入参运行,A函数的返回值作为B函数的参数,B函数的返回值作为C函数的参数.最后返回计算结果.
function A(v) {
return v + 2;
}
function B(v) {
return v * 4;
}
function C(v) {
return v - 6;
}
const fun = compose(C, B, A);
console.log(fun(5)); //结果为22
代码实现:
function compose() {
const args = arguments;
const start = args.length - 1;
return function () {
const params = Array.prototype.slice.call(arguments, 0);
let idx = start;
let result = args[idx].apply(null, params);
idx--;
while (idx >= 0) {
result = args[idx].call(null, result);
idx--;
}
return result;
};
}
29.编写函数range生成数字队列如下,三个参数分别对应起始值,结束值和步长.(计算结果包含起始值,不包含结束值)
console.log(range(10)); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(range(1, 11)); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(0, 30, 5)); //[0, 5, 10, 15, 20, 25]
实现代码:
function range(start, end, step) {
if (end == null) {
end = start;
start = 0;
}
if (step == null) {
step = 1;
}
const array = [];
const len = Math.floor((end - start) / step);
let tmp = start;
for (let i = 0; i < len; i++) {
array.push(tmp);
tmp += step;
}
return array;
}
30.字符串逃逸.为了预防xss攻击,请编写函数createEsacper将含有特殊含义的字符串进行转译.
const escapeMap = { //需要转译的队列
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '&hx27;',
'`': '٠',
};
const escape = createEsacper(escapeMap);
console.log(escape(`<script>alert(123)<\/script>`)); //输出 <script>alert(123)</script>
代码实现:
function createEsacper(keyMap) {
const keys = Object.keys(keyMap);
const reg = new RegExp(`(?:${keys.join('|')})`, 'g');
const replace = (value) => {
return keyMap[value];
};
return function (str) {
return reg.test(str) ? str.replace(reg, replace) : str;
};
}
31.存在斐波那契数列求值函数fn.为了提升计算效率,请编写缓存函数memoize生成新函数,将每次的输入参数和值配对缓存起来,方便下次快速计算.
//键名的设置
const hasher = function () {
var n = arguments[0];
return `键_${n}`;
};
const fn = function (n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
var fibonacci = memoize(fn, hasher);//斐波那契数列求值
console.log(fibonacci(10)); // 输出55
console.log(fibonacci.cache); //输出 {"键_1":1,"键_0":0,"键_2":1,"键_3":2,"键_4":3,"键_5":5,"键_6":8,"键_7":13,"键_8":21,"键_9":34,"键_10":55}
实现代码:
function memoize(fn, hasher) {
const middle = function () {
const params = arguments[0];
const cache = middle.cache;
const key = hasher ? hasher.call(null, params) : params;
if (!cache[key]) {
cache[key] = fn.apply(null, arguments);
}
return cache[key];
};
middle.cache = {};
return middle;
}
32.请编写createIndexFinder函数生成indexOf和lastIndexOf,通过传入数组和值找出值的索引.其中indexOf第三个参数为true时采用二分查找法.indexOf从正向找值的索引,lastIndexOf从反方向找.找不到时返回-1.如下:
const indexOf = createIndexFinder(1, true);
const lastIndexOf = createIndexFinder(-1);
const arr = [1, 3, 5, 7, 9];
console.log(indexOf(arr, 9, true)); //输出4
console.log(lastIndexOf(arr, 9)); //输出4
实现代码:
function createIndexFinder(dir, sorter) {
return function (array, item, flag) {
const start_index = dir > 0 ? 0 : array.length - 1; //获取起始索引
if (!flag || !sorter) {
flag = false;
}
if (flag) {
//启用二分查找法
let low = 0,
high = array.length - 1;
let mid;
while (low < high) {
mid = Math.floor((high + low) / 2);
if (array[mid] < item) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
for (let i = start_index; i < array.length && i >= 0; i += dir) {
if (isNaN(item) && isNaN(array[i])) {
//因为 NaN === NaN 为false,所以要对NaN做特殊处理
return i;
}
if (array[i] === item) {
return i;
}
}
return -1;
};
}
33. 一段文本不知道内容有多少,请用css的方式实现,当内容只有一行时居中显示,多行时居左显示
.div{
display: flex;
flex-direction: row;
justify-content: center;
}
这道题考察flex布局.flex-direction定义了主轴的方向,justify-content定义了主轴的对齐方式.文本是单行的时候居中显示,文本多行会默认居左.
34.Promise能实现异步操作的原理是什么?为什么resolve()执行后,它下一行代码仍然继续执行
如果面试官抛出这样的问题,自信的同学直接手写一个30行简版的Promise甩给他再作答.
function _Promise(fn) {
let array = [],
index = 0;
const { cache_array, cache_index } = __Promise.cache || {};
if (cache_array != null) {
array = cache_array;
}
if (cache_index != null) {
index = cache_index + 1;
}
return new __Promise(array, fn, index);
}
function __Promise(array, fn, index) {
this.then = (hanlder) => {
array.push(hanlder);
return this;
};
const resolve = (...args) => {
setTimeout(() => {
__Promise.cache = {
cache_array: array,
cache_index: index,
};
array[index].apply(null, args);
__Promise.cache = null;
}, 0);
};
fn.call(null, resolve);
return this;
}
测试代码:
new _Promise((resolve)=>{
resolve(3);
console.log(2);
}).then((v)=>{
return new _Promise((resolve)=>{
v++;
console.log(v);
resolve(v);
})
}).then((v)=>{
return new _Promise((resolve)=>{
setTimeout(()=>{
resolve(v+10)
},3000)
})
}).then((v)=>{
console.log(v);
})
执行结果:
解答:
Promise实现异步的原理是采用观察者的模式将回调函数先存储起来,等到运行resolve()时就取出回调函数执行从而达到异步调用的目的.resolve()执行完它的下一行代码还会继续执行的原因是resolve本身是一个异步调用的机制,resolve()运行完并不会立即跳到.then的回调函数中,而是会先运行resolve后面的代码再执行回调函数.
35.请设计一个请求方法request,接口访问失败后重试访问,最多重试三次.如果都失败了退出操作,但请求成功了请将结果返回.
调用如下.
//页面上调用
request({
url:"/api/getList",
data:{
id:1
}
}).then((data)=>{
console.log(data);//获得接口数据
})
测试代码:
function request(params){
let count = 3;
//控制返回结果
function execute(rResolve){
if(rResolve == null){ //第一次请求 rResolve为空
return new Promise((resolve)=>{
Post(resolve)
})
}else{
Post(rResolve);
}
}
//请求数据
function Post(resolve){
axios.post(params.url,params.data).then((res)=>{
resolve(res);
}).catch(()=>{
if(count > 0){
count--;
execute(resolve);
}
})
}
return execute();
}
36.计算多个区间的交集.每个区间用长度为2的数字数组表示,如[2,5]表示区间2到5(包括2和5).区间不限定防线.如[5,2]等同于[2,5].
请实现getIntersection函数,可接受多个区间并返回所有区间的交集(用区间表示),空集用null表示.调用如下.
getIntersection([5, 2], [1, 9], [3, 6]); // [3,5]
getIntersection([1, 7], [8, 9], [1, 2, 3]); // null
测试代码:
function getIntersection(...args) {
function clac(array1, array2) {
if (array1[0] > array1[1]) {
array1 = [array1[1], array1[0]];
}
if (array2[0] > array2[1]) {
array2 = [array2[1], array2[0]];
}
if (array1[1] < array2[0] || array1[0] > array2[1]) {
return [];
}
let min_arr, max_arr;
if (array1[1] - array1[0] <= array2[1] - array2[0]) {
min_arr = array1;
max_arr = array2;
} else {
min_arr = array2;
max_arr = array1;
}
const result = [];
for (let i = min_arr[0]; i <= min_arr[1]; i++) {
if (i >= max_arr[0] && i <= max_arr[1]) {
result.push(i);
} else if (i > max_arr[1]) {
break;
}
}
if (result.length > 0) {
return [result[0], result[result.length - 1]];
}
return result;
}
let data = args.reduce((value, cur) => {
return clac(value, cur);
});
if (data.length == 0) {
return null;
} else {
return data;
}
}
37.请编写一个统计函数stat.获取当前页面中元素节点的数量总和、元素节点的最大嵌套深度以及最大子元素个数,请用 JS 配合原生 DOM API 实现该需求(不用考虑陈旧浏览器以及在现代浏览器中的兼容性,可以使用任意浏览器的最新特性;不用考虑 shadow DOM)。比如在如下页面中运行后:
<html>
<head></head>
<body>
<div>
<span>f</span>
<span>o</span>
<span>o</span>
</div>
</body>
</html>
// 会输出:
{
totalElementsCount: 7,
maxDOMTreeDepth: 4,
maxChildrenCount: 3
}
测试代码:
function stat() {
const htmlEle = document.querySelector('html');
let count = 1;
let max_child = 0;
let deep = 1;
function executa(ele, layer) {
layer++;
const nodeList = Array.prototype.slice.call(ele.children, 0);
count += nodeList.length;
max_child = nodeList.length > max_child ? nodeList.length : max_child;
deep = layer > deep ? layer : deep;
nodeList.forEach((node) => {
if (node.children.length > 0) {
executa(node, layer);
}
});
}
executa(htmlEle, 1);
return {
totalElementsCount: count,
maxDOMTreeDepth: deep,
maxChildrenCount: max_child,
};
}
38.请使用原生代码实现一个Events模块,可以实现自定义事件的订阅、触发、移除功能
调用如下:
const fn1 = (...args) => console.log('I want sleep1', ...args);
const fn2 = (...args) => console.log('I want sleep2', ...args);
const event = new Events();
event.on('sleep', fn1, 1, 2, 3);
event.on('sleep', fn2, 1, 2, 3);
event.fire('sleep', 4, 5, 6);
// I want sleep1 1 2 3 4 5 6
// I want sleep2 1 2 3 4 5 6
event.off('sleep', fn1);
event.once('sleep', () => console.log('I want sleep'));
event.fire('sleep');
//I want sleep2 1 2 3
//I want sleep
event.fire('sleep', 4, 5, 6);
//I want sleep2 1 2 3 4 5 6
测试代码:
class Events {
loop = {};
constructor() {}
on(...args) {
const [event_name, fn, ...rest] = args;
if (!this.loop[event_name]) {
this.loop[event_name] = [];
}
const nFun = fn.bind(null, ...rest);
nFun.old = fn;
this.loop[event_name].push(nFun);
}
off(event_name, fn) {
const list = this.loop[event_name];
const index = list.findIndex((v) => {
return v.old === fn;
});
if (index != -1) {
list.splice(index, 1);
}
}
once(...args) {
this.on(...args);
const event_name = args[0];
const list = this.loop[event_name];
list[list.length - 1].once = true;
}
fire(event_name, ...args) {
const list = this.loop[event_name] || [];
const once_list = [];
list.forEach((fn, index) => {
fn(...args);
if (fn.once) {
once_list.push(index);
}
});
if (once_list.length > 0) {
const result = [];
list.forEach((item, index) => {
if (!once_list.includes(index)) {
result.push(item);
}
});
this.loop[event_name] = result;
}
}
}
39.请编写函数requestHandler对请求方法进行封装,请求成功直接返回结果.10s内最多发起3次请求失败重试,如果3次都请求失败或者请求总时长超过10s抛出异常.
调用方式如下:
// 模拟请求的方法
function request(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const num = parseInt(Math.random() * (10 - 1 +1)) + 1;
const flag = num > 7 ? false:true;
if(!flag){
resolve(null);
}else{
reject(null)
}
},500)
})
}
function requestHandler(){
}
requestHandler().then(()=>{
console.log('请求成功');
}).catch((err)=>{
console.log(err);
});
测试代码:
function requestHandler(){
let count = 0;
function execuate(){
return new Promise((Rresolve,Rreject)=>{
(function req(){
count++;
if(count > 3){
Rreject("请求三次失败");
return;
}
request().then(()=>{
Rresolve();
}).catch(()=>{
req();
})
})();
})
}
return Promise.race([execuate(),new Promise((r,reject)=>{
setTimeout(()=>{
reject("TIME_OUT")
},10000)
})])
}
40.请模拟实现Promise.race
调用方式如下:
Promise._race([
new Promise((resolve)=>{
setTimeout(()=>{
resolve(1000)
},1000)
})
,new Promise((resolve)=>{
setTimeout(()=>{
resolve(500)
},500)
})
]).then((msg)=>{
console.log(msg); // 输出500
}).catch((err)=>{
console.log(err);
})
测试代码:
Promise._race = function(list){
return new Promise((resolve,reject)=>{
list.forEach((p)=>{
p.then((data)=>{
resolve(data);
}).catch((error)=>{
reject(error);
})
})
})
}
41.请编写函数compose依次执行完所有异步任务队列,要求前一个异步任务结束后才开始执行下一个异步任务.
调用方式如下:
const tasks = [
()=>(
new Promise((resolve)=>{
setTimeout(()=>{
console.log(1);
resolve(1);
},3000)
})
),
()=>(
new Promise((resolve)=>{
setTimeout(()=>{
console.log(2);
resolve(2);
},2000)
})
),
()=>(
new Promise((resolve)=>{
setTimeout(()=>{
console.log(3);
resolve(3);
},1000)
})
),
]
function compose(list){
}
compose(tasks).then((v)=>{
console.log(v)
}); // 依次打印 1 2 3 3
测试代码:
function compose(list){
return list.reduce((cur,next)=>{
return cur.then(()=>{return next()})
},Promise.resolve())
}