收藏 筆試題

題目一:找出數組中最接近指定值的數

 

// 邊界值的處理
// 變量的命名
// API的熟悉程度
function findNext(num, arr) {
    / your code ... /
    if (!num || Object.prototype.toString.call(num) !== '[object Number]'){
        throw new TypeError('num參數不可省略且必須爲Number類型');
    }
    arr = arr || [];
    var diffArr = [];
    arr.map(function(x){
        // 對數組各個數值求差值
        diffArr.push(Math.abs(x - num));
    });
    var minimum =  Math.min.apply(null, diffArr);
    // 最後是返回數組中最接近指定值數的索引
    var result = [];
    diffArr.forEach(function(d, i){
        if(d === minimum) {
            result.push(i)
        }
    })
    return result;
}
/ TEST CASE/
findNext('s');
findNext(1,[1,2,1]);  // [0,2]
findNext(4,[2,6,10]); // [0,1]

 

題目二:CSS 多邊框實現

// your css code ...
// 第一種 
div {
    box-shadow: 0 0 0 10px red,0 0 0 16px green, 0 2px 5px 16px rgba(0,0,0,.5);
}

// 第二種 (有侷限,只能模擬出雙邊框) 

div {
    outline: 1px solid red;
    border: 1px solid green;
}
// 如果使用嵌套dom結構,檢查對普通css屬性的理解

題目三:實現一個EventEmitter用於事件監聽、觸發、移除

// 可以追問,如果要控制每個方法上回調函數數量怎麼做
// 增加一個 once api 調用一次即註銷如何實現
function Emitter() {
}
const proto = Emitter.prototype;
proto._getEvents = function() {
    if (!this._events) {
        this._events = {};
    }

    return this._events;
};

// 註冊事件
proto.on = function(event, listener) {
    const events = this._getEvents();

    events[event] = events[event] || [];

    events[event].push(listener);
    return this;
};

// 移除事件
proto.off = function(event, listener) {
    const events = this._getEvents();

    // 移除所有事件
    if (arguments.length === 0) {
        this._events = {};
        return this;
    }

    const listeners = events[event];
    if (!listeners) {
        return this;
    }

    // 移除指定事件下的所有監聽器
    if (arguments.length === 1) {
        delete events[event];
        return this;
    }

    let cb;
    for (let i = 0; i < listeners.length; i++) {
        cb = listeners[i];
        if (cb === listener) {
            listeners.splice(i, 1);
            break;
        }
    }
    return this;
};

// 觸發事件
proto.emit = function(event) {
    const events = this._getEvents();
    let listeners = events[event];
    let i;
    const args = [];
    for (let i = 1; i < arguments.length; i++) {
        args.push(arguments[i]);
    }

    if (listeners) {
        listeners = listeners.slice(0);
        for (i = 0; i < listeners.length; i++) {
            listeners[i].apply(this, args);
        }
    }

    return this;
};

 

 

題目四: 深度克隆函數deepClone

/**
* 取決於你深度拷貝的內容是什麼,是一個真正的JSON object 還是js中的任何對象
* var cloned = JSON.parse(JSON.stringify(objectToClone)); 這個處理不了function、undefined、Infinity等對象的
* 注意 Object.assign({}, original) / { ...original }也是淺拷貝函數
*/
function type(obj) {
    var toString = Object.prototype.toString;
    var map = {
        '[object Boolean]'      : 'boolean',
        '[object Number]'       : 'number',
        '[object String]'       : 'string',
        '[object Function]'     : 'function',
        '[object Array]'        : 'array',
        '[object Date]'         : 'date',
        '[object RegExp]'       : 'regExp',
        '[object Undefined]'    : 'undefined',
        '[object Null]'         : 'null',
        '[object Object]'       : 'object'
    };
    return map[toString.call(obj)];
}
function deepClone(data){
    let dataType = type(data);
    if (dataType === 'null' || dataType === 'undefined' || dataType === 'number' || dataType === 'string' || dataType === 'boolean') {
        return data;
    }
    
    // Date
    if (dataType === 'date') {
        return new Date(data.getTime());
    }
    
    // RegExp
    if (dataType === 'regExp') {
        return new RegExp(data.source, data.flags);
    }
    
    // HtmlNode
    if (data.nodeType && typeof data.cloneNode === "function"){
        return data.cloneNode(true);
    }
    // Array
    if (dataType === 'array') {
        var result = [];
        data.forEach(function(child, index) {
            result[index] = deepClone(child);
        });
        return result;
    }
    
    // Object
    if (dataType === 'object'){
        var result = {};
        if (!data.prototype){
            // 普通對象
            for (var i in data) {
                result[i] = deepClone(data[i]);
            }
        } else {
            // 函數
            if (data.constructor) {
                // 這裏會有什麼副作用
                result = new data.constructor();
            } else {
                result = data;
            }
        }
    }
    
    return result;
}

// 每當調用postMessage時,都會使用結構化克隆算法。
// 我們可以創建一個MessageChannel併發送消息。在接收端,消息包含我們原始數據對象的結構化克隆。
// 如果能回答出這個方法加分
// 這個方法也是有缺陷的:一是不能處理dom節點 另外是異步的
function structuralClone(obj) {
    return new Promise(resolve => {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
    });
}

 

題目五:針對下段html文檔實現一個簡單 AST 解析方法

// 
// 正則表達式
//
const htmlStr = `<div class="parent"><div class="child has-more" style="widht:100px; height:200px;"><a target="_blank" href="positionDetail.htm" title="歡迎應聘螞蟻金服前端工程師">FE</a></div></div>`

var tagRE = /<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g;
var attrRE = /([\w-]+)|['"]{1}([^'"]*)['"]{1}/g;

function parseTag (tag) {
    var i = 0;
    var key;
    var res = {
        type: 'tag',
        name: '',
        attrs: {},
        children: []
    };
    
    tag.replace(attrRE, function (match) {
        if (i % 2) {
            key = match;
        } else {
            if (i === 0) {
                res.name= match;
            } else {
                res.attrs[key] = match.replace(/['"]/g, '');
            }
        }
        i++;
    });
    
    return res;
};

function parse(html, options) {
    options || (options = {});
    var result = [];
    var current;
    var level = -1;
    var arr = [];
    var byTag = {};

    html.replace(tagRE, function (tag, index) {
        var isOpen = tag.charAt(1) !== '/';
        var start = index + tag.length;
        var nextChar = html.charAt(start);
        var parent;
        if (isOpen) {
            level++;
            current = parseTag(tag);
            if ( nextChar && nextChar !== '<') {
                current.children.push({
                    type: 'text',
                    content: html.slice(start, html.indexOf('<', start))
                });
            }
            byTag[current.tagName] = current;

            if (level === 0) {
                result.push(current);
            }

            parent = arr[level - 1];
            if (parent) {
                parent.children.push(current);
            }
            arr[level] = current;
        } else {
            level--;
            if ( nextChar !== '<' && nextChar) {
                parent = level === -1 ? result : arr[level].children;
                var end = html.indexOf('<', start);
                var content = html.slice(start, end === -1 ? undefined : end);
                if (!/^\s*$/.test(content)) {
                    parent.push({
                        type: 'text',
                        content: content
                    });
                }
            }
        }
    });

    return result;
};
parse(htmlStr)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章