开发技巧
洗牌算法 shuffle
const arr = new Array(10).fill(0).map((v, i) => i + 1);
for (let i = 0; i < arr.length - 1; i++) {
[arr[i], arr[i + 1]] =
Math.random() > 0.5 ? [arr[i], arr[i + 1]] : [arr[i + 1], arr[i]];
}
console.log(arr);
防抖和节流
如果我们将可能引起事件多次触发的时间段称为高频时间,防抖器的作用是在高频时间后触发,只触发一次,例如 input 框改变后搜索,我们可能只希望用户结束输入时才请求,以此节约请求数量、所以它被称为节流器。(当然也不止是这一种场景,例如 window. Resize 我们可能更关注结束后窗口的大小,这里节约的是 js 引擎的执行能力)。防抖是在高频时间内一段时间间隔内只触发一次,降低事件的发生频率。例如直播时鼠标点赞的动画,用户可能一直点击,但是我们可不希望每次点击都触发动画。
var debounce = function (delay, cb) {
var timer;
return function () {
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
cb();
}, delay);
};
};
var throttle = function (delay, cb) {
var startTime = Date.now();
return function () {
var currTime = Date.now();
if (currTime - startTime > delay) {
cb();
startTime = currTime;
}
};
};
手写 API
手写 map
Array.protype.map = function(cb){
return this.reduce((pres,cur,index)=>{
pres.push(cb.call(null,cur,index,this))
return pres
},[])
}
手写 filter
Array.prototype.filter = function (cb) {
return this.reduce((pres, cur, index) => {
cb.call(null, cur, index, this) && pres.push(cur);
return pres;
}, []);
};
手写 every
Array.prototype.every = function (cb) {
let flag = false;
for (let i = 0; i < this.length; i++) {
if (this[i] === undefined) {
continue;
}
flag = cb.call(null, this[i], i, this);
if (!flag) {
break;
}
}
return flag;
};
写这仨应该就够了,其他的思路也差不多,注意一点是 every、some 不会对稀疏数组(sparse array )进行遍历,所以需要多加一个当前元素是否为 undefined
而且 reduce
不能停止,只能用循环语句写了。
手写 bind 函数
了解 this
和 new
操作符之后,我们可以写出如何实现一个 bind
函数
Function.prototype._bind = function (context, ...args) {
let fn = this;
if (typeof fn !== "function") {
throw new Error("must be called by a function");
}
return function newFn(...newArgs) {
if (this instanceof newFn) {
return new fn(...args, ...newArgs);
}
return fn.call(context, ...args, ...newArgs);
};
};
如高亮代码所示,重点在于判断是否是 构造函数调用
。判断是否为 构造函数调用
的代码 (参考《你不知道的 Javascript》)十分复杂晦涩,但是这么写属实是妙不可言。
数组能够改变自身的方法
pop
push
shift
unshift
sort
reverse
splice
数组方法中能够中断的
ForEach 不能终端循环,故可以使用 for 或者数组方法
- Every 有一个 return falsy 就会中断循环返回真假
- Some 有一个 return true 就会中断循环返回真假
- Find 找到就终端返回元素或者 undefined
- FindIndex 返回索引返回元素索引
类型
八个,Undefined
null
number
boolean
object
symbol
bigint
隐式类型转换
首先纠正一个误区,类型转换本身没有显式、隐式的区别。只要你足够有经验,没有隐式类型转换(《你不知道的 JS》)
const foo = {
// [Symbol.toPrimitive]() {
// return true
// },
// valueOf() {
// return false
// }
};
console.log(Boolean(foo));
console.log(Number(foo));
console.log(foo == true);
在我看来,比较常见的问题集中发生在 {} 空对象
和 [] 空数组
。上面以空对象为例,可以看到默认情况下,对于 Boolean 函数,参数为空对象时,返回结果是 true
;对于 Number 函数,参数是空对象时,返回结果是 NaN
(注意,不要说构造函数,Javascript 中没有构造函数的概念,只有 构造函数调用
,即使是 ES 6 中的 class
中的 constructor
也只是寄生组合继承的语法糖)
解开注释发现 valueOf
和 [Symbol.toPrimitive]
都能够影响到相等运算符的判断。后者优先级更高。如果忽视对 Number
函数造成的影响,可以用来解决经典的 [] == true
和 {} == true
问题
Object.prototype[Symbol.toPrimitive] = function () {
return false;
};
Array.prototype[Symbol.toPrimitive] = function () {
return false;
};
console.log({} == true); // false
console.log([] == true); // false