实现数组的乱序输出
主要的实现思路就是:
- 取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换。
- 第二次取出数据数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
- 按照上面的规律执行,直到遍历完成
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < arr.length; i++) {
const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(arr)
还有一方法就是倒序遍历:
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,
randomIndex,
temp;
while (length) {
randomIndex = Math.floor(Math.random() * length--);
temp = arr[length];
arr[length] = arr[randomIndex];
arr[randomIndex] = temp;
}
console.log(arr)
Array.prototype.filter()
Array.prototype.filter = function(callback, thisArg) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
const res = [];
// 让O成为回调函数的对象传递(强制转换对象)
const O = Object(this);
// >>>0 保证len为number,且为正整数
const len = O.length >>> 0;
for (let i = 0; i < len; i++) {
// 检查i是否在O的属性(会检查原型链)
if (i in O) {
// 回调函数调用传参
if (callback.call(thisArg, O[i], i, O)) {
res.push(O[i]);
}
}
}
return res;
}
模拟new
new操作符做了这些事:
- 它创建了一个全新的对象
- 它会被执行[[Prototype]](也就是__proto__)链接
- 它使this指向新创建的对象
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
// objectFactory(name, 'cxk', '18')
function objectFactory() {
const obj = new Object();
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return typeof ret === "object" ? ret : obj;
}
实现节流函数(throttle)
节流函数原理:指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的时间才会执行回调函数。总结起来就是: 事件,按照一段时间的间隔来进行触发 。
像dom的拖拽,如果用消抖的话,就会出现卡顿的感觉,因为只在停止的时候执行了一次,这个时候就应该用节流,在一定时间内多次执行,会流畅很多
手写简版
使用时间戳的节流函数会在第一次触发事件时立即执行,以后每过 wait 秒之后才执行一次,并且最后一次触发事件不会被执行
时间戳方式:
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(
throttle(() => {
console.log(1)
}, 500),
1
)
定时器方式:
使用定时器的节流函数在第一次触发时不会执行,而是在 delay 秒之后才执行,当最后一次停止触发后,还会再执行一次函数
function throttle(func, delay){
var timer = null;
returnfunction(){
var context = this;
var args = arguments;
if(!timer){
timer = setTimeout(function(){
func.apply(context, args);
timer = null;
},delay);
}
}
}
适用场景:
DOM
元素的拖拽功能实现(mousemove
)- 搜索联想(
keyup
) - 计算鼠标移动的距离(
mousemove
) Canvas
模拟画板功能(mousemove
)- 监听滚动事件判断是否到页面底部自动加载更多
- 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
- 缩放场景:监控浏览器
resize
- 动画场景:避免短时间内多次触发动画引起性能问题
总结
- 函数防抖 :将几次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
- 函数节流 :使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
实现非负大整数相加
JavaScript对数值有范围的限制,限制如下:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_VALUE // 5e-324
Number.MIN_SAFE_INTEGER // -9007199254740991
如果想要对一个超大的整数(> Number.MAX_SAFE_INTEGER
)进行加法运算,但是又想输出一般形式,那么使用 + 是无法达到的,一旦数字超过 Number.MAX_SAFE_INTEGER
数字会被立即转换为科学计数法,并且数字精度相比以前将会有误差。
实现一个算法进行大数的相加:
function sumBigNumber(a, b) {
let res = '';
let temp = 0;
a = a.split('');
b = b.split('');
while (a.length || b.length || temp) {
temp += ~~a.pop() + ~~b.pop();
res = (temp % 10) + res;
temp = temp > 9
}
return res.replace(/^0+/, '');
}
其主要的思路如下:
- 首先用字符串的方式来保存大数,这样数字在数学表示上就不会发生变化
- 初始化res,temp来保存中间的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
- 将两个数组的对应的位进行相加,两个数相加的结果可能大于10,所以可能要仅为,对10进行取余操作,将结果保存在当前位
- 判断当前位是否大于9,也就是是否会进位,若是则将temp赋值为true,因为在加法运算中,true会自动隐式转化为1,以便于下一次相加
- 重复上述操作,直至计算结束
转化为驼峰命名
var s1 = "get-element-by-id"
// 转化为 getElementById
var f = function(s) {
return s.replace(/-\w/g, function(x) {
return x.slice(1).toUpperCase(