阿里巴巴笔试题库

题目一:解构赋值

/**
      * 请实现一个通用的Array解构赋值方法 destructuringArray,
      *     可将目标数组(targetArray)通过ES6的解构格式(formater),
      *     输出解构结果对象。
      * 考察点:
      *     字符串、数组、对象处理 / 解构概念 / 数据结构 / 正则
      *
      * 设计参考(可选提供):
      *
      * - 通过优化函数名及参数语义以方便考生快速理解题目。
      *   - 函数名为 destructuringArray(解构数组)功能是将目标数组通过以下解构格式解构为最终对象。
      *   - 入参1是目标数组:targetArray{array}
      *   - 入参2是解构格式:formater{string}
      *   - 输出为 {object},。
      *
      *   Example:
      *      destructuringArray( [1,[2,3],4], "[a,[b],c]" );
      *      // result
      *      { a:1, b:2, c:4 }
      */

    /**
    * 定义
    */
    var 
        // 先准备好目标数组
        targetArray = [1,[2,3],4],

        // 再准备好解构格式
        formater = "[a,[b],c]",

        /**
          * 解构数组
          * @param targetArray{Array} 目标数组
          * @param formater{String} 解构格式
          * @return {Object} 结果对象
          */
        destructuringArray = function(targetArray,formater){

        };

// 运行
console.dir( destructuringArray(targetArray,formater) )

参考答案一:

var
// 先准备好目标数组
    targetArray = [1, 5, [2, [3, 7]], 4],

    // 再准备好解构格式
    formater = "[a,e,[b,[c,f]],d]",

    /**
     * 解构数组
     * @param targetArray{Array} 目标数组
     * @param formater{String} 解构格式
     * @return {Object} 结果对象
     */
    destructuringArray = function(targetArray, formater) {
        const helper = {};

        const len = formater.length;
        let i = -1;

        let curVar = '';

        let curLayer = -1;

        const layer = {};

        // 只遍历一次,保证最小时间复杂度: O(n)
        while (i++ < len - 1) {
            let curCode = formater.charAt(i);

            if (curCode === '[') {
                curLayer++; // 层级加一级
            } else if (curCode === ']') {
                if (isNum(layer[curLayer])) {
                    layer[curLayer]++;
                } else {
                    layer[curLayer] = 0;
                }

                curVar && (helper[curVar] = getAryItem(targetArray, layer, curLayer));
                curVar = '';

                curLayer--;
            } else if (curCode === ',') { // 当前层级加一
                if (isNum(layer[curLayer])) {
                    layer[curLayer]++;
                } else {
                    layer[curLayer] = 0; // 从0开始
                }

                curVar && (helper[curVar] = getAryItem(targetArray, layer, curLayer));

                curVar = '';
            } else {
                curVar += curCode;
            }
        }

        return helper;
    };

function isNum(arg) {
    return typeof(arg) === 'number';
}

function getAryItem(ary, layer, curLayer) {
    if (curLayer === 0) {
        return ary[layer[curLayer]];
    }

    let res = ary;

    const keys = Object.keys(layer);

    keys.forEach((key, index) => {
        if (key <= curLayer) {
            if (index === keys.length - 1) {
                res = res[layer[key]];
            } else {
                res = res[layer[key] + 1];
            }
        }
    });

    return res;
}

// 运行
const res = destructuringArray(targetArray, formater);
console.log('最终的结果:');
console.dir(res);

参考答案二:

var 
    // 先准备好目标数组
    targetArray = [1,[2,3],4],

    // 再准备好解构格式
    formater = "[a,[b],c]",

    /**
      * 解构数组
      * @param targetArray{Array} 目标数组
      * @param formater{String} 解构格式
      * @return {Object} 结果对象
      */
    destructuringArray = function(targetArray,formater){

        var 
            // 准备结果容器
            result = {},
            // 创建编制器
            formater = eval( formater.replace(/(\w+)/g,"'$1'") )
            ;

        // 创建映射函数
        var mapping = function(targetArray,formater){
            for(var i=0; i<formater.length; i++){
                var formaterItem = formater[i],
                    targetItem = targetArray[i]
                    ;
                // 如果是数组则进行递归
                if(Array.isArray(formaterItem)){
                    mapping(targetItem,formaterItem);
                }else if(formaterItem){
                    result[formaterItem] = targetItem;
                }
            }
        };

        // 开始映射
        mapping(targetArray,formater);

        // 返回对象
        return result;
    }
    ;

