高级前端面试题 js 2

第 1 题:(滴滴、饿了么)写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

1. 更准确
因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。

2. 更快
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。主要是为了提升diff【同级比较】的效率。自己想一下自己要实现前后列表的diff,如果对列表的每一项增加一个key,即唯一索引,那就可以很清楚的知道两个列表谁少了谁没变。而如果不加key的话,就只能一个个对比了。
vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。 在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。 vue部分源码如下:

解析:第 1 题

第 2 题:[‘1‘, ‘2‘, ‘3‘].map(parseInt) what & why ?

首先让我们回顾一下,map函数的第一个参数callback:
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])
这个callback一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。

而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。

了解这两个函数后,我们可以模拟一下运行情况

parseInt(‘1‘, 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt(‘2‘, 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
parseInt(‘3‘, 2) //基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN
map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]

解析:第 2 题

第 3 题:(挖财)什么是防抖和节流?有什么区别?如何实现?

JavaScript专题之跟着underscore学防抖
JavaScript专题之跟着 underscore 学节流

防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

思路
每次触发事件时都取消之前的延时调用方法

    /* 防抖 */
    function dou(fn, wait) {
        var time = null;
        return function () {
            clearTimeout(time)
            // time = setTimeout(function () {
            //  console.log(this)//window
            //  fn.apply(this, arguments)//这样的话 this为window和直接 fn()调用是一样的效果,因为他们的this都是window
            // }, wait);
            time = setTimeout(() => {
                // console.log(this)//div
                fn.apply(this, arguments)//确保dou函数的this(上下文还是div)
            }, wait);
        }
    }
    function demo() {
        console.log(‘防抖啦‘)
    }
    // 用句柄事件绑定调用dou事件,所以this为div节点对象
    document.querySelector(‘div‘).addEventListener(‘scroll‘, dou(demo, 1000))

节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

思路
每次触发事件时都判断当前是否有等待执行的延时函数

    /* 节流 */
    function throttle(func, wait) {
        var previous = 0;
        return function () {
            var now = +new Date();
            if (now - previous > wait) {
                func.apply(this, arguments);
                previous = now;
            }
        }
    }
    function getUserAction() {
        console.log(`每秒1秒内打印一次`)
    }
    document.querySelector(‘div‘).addEventListener(‘click‘, throttle(getUserAction, 1000))

解析:第 3 题

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

Set
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has

WeakSet
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
不能遍历,方法有add、delete、has

Map
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换

WeakMap
只接受对象作为键名(null除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有get、set、has、delete

解析:第 4 题

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

解析:第 5 题

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

解析:第 6 题

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

先看ES5的继承(原型链继承)

function a() {
  this.name = ‘a‘;
}
a.prototype.getName = function getName() {
  return this.name
}
function b() {}
b.prototype = new a();
console.log(b.prototype.__proto__ === a.prototype); // true
console.log(b.__proto__ === a); // false
console.log(b.__proto__); // [Function]

ES6继承

class A {
  constructor(a) {
    this.name = a;
  }
  getName() {
    return this.name;
  }
}

class B extends A{
  constructor() {
    super();
  }
}

console.log(B.prototype.__proto__ === A.prototype); // true
console.log(B.__proto__ === A); // true
console.log(B.__proto__); // [Function: A]

对比代码可以知道,子类的继承都是成功的,但是问题出在,子类的 __proto__ 指向不一样。

ES5 的子类和父类一样,都是先创建好,再实现继承的,所以它们的指向都是 [Function] 。

ES6 则得到不一样的结果,它指向父类,那么我们应该能推算出来,它的子类是通过 super 来改造的。

根据 es6--阮一峰 在class继承里面的说法,是这样子的:

引用阮一峰的 ECMAScript6入门 的class继承篇

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

1、class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。

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

3、class 的所有方法(包括静态方法和实例方法)都是不可枚举的。

4、class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。

5、必须使用 new 调用 class。

6、class 内部无法重写类名

解析:第 7 题

第 8 题:setTimeout、Promise、Async/Await 的区别

其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行; promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

解析:第 8 题

第 9 题:(头条、微医)Async/Await 如何通过同步的方式实现异步

async await 用于把异步请求变为同步请求的方式,第一个请求的返回值作为后面一个请求的参数,其中每一个参数都是一个promise对象

例如:这种情况工作中会经常遇到

(async () => {
    var a = await A();
    var b = await B(a);
    var c = await C(b);
    var d = await D(c);
})();

解析:第 9 题

第 10 题:(头条)异步笔试题

请写出下面代码的运行结果

async function async1() {
    console.log(‘async1 start‘);
    await async2();
    console.log(‘async1 end‘);
}
async function async2() {
    console.log(‘async2‘);
}
console.log(‘script start‘);
setTimeout(function() {
    console.log(‘setTimeout‘);
}, 0)
async1();
new Promise(function(resolve) {
    console.log(‘promise1‘);
    resolve();
}).then(function() {
    console.log(‘promise2‘);
});
console.log(‘script end‘);
复制代码

解析:第 10 题

第 11 - 20 题

第 11 题:(携程)算法手写题

已知如下数组:

var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];

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

    var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
    // 方法一
    console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))
    // 方法二
    console.log(Array.from(new Set(arr.toString().split(‘,‘))).map(Number).sort((a, b) => a - b))

    // 方法三
    // 第一步:扁平化
    let newArr = [];
    function flat(originArr) {
        if ({}.toString.call(originArr) === ‘[object Array]‘) {
            for (let i of originArr) {
                if ({}.toString.call(i) === ‘[object Array]‘) {
                    arguments.callee(i)
                } else {
                    newArr.push(i)
                }
            }
        }
        return newArr;
    }
    console.log(flat(arr))
    // 第二步:去重
    var newArr1 = [];
    for (let i of newArr) {
        if (!newArr1.includes(i)) newArr1.push(i);
    }
    // 第三步:排序 可以采用相关算法处理
    console.log(newArr1.sort((a, b) => a - b))

