其他分享
首页 > 其他分享> > 面试十题(1)

面试十题(1)

作者:互联网

1、React / Vue 项目时为什么要在列表组件中写 key, 其作用是什么?

2、 ['1', '2', '3'].map(parseInt) 结果和原因?

parseInt('1',0); // 1。 parseInt() 会根据十进制来解析,所以结果为 1;
parseInt('2',1); // NaN。  超出区间范围,所以结果为 NaN;
parseInt('3',2); // NaN。 用2进制来解析,应以 0 和 1 开头,所以结果为 NaN。
parseInt('101.55',10); //以十进制解析,运算过程:向上取整数(不做四舍五入,省略小数),结果为 101。
parseInt('101',2); //以二进制解析,运算过程:1*2的2次方+0*2的1次方+1*2的0次方=4+0+1=5,结果为 5。
parseInt('101',8); //以八进制解析,运算过程:1*8的2次方+0*8的1次方+1*8的0次方=64+0+1=65,结果为 65。
parseInt('101',16); //以十六进制解析,运算过程:1*16的2次方+0*16的1次方+1*16的0次方=256+0+1=257,结果为 257。

3、什么是防抖和节流?有什么区别?如何实现?

【区别】防抖动和节流本质是不一样的。防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

防抖主要利用定时器实现
// 防抖
function debounce(fn, wait) {
    var timeout = null;
    return function() {
        clearTimeout(timeout)
        timeout = setTimeout(() => fn.apply(this, arguments), wait)
    }
}

// 处理函数
function handle() {
    console.log('防抖')
}

// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000))
1. 时间戳实现
// 节流
function throttle(fn, delay) {
    var prev = +Date.now()
    return function() {
        var now = +Date.now()
        if (now - prev >= delay) {
            fn.apply(this, arguments)
            prev = now
        }
    }
}

// 处理函数
function handle() {
    console.log('节流')
}

// 滚动事件
window.addEventListener('scroll', throttle(handle, 1000))

---------------------------------------------------
2. 定时器实现
// 节流
function throttle(fn, delay) {
    var timer = null
    return function() {
    var context = this
    var args = arguments
        if (!timer) {
            timer = setTimeout(function() {
                fn.apply(context, args)
                timer = null
            }, delay)
        }
    }
}

// 处理函数
function handle() {
    console.log('节流2 定时器节流')
}

// 滚动事件
window.addEventListener('scroll', throttle(handle, 2000))

4、介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

  1. Set类似于数组,但【成员都是唯一的】。方法主要有add()、delete()、has()、clear()。属性主要有size
// 方法1
var a = [1,2,3,4,5,5]
var b = Array.from(new Set(a))
//方法2
var a = [1,2,3,4,5,5]
var b = [...new Set(a)]

②实现两个数组的并集、交集和差集(详见阮一峰es6)

  1. Map和普通对象相比,可以用各种类型数据当做键名,不限于字符串。

5、介绍下深度优先遍历和广度优先遍历,如何实现?

代码实现

const data = [
    {
        name:'a',
        children: [
            { name:'b', children: [{ name:'e', children: [{name: 'm'}] }] },
            { name:'c', children: [{ name:'f' }] },
            { name:'d', children: [{ name:'g' }] },
        ],
    },
    {
        name:'a2',
        children: [
            { name:'b2', children: [{ name:'e2', children: [{name: 'm2'}] }] },
            { name:'c2', children: [{ name:'f2' }] },
            { name:'d2', children: [{ name:'g2' }] },
        ],
    }
]
// 深度优先遍历  使用递归
function depthFirstSearch(data) {
    const result = [];
    data.forEach(item => {
        const map = data => {
            result.push(data.name);
            data.children && data.children.forEach(child => map(child));
        }
        map(item);
    })
    return result.join(',');
}
// 广度遍历, 创建一个执行队列,当队列为空的时候则结束
function breadthFirstSearch(data) {
    let result = [];
    let queue = data;
    while (queue.length > 0) {
        [...queue].forEach(child => {
            queue.shift();
            result.push(child.name);
            child.children && (queue.push(...child.children));
        });
    }
    return result.join(',');
}
console.log(depthFirstSearch(data))
console.log(breadthFirstSearch(data))

6.请分别用深度优先思想和广度优先思想实现一个拷贝 函数?