// 运行
console.dir( destructuringArray(targetArray,formater) )

题目二:随机字符串

/**------- 编码题目一 --------- **/

/**
 * 说明:生成一个指定长度(默认6位)的随机字符,随机字符包含字母数字。
 * 输入:输入随机字符长度,无输入默认6位
 * 输出:随机字符,如"6bij0v"
 */

常规解法:在给定字符空间内取随机组合。

/**
 * 生成长度为n的随机索引序列。
 * @param {Number} n - 索引长度{1,}
 * @param {Number} max - 最大索引值{0,}
 * @param {Boolean} repeat - 是否允许重复
 */
function genRandomIndex(n = 1, max = 0, repeat = true) {
    let res = [],
        regExN = /\d+/;
    // 若n非数值或<1, 返回空Array
    if (!regExN.test(n) || n < 1) {
        return [];
    }
    // 若max非数值或<0, 返回空Array
    if (!regExN.test(max) || max < 0) {
        return [];
    }

    n = Math.floor(n);
    max = Math.floor(max);
    repeat = !repeat ? 0 : 1;

    let maxN = max + 1;

    if (!repeat && n > maxN) {
        throw Error('At non-repeated mode n must lte max + 1');
    }

    let genRndInx = (maxN) => {
        return Math.floor(Math.random() * maxN);
    };

    while (res.length < n) {
        let rndInx = genRndInx(maxN);
        if (!repeat) {
            while (res.includes(rndInx)) {
                rndInx = genRndInx(maxN);
            }
        }
        res.push(rndInx);
    }

    return res;
}

/**
 * 生成由字母及数字组成,长度为n的随机字符串。
 * @param {Number} n - default 6
 * @param {Boolean} repeat - 是否允许重复
 */
function randomstr(n = 6, repeat = true) {
    let rndInx = genRandomIndex(n, 61, repeat); // 0-9A-Za-z
    let res = rndInx.map((val, inx, ary) => {
        if(val <= 9){
            return val;
        }
        if(val >= 10 & val <= 35){
            return String.fromCharCode(val + 55);
        }
        if(val >= 36 & val <= 61){
            return String.fromCharCode(val + 61);
        }
    });
    return res.join('');
}

36进制转换解法,此解法有限制,无法区分字母大小写,无法处理是否重复的问题

/**
 * 生成由字母及数字组成,长度为n的随机字符串。
 * @param {Number} n - default 6
 */
function randomstr(n = 6) {
    let res = '';
    // 若n非数值或<=0, 返回空串
    if (!/\d+/.test(n) || n <= 0) {
        return '';
    }
    // 若n非整数,向下取整
    n = Math.floor(n);

    let max = Number.MAX_SAFE_INTEGER,
        log36max = Math.floor(Math.log(max) / Math.log(36)), // 计算以36为底,max的对数,即单次可生成的最大随机串长度
        min = parseInt('9'.repeat(log36max - 1), 36), // 确保单次生成长度为log36max
        roundn = Math.ceil(n / log36max); // 根据n判断需要生成多少轮

    for (let i = 0; i < roundn; i++) {
        res += Math.floor(Math.random() * max - min).toString(36);
    }

    return res.substr(0, n);
}

/**
 * 生成由字母及数字组成,长度为n的随机字符串。
 * @param {Number} n - default 6
 */
function randomstr(n = 6) {
    let res = '';
    // 若n非数值或<=0, 返回空串
    if (!/\d+/.test(n) || n <= 0) {
        return '';
    }

    // 若n非整数,向下取整
    n = Math.floor(n);

    while (res.length < n) {
        res += Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
    }

    return res.substr(0, n);
}

题目三

  • 请使用原生代码实现一个无线页面上长按的事件
    阅卷参考:知识域: 无线端事件、自定义事件 解析: 考察对无线事件的熟悉程度,自定义事件封装能力。
 var longTapBtn = document.getElementById('long-tap'),
    longTapTimeout, longTap = function(target) {
        var eventName = 'longTap'; // 得分点: 封装成自定义事件
        try {
            var event = document.createEvent('CustomEvent');
            event.initCustomEvent(eventName, false, true);
        } catch (e) {
            var event = document.createEvent("Event");
            event.initEvent(eventName, false, true);
        }
        target.dispatchEvent(event);
    }; // 得分点: 针对于全局定义

 window.addEventListener('touchstart', function(e) {
    longTapTimeout = setTimeout(function() {
        longTap(e.target);
    }, 1000);
 });

 window.addEventListener('touchend', function(e) {
    clearTimeout(longTapTimeout);
 }); // 得分点: 判断移动

 window.addEventListener('touchmove', function(e) {
    clearTimeout(longTapTimeout);
 });

 longTapBtn.addEventListener('longTap', function(e) {
    console.log('longTap');
 });

