1. 找到两个数组的交集
let a = new Set([1,2,3,4,5,6]);
let b = new Set([4,5,6,7,8,9]);
let jiaoji = new Set([...a].filter(x => b.has(x)));
let arr = [...jiaoji]
2.存在父子关系的list 转换成树结构
let arr = [
{id:1,name:"king1",parentId:null},
{id:2,name:"king2",parentId:1},
{id:3,name:"king3",parentId:2},
{id:4,name:"king4",parentId:2},
{id:5,name:"king5",parentId:2},
{id:6,name:"king6",parentId:null},
{id:7,name:"king7",parentId:null},
{id:8,name:"king8",parentId:6}
]
使用递归实现
function getTrees(list, parentId = 0) {
//此循环只进行一次,在第一次进行,目的是对没有父节点的 根节点进行过滤,和对它们进行子节点的填充
if (!parentId) {
let parentObj = {};
//循环当前list 把每条数据跟自己id相关联
list.forEach(element => {
parentObj[element.id] = element;
});
//返回
let filterList = list.filter(o => !parentObj[o.parentId]);
return filterList.map(o => {
let tree = getTrees(list, o.id);
return tree == false ? o : (o.children = tree, o)
});
} else {
//返回根节点下面的子节点
let filterList = list.filter(o => o.parentId == parentId);
return filterList.map(o => {
let tree = getTrees(list, o.id);
return tree == false ? o : (o.children = tree, o)
});
}
}
如果想让箭头函数向外返回一个对象字面量,则需要将该字面量包裹在小括号里。就像这样:
let getTempItem = id => ({ id: id, name: 'Temp' });
// 实际上相当于:
let getTempItem = function(id) {
return { id: id, name: 'Temp' }
};
let reflect = value => value;
// 实际上相当于:
let reflect = function(value) {
return value;
};
arr.map(o=> o*2)
// 实际上相当于:
arr.map(function(item){
return item*2
})
//ps:给所有数据项都添加属性的方法
arr.map(o=>(o.children=1,o)) //返回的所有对象都添加了children:1
当箭头函数只有一个参数时,可以直接写参数名,箭头紧随其后,箭头右侧的表达式被求值后便立即返回(没括号包裹时),不需要更多的语法铺垫。
3.1+100 递归
let addFunc = (a=1)=>a>100?0:a+addFunc(++a)
//相当于
let addFunc = (a=1)=>{return a>100?0:a+addFunc(++a)}
4.数组undefined和undefined的不同
JSON.stringify([undefined]) // "[null]"
JSON.stringify(undefined) //undefined
JSON.stringify([null]) // "[null]"
JSON.stringify(null) //"null"
JSON.stringify(NaN) //"null"
5.解构赋值对象和数值
let a = 1,b = 2,head = {next: { next: 1 }};
[a, b] = [b, a];
[head.next, head.next.next] = [head.next.next, head.next];
console.log(a, b, head);
解析: head.next = 1的时候 head.next.next已经不存在了 所以head值为{next: 1}
结果: 2 1 {next: 1}
6.函数参数传递的区别
const value = { number: 10 };
let number = 2;
const multiply = (x = { ...value }, y = number) => {
y++;
console.log((x.number *= y));
};
multiply();
multiply();
multiply(value, number);
multiply(value, number);
解析:首先函数的参数都是按值传递
... 扩展运算符在对象只有一层的时候相当于深拷贝 所以结果不受影响
当传递参数并且参数是对象的时候,此时的值传递就是地址的值的传递,就会使x.number变化
结果:30 30 30 90
7.运算符一般是从左往右
console.log(1 < 2 < 3); //true
console.log(3 > 2 > 1); //false
解析:true 和 1进行比较的时候会进行隐式转换
8.运算符优先级
let a = 3;
let num = 1 * (2 + a) && ++a || 5 > 6 && 7 < 8 || !9 ;
console.log(num);
解析:- 优先级从高到低
- 1、() 优先级最高
- 2、一元运算符 ++ -- !
- 3、算数运算符 先* / % 后 + -
- 4、关系运算符 > >= < <=
- 5、相等运算符 == != === !==
- 6、逻辑运算符 先 && 后 ||
- 7、赋值运算符
(2 + a) = 5
++a = 4
!9 = false
1 * 5 = 5
5 > 6 = false
7 < 8 = true
5 && 4 = 4
false && true = false
4 || false = 4
4 || false = 4
结果: 4
9.说一说为什么[] == ![]
①、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]
!可将变量转换成boolean类型,null、undefined、NaN以及空字符串('')取反都为true,其余都为false。
所以 ! [] 运算后的结果就是 false
也就是 [] == ! [] 相当于 [] == false
②、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0
也就是 [] == ! [] 相当于 [] == false 相当于 [] == 0
③、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())
而对于空数组,[].toString() -> '' (返回的是空字符串)
也就是 [] == 0 相当于 '' == 0
④、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)
Number('') -> 返回的是 0
相当于 0 == 0 自然就返回 true了
总结一下:
[] == ! [] -> [] == false -> [] == 0 -> '' == 0 -> 0 == 0 -> true
那么对于 {} == !{} 也是同理的
关键在于 {}.toString() -> NaN(返回的是NaN)
根据上面的规则(如果有一个操作数是NaN,则相等操作符返回 false)
总结一下:
{} == ! {} -> {} == false -> {} == 0 -> NaN == 0 -> false
10.null 和 0 的比较结果
console.log(null == 0); //false
console.log(null <= 0); //true
console.log(null < 0); //false
Number(null) == 0 // true
解析:javascript中 null不等于零,也不是零
null只等于undefuned ,剩下它俩和谁都不等
但是在关系运算符 在设计上会从尝试转为一个number考虑,所以 null < 0 或 null <= 0 时 会触发 Number(null) < 0 和 Number(null) <= 0
11.reduce的使用
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
解析:reducer 函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
reduce函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
reduce 函数还有一个可选参数 initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue,则将使用数组中的第一个元素。
在上述例子, reduce方法接收的第一个参数(Accumulator)是 x, 第二个参数(Current Value)是 y。
在第一次调用时,累加器 x为 1,当前值 “y”为 2,打印出累加器和当前值:1和 2。
例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined。在下一次调用时,累加器为 undefined,当前值为“3”, 因此 undefined和 3被打印出。
在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined ,当前值为“4”。undefined和 4被打印出。
结果:
1 2
undefined 3
undefined 4
12.三目运算的假象
const value = 'Value is' + !!Number(['0']) ? 'yideng' : 'undefined';
console.log(value);
解析:+优先级大于?
所以 原题等价于 ‘Value is false’?'yideng':'undefined'
13.模块化的问题
//counter.js
let counter = 10;
const add = () => {
console.log(counter);
};
export { counter, add };
//index.js
add();
import { counter, add } from "./counter";
counter += 1;
console.log(counter);
解析:import命令
1:import命令输入的变量都是只读的(除了对象)
//counter.js
let counter = {};
export default counter;
//index.js
import myCounter from './counter.js';
myCounter.hello = "helloCounter";
上面的代码中 myCounter的属性可以成功改写,并且其他模块也可以读到改写后得值。不过这种写法很难查错,建议凡是import的变量都当做完全只读,不要轻易改变它的属性。
2:import命令具有提升效果
3: import是静态执行,所以不能使用表达式和变量
//报错
let module = "my_module"
import {foo} from module;
4: import语句是Singleton模式
import {foo} from 'my_module'
import {bar} from 'my_module'
//等同于
import {foo,bar} from 'my_module'
虽然在两个语句加载,但是它们对应的是同一个my_module
结果:10 报错
14.符号优先级的问题
var a = { k1: 1 };
var b = a;
a.k3 = a = { k2: 2 };
console.log(a); // ?
console.log(b); // ?
//解析
点的优先级大于等号的优先级
对象以指针的形式进行存储,每个新对象都是一份新的存储地址
先执行a.k3 所以 a和b都等于 {k1:1,k3:undefined}
等号是从右向左执行,先执行 a={k2:2},a是一个新对象(b还存的a的旧地址)
a.k3是最开始执行的 {k1:1,k3:undefined}.k3 = { k2: 2 } ,b和旧的a指的是同一个地址
所以 a={k2:2} , b = {k1:1,k3:{ k2: 2 }}
15.判断字符串值的次数
let str = 'abbbcccaadbbcs';
[...str].reduce((acc,cur)=>{
acc[cur] = acc[cur] ? ++acc[cur] : 1
return acc
},{})
16.Set对象的去重(对象或数组)
方法一:
let arr = [[1,2,3],[-1,3,4],[2,4,5],[1,2,3]];
let strings = arr.map(item=>JSON.stringify(item))
let uniqueStr = [...new Set(strings)]
let uniqueArr = uniqueStr.map(item=>JSON.parse(item))
console.log(uniqueArr)
方法二:
letoldArr = [
{name:1,age:1},
{name:2,age:2},
{name:2,age:2},
{name:3,age:3},
{name:3,age:4},
{name:4,age:5},
{name:4,age:4},
{name:4,age:0},
{name:5,age:6},
{name:5,age:6},
{name:1,age:0},
{name:1,age:0},
{name:2,age:0},
{name:3,age:0},
{name:1,age:0},
];
let json = {};
oldArr.reduce((acc,cur)=>{
json[cur.name]?'':json[cur.name]=true && acc.push(cur)
return acc
},[])
17.滑动窗口问题
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
// 注意:答案中不可以包含重复的三元组。
// 示例: 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]
let threeSum = function (nums) {
const n = nums.length
if (nums == null || n < 3) return []
const res = []
// 先进行排序
nums.sort((a, b) => {
return a - b
})
// console.log(nums)
for (let i = 0; i < n - 2; i++) {
// 跳过重复元素
if (i > 0 && nums[i] === nums[i - 1]) {
continue
}
// 设置双指针分别为 (i, nums.length),在区间进行查找
let j = i + 1,
k = n - 1
while (j < k) {
let s = nums[i] + nums[j] + nums[k]
if (s === 0) {
res.push([nums[i], nums[j], nums[k]])
// 去重
while (j < k && nums[j] === nums[j + 1]) j++
while (j < k && nums[k] === nums[k - 1]) k--
j++
k--
} else if (s > 0) {
k--
} else if (s < 0) {
j++
}
}
}
return res
}
// test
console.log(threeSum([-1, 0, 1, 2, -1, -4]))
18.数组最大值的问题
let arr = [1,2,4,5,3]
let max = Math.max(...arr) // 最大值为5
let min = Math.min(...arr) // 最小值为1
19.找规律的问题
数组的n-1项进行+1,需要几次可以使数组各项值相等
//n-1项进行+1
// let arrs = [1,2,3] [2,3,3] [3,4,3] [4,4,4]
// let arrs = [1,2,5] [2,3,5] [3,4,5] [4,5,5] [5,6,5] [6,6,6]
解释:找规律
3-1+2-1 = 3
5-1+2-1=5
20.最长不重复子字符串的问题
//动态规划 自下而上
let lengthOfLongestSubstring = function (s) {
const arr = [...s]
let res = 1;
let result = arr.reduce((total, cur, i, arr) => {
if (i == 0) {
return cur;
} else {
if (!total.includes(cur)) {
return total + cur
} else if (res < total.length) {
res = total.length
return total.slice(total.indexOf(cur) + 1, total.length) + cur
} else {
return total.slice(total.indexOf(cur) + 1, total.length) + cur
}
}
}, "")
if (res < result.length) {
res = result.length
}
return res
};
console.log(lengthOfLongestSubstring("loddktdji"))
console.log(lengthOfLongestSubstring("dvdf"))
console.log(lengthOfLongestSubstring("adfafwefffdasdcx"))
21.下面代码输出什么?
async function as1() {
console.log("as1 start");
await as2();
console.log("as1 end");
}
async function as2() {
console.log("as2");
}
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
as1();
new Promise(function (resolve) {
console.log("prom1");
resolve();
}).then(function () {
console.log("prom2");
});
console.log("script end");
//script start => as1 start => as2 => prom1 => script end
//=> as1 end => prom2 => setTimeout