// 工具函数
function getEmpty (origin) {
    if(Object.prototype.toString.call(origin) === '[object Object]'){
        return {};
    }
    if(Object.prototype.toString.call(origin) === '[object Array]'){
        return [];
    }
    return origin;
};
// 广度优先拷贝
function deepCopyBFS(origin){
    let queue = []; //用队列 先进先出
    let map = new Map(); //用于记录出现过的对象,用于处理环状数据
    let target = getEmpty(origin);
    if(target !== origin){
        queue.push([origin,target]);
        map.set(origin,target);
    }

    while (queue.length) {
        let [ori,tar] = queue.shift(); //对象赋值,是指针引用
        for(let key in ori){
            //处理环状数据
            if(map.get(ori[key])){
                tar[key] = map.get(tar[key]);
                continue;
            }
    
            tar[key] = getEmpty(ori[key]);
            if(tar[key] !== ori[key]){
                 queue.push([ori[key],tar[key]]);
                 map.set(ori[key],tar[key]);
            }
        }
    }
    return target;
}
//深度优先拷贝
function deepCopyDFS(origin){
    let stack = [];//用堆 先进后出
    let map = new Map(); //用于记录出现过的对象,用于处理环
    let target = getEmpty(origin);
    if(target !== origin){
        stack.push([origin,target]);
        map.set(origin,target);
    }

    while (stack.length) {
        let [ori,tar] = stack.pop(); //对象赋值,是指针引用
        for(let key in ori){
            //处理环状
            if(map.get(ori[key])){
                tar[key] = map.get(tar[key]);
                continue;
            }

            tar[key] = getEmpty(ori[key]);
            if(tar[key] !== ori[key]){
                stack.push([ori[key],tar[key]]);
                map.set(ori[key],tar[key]);
            }
        }
    }
    return target;
}

7、ES5/ES6 的继承除了写法以外还有什么区别?

  1. class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
const bar = new Bar(); // it's ok
function Bar() {
  this.bar = 42;
}

const foo = new Foo(); // ReferenceError: Foo is not defined
class Foo {
  constructor() {
    this.foo = 42;
  }
}

2.class 声明内部会启用严格模式。

// 引用一个未声明的变量
function Bar() {
  baz = 42; // it's ok
}
const bar = new Bar();

class Foo {
  constructor() {
    fol = 42; // ReferenceError: fol is not defined
  }
}
const foo = new Foo();
  1. class 的所有方法(包括静态方法和实例方法)都是不可枚举的。
// 引用一个未声明的变量
function Bar() {
  this.bar = 42;
}
Bar.answer = function() {
  return 42;
};
Bar.prototype.print = function() {
  console.log(this.bar);
};
const barKeys = Object.keys(Bar); // ['answer']
const barProtoKeys = Object.keys(Bar.prototype); // ['print']

class Foo {
  constructor() {
    this.foo = 42;
  }
  static answer() {
    return 42;
  }
  print() {
    console.log(this.foo);
  }
}
const fooKeys = Object.keys(Foo); // []
const fooProtoKeys = Object.keys(Foo.prototype); // []
  1. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
function Bar() {
  this.bar = 42;
}
Bar.prototype.print = function() {
  console.log(this.bar);
};

const bar = new Bar();
const barPrint = new bar.print(); // it's ok

class Foo {
  constructor() {
    this.foo = 42;
  }
  print() {
    console.log(this.foo);
  }
}
const foo = new Foo();
const fooPrint = new foo.print(); // TypeError: foo.print is not a constructor
  1. 必须使用 new 调用 class。
function Bar() {
  this.bar = 42;
}
const bar = Bar(); // it's ok

class Foo {
  constructor() {
    this.foo = 42;
  }
}
const foo = Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'
  1. class 内部无法重写类名。
function Bar() {
  Bar = 'Baz'; // it's ok
  this.bar = 42;
}
const bar = new Bar();
// Bar: 'Baz'
// bar: Bar {bar: 42}  

class Foo {
  constructor() {
    this.foo = 42;
    Foo = 'Fol'; // TypeError: Assignment to constant variable
  }
}
const foo = new Foo();
Foo = 'Fol'; // it's ok
  1. ES5 和 ES6 子类 this 生成顺序不同。ES5 的继承先生成了子类实例,再调用父类的构造函数修饰子类实例,ES6 的继承先生成父类实例,再调用子类的构造函数修饰父类实例。这个差别使得 ES6 可以继承内置对象。
