下面整理一下最近这段时间面试过程当中遇到的一些手撕的代码题,其中有些题目遇到过不止一次,也都是比较基础的题目,你们能够参考一下,ps:若是你没时间刷完大部分的leetcode题,那么我建议优先刷一下leetcode的 热题 HOT 100,确实很容易被问到,并且笔试里面也会遇到,剑指也能够,可是我没刷过,具体的不是很了解。
面试
关于这个问题,在ES6中有一个新的方法arr.flat()方法,能够直接返回降维后的数组,flat的参数为降维的维度,默认为一维,能够为数字,Infinity为任意维,可是这个方法在低版本的浏览器中还不支持,因此通常会让你写一个原生的方法,这里介绍几种方法
数组
//方法一:常规方法,递归遍历,判断是否数组,须要用到额外的全局变量
var result=[]
var flat=function (arr) {
for(let i=0;i<arr.length;i++){
if(Array.isArray(arr[i])){
flat(arr[i])
}else{
result.push(arr[i]);
}
}
}
//上述方法优化
var flat=function (arr) {
var result=[]
for(let i=0;i<arr.length;i++){
if(Array.isArray(arr[i])){
result=result.concat(flat(arr[i]))
}else{
result.push(arr[i]);
}
}
return result;
}
//上述方法的简洁写法,利用箭头函数
let flat = arr => arr.reduce((begin,current)=>{
Array.isArray(current)?begin.push(...flat(current)):begin.push(current);
return begin;
},[])
//方法二:使用字符串分割
var flat=function(arr){
let strarr=arr+"";
let str=strarr.split(",");
return str;
}
复制代码
//全部顺序子序列(输出全部有序的子序列)
var subSequence=function (arr){
let res=[];
for(let i=0;i<arr.length;i++){
for(let j=i;j<arr.length;j++){
res.push(arr.slice(i,j+1))
}
}
return res;
}
console.log(subSequence([1,2,3]))
//结果为 1 1,2 1,2,3, 2 2,3 3
//全部数组元素排列组合
// var allSubSequence=function(arr){
// let len=arr.length;
// let mark=new Array(len);
// let result=[];
// var recursion=function(arr,mark,n,i){
// if(n==i){
// let temparr=[];
// for(let k=0;k<i;k++){
// if(mark[k]==1){
// temparr.push(arr[k]);
// }
// }
// result.push(temparr);
// return;
// }
// mark[n]=0;
// recursion(arr,mark,n+1,i)
// mark[n]=1;
// recursion(arr,mark,n+1,i)
// }
// recursion(arr,mark,0,len);
// return result;
// }
// console.log(allSubSequence([1,2,3,4]));
//输出结果为
// [],
// [ 4 ],
// [ 3 ],
// [ 3, 4 ],
// [ 2 ],
// [ 2, 4 ],
// [ 2, 3 ],
// [ 2, 3, 4 ],
// [ 1 ],
// [ 1, 4 ],
// [ 1, 3 ],
// [ 1, 3, 4 ],
// [ 1, 2 ],
// [ 1, 2, 4 ],
// [ 1, 2, 3 ],
// [ 1, 2, 3, 4 ]
//原理:
//原理,使用二进制的排列组合来实现
//将a,b,c,d 分别以二进制占位符表示,0即表示不存在,1表示不存在,mark数组即用来存储这个占位符
//如0000则表示空集,1111表示集合{a,b,c,d},0001表示{d},0110表示{b,c}
//本质就是将0000按二进制加到1111,每加1就输出一次其表示的数
//递归的原理图以下,当只有abc三个数时
// 0
// 0
// 1
// 0
// 0
// 1
// 1
// start
// 0
// 0
// 1
// 1
// 0
// 1
// 1
复制代码
var result=[];
var results=[];
var doExchange=function (arr, depth) {
for (let i = 0; i < arr[depth].length; i++) {
result[depth] = arr[depth][i];
if (depth !== arr.length - 1) {
doExchange(arr, depth + 1)
} else {
results.push(result.join(" ").split(" "))
}
}
}
doExchange([[1,2,3],[4,5],[6,7]],0)
for(let i=0;i<results.length;i++){
for(let j in results[i]){
results[i][j]=parseInt(results[i][j])
}
}
console.log(results)
//结果:
// [ 1, 4, 6 ],
// [ 1, 4, 7 ],
// [ 1, 5, 6 ],
// [ 1, 5, 7 ],
// [ 2, 4, 6 ],
// [ 2, 4, 7 ],
// [ 2, 5, 6 ],
// [ 2, 5, 7 ],
// [ 3, 4, 6 ],
// [ 3, 4, 7 ],
// [ 3, 5, 6 ],
// [ 3, 5, 7 ]
复制代码
这里其实考察的是函数中this的指向问题,这里用到了前文提到过的一个知识点 在js部分-ES6新增-函数相关扩展方法-普通函数中的this的四种调用模式
浏览器
//方法一:使用函数的方式实现
function Person(val){
this.name="admin";
this.getName=function(){
return this.name;
}
this.setName=function(val){
this.name=val;
}
}
var per=new Person();
console.log(per.name)//admin
console.log(per.getName())//admin
per.setName(111)
console.log(per.getName())//111
//方法二:使用对象的方式实现(这种方式不能new一个实例)
var Person={
name:"admin",
getName(){
return this.name;
},
setName(val){
this.name=val
}
}
console.log(Person.name)//admin
console.log(Person.getName())//admin
Person.setName("111")
console.log(Person.getName())//111
//方法三:使用原型链的按时实现
function Person(){ }
Person.prototype.name="admin";
Person.prototype.getName=function(){
return this.name;
};
Person.prototype.setName=function(val){
this.name=val;
};
var per=new Person();
console.log(per.name)//admin
console.log(per.getName())//admin
per.setName(111)
console.log(per.getName())//111
复制代码
toString()会把数据类型转换成string类型,也就是说无论原来是什么类型,转换后一概是string类型
valueOf()会把数据类型转换成原始类型,也就是说原来是什么类型,转换后仍是什么类型,日期类型除外
markdown
let a={
i:1,
valueOf(){
return a.i++;
}
}
console.log(a==1&&a==2) //true
let b={
i:1,
toString(){
return b.i++;
}
}
console.log(b==1&&b==2) //true
复制代码
//方法一:ES6 let 块级做用域
//注意:这里若是使用var的话,会每一个一秒输出一个5,输出5个5
for(let i = 1; i <5; i++){
setTimeout(function () {
console.log(i);
},1000*i);
}
//方法二:ES5 闭包 匿名函数and函数自动执行
// (function(i){})(i) 其中第一个()返回一个匿名函数,第二()起到当即执行的做用
for (var i = 1; i <= 10; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000 * i);
})(i);
}
复制代码
/*
*fn --> 须要防抖的函数;
*delaytime --> 毫秒数,防抖所需期限值;
*/
//防抖
//原理:当持续触发事件时,必定时间段内没有再触发事件,事件处理函数才会执行一次,若是设定的时间到来以前,又一次触发了事件,
//就从新开始延时。
function debounce(fn,delaytime){
let timer = null
return function(){
if(timer){
//进入这里说明当前存在一个执行过程,而且同时又执行了一个相同事件,故取消当前的执行过程
clearTimeout(timer)
}
timer = setTimeout(fn,delaytime)
}
}
function show_scrollPosition(){
var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop;
console.log("当前滚动条位置为:",scrollPosition);
}
window.onscroll = debounce(show_scrollPosition,1000)
//节流
//原理:规定一个期限时间,在该时间内,触发事件的回调函数只能执行一次,若是期限时间内回调函数被屡次触发,则只有一次能生效。
function throttle(fn, delay) {
let last_time
let timer = null
return function () {
let cur_time = new Date().getTime()
if (last_time && cur_time < last_time + delay) {
//若为真,则表示上次执行过,且在期限值范围内
clearTimeout(timer)
timer = setTimeout(() => {
fn();
last_time = cur_time
}, delay)
} else {
last_time = cur_time;
fn();
}
}
}
function show_scrollPosition() {
var scrollPosition = document.body.scrollTop || document.documentElement.scrollTop;
console.log("当前滚动条位置为:", scrollPosition);
}
window.onscroll = throttle(show_scrollPosition, 1000)
复制代码
function add(val){
var rs = function(oval){
return add(val + oval);
}
rs.toString = function(){
return val;
}
return rs;
}
console.log(add(1)(2)(3) == 6);
复制代码