1.防抖和节流
- 防抖
function debounce(handler, delay) {
var time;
delay = delay || 1000
return function(){
var _args = arguments
var _self = this
time && clearTimeout(time)
time = setTimeout(() => {
handler.call(_self, _args)
}, delay)
}
}
- 节流
function throttle(handler, delay) {
var lastTime = Date.now()
return function () {
var _args = arguments
var _self = this
var curTime = Date.now()
console.log('curTime - lastTime=', curTime - lastTime)
if (curTime - lastTime >= delay) {
handler.apply(_self, _args)
lastTime = Date.now()
}
}
}
- 节流升级版(时间间隔不够的时候,最后总会执行一次)
function throttle(handler, delay) {
var lastTime = Date.now()
var id;
return function(){
var _args = arguments
var _self = this
var curTime = Date.now()
console.log('curTime - lastTime=',curTime - lastTime)
if (curTime - lastTime >= delay) {
if (id){
clearTimeout(id);
}
handler.apply(_self, _args)
lastTime = Date.now()
} else {
id && clearTimeout(id);//把上一次的定时器清除
id = setTimeout(() => {
handler.apply(_self, _args)
}, 2000);
}
}
}
2.用reduce实现map函数
Array.prototype.reduceMap = function(cb){
return this.reduce((prev, cur, index, this)=>{
prev.push(cb(cur), index, this);
return prev;
},[])
}
3.实现一个retry函数
// https://blog.csdn.net/qq_40420294/article/details/101920789
function retry(cb, times, delay) {
return new Promise((resolve, reject) => {
function attempt(){
cb().then(resolve).catch(error => {
console.log('还有'+times+'次尝试');
if (times == 0) {
reject(error)
} else {
times--;
setTimeout(() => {
attempt()
}, delay);
}
})
}
attempt();
})
}
function getData(){
let p = new Promise((resolve, reject) => {
setTimeout(() => {
let num = Math.ceil(Math.random()*20) // 生成 1-20 的随机数
console.log('random num: ', num);
if (num <= 10) {
console.log('符合条件,值为: ', num);
resolve(num);
} else {
reject('数字大于10, 执行失败')
}
}, 2000);
})
return p;
}
retry(getData, 5, 500);
4.用async await实现一个中间件,计算函数执行时间
async function middle(ctx, next) {
const start = Date.now();
await next();
const end = Date.now();
console.log(`total seconds: ${end-start}`);
}
5.深拷贝
function deepClone(obj) {
let types = new Set(['boolean', 'string', 'number', 'undefined']);
let _type = typeof obj;
if (_type in types || obj === null) {
return obj;
}
let objClone = Array.isArray(obj) ? [] : {};
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
objClone[key] = (typeof(obj[key]) === 'object') ? deepClone(obj[key]) : obj[key]
}
}
return objClone;
}
let a = {
name: 'lucy',
others: {
title: 'student',
level: 666
}
}
let b = deepClone(a);
a.others.title = 'teacher';
console.log(b.others.title) // student
6.手写promise.all
Promise._all = function(list) {
return new Promise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
let p1 = new Promise((resolve, reject) => {
resolve('p1')
})
let p2 = new Promise((resolve, reject) => {
resolve('p2')
})
let p = Promise._all([p1, p2])
p.then(res => {
console.log(res)
})
// ["p1", "p2"]
7.手写promise.race
Promise.race = promises => new Promise((resolve, reject) => {
promise.forEach(promise => {
promise.then(resolve, reject)
})
})
8.计算斐波那契数列(递归、动规、闭包)
- 递归版
function fib(n) {
if (n == 0) return 0;
if (n && n <= 2) return 1;
return fib(n-1) + fib(n-2)
}
- 动规版
function fib(n) {
if (n <= 1) return 1;
let dp = [0, 1, 1]
for(let i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
- 闭包版
var fib = (function(){
let arr = [0, 1, 1];
return function(n){
if (arr[n]) return arr[n];
else {
arr[n] = fib(n-1) + fib(n-2);
return arr[n];
}
}
})()
9.斐波那契数列中第n项由多少个第一项和第二项组成
// 统计当前数字有多少个第一项,多少个第二项
// 1, 1, 2, 3, 5
function fibCount(n) {
if (n < 0) return -1;
let dp1 = [1, 0], dp2 = [0, 1];
for(let i = 2; i <= n; i++) {
dp1[i] = dp1[i-1] + dp1[i-2];
dp2[i] = dp2[i-1] + dp2[i-2];
}
return [dp1[n], dp2[n]]
}
10.js怎么实现一个私有方法(电信云一面)
function Person(name, age){
this.name = name;
this.age = age;
var _private_variable = ""; // 定义私有变量
// 定义私有方法
function privateMethod(){
_private_variable = "inner";
console.log(_private_variable);
}
privateMethod(); //调用私有方法
}
11.ajax请求过程
/**
* 1.创建XMLHttpRequest对象
* 2.创建http请求,指名请求方法、地址、异步参数(true:异步,false: 同步)
* 3.发送请求
* 4.获取数据 onreadystatechange
*/
/**
* ajax跨域:https://segmentfault.com/a/1190000012469713
* CORS, 反向代理
*/
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', 'example.php',true);
xmlHttp.send();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
document.getElementById('app').innerHTML = xmlHttp.responseText;
}
}
/**
* post请求一定要设置请求头的格式内容
* send方法在post请求时才使用字符串参数,否则不用带参数
*/
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('POST', 'example.html',true);
xmlHttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
xmlHttp.send('name=lucy&from=china');
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
document.getElementById('app').innerHTML = xmlHttp.responseText;
}
}
// xmlhttp.readyState一共有5种请求状态,从0到4发送变化:
// 0:请求未初始化;
// 1:服务器连接已建立;
// 2:请求已接收;
// 3:请求处理中;
// 4:请求已完成,且响应已就绪
// xmlhttp.status响应状态码,比较常用的有:
// 200:请求成功;
// 304:该资源在上次请求之后没有任何修改(这通常是浏览器的缓存机制,使用GET请求时尤其需要注意);
// 403:(禁止)服务器拒绝请求;
// 404:(未找到)服务器找不到请求的网页;
// 408:(请求超时)服务器等候请求时发生超时;
// 500:(服务器内部错误)服务器遇到错误,无法完成请求。
// 原文链接:https://blog.csdn.net/yangwei234/article/details/84536079
12.快速排序
function quicksort(arr) {
_quicksort_helper(arr, 0, arr.length - 1);
}
function _quicksort_helper(arr, left, right) {
if (left < right) {
var mid = _partition(arr, left, right);
_quicksort_helper(arr, left, mid-1);
_quicksort_helper(arr, mid+1,right);
}
}
function _partition(arr, left, right) {
var pivot = arr[left];
var l = left, r = right;
while(l < r) {
while (r > l && arr[r] >= pivot) --r;
arr[l] = arr[r];
while (l < r && arr[l] <= pivot) ++l;
arr[r] = arr[l];
}
arr[l] = pivot;
return l;
}
var arr = [45,24,45,89,52,31,17];
quicksort(arr);
console.log(arr);
// [17, 24, 31, 45, 45, 52, 89]
13.发布-订阅模式(★★★★★)
太常见了,一定要很熟悉
class Event {
constructor(){}
handlers = {};
addEventListener(type, handler) {
if (!(type in this.handlers)) {
this.handlers[type] = [];
}
this.handlers[type].push(handler);
}
dispatchEvent(type, ...args) {
if (!(type in this.handlers)) {
return new Error('not registered!!!')
}
this.handlers[type].forEach(handler => {
handler(...args);
});
}
removeEventListener(type, handler) {
if (!(type in this.handlers)) {
return new Error('invalid event!!!')
}
if (!handler) {
delete this.handlers[type]
} else {
const idx = this.handlers[type].findIndex(ele => ele === handler);
if (idx === -1) {
return new Error('invalid event!!!')
}
this.handlers[type].splice(idx, 1);
if (this.handlers[type].length === 0) {
delete this.handlers[type];
}
}
}
}
var event = new Event();
function load(){
console.log('load: ',...arguments);
}
function load2(){
console.log('load2: ',...arguments);
}
event.addEventListener('load', load)
event.addEventListener('load', load2)
event.dispatchEvent('load', 'hello, world');
event.removeEventListener('load', load);
event.dispatchEvent('load', 'hello, world');
补充: 观察者模式
class Subject{
constructor(){
this.state = 0;
this.observers = [];
}
getState(){
return this.state;
}
setState(value){
this.state = value;
this.notifyAllOvservers();
}
attach(observer){
this.observers.push(observer);
}
notifyAllOvservers(){
this.observers.forEach(observer => {
observer.update();
})
}
}
class Observer{
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update(){
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}
let s = new Subject();
let o1 = new Observer('o1', s);
let o2 = new Observer('o2', s);
let o3 = new Observer('o3', s);
s.setState(1);
s.setState(2);
14.给一个DOM结构,实现getElementById方法
function _getElementById(dom, id) {
let res = [];
helper(dom, id, res);
return res[0];
}
function helper(dom, id, res){
if (dom.getAttribute('id') == id) {
res.push(dom);
}
if (dom.children) {
for(let i = 0; i < dom.children.length; i++) {
helper(dom.children[i], id, res);
}
}
}
15.输出DOM结构
// 输出DOM结构
var str = '';
var el = document.documentElement;
var empty;
var level = 0;
function output(el, level) {
if (el) {
if (level > 0) {
empty = new Array(level).fill(' ');
str += empty.join('');
}
str += el.tagName;
str += '\n'; // 换行
}
if (el.children) {
for(let i = 0; i < el.children.length; i++) {
output(el.children[i], level+1);
}
}
}
output(el, level);
console.log(str)
16.实现一个红绿灯效果
const timeout = (time) => {
return new Promise(resolve => setTimeout(resolve, time))
}
var el = document.querySelector('body')
function change(){
timeout(2000).then(() => {
el.style.backgroundColor = 'red';
return timeout(3000)
}).then(() => {
el.style.backgroundColor = 'yellow';
return timeout(1000)
}).then(() => {
el.style.backgroundColor = 'green';
change()
})
}
change();
18.实现一个限流器
// https://blog.csdn.net/zz_jesse/article/details/107293743?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-5-107293743.nonecase&utm_term=js%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%B8%A6%E5%B9%B6%E5%8F%91%E9%99%90%E5%88%B6%E7%9A%84%E5%BC%82%E6%AD%A5%E8%B0%83%E5%BA%A6%E5%99%A8
class Scheduler {
// TODO
list = [];
constructor(){}
add(promiseCreator) {
this.list.push(promiseCreator)
}
max = 2;
startIndex = 0;
enter(){
for(var i = 0; i < this.max; i++){
this.run();
}
}
run(){
if (this.list.length == 0 || this.startIndex >= this.max) return;
this.startIndex++;
this.list.shift()().then(() => {
this.startIndex--;
this.enter();
})
}
}
const scheduler = new Scheduler();
const timeout = time => {
return new Promise(resolve => setTimeout(resolve, time))
}
const addTask = (time, order) => {
scheduler.add(() => timeout(time).then(() => console.log(order)))
}
addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
scheduler.enter();
// 2 3 1 4
19.实现一个sleep函数
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('sleep...')
resolve()
}, ms);
})
}
async function test(){
console.log('1');
await sleep(400)
console.log('2')
}
test();
20.实现ES6中的map函数
Array.prototype._map = function(cb) {
if (Array.isArray(this)) {
return this.reduce((prev, cur, index, arr) => {
prev.push(cb(cur, index, arr))
return prev;
}, []);
} else {
throw new Error('不是数组!')
}
}
let arr = [1,2,3,4];
let res = arr._map((ele, index, num) => {
console.log(index, num)
return ele * 2;
})
console.log(res)
0 [ 1, 2, 3, 4 ]
1 [ 1, 2, 3, 4 ]
2 [ 1, 2, 3, 4 ]
3 [ 1, 2, 3, 4 ]
[ 2, 4, 6, 8 ]