§ 实现一个函数 test word 转成 Test Word
const str1 = "test word";
const strTo1 = (str1) => {
return str1
.toLowerCase()
.replace(/( |^)[a-z]/g, (item) => item.toUpperCase());
};
strTo1(str1); // Test Word
§ 实现一个函数 font-size 转成 fontSize
// 2.实现一个函数 font-size 转成 fontSize
const str2 = "font-size";
const strTo2 = (str2) => {
let reg = /-(\w)/g;
str2 = str2.replace(reg, ($0, $1) => {
return $1.toUpperCase();
});
return str2;
};
strTo2(str2); // fontSize
§ 实现一个函数 输入fooBarTest,输出foo-bar-tes
function tuoTo(targetString) {
return targetString.replace(/([A-Z])/g, function (match) {
return "-" + match.toLowerCase();
});
}
console.log(tuoTo("fooBarTest")); // foo-bar-tes
§ 封装一个方法,实现金额格式化,输入10000000000,输出10,000,000,000
const money = '10000000000';
const result = money.replace(/(?=\B(\d{3})+$)/g, ",");
console.log(result); // 10,000,000,000
§ 封装一个chunk方法,传参chunk(['a', 'b', 'c', 'd'], 2)实现[['a', 'b'], ['c', 'd']],chunk(['a', 'b', 'c', 'd'], 3)实现[['a', 'b', 'c'], ['d']]
function chunk(arr, num) {
let len = arr.length;
if (!len || !num || num < 1) {
return [];
}
let index = 0;
let resIndex = 0;
let result = new Array(Math.ceil(len / num));
while (index < len) {
result[resIndex++] = arr.slice(index, (index += num));
}
return result;
}
console.log(chunk(["a", "b", "c", "d"], 2)); // [['a', 'b'], ['c', 'd']]
console.log(chunk(["a", "b", "c", "d"], 3)); // [['a', 'b', 'c'], ['d']]
§ 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标
const twoSum = (nums, target) => {
let arr = [];
for (let i = 0; i < nums.length; i++) {
let num = target - nums[i];
let index = nums.lastIndexOf(num);
if (index != i && index != -1) {
arr.push(i, index);
return arr;
}
}
};
twoSum([2, 7, 11, 15], 9); // [0,1]
§ 判断当前data是否为数组
const arr = []
Array.isArray(arr) // true
Object.prototype.toString.call(arr) === '[object Array]' // true
arr.constructor === Array // true
arr instanceof Array // true
§ 判断当前data是否为对象
let obj = {}
Object.prototype.toString.call(obj) === '[Object Object]'
obj.constructor === Object
Object.getPrototypeOf(obj) === Object.prototype
§ 如何判断当前对象是否为空
推荐使用:Reflect.ownKeys()
// 我们写几种常见的方法
function isEmpty01(obj) {
return JSON.stringify(obj) === '{}'
}
function isEmpty02(obj) {
return Object.keys(obj).length === 0
}
function isEmpty03(obj) {
return Object.getOwnPropertyNames(obj).length === 0
}
function isEmpty04(obj) {
let flag = true
for(let key in obj){
if(key){
flag = false
break
}
}
return flag
}
// 测试
let obj = {
a: 123
}
console.log(isEmpty01(obj)) // false
console.log(isEmpty02(obj)) // false
console.log(isEmpty03(obj)) // false
console.log(isEmpty04(obj)) // false
但是有一个问题,如果对象中是Symbol类型的值,就判断不出来,所以推荐用 Reflect.ownKeys()
const key = Symbol('123')
let obj = {
[key]: 1
}
const isEmptyOfObj = (obj) => {
return Reflect.ownKeys(obj).length === 0
}
isEmptyOfObj(obj) // false
§ 判断当前对象中是否存在某个属性
// in
// in 方法有个缺点,如果属性来自对象的原型,它仍然会返回 true
const obj = {name:"aaa",age:"bbb"}
'age' in obj // true
// Reflect.has
Reflect.has(obj, 'age') // true
// obj.hasOwnProperty
obj.hasOwnProperty('age') // true
// Object.prototype.hasOwnProperty.call
Object.prototype.hasOwnProperty.call(obj, 'age') // true
// Object.hasOwn
Object.hasOwn(obj, 'age') // true
§ 如何判断当前是否为NaN
NaN(Not a Number)有一个非常特殊的特性,NaN不等于其本身,也不等于任何
isNaN:先尝试转换为数字,如果隐式转换为Number类型失败,就会返回NaN
Numnber.isNaN():
NaN == NaN // false
NaN === NaN // false
需要注意的是:NaN与任何值相加,结果都等于NaN
console.log(NaN + 1) // NaN
console.log(NaN + null) // NaN
console.log(NaN + undefined) // NaN
通过NaN的特性,我们封装一个方法:
const is_NaN = (x) => {
return x != x
}
// 测试
is_NaN(NaN) // true
is_NaN('a' - 100) // true
is_NaN('a100') // true
is_NaN('a' + 100) // true
is_NaN(100) // false
is_NaN('100') // false
还可以使用es6中的isNaN()来判断
isNaN(NaN) // true
isNaN('a' - 100) // true
isNaN('a100') // true
isNaN('a' + 100) // true
isNaN(100) // false
isNaN('100') // false
当一个表达式中有减号,乘号,除号运算符的时候,JS引擎在计算之前会试图将表达式的每一项都转化为Number类型,如果转换失败,就会返回NaN
100 - 'abc' // NaN
'abc' * 100 // NaN
'abc' / 100 // NaN
Number('abc') // NaN
+'100abc' // NaN
§ 求数组交集,并集,差集
const arr1 = [4,9,5,9]
const arr2 = [9,4,9,8,4]
// 交集
const res1 = Array.from(new Set(arr1.filter(item => new Set(arr2).has(item)))) // [4, 9]
// 并集
const res2 = Array.from(new Set(arr1.concat(arr2))) // [4, 9, 5, 8]
// 差集
const res3 = Array.from(new Set(arr1.concat(arr2).filter(
item => !new Set(arr1).has(item) || !new Set(arr2).has(item)
))) // [5, 8]
更简单的写法:
const arr1 = [4,9,5,9]
const arr2 = [9,4,9,8,4]
// 并集
const union = [...new Set([...arr1,...arr2])] // [4, 9, 5, 8]
// 交集
const cross = [...new Set(arr1.filter((item) => arr2.includes(item)))] // [4, 9]
// 差集
const diff = union.filter((item) => !cross.includes(item)) // [5, 8]
§ 封装一个返回data数据类型的方法
const Type = {}
for(let i = 0,type;type = ['String','Number','Boolean','Null','Undefined','Array','Object'][i++];){
((type) => {
Type['is' + type] = (obj) => {
return Object.prototype.toString.call(obj) === `[object ${type}]`
}
})(type)
}
console.log(Type.isString('hello'))
§ 数组合并去重
let arr01 = [1, 2, 3, 4, 5, 5, 6, 6, 7];
let arr02 = [2, 3, 4, 4, 5, 6, 8, 8, 9, 10, 11];
const mergeArr = (arr01, arr02) => {
let i = 0,
j = 0;
let res = [];
while (i < arr01.length && j < arr02.length) {
if (arr01[i] < arr02[j]) {
if (res.length === 0 || res[res.length - 1] !== arr01[i]) {
res.push(arr01[i]);
}
i++;
} else {
if (res.length === 0 || res[res.length - 1] !== arr02[j]) {
res.push(arr02[j]);
}
j++;
}
}
while (i < arr01.length) {
if (res.length === 0 || res[res.length - 1] !== arr01[i]) {
res.push(arr01[i]);
}
i++;
}
while (j < arr02.length) {
if (res.length === 0 || res[res.length - 1] !== arr02[j]) {
res.push(arr02[j]);
}
j++;
}
return res;
};
mergeArr(arr01, arr02); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
let arr001 = [1, 2, 3, 4, 5, 5, 6, 6, 7];
let arr002 = [2, 3, 4, 4, 5, 6, 8, 8, 9, 10, 11];
let arrCon = arr001.concat(arr002)
let arrRes = new Set(arrCon)
console.log(arrRes)
§ 数组对象合并去重
// new Map
let arr = [
{ id: 1, name: "AAAA" },
{ id: 2, name: "BBBB" },
];
let arr1 = [
{ id: 1, name: "AAAA" },
{ id: 3, name: "CCCC" },
];
let arrs = [...arr, ...arr1];
let map = new Map();
for (let item of arrs) {
if (!map.has(item.id)) {
map.set(item.id, item);
}
}
let newArr = [...map.values()];
§ 对象合并去重
let obj01 = [
{ id: 1, name: "AAAA" },
{ id: 2, name: "BBBB" },
];
let obj02 = [
{ id: 1, name: "AAAA" },
{ id: 3, name: "CCCC" },
];
const objMerge = (obj01, obj02) => {
let arrs = [...obj01, ...obj02];
let map = new Map();
for (let item of arrs) {
if (!map.has(item.id)) {
map.set(item.id, item);
}
}
return [...map.values()];
};
objMerge(obj01, obj02);
/*
[
{"id": 1,"name": "AAAA"},
{"id": 2,"name": "BBBB"},
{"id": 3,"name": "CCCC"}
]
*/
§ 数组合并
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
// 1
arr1.concat(arr2);
// 2
for (let i in arr2) {
arr1.push(arr2[i]);
}
// 3
arr1.push.apply(arr1, arr2);
// 4
let newArr = [];
newArr = [...arr1, ...arr2];
§ 数组去重
let arrList = [ 1, 2, 2, "abc", "abc", true, true, false, false, undefined, undefined, NaN, NaN ];
// 1.利用Set()+Array.from()
const removeArr = (arr) => {
return Array.from(new Set(arr));
};
removeArr(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
// 2.利用数组的indexOf方法
const removeArr2 = (arr) => {
let newArr = [];
arr.map((item) => {
if (newArr.indexOf(item) === -1) {
newArr.push(item);
}
});
return newArr;
};
removeArr2(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
// 3.利用数组的includes方法
const removeArr3 = (arr) => {
let newArr = [];
arr.map((item) => {
if (!newArr.includes(item)) {
newArr.push(item);
}
});
return newArr;
};
removeArr3(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
// 4.利用map()
const removeArr4 = (arr) => {
let map = new Map();
let newArr = [];
arr.forEach((item) => {
if (!map.has(item)) {
map.set(item, true);
newArr.push(item);
}
});
return newArr;
};
removeArr4(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
// 5.利用对象
const removeArr5 = (arr) => {
let obj = {};
arr.map((item) => {
if (!obj[item]) {
obj[item] = true;
}
});
return Object.keys(obj);
};
removeArr5(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
// 6.利用双层循环+是金子的splice方法
const removeArr6 = (arr) => {
let len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
len--;
}
}
}
return arr;
};
removeArr6(arrList); // [1, 2, 'abc', true, false, undefined, NaN]
§ 数组去重,两个属性相同的对象也认为是重复的
字节面试题
function uniqueArray(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
const item1 = arr[i];
let isFind = false;
for (let j = 0; i < result.length; j++) {
const item2 = result[j];
if (equals(item1, item2)) {
isFind = true;
break;
}
}
if (!isFind) {
result.push(item1);
}
}
return result;
}
// 判断是否为原始值
function isPrimitive(value) {
return value === null || !["object", "function"].includes(typeof value);
}
// 判断两个值是否一样
function equals(value1, value2) {
if (isPrimitive(value1) || isPrimitive(value2)) {
return Object.is(value1, value2);
}
const entries1 = Object.entries(value1);
const entries2 = Object.entries(value2);
if (entries1.length !== entries2.length) {
return false;
}
for (const [key, value] of entries1) {
if (!equals(value, value2[key])) {
return false;
}
}
return true;
}
// 测试
console.log(equals({ a: 1, b: 2 }, { b: 2, a: 1 })); // true
console.log(equals({ a: 1, b: 2, c: 3 }, { b: 2, a: 1 })); // false
§ 对象去重
let resources = [
{ name: "AAA", age: "18" },
{ name: "AAA", age: "18" },
{ name: "BBB", age: "20" },
{ name: "AAA", age: "18" },
{ name: "CCC", age: "19" },
{ name: "CCC", age: "19" },
];
const resourcesFn = (resources) => {
let temp = {};
resources = resources.reduce((prev, curv) => {
if (temp[curv.name]) {
} else {
temp[curv.name] = true;
prev.push(curv);
}
return prev;
}, []);
return resources;
};
resourcesFn(resources);
/*
[
{"name": "AAA", "age": "18"},
{"name": "BBB", "age": "20"},
{"name": "CCC", "age": "19"}
]
*/
§ 数组排序
let arrSort = [32, 34, 1, 6, 45, 54, 3, 7, 5, 4];
// 1.sort快速排序
arrSort.sort((a, b) => a - b); // [1, 3, 4, 5, 6, 7, 32, 34, 45, 54]
arrSort.sort((a, b) => b - a); // [54, 45, 34, 32, 7, 6, 5, 4, 3, 1]
// 冒泡排序
const sortArr = (arr) => {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[j] > arr[j + 1]) {
const ele = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = ele;
}
}
}
};
sortArr(arrSort);
console.log(arrSort); // [1, 3, 4, 5, 6, 7, 32, 34, 45, 54]
// 插入排序
const insertSort = (arr) => {
let newArr = [arr[0]];
for (let i = 1; i < arr.length; i++) {
newArr.push(arr[i]);
for (let j = 0; j < newArr.length - 1; j++) {
if (newArr[newArr.length - j - 1] < newArr[newArr.length - j - 2]) {
let arrItem = newArr[newArr.length - j - 2];
newArr[newArr.length - j - 2] = newArr[mewArr.length - j - 1];
newArr[newArr.length - h - 1] = arrItem;
}
}
}
return newArr;
};
§ 对象排序
let objSort = [
{ name: "lk", age: 23 },
{ name: "zy", age: 15 },
{ name: "ij", age: 27 },
{ name: "er", age: 13 },
];
objSort.sort((a, b) => {
return a.age - b.age;
});
console.log(objSort);
// 根据name排序
objSort.sort((a, b) => {
if (a.name > b.name) {
return 1;
} else {
return -1;
}
});
console.log(objSort);
§ 数组扁平化
const array = [1, { a: 2 }, [3, 4, [5, 6], 7], 8, 9]
const aaa = (array) => {
return array.reduce((a,b) => {
return a.concat(
Array.isArray(b) ? aaa(b) :
Object.prototype.toString.call(b) === '[object Object]' ?
Object.values(b) : b
)
},[])
}
console.log(aaa(array))
let arrNum = [1, 2, [3, 4, 5, [6]], 7, 8, [9], 0];
arrNum.toString().split(",").map(Number); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
// 1.flat
console.log(arrNum.flat(Infinity)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
// 2.reduce
const flatten = (arr) => {
return arr.reduce((a, b) => {
if (Array.isArray(b)) {
return a.concat(flatten(b));
} else {
return a.concat(b);
}
}, []);
};
console.log(flatten(arrNum)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
// 3.扩展运算符
const flatten02 = (arr) => {
return arr.reduce((a, b) => {
if (Array.isArray(b)) {
return [...a, ...flatten02(b)];
} else {
return [...a, b];
}
}, []);
};
console.log(flatten02(arrNum)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
// 4.非递归(栈)
const flatten03 = (arr) => {
const result = [];
const stack = [...arr];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(...next);
} else {
result.unshift(next);
}
}
return result;
};
console.log(flatten03(arrNum)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
§ 数组根据字典排序
sort()排序默认是根据编码排序的
const city = ['上海','北京','杭州','广东','深圳','西安']
city.sort((a,b) => a.localeCompare(b))
// ['北京', '广东', '杭州', '上海', '深圳', '西安']
§ 实现一个深拷贝
window.structuredClone()
const obj = {
name: '123',
arr: [1, 10, '123', { aa: 'aaa' }],
data: {
name: 'abc',
age: 20,
},
field: null,
ceshi: undefined,
}
obj.other = obj;
const copy = window.structuredClone(obj)
console.log(copy)
JS逻辑
let obj = {
aaa: "zzz",
bbb: {
ccc: "xxx",
ddd: {
eee: "yyy",
fff: "ttt",
},
},
};
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
let cloneData = deepClone(obj);
console.log("原数据", obj);
console.log("克隆数据", cloneData);
这样写存在一个问题,如果克隆的对象带有引用的时候,递归就会无限循环,为了解决这个问题,重新写一个更完美的深拷贝方法:
// 测试
const obj = {
name: '123',
arr: [1, 10, '123', { aa: 'aaa' }],
data: {
name: 'abc',
age: 20
},
field: null,
ceshi: undefined
}
obj.other = obj
obj.arr.push(obj)
// 深拷贝
function deepClone(obj) {
// 当对象克隆的时候,对每个对象进行缓存,下次克隆的时候,直接读取缓存,来解决对象引用无限递归
// weakMap不影响垃圾回收,如果使用map,则会提升内存泄漏风险
const cache = new WeakMap()
// 声明子函数,避免全局变量污染
function _deepClone(obj) {
// 判断当前传参是否为非对象,如果非对象,则直接返回结果
if (obj === null || typeof obj !== 'object') {
return obj
}
// 如果当前传参是对象,则判断缓存中是否存在数据,如果存在,则直接读取缓存
if(cache.has(obj)) {
return cache.get(obj)
}
// 判断当前传过来的是数组还是对象
const result = Array.isArray(obj) ? [] : {}
// 如果缓存中不存在,则把对象加进去
cache.set(obj, result)
// 循环对象并且用递归的方式对对象的每个属性进行深度克隆
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = _deepClone(obj[key])
}
}
return result
}
return _deepClone(obj)
}
const cloneObj = deepClone(obj)
console.log(cloneObj)
§ ['a','b','c'] 数组全排列
// 递归回溯
function rank(nums) {
let arr = [];
function recursion(unusedNum, usedNum) {
if (usedNum.length == nums.length) {
arr.push(usedNum);
return;
}
for (let i = 0; i < unusedNum.length; i++) {
if (unusedNum[i] != null) {
let temp = [...unusedNum];
temp[i] = null;
recursion(temp, [...usedNum, unusedNum[i]]);
}
}
}
recursion(nums, []);
return arr;
}
rank(['a','b','c'])
/*
[
[ "a", "b", "c" ],
[ "a", "c", "b" ],
[ "b", "a", "c" ],
[ "b", "c", "a" ],
[ "c", "a", "b" ],
[ "c", "b", "a" ]
]
*/
§ [2,2,2,2,3,3,4,4,4,2,2,3,5],数组中元素重复的次数,返回[[2,4],[3,2],[4,3],[2,2],[3,1],[5,1]]
§ [2,2,2,2,3,3,4,4,4,2,2,3,5],数组中元素连续重复的次数最多的元素
let arr = [2, 2, 2, 2, 3, 3, 4, 4, 4, 2, 2, 3, 5];
function maxArr(arr) {
let num = 1;
let max = 1;
let maxItem = null;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === arr[i + 1]) {
num = num + 1;
if (max < num) {
max = num;
maxItem = arr[i];
}
} else {
num = 1;
}
}
return [maxItem, max];
}
console.log(maxArr(arr)); // [2, 4]
§ [2,2,2,2,3,3,4,4,4,2,2,3,5],数组中元素重复的次数最多的元素
function findNum(a) {
let res = [0, 0]; //定义一个数组,存放结果
for (let i = 0; i < a.length; i++) {
for (j = 0, count = 0; j < a.length; j++) {
if (a[i] == a[j]) {
++count; //找到一个重复的项,计数器就 +1
}
}
if (count > res[0]) {
res[0] = count;
res[1] = a[i];
} else if (count == res[0] && res[1] < a[i]) {
res[1] = a[i];
}
}
return res;
}
findNum([2, 2, 2, 2, 3, 3, 4, 4, 4, 2, 2, 3, 5]); // [6, 2]
§ 统计数组中重复出现过的元素 [1, 2, 4, 4, 3, 3, 1, 5, 3] -> [1, 3, 4]
function repeatArr(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
newArr.push(arr[j]);
}
}
}
return Array.from(new Set(newArr));
}
repeatArr([1, 2, 4, 4, 3, 3, 1, 5, 3]); // [1, 3, 4]
§ 封装一个函数,传入'abcdefgh' 返回 'abc-def-gh'
const chunkString = (str) => {
return str.replace(/(.{3})(?=.)/g, '$1-')
}
const res = chunkString("abcdefgh") // "abc-def-gh"
§ 实现一个版本号的排序,['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5'] => ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
const versionList = (data) => {
data.sort((a, b) => {
let i = 0;
const arr1 = a.split(".");
const arr2 = b.split(".");
while (true) {
const s1 = arr1[i];
const s2 = arr2[i];
i++;
if (s1 === undefined || s2 === undefined) {
return arr2.length - arr1.length;
}
if (s1 === s2) continue;
return s2 - s1;
}
});
return data;
};
versionList(['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5'])
// ['4.3.5', '4.3.4.5', '4.2', '2.3.3', '0.302.1', '0.1.1']
实现一个版本号的对比,如果新版本小于老版本,则返回-1,如果新版本大于老版本,则返回1,如果老版本等于新版本,则返回1
const version = (version1,version2) => {
let ver1 = version1.split(".").map(item => parseInt(item))
let ver2 = version2.split(".").map(item => parseInt(item))
for(let i = 0;i < ver1.length && i < ver2.length;i++){
if(ver1[i] < ver2[i]) return -1;
if(ver1[i] > ver2[i]) return 1;
}
if(ver1.length < ver2.length) return -1;
if(ver1.length > ver2.length) return 1;
return 0;
}
const res = version("1.0.1","1.0.2")
console.log(res)
§ 实现封装一个柯里化函数,实现自由累加得出结果
function add() {
const args = [].slice.call(arguments);
const _add = function(){
if (arguments.length === 0) {
return args.reduce((a, b) => a + b);
} else {
[].push.apply(args, arguments);
return _add;
}
};
return _add;
}
// 测试
add(1, 2, 3)() // 6
add(1,2,3)(1)(2)(3)(4,5,6)(7,8)() // 42
§ 实现一个防抖函数和一个节流函数
// 防抖
const debounce = (fn, delay = 300) => {
let timer;
return function () {
const args = arguments;
if (timer) {
clearInterval(timer);
}
timer = setTimeout(() => {
fn.apply(this.args);
}, delay);
};
};
// 测试
window.addEventListener(
"scroll",
debounce(() => {
console.log(123);
}, 1000)
);
// 节流
const throttle = (fn, delay) => {
let flag = true;
return () => {
if (!flag) return;
flag = false;
timer = setTimeout(() => {
fn();
flg = true;
}, delay);
};
};
// 测试
window.addEventListener(
"scroll",
throttle(() => {
console.log(123);
}, 1000)
);
§ 实现一个promise
-
Promise 是一个类,传入的参数是一个执行器,会立即执行
-
Promise 有三种状态
- pending 等待
- fulfilled 完成
- rejected 失败
-
状态只能改变一次,不能逆向。
- pending -> fulfilled
- pending -> rejected
-
Promise 使用 resolve 和 reject 两个函数来改变状态
-
then 方法内部做状态判断,执行对应的方法
-
有静态方法 Promise.resolve 和 Promise.reject
// 状态常量
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
state = PENDING;
value = undefined;
reason = undefined;
onResolveCallback = [];
onRejectCallback = [];
constructor(executor) {
try {
executor(this.resolve, this.reject);
} catch (err) {
this.reject(err);
}
}
resolve(data) {
if (this.state !== PENDING) {
return;
}
this.state = FULFILLED;
this.value = data;
while (this.onResolveCallback.length > 0) {
const currentResolve = this.onResolveCallback.shift();
currentResolve(this.value);
}
}
reject(err) {
if (this.state !== PENDING) {
return;
}
this.state = REJECTED;
this.reason = err;
while (this.onRejectCallback.length > 0) {
const currentReject = this.onRejectCallback.shift();
currentReject(this.reason);
}
}
static resolve(param) {
// param是Promise对象,直接返回
if (param instanceof Promise) {
return param;
}
// 转成普通的Promise
return new Promise((resolve) => {
resolve(param);
});
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
catch(fn) {
if (this.state == REJECTED) {
typeof fn == "function" && fn(this.reason);
}
}
then(resolve, reject) {
const realResolve =
typeof resolve == "function" ? resolve : (value) => value;
const realReject =
typeof reject == "function"
? reject
: (reason) => {
throw reason;
};
// 链式调用,需要返回新的Promise实例
const newPromise = new Promise((resolve, reject) => {
// 创建一个微任务,等待Promise初始化完成
const microResolve = () => {
queueMicrotask(() => {
try {
const x = realResolve(this.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
};
const microReject = () => {
queueMicrotask(() => {
try {
const x = realReject(this.reason);
resolvePromise(newPromise, x, reasolve, reject);
} catch (err) {
reject(err);
}
});
};
if (this.state == FULFILLED) {
return microResolve(this.value);
}
if (this.state == REJECTED) {
return microReject(this.reason);
}
if (this.state == PENDING) {
this.onResolveCallback.push(microResolve);
this.onRejectCallback.push(microReject);
}
});
return newPromise;
}
}
function resolvePromise(newPromise, x, resolve, reject) {
if (newPromise == x) {
return reject(new Error("循环引用"));
}
if (x instanceof Promise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
§ 手写一个轮播图,let obj = {a:{b:1}},给定一个obj与一个'a.b'返回1,如果给了'd.b',则返回null
let a = {
b: {
c: {
d: 1,
},
},
};
const getObj = (str) => {
let arr = str.split(".");
arr.shift();
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
if (a[arr[i]]) {
a = a[arr[i]];
} else {
return null;
}
}
return a;
};
let val = getObj("a.b.c.d");
console.log(val);
§ 实现一个reduce函数
Array.prototype.Reduce = function (callback) {
const initVal = arguments[1] ? arguments[1] : "";
const len = this.length;
if (!len && !initVal) {
}
if (!len) return initVal;
let total = initVal ? initVal : this[0];
let i = initVal ? 0 : 1;
for (; i < len; i++) {
total = callback(total, this[i], i, this);
}
return total;
};
// 测试
const testArr = [1, 2, 3, 4, 5];
const totalNum = testArr.Reduce((total, item, index, testArr) => {
console.log(total, item, index, testArr);
return total + item;
});
console.log(totalNum); // 15
§ 实现一个map函数
Array.prototype.Map = function (callback) {
const result = [];
const context = arguments[1] || window;
if (typeof callback === "function") {
for (let i = 0; i < this.length; i++) {
result.push(callback.call(context, this[i], i, this));
}
} else {
throw new Error("--");
}
return result;
};
// 测试
const testArr2 = [1, 2, 3, 4, 5];
const totalNum2 = testArr2.Map((item) => {
return item * 2;
});
console.log(totalNum2); // [2, 4, 6, 8, 10]
§ 实现一个filter函数
Array.prototype.Filter = function (callback) {
const result = [];
const context = arguments[1] || window;
if (typeof callback === "function") {
for (let i = 0; i < this.length; i++) {
let condition = callback.call(context, this[i], i, this);
if (condition) {
result.push(this[i]);
}
}
} else {
throw new Error("--");
}
return result;
};
// 测试
const testArr3 = [1, 2, 3, 4, 5];
const r = testArr3.Filter((item) => item === 3);
console.log(r); // [3]
§ 实现一个some函数
Array.prototype.Some = function (callback) {
const result = [];
const context = arguments[1] || window;
if (typeof callback === "function") {
for (let i = 0; i < this.length; i++) {
let condition = callback.call(context, this[i], i, this);
if (condition) {
return true;
}
}
return false;
} else {
throw new Error("--");
}
return result;
};
// 测试
const testArr4 = [1, 3, 5, 7, 9];
const result4 = testArr4.Some((item, index, testArr4) => item % 2 === 0); // false
§ 实现一个every函数
Array.prototype.Every = function (callback) {
const result = [];
const context = arguments[1] || window;
if (typeof callback === "function") {
for (let i = 0; i < this.length; i++) {
let condition = callback.call(context, this[i], i, this);
if (!condition) {
return false;
}
}
return true;
} else {
throw new Error("--");
}
return result;
};
// 测试
const testArr5 = [1, 3, 5, 7, 9];
const result5 = testArr5.Every((item, index, testArr4) => item >= 10);
console.log(result5); // false
§ 实现一个forEach函数
Array.prototype.ForEach = function (callback) {
const context = arguments[1] || window;
if (typeof callback === "function") {
for (let i = 0; i < this.length; i++) {
callback.call(context, this[i], i.this);
}
} else {
throw new Error("--");
}
};
const test5obj = [
{
name: "aaa",
age: 12,
},
{
name: "bbb",
age: 13,
},
];
test5obj.ForEach((item) => {
console.log(item);
});
§ 遍历递归树结构,每个node节点增加一个属性
const objects = [
{
name:"aaa"
},{
name:"bbb",
children:[
{
name:"ccc"
},
{
name:"eee",
children:[
{
name:"fff"
}
]
}
]
}
]
const addName1 = (data,preLast) => {
data.forEach((item) => {
console.log(preLast)
item[Object.keys(preLast)] = Object.values(preLast)[0]
if(item.children){
addName1(item.children,preLast)
}
})
}
addName1(objects,{age:"123"})
console.log(objects)
§ 遍历递归树结构,在其中一个属性后面加一个后缀
const objects = [
{
name:"aaa"
},{
name:"bbb",
children:[
{
name:"ccc"
},
{
name:"eee",
children:[
{
name:"fff"
}
]
}
]
}
]
const addName1 = (data,preLast) => {
data.forEach((item) => {
item.name = item.name + preLast
if(item.children){
addName1(item.children,preLast)
}
})
}
addName1(objects,'--zzz')
console.log(objects)
§ 浏览器运行 foo 函数,是否会导致栈溢出?
function foo {
setTimeout(foo, 0)
}
答案是不会栈溢出。首先导致栈溢出的原因是在一个函数中无限的调用自身,就会导致栈溢出,因为每次调用函数都会在栈里面执行上下文,会占用栈的一部分空间,上下文是让函数执行结束之后才会被弹出去,如果在函数里面调用自身,就会导致当前函数还没有运行结束的时候,又在调用自己,这样就会一直累计,然而栈的总大小是有限的,这样就会撑爆,所以会导致栈溢出
在这道题当中之所以不会栈溢出是因为,函数调用自己是用异步来操作的
§ 如何让下面的判断成立
if (a == 1 && a == 2 && a == 3) {
console.log("成立");
}
等号运算符规则:
- 两端类型相同,比较值
- 两端存在NaN,返回false
- undefined 和 null只有与自身比较,或者互相比较时,才会返回true
- 两端都是原始类型,转换成数字比较
- 一端是原始类型,一段是对象类型,把对象类型转换成原属类型吼进入第4步
对象如何转原始类型?
- 如果对象拥有[Symbol.toPrimitive]方法,调用该方法,如果该方法能得到原始值,使用该原始值,如果得不到原始值,则抛出异常
- 调用对象的valueOf方法,如果该方法能得到原始值,则使用该原始值,如果得不到原始值,则进入下一步(对象中默认有valueOf方法)
- 调用对象的toString()方法,如果该方法能得到原始值,则使用该原始值,如果得不到原始值,则抛出异常
所以这道题的解法是:
const a = {
const: 1,
valueOf() {
return this.const++;
},
};
if (a == 1 && a == 2 && a == 3) {
console.log("成立");
}