题目四:节流

  • 在移动开发中会有触发比较频繁的事件,比如用户滚动屏幕引起图片的懒加载等;请完成下面函数throttle
//e.g
var FuncA = function(){}; 
throttle(A,200); //让A 200ms不被调用后才执行一次;

// method为传入方法,delay为延迟时间;

注:返回一个函数 function throttle(method,delay){}

阅卷参考:参考答案:

var throttle = function(fn, delay) {
    var timer = null;
    return function() {
        var context = this,
            args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function() {
            fn.apply(context, args);
        }, delay);
    };
}

知识域: 移动 难度系数: B 解析: 本题主要考察对移动开发中常见问题:函数节流的理解;

题目五:BindX

  • 为Function扩展一个方法bindX,可以实现如下功能
  function add(num1, num2) {
      return this.value + num1 + num2;
  }

  var data = {
      value: 1
  };

  var addEx = add.bindX(data, 2);

  addEx(3);    // 6

阅卷参考:答案:

Function.prototype.bindX = function(that) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        return self.apply(that, args.concat(Array.prototype.slice.call(arguments)))
    }
};

知识域:javscript 难度系统数:C 解析:考查对javscript函数调用的理解

题目七:最长子串

  • 给定字符串,仅包含左括号(和右括号),它可能不是括号匹配的,设计算法,找出最长匹配的括号子串,返回该子串的长度。
    如:
    • (() => () => 2
    • ()() => ()() => 4
    • ()(()) => ()(()) => 6
    • ((()()) => (()()) => 6

阅卷参考:// 参考答案

 function getLongestParenthese(str) {
    const len = str.length;
    const stack = [];
    let answer = 0;
    let start = -1;
    for (let i = 0; i < len; i++) {
        if (str[i] === '(') {
            stack.push(i);
        } else {
            if (stack.length === 0) {
                start = i;
            } else {
                stack.pop();
                if (stack.length === 0) {
                    answer = Math.max(answer, i - start);
                } else {
                    answer = Math.max(answer, i - stack[stack.length - 1]);
                }
            }
        }
    }
    return answer;
 }

题目八:DSL

  • 我们知道领域特定语言(以下简称DSL)是通过编程的方式解决某个特定领域的问题。假设现在团队希望你使用JavaScript设计一个基于MVC的应用模式的DSL来实现大型WebApp的开发,简单描述下你的思路及需要注意的问题。

阅卷参考:解析: 主要考察对于大型JavaScript应用程序的开发如何平衡开发效率及可维护性,以及设计DSL需要注意的一些问题 答题思路(仅供参考) 首先需要明白什么是大型JavaScript应用程序? 基于特定的应用架构的,需要持续维护的,UI逻辑展示较为复杂的应用程序 其次再看DSL在其中承担什么作用? DSL在其中主要用来解决在某种应用架构模式下面的痛点来提升开发效率,比如针对本题可以从MVC应用模式的一些问题点出发。 再看设计DSL的一些原则 比如使用起来足够清晰 , 要具有一定的可维护性,可调试性,可扩展性等 按照上面的原则,最后简单表述如何实现一个DSL 比如功能层面使用词法分析和语法分析等, 维护层面给出一定的调试方案等。

题目九:redux中间件

  • 下面是一个redux中间件,补充代码,使得dispatch支持action为函数作为返回值
  export default function() {
    return ({ dispatch, getState }) => next => action => {
      if (______) {
        return ______;
      }
      return next(action);
    };
  };

答案: 1 typeof action === 'function' 2 action(dispatch, getState)  难度系统数:B 解析:考查对redux中间件的了解

题目十:正则

  • 请使用2-3次replace方法,将下面字符串转换成JSON
// 注意:一个replace中有两处填写,其中有一处填写,默认已使用一次replace
// 字符串str
{
    msg = "{\"url\":\"MBU0016G7G004SS00081\",\"data\":{\"a\":1,\"b\":[2,3]}}";
}

function string2Json(str) {
    str = str.replace(/{\n.+=.\"/, '')
        .replace(/\";\n}/g, '')
        .replace(/\\/g, '')
    return JSON.parse(str)
}

阅卷参考:答案: 1. /{\n.+=."/, ‘’ 2. /";\n}/g, ‘’, 3. /\/g, ‘’ 知识域: JS正则表达式 难度系统数:B 解析: 题干信息较少,但最后JSON.paese()提醒只需将字符串转换为满足JSON的字符串即可

题目十一:undo/redo

  • 一些具有操作记录的系统,如店铺装修、富文本编辑等,都具有undo/redo(撤销/恢复)功能,可实现界面操作过程的撤销和恢复。简述下由你来开发undo/redo功能的原理和思路。

[功能设计] 考查对一具体需求的分析和抽象能力,再展开设计。能否抽象出核心原理,在于运行过程中产生数据的存储,undo/redo指令触发数据应用更新到视图上的过程。

[架构设计] 考查对小型功能库设计的技术分析和技术方案选型过程,通用性扩展性是否思考到位。更佳的能否明确的时序图以及架构图。 - [技术实施] 技术实施上的具体考量,比如是否能描述数据存储的结构设计,使用命令模式或是Immutable(持久化数据结构)等模式设计,是否有流程机制方面的控制等。

[功能缺陷] 能够理性分析出自己在某种思路下的设计缺陷和优点,以及适用的场景,并且可以阐述缺陷与优点的平衡。 难度系统数:A

题目十二:UI

  • 编写 HTML 和 CSS 代码,实现如下需求:
  1. 实现一个 Button 组件的 UI 样式, body{font-size: 14px;}
  2. Button 的 padding 和 border 计算在宽度内
  3. 有三种层级: primary, secondary ,normal
  4. 有 三种尺寸: 所有尺寸下 Button 的文本均垂直居中对齐。 large(height:40px;font-size:28px;padding:0 20px;) medium(height:28px;font-size:16px;padding: 0 12px;) small(height:16px;font-size:12px;padding:0 8px;)
  5. 有三种状态:
    所有状态下均无边框。
    normal
    • primary(background-color: #FF7519; color:#FFF;)
    • secondary(background-color: #2196F3; color: #FFF;)
    • normal(background-color:#FFF; color: #333;)

hover

  • primary(background-color: #EB650C; color: #FFF;)
  • secondary(background-color: #0F87E6; color: #FFF;)
  • normal(background-color:#D9DCE0; color: #333;)

disabled

  • primary, secondary, normal(background-color: #CCC; color: #FFF;)
    1. 所有 Button 样式都有3px 的圆角,Hover 状态下有 0.3 秒的 easy-out 渐变动画
    2. 注意规避与其他 UI 库的样式冲突
    3. 考虑以后如果增加一种无 padding,且 height: 14px; font-size: 10px; 的 mini 尺寸的实现方案。
Normal: 
<button class="ui-button ui-button-primary ui-button-large">primary</button> 
<button class="ui-button ui-button-secondary ui-button-large">secondary</button> 
<button class="ui-button ui-button-normal ui-button-large">normal</button> 
disabled: 
<button class="ui-button ui-button-primary ui-button-large disabled">primary</button> 
<button class="ui-button ui-button-secondary ui-button-medium disabled">secondary</button> 
<button class="ui-button ui-button-normal ui-button-small disabled">normal</button> 
Size: 
<button class="ui-button ui-button-primary ui-button-large">large</button> 
<button class="ui-button ui-button-secondary ui-button-medium">medium</button> 
<button class="ui-button ui-button-normal ui-button-small">small</button> 
<button class="ui-button ui-button-normal ui-button-mini">mini</button>

<style type="text/css">
/* Bounding */
.ui-button{
    border-radius:3px;
    border:none;
    transition: all .3s ease-out;
    outline:none;
}
/* state: normal & hover & disabled */
.ui-button-primary{
    background: #FF7519;
    color: #FFF;
}
.ui-button-primary:hover{
    background: #EB650C;
    color: #FFF;
}
.ui-button-secondary{
    background: #2196F3;
    color: #FFF;
}
.ui-button-secondary:hover{
    background: #0F87E6;
    color: #FFF;
}
.ui-button-normal{
    background: #FFF;
    color: #333;
}
.ui-button-normal:hover{
    background: #D9DCE0;
    color: #333;
}
.ui-button-primary.disabled,
.ui-button-secondary.disabled,
.ui-button-normal.disabled{
    background: #CCC;
    color: #FFF;
}
/* size */
.ui-button-large{
    height: 40px;
    padding: 0 20px;
    line-height: 40px;
    font-size: 18px;
}
.ui-button-medium{
    height: 28px;
    padding: 0 12px;
    line-height: 28px;
    font-size: 14px;
}
.ui-button-small{
    height: 24px;
    padding: 0 12px;
    line-height: 22px;
    font-size: 12px;
}
.ui-button-mini{
    height:14px;
    line-height:10px;
    font-size: 10px \9;
    font-size:10px;
    transform: scale(0.7);
}
</style>

题目十三:media query

  • 我们经常用媒体查询来对页面进行定义,我们希望在iPhone5(宽:320px、高:568px),横屏时候会有不同的列表样式,请问以下空格应该填写什么内容?
 @media all and (______) and (______){
    /* 横屏样式 */
 }

min-width: 321px

max-width: 568px

题目十四:布局

  • 请使用两种不同的css方法(要求dom结构不同)实现下图所示的条形图。从左到右的条形分为记为A, B, C, D, E。 A的高度为30%, 颜色为#f00; B的高度为80%,颜色为#ddd;C的高度为70%,颜色为#0fd;D的高度为60%,颜色为#ff0; E的高度为90%, 颜色为#234,每个条形之间的距离可以任意设置(可以考虑使用css3新属性来实现)

image.png

阅卷参考:解法1:常用布局办法 html代码 解析:考察布局能力(可以采用传统的absolute,也可以使用flex布局),另外考验一下line-gradient灵活应用。

<div class="graph">
<div class="bar bar1"></div>
<div class="bar bar2"></div>
<div class="bar bar3"></div>
<div class="bar bar4"></div>
<div class="bar bar5"></div>
</div>
<style type="text/css">
.graph {
    width: 500px;
    height: 400px;
    border: 2px solid #000;
    border-top: none;
    border-right: none;
    position: relative
}
.bar {
    position: absolute;
    bottom: 0;
    width: 50px
}
.bar1 {
    left: 10px;
    height: 30%;
    background: #f00
}
.bar2 {
    left: 80px;
    height: 80%;
    background: #ddd
}
.bar3 {
    left: 160px;
    height: 70%;
    background: #0fd
}
.bar4 {
    left: 240px;
    height: 60%;
    background: #ff0
}
.bar5 {
    left: 320px;
    height: 90%;
    background: #234
}
</style>
<!-- 解法2:利用line-gradient实现 html代码 -->
<div class="graph"></div>
<style type="text/css">
.graph {
    width: 500px;
    height: 400px;
    border: 2px solid #000;
    border-top: none;
    border-right: none;
    background: linear-gradient(to top, #f00 30%, transparent 0) 20px 0, linear-gradient(to top, #ddd 80%, transparent 0) 90px 0, linear-gradient(to top, #0fd 70%, transparent 0) 160px 0, linear-gradient(to top, #ff0 60%, transparent 0) 230px 0, linear-gradient(to top, #234 90%, transparent 0) 300px 0;
    background-size: 50px 100%;
    background-repeat: no-repeat;
}
 </style>

题目十五:点击隐藏

  • 请在下面的空白处填空,实现下面的功能(可不考虑浏览器兼容性)
    在文档流中存在一个元素为A, 除去点击A元素,点击文档流中的任何一个元素让A元素隐藏。
    • 方法1:
      
      var A = document.getElementById('a');

document.addEventListener('click', function(e) { A.style.display = 'none'; }, false)

A.addEventListener('click', function(e) { e.stopPropagation(); })


   - 方法2:
```javascript
var hiddenTimeout, A = document.getElementById('a');

document.addEventListener('click', function(e) {
    hiddenTimeout = setTimeout(
    function() {
        A.style.display = 'none';
    }, 20)
}, __true__)

A.addEventListener('click', function(e) {
    if (hiddenTimeout) {
        __clearTimeout(hiddenTimeout)__;
    }
})

本题着重考察对于JavaScript的DOM事件执行顺序的理解,核心思路是冒泡的情况下阻止元素冒泡,捕获的情况下使用延时移除隐藏的行为

题目十六:find函数

考核点:

  • 链式调用
  • 简单的SQL
  • 闭包
/*
请实现find函数,使下列的代码调用正确。

  约定:

title数据类型为String
userId为主键,数据类型为Number
*/

var data = [
    {userId: 8, title: 'title1'},
    {userId: 11, title: 'other'},
    {userId: 15, title: null},
    {userId: 19, title: 'title2'}
];

var find = function(origin) {
    //请补充你的代码
}

//查找data中,符合条件的数据,并进行排序
var result = find(data).where({
    "title": /\d$/
}).orderBy('userId', 'desc');

console.log(result); // [{ userId: 19, title: 'title2'}, { userId: 8, title: 'title1' }];

本题着重考核综合素质以及解决问题的思路。 1. 链式调用 2. 闭包 3. 简单的SQL理解 4. 简单的过滤与排序 5. 简单的正则 参考答案:

var find = function(origin) {
    return {
        where: function(field, value) {
            return where(origin, field, value);
        }
    }
};
//查询条件
var where = function(origin, field, rule) {
    var result = filter(origin, field, rule);
    return {
        orderBy: function(orderField, direction) {
            return orderBy(result, orderField, direction)
        }
    }
};
//排序, 考虑到可能出现asc的情况加分
var orderBy = function(origin, field, direction) {
    return origin.sort(function(left, right) {
        var result = left[field] - right[field];
        return direction === 'desc' ? -result : result;
    });
};
//过滤
var filter = function(origin, field, rule) {
    var result = [];
    for (var i = 0; i < origin.length; i++) {
        var item = origin[i];
        var value = item[field];
        //需要考虑到value可能为null
        if (value && rule.test(value)) {
            result.push(item);
        }
    };
    return result;
}

题目十七:react 编译后代码

  • React可以视为MVC中的,在给React组件添加属性时,class属性需要写成__,JSX中的下面代码:
 <div>
     <input onChange={this.onChange} value={this.state.text} />
 </div>

编译成JS是__

阅卷参考:

答案:V,className, React.createElement("div", null, React.createElement("input", {onChange: this.onChange, value: this.state.text}) ) 知识域:React 难度系统数:B+ 解析: 考察一些基础概念和编译后结构

题目十八:react使用this指向问题

  • 请完成空白处代码,使得以下组件在选择时能够将对应的值打印出来
class Counter extends React.Component {
    select(val) {
        console.log('you have select ' + val);
    }
    render() {
        return (<ul>
           { 
               ['a','b','c'].map((item, index) => {
                   return <li onClick={______}>{item}</li>
               })
           }
       </ul>)
    }
}

阅卷参考:答案:

[普通答案] this.select.bind(this, item)

[lambda答案] ()=>{this.select(item)}

知识域:React事件, 作用域绑定 难度系数:B 解析:考察React中事件的自动绑定。对于组件本身的事件方法,官方推荐的写法是必须使用bind(this)的形式绑定:this.method.bind(this)}

题目十九:节流

  • 在编写搜索框组件的过程中,要求搜索结果可以实时响应,然而每次用户输入都向服务器发请求会给服务器造成过大的压力,所以要求在用户不断的输入文本的同时,组件会 按固定的频率发送搜索请求。请实现一个通用的函数,它接受第一个参数 action 代表一个函数,第二个参数 threshold 用于指定频率,它返 回一个新的函数,当新的函数在被不断调用时,action 会按指定频率运行。用例如下:
  • (Edit by tiny 原题题设有点问题)在编写文本编辑器组件的过程中,要求不断的帮助用户记录草稿,然而每次用户输入都向服务器发请求会给服务器造成过大的压力,所以要求在用户不断的输入文本的同时,组件会 按固定的频率发送保存草稿请求。请实现一个通用的函数,它接受第一个参数 action 代表一个函数,第二个参数 threshold 用于指定频率,它返 回一个新的函数,当新的函数在被不断调用时,action 会按指定频率运行。用例如下:
 const yourFunction = function(func, threshold) {
    // 请实现
 }
 const triggerSearch = yourFunction((val) => {
    const {
        onSearch
    } = this.props
    onSearch(val)
 }, 300)
 triggerSearch(searchText)

阅卷参考:参考答案:

// leading, trailing 皆为 true...,可配置更佳
export const throttle = function(func, thresold) {
    let called = false,
        lastargs = null
    return function(...args) {
        const context = this lastargs = args
        if (!called) {
            func.apply(context, args) lastargs = null called = true setTimeout(\_ => {
                called = false
                if (lastargs) func.apply(context, lastargs)
            }, thresold)
        }
    }
}

题目二十: EventStore

  • React基于单项数据流,对于组件间的通信支持不够好。业界解决组件间的通信的方案正为此而生。现需要模拟一个全局的EventStore,使得可以满足以下条件,以支持组件间的通信。

事件绑定(同一个key可以绑定多个事件):

EventStore.on({key:'counter/add', fn:function(payload){ 
 // payload是触发时传入的数据
}})

事件触发:

EventStore.trigger({key:'counter/add', payload:{step:5}});

阅卷参考:答案:

var EventStore = {
    events: {},
    on: function(eventObj) {
        /* 监听事件 */
        if (!eventObj || !eventObj[key] || !eventObj[fn]) return;
        var key = eventObj.key,
            fn = eventObj.fn;
        if (!this.events[key]) {
            this.events[key] = [];
        }
        this.events[key].push(fn);
    },
    trigger: function(eventObj) {
        /* 触发事件 */
        if (!eventObj || !eventObj[key]) return;
        var key = eventObj.key,
            payload = eventObj.payload || {};
        if (this.events[key]) {
            for (var i = 0, ln = this.events[key].length; i < ln; i++) {
                this.eventskey(payload);
            }
        } else {
            // do nothing...
        }
    }
};

知识域:React事件, 组件间通信实践 难度系数:B 解析:官方的通信解决Flux的dispatcher基于EventStore实现。考察对于解决组件间通信。

题目二十一: 內插

  • 內插是数学领域数值分析中的通过已知的离散数据求未知数据的过程或方法。根据若干离散的数据数据,得到一个连续的函数(也就是曲线)或者更加密集的离散方程与已知数据相吻合。这个过程叫做拟合。內插是曲线必须通过已知点的拟合。
    线性插值法是其中很简单的一种插值算法。如图所示:
    image.png
    已知坐标 (x0, y0) 与 (x1, y1),要得到 [x0, x1] 区间内某一位置 x 在直线上的值。则公式为
    image.png
    现在我拥有一份2000-2010的统计数据,但其中丢失了两年的数据(非2000年和2010年,不确定),并且顺序被打乱,请使用线性插值法编写一个函数insert,通过javascript补全这两年的数据。
    
    var data = [
    {year:2006,data:450},
    {year:2001,data:120},
    {year:2002,data:160},
    {year:2008,data:600},
    {year:2005,data:380},
    {year:2000,data:100},
    {year:2004,data:300},
    {year:2009,data:700},
    {year:2010,data:810}
    ]

function insert(data) { // }

insert(data);

<br />阅卷参考:参考解法: <br />知识域:Javascript 难度系统数:B 解析:考察对数据的处理能力 

<a name="8215af16"></a>
## 题目二十二: 顺序变换

```javascript
//有一个数组,里面只存在 _ 和 字母,比如 [‘_‘, ‘d’, ‘c’, ‘_‘, ‘e’, ‘_‘, ‘a’, ‘*‘]。现在需要把这个数组中的所有星号移动到左边,所有的字母移动到右边,所有字母的顺序不能改变。

var arr = ['*', 'd', 'c', '*', 'e', '*', 'a', '*'];

 function parse(arr){
     ...
 }

 console.log(parse(arr));

阅卷参考:解析: - 算法基础 - 发散思维 - 数组的相关用法

//参考解法一:常规思维
function parse(arr) {
    var counter = 0;
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == '*') {
            arr.splice(i, 1);
            counter++;
            i--;
        }
    }
    while (counter > 0) {
        arr.unshift('*');
        counter--;
    }
    return arr;
}
//参考解法二: 发散思维
function parse(arr) {
    var str = arr.join(''),
        counter = 0;
    str = str.replace(/\*/g, function() {
        counter++;
        return '';
    }) while (counter > 0) {
        str = '*' + str;
        counter--;
    }
    return str.split('');
}

题目二十三: type判断

  • 请为Common类编写一个getType方法,能够识别出不同的数据类型。
 var A = [1, 2];
 var B = "String";
 var C = /[\w]/g;
 var D = new Date();
 var E = new Error("something wrong");

 console.log(Common.getType(A)) //array
 console.log(Common.getType(B)) //string
 console.log(Common.getType(C)) //regexp
 console.log(Common.getType(D)) //date
 console.log(Common.getType(E)) //error

阅卷参考:解析:该题主要考核对数据类型的理解、底层基础知识以及编码通用性的考虑,实际很简单,但是容易进入误区,特别是如何进行识别数据类型 难度:B+ 领域:Javascript 参考答案

var Common = {
    // 定义所有数据类型
    _DATATYPES: ["Object", "Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Error", "Null", "Undefined"],
    // 数据类型对应MAP
    _TYPES: {},
    // 数据类型MAP是否初始化
    _INITED: false,
    getType: function(obj) {
        var result;
        // 初始化数据类型
        if (!this._INITED) {
            for (var i = 0; i < this._DATATYPES.length; i++) {
                var n = this._DATATYPES[i];
                this._TYPES["[object " + n + "]"] = n.toLowerCase();
            }
            this._INITED = true;
        }
        // 重点在于({}).toString.call的应用
        result = this._TYPES[({}).toString.call(obj)];
        return result ? result : typeof obj;
    }
};

题目二十四:filter实现

  • 使用JS实现Array的原生filter方法(具体实现功能看下面介绍),定义如下,filter(predicate, [context]), 返回根据predicate函数返回为true的时候的所有value组成的新的values数组。
    要求:
    • 不改变原数组。

参数解释:

  • predicate是一个函数(假如不是函数就抛出一个异常),将接受三个参数,列表每一项的值value, 列表每一项的索引值index, 原列表list;
  • context表示可以执行predicate时的上下文。

使用例子:

 var a = [1, 2, 3];
 var b = a.filter(function(value) {
     return value <= 2;
 });
 // b ==> [1, 2]

阅卷参考:答案:

if (!Array.prototype.filter) {
    Array.prototype.filter = function(fun /*, thisArg*/ ) {
        'use strict';
        if (this === void 0 || this === null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== 'function') {
            throw new TypeError();
        }
        var res = [];
        var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
        for (var i = 0; i < len; i++) {
            if (i in t) {
                var val = t[i];
                if (fun.call(thisArg, val, i, t)) {
                    res.push(val);
                }
            }
        }
        return res;
    };
}

题目二十五:怎么打印出来

5分钟简历筛选用的,本题现实中肯定不会遇到,属于脑筋急转弯类型,考察选手对defineProperty的掌握

// 本代码在浏览器中执行,在此填充代码是下面的打印能够成功执行
//
//
//
if (a === 'animal' && a === 'dog' && a === 'husky') {
    console.log('Haha, husky');
}

参考答案

var counter = 0;
Object.defineProperty(window, 'a', {
    get: function () {
        return ['animal', 'dog', 'husky'][counter++];
    }
});

if (a === 'animal' && a === 'dog' && a === 'husky') {
    console.log('Haha, husky');
}

题目二十六:拆分HTML片段

现有一段HTML,如下:

Enjoy the <span style='color: #FF6A00; font-style: bold;'>Better</span> <span style='color: #FF6A00;'>Exchange rate</span> by clicking this button.

要求使用正则将标签和文字分隔,分隔结果如下:

"Enjoy the "
"<span style='color: #FF6A00; font-style: bold;'>Better</span>"
" "
"<span style='color: #FF6A00;'>Exchange rate</span>"
" by clicking this button."

题目二十七: 实现一个 Promise Chain

创建一个 Promise Chain ,实现有序 Promise 的串行调用:

const promise1 = () => Promise.resolve(1);
const promise2 = () =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve(2);
    }, 2000);
  });
const promise3 = () =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve(3);
    }, 3000);
  });

const promiseList = [promise1, promise2, promise3];

/**
 * 创建一个 Promise Chain ,实现顺序 Promise 的串行调用
 * @param {Array} promiseList Promise 组成的数组
 */
function promiseChain(promiseList) {
  // 代码实现

}

promiseChain(promiseList).then(() => {
  console.log('所有 Promise 执行完毕。');
});

期望输出结果:

1 // 立即输出
2 // 2s 后输出
3 // 3s 后输出
所有 Promise 执行完毕。

参考答案:

/**
 * 创建一个 Promise Chain ,实现顺序 Promise 的串行调用
 * @param {Array} promiseList Promise 组成的数组
 */
function promiseChain(promiseList) {
  // 实现内容
  if (!(Array.isArray(promiseList))) return Promise.reject(new Error('入参错误'));
  return promiseList.reduce((pre, cur) => {
    return pre.then(() => cur()).then(result => console.log(result));
  }, Promise.resolve(0));
}