解析:第 11 题

第 12 题:(滴滴、挖财、微医、海康)JS 异步解决方案的发展历程以及优缺点。

1. 回调函数(callback)

    setTimeout(() => {
        // callback 函数体
    }, 1000)

缺点:回调地狱,不能用 try catch 捕获错误,不能 return`
回调地狱的根本问题在于

缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转,嵌套函数过多的多话,很难处理错误

    ajax(‘XXX1‘, () => {
        // callback 函数体
        ajax(‘XXX2‘, () => {
            // callback 函数体
            ajax(‘XXX3‘, () => {
                // callback 函数体
            })
        })
    })

优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)

2. Promise
Promise就是为了解决callback的问题而产生的。

Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被Promise.resolve() 包装

优点:解决了回调地狱的问题

    ajax(‘XXX1‘)
        .then(res => {
            // 操作逻辑
            return ajax(‘XXX2‘)
        }).then(res => {
            // 操作逻辑
            return ajax(‘XXX3‘)
        }).then(res => {
            // 操作逻辑
        })

缺点:无法取消 Promise ,错误需要通过回调函数来捕获

3. Generator
特点:可以控制函数的执行,可以配合 co 函数库使用

    function* fetch() {
        yield ajax(‘XXX1‘, () => { })
        yield ajax(‘XXX2‘, () => { })
        yield ajax(‘XXX3‘, () => { })
    }
    let it = fetch()
    let result1 = it.next()
    let result2 = it.next()
    let result3 = it.next()

4. Async / await
async、await 是异步的终极解决方案

优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题

缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

async function test() {
        // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
        // 如果有依赖性的话,其实就是解决回调地狱的例子了
        await fetch(‘XXX1‘)
        await fetch(‘XXX2‘)
        await fetch(‘XXX3‘)
    }

解析:第 12 题

第 13 题:(微医)Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

    const promise = new Promise((resolve, reject) => {
        console.log(1)
        resolve()
        console.log(2)
    })

    promise.then(() => {
        console.log(3)
    })

    console.log(4)

执行结果是:1243
promise构造函数是同步执行的,then方法是异步执行的

解析:第 13 题

第 14 题:(兑吧)情人节福利题,如何实现一个 new

先理清楚 new 关键字调用函数都的具体过程,那么写出来就很清楚了

首先创建一个空的对象,空对象的 ___proto____属性指向构造函数的原型对象
把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象
如果构造函数返回一个非基本类型的值,则返回这个值,否则上面创建的对象

function _new(fn, ...arg) {
    var obj = Object.create(fn.prototype);
    const result = fn.apply(obj, ...arg);
    return Object.prototype.toString.call(result) == ‘[object Object]‘ ? result : obj;
}

解析:第 14 题

第 15 题:(网易)简单讲解一下http2的多路复用

解析:第 15 题

第 16 题:谈谈你对TCP三次握手和四次挥手的理解

解析:第 16 题

第 17 题:A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态

如果A 与 B 建立了正常连接后,从未相互发过数据,这个时候 B 突然机器重启,问 A 此时处于 TCP 什么状态?如何消除服务器程序中的这个状态?(超纲题,了解即可)

解析:第 17 题

第 18 题:(微医)React 中 setState 什么时候是同步的,什么时候是异步的?

解析:第 18 题

第 19 题:React setState 笔试题,下面的代码输出什么?

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }

  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
};
复制代码

解析:第 19 题

第 20 题:介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?

解析:第 20 题

原文地址:https://www.cnblogs.com/fs0196/p/12639206.html

时间: 2024-08-11 18:39:57

高级前端面试题 js 2的相关文章

前端面试题 -- JS篇

前端面试题 -- JS篇 类型 1.js中有哪些数据类型,并解释清楚原始数据类型和引用数据类型 js中共有null,undefined, string,number,boolean,object六种数据类型. 原始数据类型: null,undefined, string,number,boolean 引用数据类型:object 两者的区别:1)值存储方式不同: 原始数据类型:将变量名和值都存储在栈内存中 引用数据类型:将变量名存储在栈内存中,将值存储在堆内存中,并在栈内存中存储值的地址,该地址指

前端面试题 | JS部分(附带答案)

目前在找工作,所以各方收集了一堆面试题.其实刷面试题的过程也能更新自己对知识的认识,所以也提醒自己多看多理解.如果对下面题目有更深理解,会实时更新.遇到新题目,也会不定时更新.希望能帮助到部分朋友- 一.各种函数方法定义及区别 1. typeof运算符和instanceof运算符以及isPrototypeOf()方法的区别 typeof是一个运算符,用于检测数据的类型,比如基本数据类型null.undefined.string.number.boolean,以及引用数据类型object.func

前端面试题js和jQuery

真正面试中,面试官往往采用的是由难到易的套路,那javascript和jQuery就是重中之重了,以及针对项目和所用技术方面的一些问题也就是你的必备储粮啦! JavaScript 什么是闭包(closure),为什么要用它? 闭包简单的说就是一个函数能访问外部函数的变量,这就是闭包,不理解就看代码,例如: function aa(x){ var num=1; function bb(y){ console.log(x+y+(++num)); } } aa函数中的bb函数就是闭包了,bb函数可以使

各大互联网公司前端面试题(js)

对于巩固复习js更是大有裨益.    初级Javascript: 1.JavaScript是一门什么样的语言,它有哪些特点? 没有标准答案. 2.JavaScript的数据类型都有什么? 基本数据类型:String,Boolean,Number,Undefined, Null 引用数据类型:Object(Array,Date,RegExp,Function) 那么问题来了,如何判断某变量是否为数组数据类型? 方法一.判断其是否具有“数组性质”,如slice()方法.可自己给该变量定义slice方

前端面试题(JS篇)

原题地址:http://handyxuefeng.blog.163.com/blog/static/454521722013111714040259/ 好吧,最近打算换工作,所以关注比较多的是面试题,这套还不错,留下~~ 1.JS相关问题: 解释下事件代理(基础题) 解释下 JavaScript 中 this 是如何工作的(始终指向调用当前函数的对象) 解释下原型继承的原理. AMD vs. CommonJS? 什么是哈希表? 解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式):f

Web前端面试题-1

Web前端面试题 Web前端面试题 1 HTML/CSS部分 4 1.什么是盒子模型?有几种? 4 2.行内元素有哪些?块级元素有哪些? 空(void)元素有那些? 4 3.CSS实现垂直水平居中 4 4.简述一下src与href的区别 5 5.什么是CSS Hack? 5 6.简述同步和异步的区别 6 7.px和em的区别 6 8. 什么叫优雅降级和渐进增强? 6 9.浏览器的内核分别是什么? 7 10.XHTML和HTML有什么区别 7 12.前端页面有哪三层构成,分别是什么?作用是什么?

前端排序算法总结;前端面试题2.0;JavaScript异步编程

1.前端 排序算法总结 排序算法可能是你学编程第一个学习的算法,还记得冒泡吗? 当然,排序和查找两类算法是面试的热门选项.如果你是一个会写快排的程序猿,面试官在比较你和一个连快排都不会写的人的时候,会优先选择你的.那么,前端需要会排序吗?答案是毋庸置疑的,必须会.现在的前端对计算机基础要求越来越高了,如果连排序这些算法都不会,那么发展前景就有限了.本篇将会总结一下,在前端的一些排序算法. https://segmentfault.com/a/11... 2.前端面试题 V2.0 详见: 这是一份

Web前端面试题集锦

Web前端面试题集锦 前端开发面试知识点大纲: 注意 转载须保留原文链接(http://www.cnblogs.com/wzhiq896/p/5927180.html )作者:wangwen896 HTML&CSS: 对Web标准的理解.浏览器内核差异.兼容性.hack.CSS基本功:布局.盒子模型.选择器优先级及使用.HTML5.CSS3.移动端适应. JavaScript: 数据类型.面向对象.继承.闭包.插件.作用域.跨域.原型链.模块化.自定义事件.内存泄漏.事件机制.异步装载回调.模板

前端面试题2016--HTML

本文由我收集总结了一些前端面试题,初学者阅后也要用心钻研其中的原理,重要知识需要系统学习.透彻学习,形成自己的知识链.万不可投机取巧,临时抱佛脚只求面试侥幸混过关是错误的!也是不可能的!不可能的!不可能的! 前端还是一个年轻的行业,新的行业标准, 框架, 库都不断在更新和新增.正如赫门在2015深JS大会上的<前端服务化之路>主题演讲中说的一句话:“每18至24个月,前端都会难一倍”,这些变化使前端的能力更加丰富.创造的应用也会更加完美.所以关注各种前端技术,跟上快速变化的节奏,也是身为一个前