[toc]
必赛三六科技发展有限公司面试题(北京)
1.请实现一个模块math,支持链式调用math.add(2,4).minus(3).times(2);
class Math {constructor(value) {let hasInitValue = true;if (value === undefined) {value = NaN;hasInitValue = false;}Object.defineProperties(this, {value: {enumerable: true,value: value,},hasInitValue: {enumerable: false,value: hasInitValue,},});}add(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv + cv, init);return new Math(value);}minus(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv - cv, init);return new Math(value);}times(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv * cv, init);return new Math(value);}divide(...args) {const init = this.hasInitValue ? this.value : args.shift();const value = args.reduce((pv, cv) => pv / cv, init);return new Math(value);}toJSON() {return this.valueOf();}toString() {return String(this.valueOf());}valueOf() {return this.value;}[Symbol.toPrimitive](hint) {const value = this.value;if (hint === 'string') {return String(value);} else {return value;}}}export default new Math();
2.请简述ES6代码转成ES5代码的实现思路。
- 将代码字符串解析成抽象语法树,即所谓的 AST
- 对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
- 根据处理后的 AST 再生成代码字符串
比如,可以使用 @babel/parser 的 parse 方法,将代码字符串解析成 AST;使用 @babel/core 的 transformFromAstSync 方法,对 AST 进行处理,将其转成 ES5 并生成相应的代码字符串;过程中,可能还需要使用 @babel/traverse 来获取依赖文件等。
3.请实现一个深拷贝。
<!--普通的深层拷贝函数: -->function deepCopy( source ) {if (!isObject(source)) return source; //如果不是对象的话直接返回let target = Array.isArray( source ) ? [] : {} //数组兼容for ( var k in source ) {if (source.hasOwnProperty(k)) {if ( typeof source[ k ] === 'object' ) {target[ k ] = deepCopy( source[ k ] )} else {target[ k ] = source[ k ]}}}return target}function isObject(obj) {return typeof obj === 'object' && obj !== null}// 缺点:(1)无法保持引用(2)当数据的层次很深,会栈溢出<!--防栈溢出函数-->function cloneLoop(x) {const root = {};// 栈const loopList = [{parent: root,key: undefined,data: x,}];while(loopList.length) {// 深度优先const node = loopList.pop();const parent = node.parent;const key = node.key;const data = node.data;// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素let res = parent;if (typeof key !== 'undefined') {res = parent[key] = {};}for(let k in data) {if (data.hasOwnProperty(k)) {if (typeof data[k] === 'object') {// 下一次循环loopList.push({parent: res,key: k,data: data[k],});} else {res[k] = data[k];}}}}return root;}<!--最简单的深拷贝方式-->function clone(obj) {return JSON.parse(JSON.stringify(obj));}
4.什么是防抖和节流?有什么区别?如何实现?
防抖和节流都是指防止用户短时间内多次重复触发某一行为区别在于:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
<!--防抖-->function debounce (func, wait = 50, immediate = true) {let timer, context, args// 延迟执行函数const later = () => setTimeout(() => {// 延迟函数执行完毕,清空缓存的定时器序号timer = null// 延迟执行的情况下,函数会在延迟函数中执行// 使用到之前缓存的参数和上下文if (!immediate) {func.apply(context, args)context = args = null}}, wait);// 这里返回的函数是每次实际调用的函数return function(...params) {// 如果没有创建延迟执行函数(later),就创建一个if (!timer) {timer = later()// 如果是立即执行,调用函数// 否则缓存参数和调用上下文if (immediate) {func.apply(this, params)} else {context = thisargs = params}// 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个// 这样做延迟函数会重新计时} else {clearTimeout(timer)timer = later()}}}<!--节流-->function throttle(fn) {let canRun = true; // 通过闭包保存一个标记return function () {if (!canRun) return; // 在函数开头判断标记是否为 true,不为 true 则 returncanRun = false; // 立即设置为 false// 将外部传入的函数的执行放在 setTimeout 中setTimeout(() => {fn.apply(this, arguments);// 最后在 setTimeout 执行完毕后再把标记设置为 true(关键)//表示可以执行下一次循环了。当定时器没有执行的时候//标记永远是 false,在开头被 return 掉canRun = true;}, 500);};}
5.如何实现函数柯里化?
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。———— from 维基百科
<!--方法一-->const curry = ( fn, arr = []) => {return (...args) => {//判断参数总数是否和fn参数个数相等if([...arr, ...args].length === fn.length){return fn(...arr, ...args) //拓展参数,调用fn}else{return curry(fn,[...arr, ...args]) //迭代,传入现有的所有参数}}}<!--方法二-->const curry = ( fn, arr = []) => (...args) => ( a => a.length === fn.length? fn(...a) : curry(fn, a))([...arr, ...args])
6.[“1”,”2”,”3”,”4”,”5”,6,7,8,9,10,11,12,13,14,15].map(parselnt);
[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19]<!--parseInt(数字, 该数字为几进制的数)-->
