用reduce实现map
Promise和setTimeout综合输出顺序问题,宏事件微事件考察。
- 浏览器运行机制详解
- 重点在于:在执行异步队列时,执行完一个宏任务 ==> 执行完所有微任务 ===> 执行下一个宏任务,即优先执行微任务。
手写一个双向绑定,input输入的值响应在p标签上
实现左右布局,输入框自适应,按钮定宽的样式
<style>
div {
display: flex;
}
input {
flex: 1;
}
button{
width: 100px;
}
</style>
<div>
<input type="text" />
<button>提交</button>
</div>
伪数组转换为数组的方法
let fakeArr = {
0: 2,
1: 3,
2: 4,
3: 4,
length: 4
}
console.log(Array.prototype.slice.call(fakeArr, 0))
console.log(Array.from(fakeArr))
抽取数组中n个数字,其和为sum的函数
一个元素,先transform:translate(100px,100px),再rotate(45deg)的效果,以及两个操作颠倒执行后的效果
rotate会导致元素的X、Y轴旋转
响应码的意义
判断输出
function A(params) {
this.c = 1;
}
let a = new A();
A.prototype = {
c: 1,
d: 2
};
console.log(a.c);
console.log(a.d);
a始终指向A之前无数据的原型a实例本身包含属性c- 所以输出
1 undefined
手写一个360旋转的动画
/* animation-name 规定需要绑定到选择器的 keyframe 名称。。
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function 规定动画的速度曲线。
animation-delay 规定在动画开始之前的延迟。
animation-iteration-count 规定动画应该播放的次数。
animation-direction 规定是否应该轮流反向播放动画。*/
@keyframes circle {
100% {
transform: rotate(360deg);
}
}
.box1 {
animation: circle 3s linear 0s infinite;
}
keydown keypress keyup的区别
key相关事件发生顺序:onkeydown onkeypress onkeyup,只有onkeyup能够获取输入框完整的输入。
输入查询时, 两次的ajax都会修改dom, 如何解决
==和===的比较规则
- 双等号
==:
- 如果两个值类型相同,再进行三个等号(===)的比较
- 如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
- 如果一个是null,一个是undefined,那么相等
- 如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较
- 三等号
===:
- 如果类型不同,就一定不相等
- 如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
- 如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
- 如果两个值都是true,或是false,那么相等
- 如果两个值都引用同一个对象或是函数,那么相等,否则不相等
- 如果两个值都是null,或是undefined,那么相等
rem的使用
react如何绑定事件处理函数
sum(1)(2)(3) sum(1, 2, 3)的实现
function add(...args) {
const totalArgs = [...args]
// 每次调用函数,都是往参数数组里添加参数,然后再返回当前函数
const result = function (...params) {
totalArgs.push(...params)
return result
}
// RHS引用的时候,把参数数组求和返回
result.toString = () => {
return totalArgs.reduce((p, n) => {
return p + n
})
}
return result
}
console.log(add(1, 2, 3))
console.log(add(1)(2, 3))
console.log(add(1)(2)(3))
防抖节流
从实现的效果来解释(延时2s为例),可能更容易理解和记忆:
- 防抖: 如果没到时限,就清了计时器,重新倒计时。最后一次触发才会会执行操作
- 节流: 如果没到时限,本次触发无效。每两秒执行一次操作
为了代码框架更清晰,没有实现函数执行上下文和参数等细节
防抖
const debounce = (fn) => {
let timer
return () => {
// 如果没到时限,就清了计时器,重新倒计时
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
timer = null
}, 2000)
}
}
节流
1. 定时器版
第一次触发也需要等2s后执行
const throttle = (fn) => {
let timer
return () => {
// 如果没到时限,本次触发无效
if (timer) return
timer = setTimeout(() => {
fn()
timer = null
}, 2000)
}
}
2. 时间戳版
第一次触发立即执行(需页面加载和触发时间超过2s)
const throttle1 = (fn) => {
let previos = Date.now()
return () => {
// 如果没到时限,本次触发无效
if (Date.now() - previos > 2000) return
// 如果触发有效,记住本次触发的时间
previos = Date.now()
fn()
}
}
3. 组合版本
组合版本可以保留最后一次的触发操作
const throttle2 = (fn) => {
let previos = Date.now()
let timer
return () => {
clearTimeout(timer)
// 如果超过了时限,直接执行,
if (Date.now() - previos > 2000) {
previos = Date.now()
fn()
} else {
// 如果没超过时限,定倒计时
timer = setTimeout(() => {
// !!连续触发的最后一次,才会执行到这里。因为只有最后一次的定时器没有被clear。!!
fn()
timer = null
previos = Date.now()
}, 2000)
}
}
}
手写promise
class Promise1 {
constructor(start) {
this.status = 'pending'
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
this.successCBList = []
this.errCBList = []
this.value
this.reason
// 初始化promise
start(this._resolve, this._reject)
}
// 异步任务完成时,会被调用,这里就是异步任务完成的时候
_resolve(data) {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.value = data
this.successCBList.map(CB => {
this.value = CB(this.value)
})
}
}
_reject(err) {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = err
this.errCBList.map(CB => {
CB(this.reason)
})
}
}
then(successCB, errCB) {
// then函数返回一个promise,所以才可以链式调用then
const p2 = new Promise1((resolve, reject) => {
if (this.status === 'fulfilled') {
// 如果状态未为成功,那么就执行then方法的成功回调
const x = successCB(this.value)
// 并处理成功回调的返回值,为链式调用做准备
resolvePromise(x, resolve, reject);
}
if (this.status === 'rejected') {
// 如果状态为失败,那么就执行then方法的失败回调
const x = errCB(this.reason)
// 并处理成功回调的返回值,为链式调用做准备
resolvePromise(x, resolve, reject);
}
// 如果状态为pending,那就把 成功和失败的回调 处理后保存起来,等待状态变化后调用
if (this.status === 'pending') {
this.successCBList.push(() => {
const x = successCB(this.value)
resolvePromise(x, resolve, reject);
})
this.errCBList.push(() => {
const x = errCB(this.reason)
resolvePromise(x, resolve, reject);
})
}
})
return p2
}
catch(CB) {
this.errorCB = CB
}
}
// 解析then的返回值
function resolvePromise(x, resolve, reject) {
// then的返回值x,如果为promise,那么就把promise执行了,然后把结果传给p2的resolve
// 那么链式调用的下一个then,就可以获取到上一个then的结果了
if(x instanceof Promise1) {
x.then(res =>{
// then函数中有可能嵌套返回promise
resolvePromise(res, resolve, reject)
},err =>{
reject(err)
})
}else{
resolve(x)
}
}
// 我们没办法知道一个异步任务何时完成,所以我们需要规定在任务完成的时候必须执行一个回调函数,它就是resolve/reject。
// 这样我们就可以在回调函数里(源码里的_resolve、_reject),改变promise的状态,并且执行then/catch的callback
const p1 = new Promise1((resolve, reject) => {
// setTimeout(() => {
resolve(123)
// }, 1000)
})
p1.then(data => {
console.log(data);
}).then(() => {
console.log(123);
return new Promise1((resolve, reject) => {
setTimeout(() => {
resolve(new Promise1((resolve, reject) => {
setTimeout(() => {
resolve(456)
}, 1000)
}))
}, 1000)
})
}).then(data => {
console.log(data);
})
并发限制
var urls = [
'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg',
'https://www.kkkk1000.com/images/getImgData/gray.gif',
'https://www.kkkk1000.com/images/getImgData/Particle.gif',
'https://www.kkkk1000.com/images/getImgData/arithmetic.png',
'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif',
'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg',
'https://www.kkkk1000.com/images/getImgData/arithmetic.gif',
'https://www.kkkk1000.com/images/wxQrCode2.png'
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function() {
console.log('一张图片加载完成');
resolve();
}
img.onerror = reject
img.src = url
})
};
实现一个异步串行加载,然后执行三次就好了,相当于有三条队列来加载图片
function limitFn(limit) {
// 这相当于一个队列,来异步串行加载图片,也就是一个加载完了再加载另一个
let load = () => {
if (urls.length === 0) return
let url = urls.shift()
loadImg(url).then(load)
}
// 开启多条队列,就相当于把并发数量控制为limit
for (let i = 0; i < limit; i++) {
load()
}
}
limitFn(2)
让函数fn每间隔delay秒执行一次,执行times次
实现repeat(fn, times, delay)函数
1. 使用for await实现异步串行
function delay(func, times, delay) {
return async function(content) {
for (var i = 0; i < times; i++) {
await new Promise(resolve => {
setTimeout(() => {
func.call(this, content);
resolve(true);
}, delay);
});
}
};
}
delay(console.log, 3, 4000)(123);
2. 使用promise实现异步串行
function delay(fn, times, delay) {
return async function(content) {
let p = Promise.resolve()
let p1 = () => new Promise((resolve => {
setTimeout(() => {
fn(content)
resolve(true)
}, delay)
}))
for (var i = 0; i < times; i++) {
p = p.then(() => p1())
}
};
}
delay(console.log, 3, 4000)(123);