2019年面试题1

基础题

1、请写出你了解的Array方法,至少6个?

push:将一个或多个元素添加到数组的末尾,并返回该数组的新长度。

unshift:将一个或多个元素添加到数组的开头,并返回该数组的新长度。

pop:从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。

shift:从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

splice:增删改原素组,返回被修改的元素的数组集合。

/** 
    array.splice(start[, deleteCount[, item1[, item2[, ...]]]]) 
    中括号内为可选参数
**/
let arr = ['Jan', 'March', 'April', 'June'];
//增:在角标为1的位置插入元素,删除之后的0个元素: ['Jan','Tom', 'March', 'April', 'June']
arr.splice(1, 0, 'Tom'); 
// 改:在角标为1的位置插入元素,删除之后的1个元素,也就达到了替换的目的:['Jan','Luck', 'March', 'April', 'June']
arr.splice(1, 1, 'Luck'); 
// 删:从角标为1的位置开始删除,删除两个元素: ['Jan', 'April', 'June']
arr.splice(1, 2); 
View Code

concat:数组合并

此题考察引用数据类型array的了解

2、写出下题输出结果?

console.log(1);//step1:同步执行,打印1
setTimeout(function(){//step2:处理异步--将宏任务回调放入宏任务的EventQueue(下一轮执行时调用,次轮才执行上轮的EventQueue宏任务回调)
    console.log(5); //setp6:处理异步--从宏任务EventQueue取出回调执行,打印5
},0)
new Promise(function(resolve){
    resolve();
    console.log(6); //step3:同步执行,打印6
}).then(function(){//step4:处理异步--将微任务回调放入微任务的EventQueue,(本轮同步完成后,就执行EventQueue微任务回调)
    console.log(7);//setp5:处理异步--从微任务EventQueue取出回调执行,打印7。至此第一轮事件循环结束了,我们开始第二轮循环
});
console.log(8);//step3:同步执行,打印8

/**这段代码作为宏任务,进入主线程。
    执行同步代码console.log(),立即执行打印1,
    遇到setTimeout,那么等待0s后将其回调函数注册后分发到宏任务event queue.
    接下来遇到Promise, new Promise作为同步立即执行,then函数分发到微任务event queue
    遇到console.log(8), 立即执行打印8
    整体代码script作为第一个宏任务执行结束, 查看当前有没有可执行的微任务,执行then的回调。(第一轮事件循环结束了,我们开始第二轮循环)
    从宏任务的event queue开始,我们发现了宏任务event queue中还有个setTimeout对应的回调函数,立即执行。 执行结果: 1-6-8-7-5

    微任务 promise  process.nextTick          
    宏任务 setTimeout  setInterval  I/O  script
    同一次事件循环中  微任务永远在宏任务之前执行,在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
    setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务
    宏任务需要多次事件循环才能执行完,微任务是一次性执行完的
**/

此题考查了同步异步,事件循环,任务队列相关知识点

参考:
https://www.jianshu.com/p/dc9424a4948e

https://www.jianshu.com/p/2a5954b78ff5

https://www.jianshu.com/p/1368d375aa66

3、下边console输出什么?

var name = 'the window';
function func(){
    console.log(this.name);
};
var obj = {
    name: 'my obj',
    getNameFunc:function(fn){
        fn && fn(); //打印the window
        return function(){
            return this.name;
        };
    }
};
//var fun = object.getNameFunc();返回的是一个function.此时,fun中的this指向是window,所以this.name是The window
//var fun= function(){}fun是window中的。相当于window.fun = function(){}
console.log(obj.getNameFunc(func)()) //打印the window 

 此题考查了this相关知识

4、看下边代码,打印什么?

let obj = { a: 0 };
function fn(obj){
    obj.a = 1; //函数参数
    obj = { a: 2 };
    obj.b = 2;
    console.log(obj);// {a: 2, b: 2}
};
fn(obj);
console.log(obj) // {a: 1}
/**
引用类型的实参,会按照共享传递方式传递,传递的是实参引用的副本,
所以会表现为改变形参的属性会影响外边的,重新赋值则会切断与实参的共享联系不会影响到实参
**/

此题考查了函数参数传递知识

参考:https://www.cnblogs.com/xcsn/p/9158727.html

 5、手写一个原生的bind方法?

了解bind方法