function MyES5Array() {
  Array.call(this, arguments);
}

// it's useless
const arrayES5 = new MyES5Array(3); // arrayES5: MyES5Array {}

class MyES6Array extends Array {}

// it's ok
const arrayES6 = new MyES6Array(3); // arrayES6: MyES6Array(3) [empty*3]

8、setTimeout、Promise、Async/Await 的区别

注意setTimeout是异步执行函数 , 当js主线程运行到此函数时,不会等待setTimeout中的回调函数 ,会直接进行setTimeout下面的语句(尽管setTimeout的延迟时间为0时) 当执行完当前事件循环的时候,setTimeout中的回调会在下次事件循环中被执行

console.log('setTimeout start');
 
setTimeout(function(){
    console.log('setTimeout execute');
})
 
console.log('setTimeout end ');

// setTimeout start => setTimeout end => setTimeout execute

Promise 本身是同步的立即执行函数,当在执行体中执行resolve()或者reject()的时候,此时是异步操作,等主栈完成后,才会去执行resolve()/reject()中的方法

console.log('script start');
var promise1 = new Promise(function (resolve) {
    console.log('promise1');
    resolve();
    console.log('promise1 end');
}).then(function () {
    console.log('promise2');
})

setTimeout(function () {
    console.log('setTimeout');
})

console.log('script end');

// script start => promise1 => promise1 end =>script end =>promise2 => setTimeout

async函数返回一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成(await的函数),在执行函数体后面的语句,可以理解为,async让出了线程,跳出了async函数体,因此await函数后的语句相当于在then回调中执行.await的含义为等待,也就是async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。

async function async1(){
    console.log('async1 start');
    await async2(); 
    //等待 async2()返回之后 再执行下面的语句 ,
    // 相当于将 console.log('async1 end')异步化了 相当于 console.log('async1 end')在then之后执行了
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start');
async1();
console.log('script end')

// script start->async1 start->async2->script end->async1 end
async function async1() {
	console.log('async1 start');
	await async2();
	console.log('asnyc1 end');
}
async function async2() {
	console.log('async2');
}
console.log('script start');
setTimeout(() => {
	console.log('setTimeout');
}, 0);
async1();
new Promise(function (reslove) {
	console.log('promise1');
	reslove();
}).then(function () {
	console.log('promise2');
})
console.log('script end');

// 结果
script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeout
  1. 主进程必须是空闲的状态,如果到时间了,主进程不空闲也不会执行你的回掉函数
  2. 这个回掉函数需要等到插入异步队列时前面的异步函数都执行完了,才会执行

9. async/await 如何通过同步的方式实现异步

function requestA() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({ age: 20 });
        }, 1000 * 2);
    });
}

async function getData() {
    console.log('数据加载第一步');
    let result = await requestA();
    console.log('数据加载第二步');

    return result;
}

getData().then(res => {
    console.log('数据请求完毕', res);
});

// 数据加载第一步
// 2秒后
// 数据加载第二步
// 数据请求完毕 { age: 20 }

10. 已知如下数组,编写一个程序将数组扁平化并去除其中重复部分数据,最终得到一个升序且不重复的数组

var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
  1. 数组flat方法
let newArr = arr.flat(Infinity)
  1. 正则
let strArr = JSON.stringify(arr);
let newArr = strArr.replace(/(\[\]))/g, '').split(',');
  1. 递归
let newArr = [];
let fn = function(arr) {
    for(let i = 0; i < arr.length; i++) }{
        let item = arr[i];
        if (Array.isArray(arr[i])){
            fn(item);
        } else {
            newArr.push(item);
        }
    }
}
  1. reduce
function faltten(arr) {
    return arr.reduce((accumulator, current) => {
        return accumulator.concat(
            Array.isArray(current) ? 
            faltten(current) : 
            current
            );
    }, []);
}
let newArr = flatten(arr)
console.log(flatten(arr))
  1. 扩展运算符
while(arr.some(Array.isArray)){
    arr=[].concat(...arr)
}
console.log(arr);
[...new Set(newArr)]
Array.from(new Set(newArr))
.sort((a, b) => a - b)

标签:function,console,log,面试,key,new,十题,setTimeout
来源: https://www.cnblogs.com/jialuchun/p/15115113.html