// 提到bind方法,估计大家还会想到call方法、apply方法;它们都是Function对象内建的方法,它们的第一个参数都是用来更改调用方法中this的指向。需要注意的是bind 是返回新的函数,以便稍后调用;apply 、call 则是立即调用原函数
function People() {
    console.log(this);
};
var func = People.bind({name:'阎涵'});
func(); //作为普通函数调用,这是bind最常用的场景  
new func(); //作为构造的时候,bind的决定this的参数(即第一个参数)失效。这是经常面试的,实际开发没意义

手写一个简单的bind方法

//手写简单的一个bind方法,bind不能额外传递参数,不区分构造和普通使用
Function.prototype.myBind = function(obj){
    const oldFun = this;
    const BindFn = function(){
        return oldFun.apply(obj);
    };
    return BindFn;
};

//使用
function People() {
    console.log(this);
};
var func = People.myBind({name:'阎涵'});
func(); 
new func(); 

//弊端:不能传递参数,而且两种用法都只作为普通函数调用,不能作为构造函数

写一个完整的bind方法

//手写一个bind方法
Function.prototype.myBind = function(obj){
    //bind方法预置新参数
    const arg = [...arguments]; 
    arg.shift();

    //保存原始函数(主要是存储当前this)
    const oldFun = this;

   //新创建一个的函数(基于原始函数)
    const BindFn = function(){
        console.warn( this instanceof BindFn?'当构造用':'当普通函数用'); //若普通调用则this是Window对象,然为构造方式调用
        this instanceof BindFn && (obj = this);
        return oldFun.apply(obj,[...arg,...arguments]); //修改this,传递参数[bind预置+自身传递参数]
    };

    // 维护原型关系
    BindFn.prototype = this.prototype;

    //返回(已经绑定this后的修改完的原始函数)新函数
    return BindFn;
};

//使用
function People() {
    this.sayHaHa = function(){console.log('哈哈')};
    console.log(this,arguments);
};
People.prototype.speak =  function(){console.log('我在people原型上哦')};
var func = People.myBind({name:'阎涵'},'我是bind预置参数');
func('我是自身传递参数'); //普通使用
const p = new func(); //构造使用 通过维护原型关系,p对象既有People类的实例成员,也有People类的原型成员。
p.sayHaHa();
p.speak();
console.log(p)

编程题

1、给定一个n个对象颜色为红色,白色或蓝色的数组,对其进行排序,使相同颜色的对象相邻,颜色为红色,白色和蓝色。在这里,我们将使用0,1和2的整数分别表示红色,白色和蓝色。比如,输入[2,0,2,1,1,0]排序后[0,0,1,1,2,2]。---力扣(LeetCode)中级算法

2、将类似以下JSON表示的树状结构(可以无限层级)
通过parseDOM函数(使用document.createElement,document.createTextNode,appendChild等方法)
生成一颗DOM树(返回一个element元素)

const JsonTree = {
        "tagName": "ul",
        "props": {
            "className": "list",
            "data-name": "jsontree"
        },
        "children": [{
                "tagName": "li",
                "children": [{
                    "tagName": "img",
                    "props": {
                        "src": "//img.alicdn.com/tps/TB1HwXxLpXXXXchapXXXXXXXXXX-32-32.ico",
                        "width": "16px"
                    }
                }]
            },
            {
                "tagName": "li",
                "children": [{
                    "tagName": "a",
                    "props": {
                        "href": "https://www.aliyun.com",
                        "target": "_blank"
                    },
                    "children": "阿里云"
                }]
            }
        ]
    };
View Code

答案:

function parseDOM(jsontree) {
        const {
            tagName,
            props,
            children
        } = jsontree;
        const element = document.createElement(tagName);
        //请实现过程
        //....
        for (let _key in props) {
            element[_key] = props[_key];
        }
        if (children && typeof(children) === "object") {
            for (let i = 0; i < children.length; i++) {
                element.appendChild(parseDOM(children[i]));
            }
        } else {
            if (children) {
                element.appendChild(document.createTextNode(children));
            }
        }
        return element;
    }
    document.getElementsByTagName(“body ")[0].appendChild(parseDOM(JsonTree));
View Code

首先这个面试题很切合实际,在日常的开发过程中经常会遇到这种类型的数据。主要考我们对递归算法的熟练程度。具体的知识点就是题中列出的3个DOM操作的知识。
参考答案的思路是把每次创建完成的节点添加到父元素中

原文地址:https://www.cnblogs.com/dshvv/p/11647122.html