bytedance面试题1

2021/04/02 Hide 共 238011 字,约 681 分钟

bytedance面试题1

1. JS实现一个带并发限制的异步调度器

【Question】

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4// 一开始,1、2两个任务进入队列// 500ms时,2完成,输出2,任务3进队// 800ms时,3完成,输出3,任务4进队// 1000ms时,1完成,输出1// 1200ms时,4完成,输出4

【Answer】
class Scheduler {
concurrency = 2
running = 0
queue = []
add(task) {
return new Promise(resolve => {
this.queue.push({
taskGenerator: task,
resolve
})
this.schedule()
})
}
schedule() {
while (this.queue.length > 0 && this.running < this.concurrency) {
const curTask = this.queue.shift()
this.running += 1
curTask.taskGenerator().then(result => {
this.running -= 1
curTask.resolve(result)
this.schedule()
})
}
}
}

2. 写一个加法函数(sum),使他可以同时支持sum(x,y)和sum(x)(y)两种调用方式。

【Question】

写一个按照下面两种方式都能正常调用的 sum 方法
```javascript
console.log(sum(2,3)); // 输出5
console.log(sum(2)(3)); // 输出5
```

【Answer】
答案一
function sum(a,b){
if(b) {
return a+b
}else{
return function(c){
return a+c
}
}
}
答案二
function sum(){
var arg=arguments
if(arg.length==2) {
return arg[0]+arg[1];
}else{
return function(c){
return arg[0]+c
}
}
}

3. ES5,ES6中this指向考察

【Question】

  1. 以下代码输出什么结果,this.name中this指向什么:
    window.name = 'ByteDance';
    function A () {
    this.name = 123;
    }
    A.prototype.getA = function(){
     console.log(this);
     return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    
  2. 如何使funcA()返回undefined?
  3. 下面ES6中又会发生什么,this是什么?
    window.name = 'ByteDance';
    class A {
     constructor() {
      	this.name = 123;
     }
     getA() { 
       console.log(this);
         return this.name + 1; 
     }
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    

【Answer】
1. 输出`Bytedance1`, this指向widnow;
2. 正确使用applay / call;
3. 发生异常:Uncaught TypeError: Cannot read property 'name' of undefined,this为undefined;

4. 请问什么是跨域?跨域请求资源有几哪种方式?

【Question】 何为跨域?跨域请求资源有几哪种方式?

【Answer】
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
跨域请求资源的方式主要有:  
(1)JSONP 动态创建script标签  
但缺点是只支持get请求,并且很难判断请求是否失败(一般通过判断请求是否超时)。  
(2)Proxy代理  
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。  
(3)CORS跨域  
是现代浏览器提供的一种跨域请求资源的方法,需要客户端和服务器端的同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信  
服务响应头返回,Access-Control-Allow-Origin: *

5. 简述React Fiber原理

【Question】

试描述React Fiber的原理。

【Answer】

官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。

之前React的更新过程是同步的,所有更新逻辑会在一帧之内完成,如果组件过于复杂则会导致更新时间超过一帧,其他事务包括用户输入都会被延迟响应,从而引发卡顿。如下图:


破解方式——分片。

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

实现使用的API:requestIdleCallback

Q.为什么引入Fiber架构?原架构有何不足?
A.原架构采用递归遍历方式来更新DOM树,一旦开始,即占用主线程,无法中断,这在页面上会引起问题,如input输入后页面卡顿等

Q.Fiber如何解决该问题
A.时间分片和暂停

Q.Fiber如何实现?
A.使用链表结构,将递归遍历更改为循环遍历,然后配合requestIdleCallback API,实现任务拆分、中断和恢复

Q.Fiber如何实现比较?
A.双缓冲技术,在diff过程中创建新的DOM Tree,diff完成之后生成EffectList,即需要更新的地方,之后进入commit阶段,该阶段不允许中断。

Q.React Hook基于Fiber架构,hook的复用是如何实现的?
A.hook的数据存在于Fiber节点的数据结构中,具体为memoizedState中,该字段中存储了所有hook相关的信息,https://www.jianshu.com/p/d6244228a427 (重要)



6. 请简要描述ES6 module require、exports以及module.exports的区别

【Question】 考察候选人对es6,commonjs等js模块化标准的区别和理解

【Answer】
* CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
* ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
* CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
* export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
* ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性
* 混合使用介绍:https://github.com/ShowJoy-com/showjoy-blog/issues/39

7. 浏览器缓存机制考察

【Question】 浏览器缓存机制考察,包括cache-control , etag, expire, last-modify-time 以及 200 from cache、304

【Answer】
1、cache-control 和 expire 在浏览器端控制  Cache-Control的max-age>expire
2、etag 和 last-modify-time主 要服务器端对比使用

8. 版本号排序

【Question】 versions是一个项目的版本号列表,因多人维护,不规则

var versions=['1.45.0','1.5','6','3.3.3.3.3.3.3']

要求从小到大排序,注意’1.45’比’1.5’大

var sorted=['1.5','1.45.0','3.3.3.3.3.3','6']

【Answer】
```javascript
function sortVersion(arr) {
    return arr.sort((a, b) => {
        const arrA = a.split('.')
        const arrB = b.split('.')
        for (let i = 0; i < arrA.length; i++) {
            if (arrA[i] === undefined) {
                return -1
            } else if (arrB[i] === undefined) {
                return 1
            } else if (parseInt(arrA[i]) === parseInt(arrB[i])) {
                continue
            } else {
                return parseInt(arrA[i]) > parseInt(arrB[i])
            }
        }
    })
}
```

9. JS限流调度器

【Question】

实现JS限流调度器,方法add接收一个返回Promise的函数,同时执行的任务数量不能超过两个。

class Scheduler {
    async add(promiseFunc: () => Promise<void>): Promise<void> {
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
// log: 2 3 1 4


【Answer】


class Scheduler {
    constructor() {
        this.concurrency = 0
        this.queue = []
    }
    async add(promiseFunc) {
        if (this.concurrency >= 2) {
            return new Promise(r => {
                this.queue.push(() => promiseFunc().then(r))
            })
        }
        this.concurrency += 1
        await promiseFunc()
        this.concurrency -= 1
        let next = this.queue.shift()
        if (next) {
            this.add(next)
        }
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)



10. 实现一个简单的Event类(观察者模式)

【Question】

请实现一个观察者模式,拥有四个方法on,off,once和trigger


const Event = {

on() {} // 绑定

off() {} // 解绑

once() {} // 绑定一次

trigger() {} // 触发事件

};

【Answer】

```javascript function Event() { if (!(this instanceof Event)) { return new Event(); } this._callbacks = {}; } Event.prototype.on = function (type, handler) { this_callbacks = this._callbacks || {}; this._callbacks[type] = this.callbacks[type] || []; this._callbacks[type].push(handler); return this; }; Event.prototype.off = function (type, handler) { var list = this._callbacks[type]; if (list) { for (var i = list.length; i >= 0; --i) { if (list[i] === handler) { list.splice(i, 1); } } } return this; }; Event.prototype.trigger = function (type, data) { var list = this._callbacks[type]; if (list) { for (var i = 0, len = list.length; i < len; ++i) { list[i].call(this, data); } } }; Event.prototype.once = function (type, handler) { var self = this; function wrapper() { handler.apply(self, arguments); self.off(type, wrapper); } this.on(type, wrapper); return this; }; ```


【Question】 请说明 cookie、sessionStorage、localStorage 之间的区别、以及在你项目中的应用?

【Answer】
 a) cookie,HTTP Cookie(也叫Web cookie或者浏览器Cookie)是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。比较经典的,可以它用来确定两次请求是否来自于同一个浏览器,从而能够确认和保持用户的登录状态。Cookie的使用使得基于无状态的HTTP协议上记录稳定的状态信息成为了可能。
b) sessionStorage,为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
c) localStorage,localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

区别:
localStorage、sessionStorage 是 Web Storage Api 的组成 API,其为了解决 Cookie 的一些缺陷,服务端 Set 的 cookie 每次会携带在本域下所有的请求上,对性能有损耗。SessionStorage 存储有个期限,当关闭浏览器后就不再存在,但 localStorage 依然存在,需要明确删除。


12. 请简述js浏览器事件循环机制

【Question】


【Answer】

浏览器 Event Loop 是 HTML 中定义的规范,Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

  • JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

  • 同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

  • Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

  • 定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

  • 宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。


13. 何为https?https和http2有什么关系?

【Question】 简要描述HTTPS的安全机制,以及在web服务工程实践中需要注意的问题;描述http2的基本机制

【Answer】
HTTPS是指建立在安全的传输层(通常是tls/ssl)上的HTTP协议,通过对服务器的证书的认证,解决中间人攻击等问题。
证书(certificate)由客户端信任的的证书机构(CA)颁发,通过common name或SAN对服务进行描述;客户端通过CA的根证书对证书进行校验,并将请求域名和证书的common name/DNS域名进行验证,以检验证书的有效性。
目前,很多web api如Notification/web rpc/Service Worker等,都要求必须使用https。
在工程实践中,https存在以下需要注意的问题:
  - js/css等资源必须以https形式加载,否则浏览器将拒绝执行,所以CDN必须完成对https的支持
	- 非https请求的图片等资源不会携带referer
	
	http2是http协议的一个新版本,既可以明文传输也可以在https中使用。浏览器和服务器通过tls的ALPN/SNI等机制可以进行协议协商,决定使用什么协议

14. 用数组的reduce方法实现map方法

【Question】 用数组的reduce方法实现map方法

【Answer】
```
// 代码实现
Array.prototype.map2 = function(f) {
  return this.reduce(function(result, x, index, arr) {
    result.push(f(x, index));
    return result;
  }, []);
}

// 测试代码
var res = [1, 3, 5, 7].map2(function(item, idx){
  return item * 2;
});
console.log(res);
```

15. js异步操作与计算题

【Question】

for (var i = 0; i < 6; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

>1. console.log(new Date, i);得到的结果是什么? >1. 怎样优化,可以变成: 0 -> 1 -> 2 -> 3-> 4 ->5 >1. 如果继续优化,实现console.log(new Date, i);代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4(sleep),之后再暂停5秒,然后输出5, 实现结果类似: >1. 2017-08-31T04:38:23: 0 <— start IIFE >1. 2017-08-31T04:38:24: 1 <— sleep 1s >1. 2017-08-31T04:38:25: 2 <— sleep 1s >1. 2017-08-31T04:38:26: 3 <— sleep 1s >1. 2017-08-31T04:38:27: 4 <— sleep 5s >1. 2017-08-31T04:38:32: 5

【Answer】
1. 属于结果是暂停1S,然后输出6个6,setTimeout属于异步执行
1. 实现0-> 1 -> 2 -> 3-> 4 ->5,用闭包或者var改成let
1. 模拟编程中的sleep实现,参考答案:
```
// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeoutMS) => new Promise((resolve) => {
  setTimeout(resolve, timeoutMS)
});
(async () => {  // 声明即执行的 async 函数表达式
  for (let i = 0; i < 6; i++) {
      if (i < 5) {
        console.log(new Date(), i)
        await sleep(1000)
      } else {
        await sleep(4000)
        console.log(new Date(), i)
      }
    }
})()
```

16. 简单的实现Promise.all

【Question】



function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000);
    })
}
PromiseAll([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

Promise.all([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err=>{
    console.log(err)
})


【Answer】


function PromiseAll(list) {

    return new Promise((resolve, reject) => {

        let count = 0;

        let len = list.length;

        let result = [];

        list.forEach((item,index) => {

            item.then(res => {

                count++;

                result[index] = res;

                if (count === len) {

                    resolve(result);

                }

            }).catch(err => {

                reject(err)

            })

        })

    })

}



17. ES6 import的原理

【Question】 请描述ES6 import的原理以及与commonjs的require的区别

【Answer】
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

18. 不借助变量交换两个数

【Question】 var a = 1, b = 2; function swap(a,b){ …. } swap(a,b) console.log(a, b) // 2,1

【Answer】
方法一、
```
function swap(a,b){
  b=b-a;
  a=a+b;
  b=a-b;
  return [a,b]
}
```
方法二、
```
function swap(a,b){
  return [a, b] = [b, a]
}
```
方法三、
```
function swap(a,b){
  var a=a^b;
  var b=b^a;
  var a=a^b;
	return [a,b]
}
```

19. 实现垂直居中

【Question】


    <div id="block">        
    </div>

id为block的元素不定高不定宽,请实现它在浏览器窗口的居中显示。

【Answer】
```css
#block {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
```

20. 请回答当我们在使用new操作符时,它在对象操作的过程中具体做了什么

【Question】 考察候选人对原型链操作和js对象的理解

【Answer】
1. 简单回答:
1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
1. 属性和方法被加入到 this 引用的对象中。
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
```javascript
function Animal(name) {
      this.name = name;
}
  Animal.prototype.run = function() {
      console.log(this.name + 'can run...');
}
var cat = new Animal('cat'); //    
new Animal('cat')=function(){
let obj={}; //       
obj.__proto__=Animal.prototype; // obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,'cat');//   this        
}
```



21. css3实现多行文字截断处理

【Question】 用css分别实现单行截断和多行截断字符串,最后以…为结尾

【Answer】
单行:
```
.text-overflow ( @class ){
    .@{class} {
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
    }
}
```
多行:
```
.multi-text-overflow ( @class, @line ){
    .@{class} {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        display: box;
        -webkit-line-clamp: @line;
        -webkit-box-orient: vertical;
    }
}
```

22. 请介绍react diff算法和策略

【Question】 react的diff算法和策略了解多少,为什么react的diff性能好,遵循什么样的策略可以把 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

【Answer】
React分别对 tree diff、component diff 以及 element diff做了算法优化,
做了一些假设
1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
3.对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
tree diff:React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较
component diff:
a.如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
b.如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
c.对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff
element diff:
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,减少增加和删除
详见:https://zhuanlan.zhihu.com/p/20346379

23. 函数科里化

【Question】

实现如下函数add,使如下执行都等于9

add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】


function curry(fn) {
  return function res(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return res.apply(this, args.concat(args2));
      }
    }
  }
}



24. 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?应该怎么解决?

【Question】 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?怎么解决?

【Answer】
考察一下JS中整数的安全范围的概念,在头条经常会遇到长整型到前端被截断的问题,需要补一个字符串形式的id供前端使用。
主要会涉及到JS中的最大安全整数问题
https://segmentfault.com/a/1190000002608050

25. JavaScript this 考察

【Question】

下面代码输出的结果是什么?

var length = 10;

function fn() {

 return this.length+1;

}

var obj = {

 length: 5,

 test1: function() {

  return fn();

 }

};

obj.test2=fn;

//下面代码输出是什么

console.log(obj.test1())

console.log(fn()===obj.test2())

【Answer】

11, false(11===6)


26. requestAnimationFrame 和 setTimeout 的区别

【Question】 requestAnimationFrame 和 setTimeout 都可以用来实现动画,它们的区别是什么

【Answer】
1. 执行频率不同,前者按照屏幕刷新频率执行,后者自行控制,可能有无用开销(执行频率小于刷新频率,即1帧执行多次)
2. 前者在页面不可见时,会停止执行(省电),后者在页面不可见时仍会执行,带来不必要开销


27. 编码-js高阶函数考察

【Question】

实现一个repeat方法,要求如下:


// 需要实现的函数

function repeat (func, times, wait) {

// 补全

}


// 使下面调用代码能正常工作

const repeatFunc = repeat(console.log, 4, 3000);

repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒


【Answer】

考点1:能意识到repeat返回的是一个函数,知道参数怎么传递。

考点2:setTimeout的时间,微任务


参考答案

function repeat(fn, times, wait) {

if(typeof times !== 'number') return;

if(typeof wait !== 'number') return;

return function(str){

for(let i = 0; i < times; i++){

setTimeout(()=>{

fn(str)

}, i * wait)

}

}


28. Vue框架中组件消息通信方式

【Question】 考察候选人对Vue框架的消息通信方式了解程度:

  1. vue父子组件通信方式?
  2. 非父子组件通信方式?
  3. 前两问OK,追问:当一个父组件与子组件中间隔着很多层组件怎么办?

【Answer】
1. 父子组件通信方式
在Vue中,父子组件的关系可以总结为props down, events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。

2. 非父子组件通信
两个独立的组件之间通信,可以借助一个空的Vue实例作为中央事件总线,空实例相当于代理人的形式进行消息监听或触发

3. 父子之间层级过多时
当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。
这块如果了解开源的Element组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以`[组件名, 消息名, 参数]`三元组进行消息传递,降低长链传播成本;
具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

29. 什么是 XSS,怎么造成的,有什么防御方法?

【Question】 考察面试者对于 XSS 是否了解,是否足够重视。

【Answer】
XSS 就是在 web 中能够通过某种方式产生执行任意 JavaScript 脚本的情况,
最常见的一种情况就是将用户的输入,直接放到当前 runtime 中,比如用户输入直接放到页面的 html 里面,
立刻显示出来。
XSS 实际上是非常危险的,因为理论上讲,如果能够执行 JavaScript,实际上攻击者可以做任何事情。
简单的就是输出点什么,偷偷 cookie,或者结合 CSRF 攻击,或者让浏览器跳转一下,
复杂点的甚至可以改掉当前整个页面,伪造一切用户看到东西,危害无穷。
如果这种输入存储到数据库中,就会变成一个永久型的 XSS,危害就更大了。
防止 XSS 最简单的就是使用各种框架,如 React、Vuejs 等,对用户输入进行 html 转义。
另外,服务端要设置 httpOnly 的 header,防止 JavaScript 操作 cookie。
当然,服务端也可以对输入进行转义或者过滤监测。

30. webpack插件编写

【Question】

  1. 有用过webpack么?说说该工具的优缺点?
  2. 有开发过webpack插件么?
  3. 假如要在构建过程中去除掉html中的一些字符,如何编写这个插件?

【Answer】
webpack优缺点:
* 概念牛,但文档差,使用起来费劲
* 模块化,让我们可以把复杂的程序细化为小的文件
* require机制强大,一切文件介资源
* 代码分隔
* 丰富的插件,解决less、sass编译

开发插件的两个关键点Compiler和Compilation:
* compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。
* compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

插件编写可参考:https://doc.webpack-china.org/development/how-to-write-a-plugin

31. 如何实现微信扫码登录?

【Question】 综合题,考察网络、前端、认证等多方面知识

【Answer】
参考答案:
https://zhuanlan.zhihu.com/p/22032787
具体步骤:
1. 用户 A 访问微信网页版,微信服务器为这个会话生成一个全局唯一的 ID,上面的 URL 中 obsbQ-Dzag== 就是这个 ID,此时系统并不知道访问者是谁。
2. 用户A打开自己的手机微信并扫描这个二维码,并提示用户是否确认登录。
3. 手机上的微信是登录状态,用户点击确认登录后,手机上的微信客户端将微信账号和这个扫描得到的 ID 一起提交到服务器
4. 服务器将这个 ID 和用户 A 的微信号绑定在一起,并通知网页版微信,这个 ID 对应的微信号为用户 A,网页版微信加载用户 A 的微信信息,至此,扫码登录全部流程完成

32. 设计类似 Vue.js 双向绑定功能的核心逻辑“监听对象属性变化”功能

【Question】 实现一个类,可以监听对象属性的值变化。加分项:考虑对象存在值为数组或对象的属性。

	class Observe {
		constructor(data: Object) {
		}
		// 监听属性变更
		$on() {
		}
		// 触发属性变更事件
		$emit() {
		}
	}
	const data = new Observer({
		a: 1
	});
	coonsole.log(data.a) // console: 1
	data.$on('a', (newValue, oldValue) =&gt; {
		// this === data
		console.log(newValue, oldValue);
	});
	data.a = 2 // console: 2 1

【Answer】
待补充

33. 请简要描述

【Question】

【Answer】
### 作用:
defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
### 区别:
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

34. 原型链、this指针、自有属性考察

【Question】

var a= function () { this.b =3; }
var c = new a();
a.protorype.b = 9;
var b = 7;
a();

问:

console.log(b);
console.log(c.b); 

分别输出什么?

【Answer】
- 第一个 `b = 3`
- 第二个 `c.b = 3`

【Question】

如题

【Answer】
cookie 在浏览器中存在,并且每个请求都会带上,而且是可以设置失效时间的,失效前就会有效。 session 是一次会话有效,也就是说,浏览器关闭 session 就会失效。 其实这道题目考察的一个重点是在于,session 这种机制是怎么在浏览器中实现的呢? 实际上 session 的实现也是在浏览器中放了一个 cookie,失效时间是一次会话失效。

36. JS异步队列macrotask和microtask

【Question】

console.log('begin')
setTimeout(() =&gt; {
	console.log('setTimeout 1')
	Promise.resolve().then(() =&gt; {
		console.log('promise 1')
		setTimeout(() =&gt; {
			console.log('setTimeout2 between promise1&amp;2')
		})
	}).then(() =&gt; {
		console.log('promise 2')
	})
}, 0)
console.log('end')

【Answer】
```
begin
end
setTimeout 1
promise 1
promise 2
setTimeout2 between promise1&2
```

37. 如何理解虚拟DOM?

【Question】 如何理解虚拟DOM?

【Answer】
对虚拟dom和diff算法中的一些细节理解与考察,[https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13)

38. 如何判断一个 JS 对象为空对象

【Question】 如何判断一个 JS 对象为空对象 ?空对象形如{}

【Answer】
1. 使用 `for in`
	```javascript
	function isEmptyObject(obj){
  	for(var key in obj){
    	return false
		};
		return true
	};
	```
2. 通过 JSON.stringify 方法来判断
	```javascript
	if(JSON.stringify({}) === '{}'){
		console.log('empty obj');
	}
	```
3. 使用 ES6 增加的 Object.keys()
	```javascript
	if(Object.keys(obj).length === 0){
		console.log('empty obj');
	}
	```

39. 什么是闭包?实现每隔1秒输出数组中的一个数字

【Question】 解释下js中的闭包概念,解释OK,给出编程题目考察基本功

【Answer】
```js
function fun(arr) {
    var i, len;
    for (i = 0, len = arr.length; i < len; i++) {
      (function(i){
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
}
```

40. promise运行过程解答

【Question】 如下代码的运行结果是什么?

 process.nextTick(() => {console.log('nextTick')})
Promise.resolve().then(()=> {console.log('promise1');}).then(()=> {
  console.log('promise2');
});
setImmediate(() => {console.log('setImmediate')})
console.log('end') 

【Answer】
1. end -> nextTick -> promise1 -> promise2-> setImmediate
1. process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。
1. 事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

41. 请简述常见web安全及防护原理

【Question】 常见web安全及防护原理,请举例说明。

【Answer】
1、SQL注入原理  
		就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点  
1. 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2. 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。  
2、XSS原理及防范  
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者JavaScript代码。
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法  
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

3、CSRF原理及防范  
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法

42. 数字格式化问题:1234567890 –> 1,234,567,890

【Question】 数字格式化问题,将1234567890 –> 1,234,567,890

【Answer】
非正则实现
```javascript
let test = '1234567890'
function formatCash(str) {
  let arr = []
  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))
    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))
  }
  return arr.join(',')
}
console.log(formatCash(test)) // 1,234,567,890
```
正则实现
```javascript
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
console.log(format) // 1,234,567,890
```

43. 模拟实现loadash中的_.get()函数,实现如下传入参数取值效果

【Question】

function get() {
  // 请补全函数参数和实现逻辑
}
const obj = { selector: { to: { toutiao: 'FE coder' } }, target: [1, 2, { name: 'byted' }] };
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')

//  输出结果:
// ['FE coder', 1, 'byted']

【Answer】
```javascript
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
```
1. Use Array.map() for each selector
2. String.replace() to replace square brackets with dots
3. String.split('.') to split each selector
4. Array.filter() to remove empty values
5. Array.reduce() to get the value indicated by it

44. 合并两个有序数组

【Question】 合并两个有序数组

【Answer】
```
function mergeSortedArray(a, b){
  var merged = [], 
      aElm = a[0],
      bElm = b[0],
      i = 1,
      j = 1;
  if(a.length ==0)
    return b;
  if(b.length ==0)
    return a;
  while(aElm || bElm){
   if((aElm && !bElm) || aElm < bElm){
     merged.push(aElm);
     aElm = a[i++];
   }   
   else {
     merged.push(bElm);
     bElm = b[j++];
   }
  }
  return merged;
}
```
验证
```
mergeSortedArray([2,5,6,9], [1,2,3,29]);
结果 [1, 2, 2, 3, 5, 6, 9, 29]
```

45. 进行CSRF漏洞扫描的原理和防御方式是什么?

【Question】 如题

【Answer】
CSRF 就是在用户不知情的情况下,发出了请求,让用户做了不该做的操作。
举个例子,比如你的一个网站中有个 img 标签,src 指向的是微博关注某人的接口,
那么当用户访问你的网站时,就会在微博上关注那个人,而且这个操作用户是不知情的。
因为 img src 发出的跨域请求,也是会携带 cookie 的,所以如果用户在微博登录过,
那么就会带有微博的登录授权。同理,如果是其他操作,可能也存在这种漏洞,比较危险的情况就是付款。
一般会采用 CSRF token 的方式防御,就是关键请求得要换取一个一次有效的 token 才有权限。


46. 判断一个字符串是否是回文字符串

【Question】 判断一个字符串是否是回文字符串,回文字符串是对称字符串的形式,例如:did,eve, dad, level

【Answer】
```
function isPalindrome(str){
  var i, len = str.length;
  for(i=0; i isPalindrome('madam')
  = true
> isPalindrome('toyota')
  = false
```

47. box-sizing 实践

【Question】


<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 10px;
        height: 10px;
        border: 1px solid red;
        margin: 2px;
        padding: 2px;
        background: blue;
      }

      #borderBox {
        box-sizing: border-box;
      }

      #contentBox {
        box-sizing: content-box;
      }
    </style>
  </head>
  <body>
    <div>请问下面两个 div 元素,蓝色区域的宽高各是多少像素?</div>
    <div id="borderBox" class="box"></div>
    <div id="contentBox" class="box"></div>
  </body>
</html>


【Answer】

borderBox:10px(width) - 1px(border) * 2 = 8px

contentBox 10px(width) + 2px(padding) *2 = 14px


答题要点:除了验证候选人是否真正了解 box-sizing 之外,也考察候选人是否了解 background 会影响元素的 padding 区域,而不影响 margin 区域这个特点


48. 链式调用+延迟计算

【Question】

写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....)


console.log(sum(1,2,3)(4)) => 输出 10



考察链式调用,闭包,延迟计算,函数toStirng/valueOf




【Answer】


function sum(...args) {
  function next(...innerArgs) {
    args.push(...innerArgs);
    return next;
  }
  next.valueOf = next.toString = () => {
    return args.reduce((r, c) => r + c, 0);
  };

  return next;
}



49. 请描述micro task 与 macro task的区别及应用

【Question】


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');


【Answer】

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout


50. 数组flat函数设计

【Question】 设计一个flat函数将如下数组arr=[1,2,[‘3’,4,’5’,[6,[7,8],9]]]输出为1,2,’3’,4,’5’,6,7,8,9。至少写出两种方法,要求不能改变数组中的原始数据类型

【Answer】
*  方法一:递归
```javascript
function flat(array) {
    var result = [];
    var each = function(arr) {
      arr.forEach(item => {
        if (item instanceof Array) {
          each(item);
        } else {
          result.push(item);
        }
      });
    };
    each(array);
    return result;
  }
var arr=[1,2,['3',4,'5',[6,[7,8],9]]];flat(arr).forEach(item=>{console.log(item)})

```
*  方法二:toString(格式转换),无法保证类型
```javascript
Array.prototype.toString = function() {
  return this.join(',');
};
console.log([1,2,[3,4,[5,6,7]]]+'');
```
*  方法三:Iterator
```javascript
Array.prototype[Symbol.iterator] = function() {
  let arr = [].concat(this),
    index = 0;
  let getFirst=function(array){
    let first=array[0];
    if(first instanceof Array){
      return getFirst(array[0])
    }else if(first!==undefined){
      return array.shift()
    }else{
      return ''
    }
  }
  return {
    next: function() {
      let item=getFirst(arr);
      if(item){
        return {
          value:item,
          done:false
        }
      }else{
        return {
          done:true
        }
      }
    }
  }
}
var t=[1,2,['3',4,'5',[6,[7,8],9]]];
for(let i of t){console.log(i)}
```

【Question】 基础题考察 cookie 和 localStorage 的理解。

【Answer】
存储在 Cookie 中每个 request 都会带上,而放在 localStorage 中,仅有浏览器中会存储。

52. 请说说HTML的Meta标签的用途,并列举一些常用的meta标签

【Question】

【Answer】
考察对网页结构和语义的理解 

```
The HTML  element represents metadata that cannot be represented by other HTML meta-related elements, like , , 

53. 说说前端优化?图片懒加载原理是什么?

【Question】

  • 考察前端的一些优化方式
  • 图片懒加载原理

【Answer】
1. 优化手段:雅虎的34条优化手段,比如:代码压缩、减少请求、cdn、缓存
2. 图片懒加载原理:img标签设置占位属性(data-src),存储真正的图片地址;原src设置占位图片地址;当图片(快)进入用户可视区域的时候进行地址替换;

54. 请谈谈你对ES6的箭头函数的理解

【Question】

var func1 = x => x;
var func2 = x => {x}; 
var func3 = x => ({x});
console.log(func1(1));
console.log(func2(1));
console.log(func3(1));

请写出程序运行结果。

【Answer】
程序运行结果为:
第一个:1
第二个:undefined
第三个:{x: 1}

55. 无重复字符的最长子串

【Question】

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

样例:


  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。



【Answer】


var lengthOfLongestSubstring = function(s: string) {
    let list = s.split("");
    let son = [];
    let max = [];
    for (let i = 0; i < list.length; i++) {
        let current = list[i];
        let index = son.indexOf(current);
        if (index === -1) {
            son.push(current);
        } else {
            let sameIndex = i - son.length + index;
            if (son.length > max.length) {
                max = [...son];
            }
            son = son.slice(sameIndex + 1, son.length);
            son.push(current);
        }
    }
    return max.length;
};



56. 列举一个近期做的最能体现设计能力的项目

【Question】 请举出一个你近期做的项目,项目需要最能体现设计能力, 请从以下角度说明:

  1. 项目描述
  2. 技术选型
  3. 模块化
  4. 模块之间通信
  5. 工程化
  6. 前后端数据流

【Answer】
这是一个开放式的工程设计题目,没有固定答案,评分参考评分标准

57. 实现一个 JSONP

【Question】 函数签名如下:

function jsonp(url, callback) {
  // TODO
}

【Answer】
主要考察如何处理第二个参数 `callback` 的问题,
加分项比如超时处理 onerror 的处理, xss 考虑等等

```
const kCallBackMap = {};
function uuid() {
  return ...;
}

function jsonp(url, callback) {
  const callbackId = uuid();
  url += 'callback=' + callbackId;
	window[calbackId] = callback;
	
	const script = document.createElement('script');
	script.src = url;
	document.head.appendChild(script);
}
```

58. 请谈一谈JAVAscript的作用域和this

【Question】

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

【Answer】
```
window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

主要考察javascript的作用域和this指向。作用域是静态的,声明时确定;this是动态的,运行时确定。
```

59. 请问CSS position有哪些定位方式

【Question】 CSS position有哪些定位方式,每种方式是如何定位的?

【Answer】
### position取值
relative, fixed,absolute和staic、sticky 5种
### 定位方式
*  staic-默认位置;元素会像通常那样流入页面。顶部,底部,左,右,z-index属性不适用。  
*  relative-元素的位置相对于自身进行调整,而不改变布局(从而为未被定位的元素留下一个空白)。  
*  absolute-该元素从页面的流中移除,并相对于其最近位置的祖先定位(非static)在指定位置,如果有的话,或者与初始包含块相对。绝对定位的框可以有边距,并且不会与其他边距折叠。这些元素不影响其他元素的位置。  
*  fixed元素是定位在相对于窗口。  
*  sticky,是相对定位和固定定位的混合。该元素被视为相对位置,直到它越过指定的阈值,此时它被视为固定位置。  


60. 请介绍一下Oauth2.0 的认证过程

【Question】 如题

【Answer】
可以参考 http://www.jianshu.com/p/0db71eb445c8 或者 
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 的答案,
回答的一个重点是 code(授权码)仅一次有效,并且要有失效时间,而且很短,比如一分钟,
因为浏览器收到会立刻跳转。
还有就是服务端可以根据 code 结合相应的 sercet 去获取 token,要说清楚。

61. express中间件的原理

【Question】

express中间件的实现原理 并给出实现

【Answer】
主要考察候选人对中间件的理解 参考代码
  export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } 
koa中间件主要使用 generator和promise可参考https://github.com/tj/co


62. 实现es6字符串模板方法sprintf

【Question】


const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
	name: 'Yiming Zhang',
	city: 'FuJian',
});
console.log(result); // My name is Yiming Zhang,I'm from FuJian


【Answer】


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);



63. 登录表单设计/扫码登录/第三方登录

【Question】

  1. 请实现一个登录表单
  2. 用GET方法行不行?csrf是什么?如何防御?
  3. cookie-sesssion的工作机制
  4. 你已经登录产品的App端,要在web实现扫码登录,该如何设计?
  5. 接入第三方登录(如微信),如何设计?

【Answer】
1. 正确书写html
2. 正确回答GET和POST的区别,从语义、弊端、安全等方面。csrf的防御:token,samesite,referer校验(弊端)等
3. 正确理解cookie-session的工作机制,sessionId的设计,存储
4. 考察对司空见惯的扫码登录,是否有思考其实现。正确设计 Client/Server/App 三方流程,设计二维码存储的内容,client通知有轮训或websocket等解决方案
5. 正确理解 Client/Server/App/Weixin Server 四方流程,理解oauth2协议

64. 作用域以及变量提升

【Question】

请写出下题的结果:

var a = 1; 
function b() { 
    a = 10; 
    return; 
    function a() {} 
} 
b(); 
console.log(a);   

【Answer】
结果:1

65. setTimeout 和 Promise

【Question】

请写出程序的输出内容

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function(resolve) {
  console.log(2);
  for(var i=0 ; i < 10000 ; i++) {
    if (i == 9999) {
      resolve();
    }
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);


【Answer】

正确答案:2 3 5 4 1。重点关注:候选人是否把 2 写在第一位,以及 4 和 1 的顺序。


66. requestIdleCallback和requestAnimationFrame有什么区别?

【Question】

requestIdleCallback和requestAnimationFrame有什么区别?

【Answer】

requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。

我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了。

一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调。

由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了。


67. 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Question】 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Answer】
`
Array.prototype.findDuplicate = function (n) {
    var results = [];
    if (typeof n != 'number' || isNaN(n)) {
        return results;
    }
    
    var itemFreqs = {};
    this.forEach(function (item) {
        if (!itemFreqs[item]) {
            itemFreqs[item] = 0;
        }
        itemFreqs[item] ++;
    });
    
    for (var item in itemFreqs) {
        if (itemFreqs[item] >= n) {
            results.push(item);
        }
    }
    
    return results;
}

`

68. 请回答DOM中对应创建、移除、追加、复制、查找节点的方法是什么?

【Question】 考察候选人对原生dom操作的方法的理解和掌握熟练程度

【Answer】
1.  创建新节点
	*  createDocumentFragment() //创建一个DOM片段
	*  createElement() //创建一个具体的元素
	*  createTextNode() //创建一个文本节点

1.  克隆节点
*  cloneNode()

1. 添加节点
*  appendChild()
*  insertBefore()

1. 移除节点
*  removeChild()

1. 替换节点
*  replaceChild()

1. 查找节点
*  querySelector()
*  querySelectorAll()
*  getElementById()
*  getElementsByName()
*  getElementsByTagName()



69. 请描述如何用原生JS实现数字的货币格式化

【Question】

# 如何用原生JS实现数字的货币格式化,例如数字6123456789格式化后为6,123,456,789,不低于两种方法。

【Answer】

方法一: (6123456789).toLocaleString('en-US') // 6,123,456,789


方法二: (6123456789).toString().split('').reverse().join('').replace(/\d{3}/g,function($1){return $1+','}).split('').reverse().join('')



70. let,const,var的区别

【Question】 请说明一下let,const,var的区别 并回答如下代码会不会报错

const a = {};
a.test = 1;

【Answer】
考察候选人对es6变量声明的理解
1. let声明的变量拥有块级作用域
2. let声明的全局变量不是全局对象的属性
3. let不能重新声明变量
4. const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

上面代码只是针对a的引用 并不会报错

71. 如何实现链式调用

【Question】 请实现函数 a, b, c,使调用方式为 a().b().c() 时,结果为输出 a b c。 如果上面问题回答出来了,并且是在 a 函数内部 return Object 实现, 那么可以补充问下如何能够实现让三个函数任意链式顺序调用。 如 a().c().b() 或 b().a().c() 。

【Answer】
这道题主要就是考察面试者对 JavaScript 的 Object 概念理解是否清晰,
最好的答案是直接将 a b c 三个函数挂载到 runtime 中的某个全局变量中,比如可以是 window。
然后在每个函数内 return window 就可以了。
当然,也可以按照第一道题目的顺序,分别在相应函数内 return 下个函数,但是这样做无法调换顺序。

72. 实现千位分隔符

【Question】 给一个数字,比如:1234567.90,转化成:1,234,567.90

【Answer】
```js
function commafy(num) {
  return num && num
      .toString()
      .replace(/^\d+/, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','));
}
console.log(commafy(1234567.90)); //1,234,567.90
```

73. 编写javascript深度克隆函数deepClone

【Question】 编写javascript深度克隆函数deepClone

【Answer】
```javascript
function deepClone(obj) {
    var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }

        return new RegExp(obj.source, flags.join(''));
    }

    var result = Array.isArray(obj) ? [] :
        obj.constructor ? new obj.constructor() : {};

    for (var key in obj ) {
        result[key] = deepClone(obj[key]);
    }

    return result;
}

function A() {
    this.a = a;
}

var a = {
    name: 'qiu',
    birth: new Date(),
    pattern: /qiu/gim,
    container: document.body,
    hobbys: ['book', new Date(), /aaa/gim, 111]
};

var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
```

74. 请谈谈你对JS单线程以及setTimeout的理解

【Question】

setTimeout(function() {
	setTimeout(function() { console.log(1) }, 100)
	console.log(2)
	setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
	console.log(4)
}, 100)
console.log(5)

请说出上面代码的输出顺序以及原因?如果吧4改为101ms呢?

【Answer】
正确顺序为:5 2 3 4 1
如果4改为101ms则执行顺序还是不变
原因:
1.  JS单线程
2. setTimeout不在当前eventloop。且执行顺序依赖入队顺序。setTimeout 0是放入下一个loop的队尾
3. 虽然4和1都是100ms延迟的标记,但是4先入队列。
4. setTimeout的time是个标记,会在eventloop循环去检测,符合条件的执行,不符合条件的延后到下一个eventloop,这执行过程本身又有时间,因此尽管101>100,但是在一个执行周期内,他们都会被触发,4先入队所以不变

75. async & forEach 考察

【Question】 以下代码的运行结果

const list = [1, 2, 3];
const square = num =&gt; {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(num * num);
        }, 1000);
    });
}
function test() {
    list.forEach(async x =&gt; {
        const res = await square(x);
        console.log(res);
    });
}
test()

如果希望每隔1s输出一个结果,应该如何改造?

【Answer】
1s 后输出 1 4 9  
改为 for 循环:
```javascript
async function test() {
    for (let x of list) {
        const res = await square(x);
        console.log(res)
    }
}
```


76. css单位的百分比

【Question】 给一个div设置它父级div的宽度是100px,然后再设置它的padding-top为20%。
问现在的div有多高?如果父级元素定位是absolute呢?

【Answer】
现有div的高度等于自身高度+父级块的宽度*20%,如果父级元素定位是absolute,结果不变;
当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!

77. NodeJS实现简单的HTTP代理和隧道代理

【Question】 Web代理一般包括普通的HTTP代理和隧道代理,谈谈理解。 NodeJS实现一个简单的HTTP代理,如在本地 8888 端口开启 HTTP 代理服务,修改浏览器的 HTTP 代理为 127.0.0.1:8888 后再访问 HTTP 网站,代理可以正常工作 对隧道代理了解多少,能否实现?

【Answer】
http普通代理:HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。
```
// http 普通代理
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(8888, '0.0.0.0');
```
隧道代理:HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发
```
const http = require('http');
const net = require('net');
const url = require('url');

function connect(cReq, cSock) {
  const u = url.parse('http://' + cReq.url);

  const pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer().on('connect', connect).listen(8888, '0.0.0.0');
```
合二为一
```
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

function connect(cReq, cSock) {
  var u = url.parse('http://' + cReq.url);

  var pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer()
  .on('request', request)
  .on('connect', connect)
  .listen(8888, '0.0.0.0');
```
需要注意的是,大部分浏览器配完隧道代理,默认只会让https走隧道代理,http如果需要走隧道代理,还需要写个Nodejs的验证
```
const options = {
  hostname: '127.0.0.1',
  port: 8888,
  path: 'toutiao.com:80',
  method: 'CONNECT'
};

const req = http.request(options);

req.on('connect', function(res, socket) {
  socket.write('GET / HTTP/1.1\r\n' +
    'Host: toutiao.com\r\n' +
    'Connection: Close\r\n' +
    '\r\n');

  socket.on('data', function(chunk) {
    console.log(chunk.toString());
  });

  socket.on('end', function() {
    console.log('socket end.');
  });
});

req.end();
```

78. 假设一个网页嵌入一个iframe,如何更改iframe内dom样式?

【Question】 假设一个网页嵌入一个iframe,如何更改这个iframe内dom样式

【Answer】
区分同源和不同源解决方案,同源可以通过document.getElementById('iframeId').contentWindow.document,
不同源:分iframe的嵌入的页面是否自己可控,可控可以通过postMessage方式更改,iframe页面监听message事件;如果页面不可控,应该无解。
可以追问iframe有同源策略限制,举个例子说明

79. 数组随机排序

【Question】

var arr=[1,2,3,4,5,6]

【Answer】
方法一、
```javascript
arr.map(item=>{
    return {
        value:item,
        key:Math.random()
    }
})
.sort((a,b)=>a.key-b.key)
.map(item=>item.value)
```
方法二、
```
var arrayToRand = (arr) => {
    for(let i=0; i

80. js事件模型

【Question】 浏览器的事件模型?在当前的事件模型中,哪些事件可以冒泡,哪些不会冒泡,为什么?不冒泡的元素,如何来实现事件代理?

【Answer】
考察浏览器事件模型,看看是不是了解事件模型背后的设计意图。

浏览器开发团队遇到的问题:页面上哪一部分会拥有某个特定的事件?比如单击一个嵌套的同心div,那么到底哪一个div会拥有这个点击事件?实际上难以确定点击者的意图,团队给出的解决方式是所有div都将拥有这个事件,于是产生了事件流模型。如上一个问题所述,“事件”的概念在GUI编程中如此之重要,而这种流式模型能给予其很大的灵活性和控制
对于能精确确定意图的(这种冒泡的话一般也会带来问题,比如mouseleave),或者不可能产生嵌套的媒体类元素,冒泡就不是必须的;对于不冒泡的元素,可以在捕获阶段代理,DOM2级规范addEventListener的第三个参数

81. 请列举说明几个在web中实现长连接的技术方案或手段

【Question】 本地主要考察候选人对长连接技术的概念理解和区分,如果能回答答出大致的名词可以继续追问一些具体的激技术实现细节和存在的优缺点等等。

【Answer】
参考答案:
1. https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet/12855533#12855533
1. https://blog.csdn.net/liang0000zai/article/details/40537059

* Long Polling
* Server-Sent Events
* Websockets
* Comet

82. 函数作用域

【Question】 用代码实现JavaScript中Function的bind方法的polyfill

【Answer】
```
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                                 ? this
                                 : oThis || this,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```

83. CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Question】 CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Answer】
  - `content-box` 默认值,width内容宽度
	- `border-box` width 包含`padding`和`border`

84. JS的new操作符具体做了什么

【Question】 JS的new操作符具体做了什么,描述一下,最好可以体现在代码上

【Answer】
```
function A() {
  this.name = 'a';
  this.getName = function() {
    return this.name;
  }
}
var a = new A();

var aa = new Object();
aa.__proto__ = A.prototype;
A.call(aa);
// 还有最后一步,如果发现A返回的是一个Object类(非primitive类型),则直接返回A的返回值,否则把aa返回出去
```

85. JS编码二叉树的实现与遍历

【Question】 JS编码实现一个二叉树的构造函数,包括节点类Node,树类BST,插入节点函数insert, 并且满足 1.左子节点的值 < 父节点的值 <= 右子节点的值 2.可以实现先序,中序,后续遍历

【Answer】
```
// 二叉树
function BST() {
  this.root = null;
}

BST.prototype.insert = function(data) {
  var n = new Node(data, null, null);
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    for (;;) {
      if (data < current.data) {
        if (current.left === null) {
          current.left = n;
          break;
        } else {
          current = current.left;
        }
      } else {
        if (current.right === null) {
          current.right = n;
          break;
        } else {
          current = current.right;
        }
      }
    }
  }
}

// 先序遍历
BST.prototype.preOrder = function(node) {
  if (node !== null) {
    console.log(node.show() + " ");
    this.preOrder(node.left);
    this.preOrder(node.right);
  }
}

// 中序遍历
BST.prototype.inOrder = function(node) {
  if (node !== null) {
    this.inOrder(node.left);
    console.log(node.show() + " ");
    this.inOrder(node.right);
  }
}

// 后序遍历
BST.prototype.postOrder = function(node) {
  if (node !== null) {
    this.postOrder(node.left);
    this.postOrder(node.right);
    console.log(node.show() + " ");
  }
}

// 节点对象
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = function() {
    return this.data;
  }
}

// 测试代码
var bst = new BST();
var nums = [10, 3, 18, 2, 4, 13, 21, 9, 8, 9];
for (var i = 0; i < nums.length; i++) {
  bst.insert(nums[i]);
}
bst.preOrder(bst.root);
bst.inOrder(bst.root);
bst.postOrder(bst.root);
```

86. 简述一下src与href的区别

【Question】 描述一下html中的src与href的区别和使用场景是什么

【Answer】
基本答案:src用于指向外部资源的位置替换当前元素,href用于在当前文档和引用资源之间确立联系。
1.  src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;
在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。
这也是为什么将js脚本放在底部而不是头部。
 
1.  href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
这也是为什么建议使用link方式来加载css,而不是使用@import方式。

87. js运行机制

【Question】 下面一段代码的输出:

(function() {
  console.log('this is the start');
  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });
  console.log('this is just a message');
  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);
  console.log('this is the end');
})();

【Answer】
因为前端编程基本属于「Event-driven programming」范式,这是GUI之类的交互式程序的基础,区别于传统的批处理式编程。一个页面上的交互行为,基本都是由用户发起的,然而用户的行为意图是难以预测的,所以需要异步的驱动机制来应对
因此有进一步问题:
平时都说JS是单线程执行的,那它是如何实现非阻塞式执行页面JS的?
考察对EventLoop概念的理解,核心是会在调用栈之外建立一个Event Table。可以将Event Table想象成一个电话注册本:调用栈会告诉event table注册一些特定的函数,并且在指定事件发生时会调用他们。当这些指定事件发生时,event table仅仅是简单地把要调用的函数移入Event Queue中去。event queue提供了一个简单等待区域,函数在此区域内等待被移入调用栈进行调用。 『究竟什么情况下,event queue中的函数才会被移入调用栈中?』。实际上,JavaScript 遵从一个简单的法则:存在一个监控进程不断检查调用栈是否为空,当调用栈为空的时候,检查事件队列(event queue)中是否有待调用的函数。如果事件队列中存在待调用的函数,队列头部的函数被移入调用栈执行。如果事件队列为空,监控进程就保持轮询状态。 这意味着js中的定时器的精度,实际上是没有保障的,你写一个setTimeout(function(){ do xxxx}, 1000); 并没办法保证它刚好是在1000ms之后调用,因为之前的代码执行可能非常耗时,也可能事件队列中有其他事件排在前面。 这样就出现了题目中的情况。 更多可参考:http://metaphor.space/2016/04/26/javascript-event-loop/; https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop;还有《你不知道的Javascript中卷》141页~143页,事件循环章节 值得一提的是:我们平常说JS是单线程执行的,但浏览器不是,浏览器是多线程的,有的线程负责网络请求,有的负责渲染页面等;不要搞混了 另外,ES6给JS带来了新的特性,比如加入了可以创建多线程的worker,以及更精准控制事件调度的Promise

88. 请问for of和for in的区别

【Question】 for of和for in的区别? for of可以用在普通对象上吗?

【Answer】
考察候选人对for 循环的理解 以及对es6中的for of和iterator理解

for in不多做解释了 for of主要是对实现了 Symbol.iterator 接口进行遍历

自定义for of
```
var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
```

89. 字符串的排列组合计算

【Question】 输入一个字符串,打印出该字符串中字符的所有排列的情况。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba.

function calc(str){
    var strArray = str.split('');
    // 补全代码
  }
  console.log(calc('ab')) // ['a','b']  ['b','a']

【Answer】
```javascript
function calc(str){
    var strArray = str.split('');
    var path = [];
    var docalc = function(array){
      if(array.length===1){
        path.push(array[0]);
        console.log(path);
        path.pop();
        return;
      }
      for(var i=0;i

90. DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Question】 DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Answer】
*  不冒泡的事件有blur、focus、load、unload、abort、error、mouseenter、mouseleave、resize
*  每个 event 都有一个event.bubbles属性,通过该属性可知是否冒泡

91. JavaScript实现对象深拷贝方法

【Question】 编码实现JavaScript实现对象深拷贝

【Answer】
var clone = function(v) {  
  var o = v.constructor === Array ? [] : {};  
  for (var i in v) {  
    o[i] = typeof v[i] === "Object" ? clone(v[i]) : v[i];  
  }  
  return o;  
}  

92. 故障分析-HTTPS证书不被信任

【Question】

如下图,在不同的设备上,同时访问同一个域名,一个设备显示证书不被信任,另一个设备正常,再使用多个其他设备访问,依然正常。分析可能的原因?以及需要获取的进一步的信息?

正常的设备

ssl_success.png<p>异常的设备</p>ssl_error.png<p>
</p>

【Answer】

需要进行的进一步的操作:

1) 查看证书详情:路径/SN/哈希值

2) 查看DNS解析结果

3) 查看系统时间/版本/浏览器版本

可能的原因:

1) 代理工具/安全软硬件

2) DNS劫持/路由劫持

3) 时间偏差

4) 操作系统/浏览器版本差异


93. 请实现一个CodingMan函数实现以下功能

【Question】


实现一个CodingMan,可以按照以下方式调用:
CodingMan(“Hank”)输出:
Hi! This is Hank!

CodingMan(“Hank”).sleep(10).eat(“dinner”)
输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

CodingMan(“Hank”).eat(“dinner”).eat(“supper”)
输出
Hi This is Hank!
Eat dinner~
Eat supper~

CodingMan(“Hank”).sleepFirst(5).eat(“supper”)
输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。


【Answer】


class _CodingMan {
    constructor(name) {
        this.tasks = [];
        const task = () => {
            console.log(`Hi! This is ${name}`);
            this.next();
        }
        this.tasks.push(task);
        setTimeout(() => {               // 把 this.next() 放到调用栈清空之后执行
            this.next();
        }, 0);
    }

    next() {
        const task = this.tasks.shift(); // 取第一个任务执行
        task && task();
    }

    sleep(time) {
        this._sleepWrapper(time, false);
        return this;                     // 链式调用
    }

    sleepFirst(time) {
        this._sleepWrapper(time, true);
        return this;
    }

    _sleepWrapper(time, first) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                this.next();
            }, time * 1000)
        }
        if (first) {
            this.tasks.unshift(task);     // 放到任务队列顶部
        } else {
            this.tasks.push(task);        // 放到任务队列尾部
        }
    }

    eat(name) {
        const task = () => {
            console.log(`Eat ${name}`);
            this.next();
        }
        this.tasks.push(task);
        return this;
    }
}

function CodingMan(name) {
    return new _CodingMan(name);
}



94. 实现如下函数add,使如下执行都等于9

【Question】


add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】

// 较通用的实现

function currying(fn, length) {

 length = length || fn.length;

 return function (...args) {

  return args.length >= length

   ? fn.apply(this, args)

   : currying(fn.bind(this, ...args), length - args.length) 

 }

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 通用实现2

function currying(fn, length) {

return function(...args) {

if (args.length >= length) {

return args.slice(0, length).reduce((t, i) => t += i);

}

return function(..._args) {

return add.apply(null, [...args, ..._args]);

}

}

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 直接的实现

function add(...args) {

if (args.length >= 3) {

return args.slice(0, 3).reduce((t,i) => t += i);

}

return function(..._args) {

return add(args.concat(_args));

}

}


95. 介绍一下你了解的 WebSocket

【Question】 简单介绍一下 WebSocket,ws 协议和 http 协议的关系是什么,WebSocket 如何校验权限? WebSocket 如何实现 SSL 协议的安全连接?

【Answer】
WebSocket 是基于 http 的,所以建立 WebSocket 连接前,
浏览器会通过 http 的方式请求服务器建立连接,
这个时候可以通过 http  的权限校验方式来校验 WebSocket,比如设置 Cookie。
同理,WebSocket 实现 SSL 协议也同 https 类似,会升级为 wss 连接。
另外,当然也可以在 WebSocket 中还可以通过加密或者 token 等方式,实现自己额外的加密传输和权限判断方式。
更多可参考 https://security.tencent.com/index.php/blog/msg/119


96. 请谈谈iframe有哪些缺点?

【Question】 iframe通常有哪些用途,主要缺点是什么

【Answer】
(1)iframe会阻塞主页面的Onload事件;
(2)搜索引擎的检索程序无法解读这种页面,不利于SEO;
(3)iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
(4)页面简的通信问题
使用iframe之前需要考虑这(1)(3)两个缺点。
如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

97. 请简述JAVAScript事件模型和事件代理

【Question】 简述一下JavaScript事件模型和事件代理,事件代理有哪些优点?

【Answer】
## 事件模型
事件三个阶段:事件捕获,目标,事件冒泡(低版本ie不支持捕获阶段)
## 事件代理及优点: 
把事件委托到其父对象上,借助事件冒泡机制,实现对节点的事件代理。  
### 优点  
*  可以大量节省内存占用,减少事件注册
*  当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

98. 根据id从多叉树里面查找出对应的节点的name

【Question】


一个树形的数据(如下数据),面试官给你一个id,然后拿到对应的name?
  var cityData = [
      {
        id: 1,
        name: '广东省',
        children: [
          {
            id: 11,
            name: '深圳',
            children: [
              {
                id: 111,
                name: '宝安',
                children: [
                  {
                    id: 1111,
                    name: '西乡',
                    children:[
                      {
                        id: 11111,
                        name: '坪洲',
                        children:[]
                      },
                      {
                        id: 11112,
                        name: '灵芝',
                        children:[]
                      }
                    ]
                  },
                  {
                    id: 1112,
                    name: '南山',
                    children:[
                      {
                        id: 11121,
                        name: '科技园',
                        children:[]
                      }
                    ]
                  }
                ]
              },
              {
                id: 112,
                name: '福田',
                children: []
              }
            ]
          },
          {
            id: 12,
            name: '广州',
            children: [
              {
                id: 122,
                name: '白云区',
                children: [
                  {
                    id: 1222,
                    name: '白云区',
                    children: []
                  }
                ]
              },
              {
                id: 122,
                name: '珠海区',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 2,
        name: '湖南省',
        children: []
      }
    ];


【Answer】


主要考查深度/广度优先遍历,递归算法
方法1:递归

let result = ''

// 递归实现
const recursion = (cityData, id) => {
  // cityData数据为空的时候直接返回
  if (!cityData || !cityData.length) return;
  // 常规循环cityData
  for (let i = 0, len = cityData.length; i < len; i++) {
    const childs = cityData[i].children;
    
    // 如果匹配到id的话,就是我们要的结果
    if (cityData[i].id === id) {
      result = cityData[i].name
    }
    // 如果还有子节点,执行递归
    if(childs && childs.length > 0){
      recursion(childs, id);
    }
  }
  return result
};

const r = recursion(cityData, 11112);
console.log(r) // 灵芝


方法2:广度优先遍历
let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定义一个数据栈
  let stack = [];

  let item = null;

  //先将第一层节点放入栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 将数据栈的第一个取出来
    item = stack.shift();
    // 如果符合就赋值给result
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

let r1 = range(cityData, 11112);

console.log(r1) // 灵芝


方法3:深度优先遍历
let result = ''

const deep = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 先定义一个数据栈
  let stack = []
  let item = null

  //先将第一层节点放入数据栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i])
  }
  // 循环
  while (stack.length) {
    item = stack.shift()
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈顶
    if (item.children && item.children.length) {
      // 注意这里调换了顺序
      stack = item.children.concat(stack);
    }
  }
  return result
};

let r3 = deep(cityData, 11112)
console.log(r3) // 灵芝


方法4:正则

const regular = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 数据转成字符串
  let cityStr = JSON.stringify(cityData)
  // 定义正则
  let reg = new RegExp(`"id":${id},"name":"([^\\x00-\\xff]+)",`)
  // 取到正则的子字符串并返回
  return (cityStr.match(reg))[1]
}

let r4 = regular(cityData, 11112);

console.log(r4) // 灵芝



99. js浮点运算

【Question】 console.info(0.7+0.1)会得到什么

【Answer】
输出0.799999


100. macro micro 任务队列(async/await版)

【Question】

async function async1() {

 console.log('async1 start');

 await async2();

 console.log('async1 end');

}

async function async2() {

 console.log('async2 start');

 return new Promise((resolve, reject) => {

  resolve();

  console.log('async2 promise');

 })

}

console.log('script start');

setTimeout(function() {

 console.log('setTimeout');

}, 0);  

async1();

new Promise(function(resolve) {

 console.log('promise1');

 resolve();

}).then(function() {

 console.log('promise2');

}).then(function() {

 console.log('promise3');

});

console.log('script end');

【Answer】

chrome 和 node 都是以下顺序



101. JS实现一个带并发限制的异步调度器

【Question】

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4// 一开始,1、2两个任务进入队列// 500ms时,2完成,输出2,任务3进队// 800ms时,3完成,输出3,任务4进队// 1000ms时,1完成,输出1// 1200ms时,4完成,输出4

【Answer】
class Scheduler {
concurrency = 2
running = 0
queue = []
add(task) {
return new Promise(resolve => {
this.queue.push({
taskGenerator: task,
resolve
})
this.schedule()
})
}
schedule() {
while (this.queue.length > 0 && this.running < this.concurrency) {
const curTask = this.queue.shift()
this.running += 1
curTask.taskGenerator().then(result => {
this.running -= 1
curTask.resolve(result)
this.schedule()
})
}
}
}

102. 写一个加法函数(sum),使他可以同时支持sum(x,y)和sum(x)(y)两种调用方式。

【Question】

写一个按照下面两种方式都能正常调用的 sum 方法
```javascript
console.log(sum(2,3)); // 输出5
console.log(sum(2)(3)); // 输出5
```

【Answer】
答案一
function sum(a,b){
if(b) {
return a+b
}else{
return function(c){
return a+c
}
}
}
答案二
function sum(){
var arg=arguments
if(arg.length==2) {
return arg[0]+arg[1];
}else{
return function(c){
return arg[0]+c
}
}
}

103. ES5,ES6中this指向考察

【Question】

  1. 以下代码输出什么结果,this.name中this指向什么:
    window.name = 'ByteDance';
    function A () {
    this.name = 123;
    }
    A.prototype.getA = function(){
     console.log(this);
     return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    
  2. 如何使funcA()返回undefined?
  3. 下面ES6中又会发生什么,this是什么?
    window.name = 'ByteDance';
    class A {
     constructor() {
      	this.name = 123;
     }
     getA() { 
       console.log(this);
         return this.name + 1; 
     }
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    

【Answer】
1. 输出`Bytedance1`, this指向widnow;
2. 正确使用applay / call;
3. 发生异常:Uncaught TypeError: Cannot read property 'name' of undefined,this为undefined;

104. 请问什么是跨域?跨域请求资源有几哪种方式?

【Question】 何为跨域?跨域请求资源有几哪种方式?

【Answer】
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
跨域请求资源的方式主要有:  
(1)JSONP 动态创建script标签  
但缺点是只支持get请求,并且很难判断请求是否失败(一般通过判断请求是否超时)。  
(2)Proxy代理  
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。  
(3)CORS跨域  
是现代浏览器提供的一种跨域请求资源的方法,需要客户端和服务器端的同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信  
服务响应头返回,Access-Control-Allow-Origin: *

105. 简述React Fiber原理

【Question】

试描述React Fiber的原理。

【Answer】

官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。

之前React的更新过程是同步的,所有更新逻辑会在一帧之内完成,如果组件过于复杂则会导致更新时间超过一帧,其他事务包括用户输入都会被延迟响应,从而引发卡顿。如下图:


破解方式——分片。

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

实现使用的API:requestIdleCallback

Q.为什么引入Fiber架构?原架构有何不足?
A.原架构采用递归遍历方式来更新DOM树,一旦开始,即占用主线程,无法中断,这在页面上会引起问题,如input输入后页面卡顿等

Q.Fiber如何解决该问题
A.时间分片和暂停

Q.Fiber如何实现?
A.使用链表结构,将递归遍历更改为循环遍历,然后配合requestIdleCallback API,实现任务拆分、中断和恢复

Q.Fiber如何实现比较?
A.双缓冲技术,在diff过程中创建新的DOM Tree,diff完成之后生成EffectList,即需要更新的地方,之后进入commit阶段,该阶段不允许中断。

Q.React Hook基于Fiber架构,hook的复用是如何实现的?
A.hook的数据存在于Fiber节点的数据结构中,具体为memoizedState中,该字段中存储了所有hook相关的信息,https://www.jianshu.com/p/d6244228a427 (重要)



106. 请简要描述ES6 module require、exports以及module.exports的区别

【Question】 考察候选人对es6,commonjs等js模块化标准的区别和理解

【Answer】
* CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
* ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
* CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
* export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
* ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性
* 混合使用介绍:https://github.com/ShowJoy-com/showjoy-blog/issues/39

107. 浏览器缓存机制考察

【Question】 浏览器缓存机制考察,包括cache-control , etag, expire, last-modify-time 以及 200 from cache、304

【Answer】
1、cache-control 和 expire 在浏览器端控制  Cache-Control的max-age>expire
2、etag 和 last-modify-time主 要服务器端对比使用

108. 版本号排序

【Question】 versions是一个项目的版本号列表,因多人维护,不规则

var versions=['1.45.0','1.5','6','3.3.3.3.3.3.3']

要求从小到大排序,注意’1.45’比’1.5’大

var sorted=['1.5','1.45.0','3.3.3.3.3.3','6']

【Answer】
```javascript
function sortVersion(arr) {
    return arr.sort((a, b) => {
        const arrA = a.split('.')
        const arrB = b.split('.')
        for (let i = 0; i < arrA.length; i++) {
            if (arrA[i] === undefined) {
                return -1
            } else if (arrB[i] === undefined) {
                return 1
            } else if (parseInt(arrA[i]) === parseInt(arrB[i])) {
                continue
            } else {
                return parseInt(arrA[i]) > parseInt(arrB[i])
            }
        }
    })
}
```

109. JS限流调度器

【Question】

实现JS限流调度器,方法add接收一个返回Promise的函数,同时执行的任务数量不能超过两个。

class Scheduler {
    async add(promiseFunc: () => Promise<void>): Promise<void> {
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
// log: 2 3 1 4


【Answer】


class Scheduler {
    constructor() {
        this.concurrency = 0
        this.queue = []
    }
    async add(promiseFunc) {
        if (this.concurrency >= 2) {
            return new Promise(r => {
                this.queue.push(() => promiseFunc().then(r))
            })
        }
        this.concurrency += 1
        await promiseFunc()
        this.concurrency -= 1
        let next = this.queue.shift()
        if (next) {
            this.add(next)
        }
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)



110. 实现一个简单的Event类(观察者模式)

【Question】

请实现一个观察者模式,拥有四个方法on,off,once和trigger


const Event = {

on() {} // 绑定

off() {} // 解绑

once() {} // 绑定一次

trigger() {} // 触发事件

};

【Answer】

```javascript function Event() { if (!(this instanceof Event)) { return new Event(); } this._callbacks = {}; } Event.prototype.on = function (type, handler) { this_callbacks = this._callbacks || {}; this._callbacks[type] = this.callbacks[type] || []; this._callbacks[type].push(handler); return this; }; Event.prototype.off = function (type, handler) { var list = this._callbacks[type]; if (list) { for (var i = list.length; i >= 0; --i) { if (list[i] === handler) { list.splice(i, 1); } } } return this; }; Event.prototype.trigger = function (type, data) { var list = this._callbacks[type]; if (list) { for (var i = 0, len = list.length; i < len; ++i) { list[i].call(this, data); } } }; Event.prototype.once = function (type, handler) { var self = this; function wrapper() { handler.apply(self, arguments); self.off(type, wrapper); } this.on(type, wrapper); return this; }; ```


【Question】 请说明 cookie、sessionStorage、localStorage 之间的区别、以及在你项目中的应用?

【Answer】
 a) cookie,HTTP Cookie(也叫Web cookie或者浏览器Cookie)是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。比较经典的,可以它用来确定两次请求是否来自于同一个浏览器,从而能够确认和保持用户的登录状态。Cookie的使用使得基于无状态的HTTP协议上记录稳定的状态信息成为了可能。
b) sessionStorage,为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
c) localStorage,localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

区别:
localStorage、sessionStorage 是 Web Storage Api 的组成 API,其为了解决 Cookie 的一些缺陷,服务端 Set 的 cookie 每次会携带在本域下所有的请求上,对性能有损耗。SessionStorage 存储有个期限,当关闭浏览器后就不再存在,但 localStorage 依然存在,需要明确删除。


112. 请简述js浏览器事件循环机制

【Question】


【Answer】

浏览器 Event Loop 是 HTML 中定义的规范,Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

  • JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

  • 同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

  • Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

  • 定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

  • 宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。


113. 何为https?https和http2有什么关系?

【Question】 简要描述HTTPS的安全机制,以及在web服务工程实践中需要注意的问题;描述http2的基本机制

【Answer】
HTTPS是指建立在安全的传输层(通常是tls/ssl)上的HTTP协议,通过对服务器的证书的认证,解决中间人攻击等问题。
证书(certificate)由客户端信任的的证书机构(CA)颁发,通过common name或SAN对服务进行描述;客户端通过CA的根证书对证书进行校验,并将请求域名和证书的common name/DNS域名进行验证,以检验证书的有效性。
目前,很多web api如Notification/web rpc/Service Worker等,都要求必须使用https。
在工程实践中,https存在以下需要注意的问题:
  - js/css等资源必须以https形式加载,否则浏览器将拒绝执行,所以CDN必须完成对https的支持
	- 非https请求的图片等资源不会携带referer
	
	http2是http协议的一个新版本,既可以明文传输也可以在https中使用。浏览器和服务器通过tls的ALPN/SNI等机制可以进行协议协商,决定使用什么协议

114. 用数组的reduce方法实现map方法

【Question】 用数组的reduce方法实现map方法

【Answer】
```
// 代码实现
Array.prototype.map2 = function(f) {
  return this.reduce(function(result, x, index, arr) {
    result.push(f(x, index));
    return result;
  }, []);
}

// 测试代码
var res = [1, 3, 5, 7].map2(function(item, idx){
  return item * 2;
});
console.log(res);
```

115. js异步操作与计算题

【Question】

for (var i = 0; i < 6; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

>1. console.log(new Date, i);得到的结果是什么? >1. 怎样优化,可以变成: 0 -> 1 -> 2 -> 3-> 4 ->5 >1. 如果继续优化,实现console.log(new Date, i);代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4(sleep),之后再暂停5秒,然后输出5, 实现结果类似: >1. 2017-08-31T04:38:23: 0 <— start IIFE >1. 2017-08-31T04:38:24: 1 <— sleep 1s >1. 2017-08-31T04:38:25: 2 <— sleep 1s >1. 2017-08-31T04:38:26: 3 <— sleep 1s >1. 2017-08-31T04:38:27: 4 <— sleep 5s >1. 2017-08-31T04:38:32: 5

【Answer】
1. 属于结果是暂停1S,然后输出6个6,setTimeout属于异步执行
1. 实现0-> 1 -> 2 -> 3-> 4 ->5,用闭包或者var改成let
1. 模拟编程中的sleep实现,参考答案:
```
// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeoutMS) => new Promise((resolve) => {
  setTimeout(resolve, timeoutMS)
});
(async () => {  // 声明即执行的 async 函数表达式
  for (let i = 0; i < 6; i++) {
      if (i < 5) {
        console.log(new Date(), i)
        await sleep(1000)
      } else {
        await sleep(4000)
        console.log(new Date(), i)
      }
    }
})()
```

116. 简单的实现Promise.all

【Question】



function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000);
    })
}
PromiseAll([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

Promise.all([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err=>{
    console.log(err)
})


【Answer】


function PromiseAll(list) {

    return new Promise((resolve, reject) => {

        let count = 0;

        let len = list.length;

        let result = [];

        list.forEach((item,index) => {

            item.then(res => {

                count++;

                result[index] = res;

                if (count === len) {

                    resolve(result);

                }

            }).catch(err => {

                reject(err)

            })

        })

    })

}



117. ES6 import的原理

【Question】 请描述ES6 import的原理以及与commonjs的require的区别

【Answer】
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

118. 不借助变量交换两个数

【Question】 var a = 1, b = 2; function swap(a,b){ …. } swap(a,b) console.log(a, b) // 2,1

【Answer】
方法一、
```
function swap(a,b){
  b=b-a;
  a=a+b;
  b=a-b;
  return [a,b]
}
```
方法二、
```
function swap(a,b){
  return [a, b] = [b, a]
}
```
方法三、
```
function swap(a,b){
  var a=a^b;
  var b=b^a;
  var a=a^b;
	return [a,b]
}
```

119. 实现垂直居中

【Question】


    <div id="block">        
    </div>

id为block的元素不定高不定宽,请实现它在浏览器窗口的居中显示。

【Answer】
```css
#block {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
```

120. 请回答当我们在使用new操作符时,它在对象操作的过程中具体做了什么

【Question】 考察候选人对原型链操作和js对象的理解

【Answer】
1. 简单回答:
1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
1. 属性和方法被加入到 this 引用的对象中。
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
```javascript
function Animal(name) {
      this.name = name;
}
  Animal.prototype.run = function() {
      console.log(this.name + 'can run...');
}
var cat = new Animal('cat'); //    
new Animal('cat')=function(){
let obj={}; //       
obj.__proto__=Animal.prototype; // obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,'cat');//   this        
}
```



121. css3实现多行文字截断处理

【Question】 用css分别实现单行截断和多行截断字符串,最后以…为结尾

【Answer】
单行:
```
.text-overflow ( @class ){
    .@{class} {
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
    }
}
```
多行:
```
.multi-text-overflow ( @class, @line ){
    .@{class} {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        display: box;
        -webkit-line-clamp: @line;
        -webkit-box-orient: vertical;
    }
}
```

122. 请介绍react diff算法和策略

【Question】 react的diff算法和策略了解多少,为什么react的diff性能好,遵循什么样的策略可以把 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

【Answer】
React分别对 tree diff、component diff 以及 element diff做了算法优化,
做了一些假设
1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
3.对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
tree diff:React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较
component diff:
a.如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
b.如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
c.对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff
element diff:
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,减少增加和删除
详见:https://zhuanlan.zhihu.com/p/20346379

123. 函数科里化

【Question】

实现如下函数add,使如下执行都等于9

add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】


function curry(fn) {
  return function res(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return res.apply(this, args.concat(args2));
      }
    }
  }
}



124. 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?应该怎么解决?

【Question】 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?怎么解决?

【Answer】
考察一下JS中整数的安全范围的概念,在头条经常会遇到长整型到前端被截断的问题,需要补一个字符串形式的id供前端使用。
主要会涉及到JS中的最大安全整数问题
https://segmentfault.com/a/1190000002608050

125. JavaScript this 考察

【Question】

下面代码输出的结果是什么?

var length = 10;

function fn() {

 return this.length+1;

}

var obj = {

 length: 5,

 test1: function() {

  return fn();

 }

};

obj.test2=fn;

//下面代码输出是什么

console.log(obj.test1())

console.log(fn()===obj.test2())

【Answer】

11, false(11===6)


126. requestAnimationFrame 和 setTimeout 的区别

【Question】 requestAnimationFrame 和 setTimeout 都可以用来实现动画,它们的区别是什么

【Answer】
1. 执行频率不同,前者按照屏幕刷新频率执行,后者自行控制,可能有无用开销(执行频率小于刷新频率,即1帧执行多次)
2. 前者在页面不可见时,会停止执行(省电),后者在页面不可见时仍会执行,带来不必要开销


127. 编码-js高阶函数考察

【Question】

实现一个repeat方法,要求如下:


// 需要实现的函数

function repeat (func, times, wait) {

// 补全

}


// 使下面调用代码能正常工作

const repeatFunc = repeat(console.log, 4, 3000);

repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒


【Answer】

考点1:能意识到repeat返回的是一个函数,知道参数怎么传递。

考点2:setTimeout的时间,微任务


参考答案

function repeat(fn, times, wait) {

if(typeof times !== 'number') return;

if(typeof wait !== 'number') return;

return function(str){

for(let i = 0; i < times; i++){

setTimeout(()=>{

fn(str)

}, i * wait)

}

}


128. Vue框架中组件消息通信方式

【Question】 考察候选人对Vue框架的消息通信方式了解程度:

  1. vue父子组件通信方式?
  2. 非父子组件通信方式?
  3. 前两问OK,追问:当一个父组件与子组件中间隔着很多层组件怎么办?

【Answer】
1. 父子组件通信方式
在Vue中,父子组件的关系可以总结为props down, events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。

2. 非父子组件通信
两个独立的组件之间通信,可以借助一个空的Vue实例作为中央事件总线,空实例相当于代理人的形式进行消息监听或触发

3. 父子之间层级过多时
当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。
这块如果了解开源的Element组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以`[组件名, 消息名, 参数]`三元组进行消息传递,降低长链传播成本;
具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

129. 什么是 XSS,怎么造成的,有什么防御方法?

【Question】 考察面试者对于 XSS 是否了解,是否足够重视。

【Answer】
XSS 就是在 web 中能够通过某种方式产生执行任意 JavaScript 脚本的情况,
最常见的一种情况就是将用户的输入,直接放到当前 runtime 中,比如用户输入直接放到页面的 html 里面,
立刻显示出来。
XSS 实际上是非常危险的,因为理论上讲,如果能够执行 JavaScript,实际上攻击者可以做任何事情。
简单的就是输出点什么,偷偷 cookie,或者结合 CSRF 攻击,或者让浏览器跳转一下,
复杂点的甚至可以改掉当前整个页面,伪造一切用户看到东西,危害无穷。
如果这种输入存储到数据库中,就会变成一个永久型的 XSS,危害就更大了。
防止 XSS 最简单的就是使用各种框架,如 React、Vuejs 等,对用户输入进行 html 转义。
另外,服务端要设置 httpOnly 的 header,防止 JavaScript 操作 cookie。
当然,服务端也可以对输入进行转义或者过滤监测。

130. webpack插件编写

【Question】

  1. 有用过webpack么?说说该工具的优缺点?
  2. 有开发过webpack插件么?
  3. 假如要在构建过程中去除掉html中的一些字符,如何编写这个插件?

【Answer】
webpack优缺点:
* 概念牛,但文档差,使用起来费劲
* 模块化,让我们可以把复杂的程序细化为小的文件
* require机制强大,一切文件介资源
* 代码分隔
* 丰富的插件,解决less、sass编译

开发插件的两个关键点Compiler和Compilation:
* compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。
* compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

插件编写可参考:https://doc.webpack-china.org/development/how-to-write-a-plugin

131. 如何实现微信扫码登录?

【Question】 综合题,考察网络、前端、认证等多方面知识

【Answer】
参考答案:
https://zhuanlan.zhihu.com/p/22032787
具体步骤:
1. 用户 A 访问微信网页版,微信服务器为这个会话生成一个全局唯一的 ID,上面的 URL 中 obsbQ-Dzag== 就是这个 ID,此时系统并不知道访问者是谁。
2. 用户A打开自己的手机微信并扫描这个二维码,并提示用户是否确认登录。
3. 手机上的微信是登录状态,用户点击确认登录后,手机上的微信客户端将微信账号和这个扫描得到的 ID 一起提交到服务器
4. 服务器将这个 ID 和用户 A 的微信号绑定在一起,并通知网页版微信,这个 ID 对应的微信号为用户 A,网页版微信加载用户 A 的微信信息,至此,扫码登录全部流程完成

132. 设计类似 Vue.js 双向绑定功能的核心逻辑“监听对象属性变化”功能

【Question】 实现一个类,可以监听对象属性的值变化。加分项:考虑对象存在值为数组或对象的属性。

	class Observe {
		constructor(data: Object) {
		}
		// 监听属性变更
		$on() {
		}
		// 触发属性变更事件
		$emit() {
		}
	}
	const data = new Observer({
		a: 1
	});
	coonsole.log(data.a) // console: 1
	data.$on('a', (newValue, oldValue) =&gt; {
		// this === data
		console.log(newValue, oldValue);
	});
	data.a = 2 // console: 2 1

【Answer】
待补充

133. 请简要描述

【Question】

【Answer】
### 作用:
defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
### 区别:
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

134. 原型链、this指针、自有属性考察

【Question】

var a= function () { this.b =3; }
var c = new a();
a.protorype.b = 9;
var b = 7;
a();

问:

console.log(b);
console.log(c.b); 

分别输出什么?

【Answer】
- 第一个 `b = 3`
- 第二个 `c.b = 3`

【Question】

如题

【Answer】
cookie 在浏览器中存在,并且每个请求都会带上,而且是可以设置失效时间的,失效前就会有效。 session 是一次会话有效,也就是说,浏览器关闭 session 就会失效。 其实这道题目考察的一个重点是在于,session 这种机制是怎么在浏览器中实现的呢? 实际上 session 的实现也是在浏览器中放了一个 cookie,失效时间是一次会话失效。

136. JS异步队列macrotask和microtask

【Question】

console.log('begin')
setTimeout(() =&gt; {
	console.log('setTimeout 1')
	Promise.resolve().then(() =&gt; {
		console.log('promise 1')
		setTimeout(() =&gt; {
			console.log('setTimeout2 between promise1&amp;2')
		})
	}).then(() =&gt; {
		console.log('promise 2')
	})
}, 0)
console.log('end')

【Answer】
```
begin
end
setTimeout 1
promise 1
promise 2
setTimeout2 between promise1&2
```

137. 如何理解虚拟DOM?

【Question】 如何理解虚拟DOM?

【Answer】
对虚拟dom和diff算法中的一些细节理解与考察,[https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13)

138. 如何判断一个 JS 对象为空对象

【Question】 如何判断一个 JS 对象为空对象 ?空对象形如{}

【Answer】
1. 使用 `for in`
	```javascript
	function isEmptyObject(obj){
  	for(var key in obj){
    	return false
		};
		return true
	};
	```
2. 通过 JSON.stringify 方法来判断
	```javascript
	if(JSON.stringify({}) === '{}'){
		console.log('empty obj');
	}
	```
3. 使用 ES6 增加的 Object.keys()
	```javascript
	if(Object.keys(obj).length === 0){
		console.log('empty obj');
	}
	```

139. 什么是闭包?实现每隔1秒输出数组中的一个数字

【Question】 解释下js中的闭包概念,解释OK,给出编程题目考察基本功

【Answer】
```js
function fun(arr) {
    var i, len;
    for (i = 0, len = arr.length; i < len; i++) {
      (function(i){
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
}
```

140. promise运行过程解答

【Question】 如下代码的运行结果是什么?

 process.nextTick(() =&gt; {console.log('nextTick')})
Promise.resolve().then(()=&gt; {console.log('promise1');}).then(()=&gt; {
  console.log('promise2');
});
setImmediate(() =&gt; {console.log('setImmediate')})
console.log('end') 

【Answer】
1. end -> nextTick -> promise1 -> promise2-> setImmediate
1. process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。
1. 事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

141. 请简述常见web安全及防护原理

【Question】 常见web安全及防护原理,请举例说明。

【Answer】
1、SQL注入原理  
		就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点  
1. 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2. 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。  
2、XSS原理及防范  
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者JavaScript代码。
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法  
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

3、CSRF原理及防范  
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法

142. 数字格式化问题:1234567890 –> 1,234,567,890

【Question】 数字格式化问题,将1234567890 –> 1,234,567,890

【Answer】
非正则实现
```javascript
let test = '1234567890'
function formatCash(str) {
  let arr = []
  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))
    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))
  }
  return arr.join(',')
}
console.log(formatCash(test)) // 1,234,567,890
```
正则实现
```javascript
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
console.log(format) // 1,234,567,890
```

143. 模拟实现loadash中的_.get()函数,实现如下传入参数取值效果

【Question】

function get() {
  // 请补全函数参数和实现逻辑
}
const obj = { selector: { to: { toutiao: 'FE coder' } }, target: [1, 2, { name: 'byted' }] };
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')

//  输出结果:
// ['FE coder', 1, 'byted']

【Answer】
```javascript
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
```
1. Use Array.map() for each selector
2. String.replace() to replace square brackets with dots
3. String.split('.') to split each selector
4. Array.filter() to remove empty values
5. Array.reduce() to get the value indicated by it

144. 合并两个有序数组

【Question】 合并两个有序数组

【Answer】
```
function mergeSortedArray(a, b){
  var merged = [], 
      aElm = a[0],
      bElm = b[0],
      i = 1,
      j = 1;
  if(a.length ==0)
    return b;
  if(b.length ==0)
    return a;
  while(aElm || bElm){
   if((aElm && !bElm) || aElm < bElm){
     merged.push(aElm);
     aElm = a[i++];
   }   
   else {
     merged.push(bElm);
     bElm = b[j++];
   }
  }
  return merged;
}
```
验证
```
mergeSortedArray([2,5,6,9], [1,2,3,29]);
结果 [1, 2, 2, 3, 5, 6, 9, 29]
```

145. 进行CSRF漏洞扫描的原理和防御方式是什么?

【Question】 如题

【Answer】
CSRF 就是在用户不知情的情况下,发出了请求,让用户做了不该做的操作。
举个例子,比如你的一个网站中有个 img 标签,src 指向的是微博关注某人的接口,
那么当用户访问你的网站时,就会在微博上关注那个人,而且这个操作用户是不知情的。
因为 img src 发出的跨域请求,也是会携带 cookie 的,所以如果用户在微博登录过,
那么就会带有微博的登录授权。同理,如果是其他操作,可能也存在这种漏洞,比较危险的情况就是付款。
一般会采用 CSRF token 的方式防御,就是关键请求得要换取一个一次有效的 token 才有权限。


146. 判断一个字符串是否是回文字符串

【Question】 判断一个字符串是否是回文字符串,回文字符串是对称字符串的形式,例如:did,eve, dad, level

【Answer】
```
function isPalindrome(str){
  var i, len = str.length;
  for(i=0; i isPalindrome('madam')
  = true
> isPalindrome('toyota')
  = false
```

147. box-sizing 实践

【Question】


<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 10px;
        height: 10px;
        border: 1px solid red;
        margin: 2px;
        padding: 2px;
        background: blue;
      }

      #borderBox {
        box-sizing: border-box;
      }

      #contentBox {
        box-sizing: content-box;
      }
    </style>
  </head>
  <body>
    <div>请问下面两个 div 元素,蓝色区域的宽高各是多少像素?</div>
    <div id="borderBox" class="box"></div>
    <div id="contentBox" class="box"></div>
  </body>
</html>


【Answer】

borderBox:10px(width) - 1px(border) * 2 = 8px

contentBox 10px(width) + 2px(padding) *2 = 14px


答题要点:除了验证候选人是否真正了解 box-sizing 之外,也考察候选人是否了解 background 会影响元素的 padding 区域,而不影响 margin 区域这个特点


148. 链式调用+延迟计算

【Question】

写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....)


console.log(sum(1,2,3)(4)) => 输出 10



考察链式调用,闭包,延迟计算,函数toStirng/valueOf




【Answer】


function sum(...args) {
  function next(...innerArgs) {
    args.push(...innerArgs);
    return next;
  }
  next.valueOf = next.toString = () => {
    return args.reduce((r, c) => r + c, 0);
  };

  return next;
}



149. 请描述micro task 与 macro task的区别及应用

【Question】


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');


【Answer】

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout


150. 数组flat函数设计

【Question】 设计一个flat函数将如下数组arr=[1,2,[‘3’,4,’5’,[6,[7,8],9]]]输出为1,2,’3’,4,’5’,6,7,8,9。至少写出两种方法,要求不能改变数组中的原始数据类型

【Answer】
*  方法一:递归
```javascript
function flat(array) {
    var result = [];
    var each = function(arr) {
      arr.forEach(item => {
        if (item instanceof Array) {
          each(item);
        } else {
          result.push(item);
        }
      });
    };
    each(array);
    return result;
  }
var arr=[1,2,['3',4,'5',[6,[7,8],9]]];flat(arr).forEach(item=>{console.log(item)})

```
*  方法二:toString(格式转换),无法保证类型
```javascript
Array.prototype.toString = function() {
  return this.join(',');
};
console.log([1,2,[3,4,[5,6,7]]]+'');
```
*  方法三:Iterator
```javascript
Array.prototype[Symbol.iterator] = function() {
  let arr = [].concat(this),
    index = 0;
  let getFirst=function(array){
    let first=array[0];
    if(first instanceof Array){
      return getFirst(array[0])
    }else if(first!==undefined){
      return array.shift()
    }else{
      return ''
    }
  }
  return {
    next: function() {
      let item=getFirst(arr);
      if(item){
        return {
          value:item,
          done:false
        }
      }else{
        return {
          done:true
        }
      }
    }
  }
}
var t=[1,2,['3',4,'5',[6,[7,8],9]]];
for(let i of t){console.log(i)}
```

【Question】 基础题考察 cookie 和 localStorage 的理解。

【Answer】
存储在 Cookie 中每个 request 都会带上,而放在 localStorage 中,仅有浏览器中会存储。

152. 请说说HTML的Meta标签的用途,并列举一些常用的meta标签

【Question】

【Answer】
考察对网页结构和语义的理解 

```
The HTML  element represents metadata that cannot be represented by other HTML meta-related elements, like , , 

153. 说说前端优化?图片懒加载原理是什么?

【Question】

  • 考察前端的一些优化方式
  • 图片懒加载原理

【Answer】
1. 优化手段:雅虎的34条优化手段,比如:代码压缩、减少请求、cdn、缓存
2. 图片懒加载原理:img标签设置占位属性(data-src),存储真正的图片地址;原src设置占位图片地址;当图片(快)进入用户可视区域的时候进行地址替换;

154. 请谈谈你对ES6的箭头函数的理解

【Question】

var func1 = x =&gt; x;
var func2 = x =&gt; {x}; 
var func3 = x =&gt; ({x});
console.log(func1(1));
console.log(func2(1));
console.log(func3(1));

请写出程序运行结果。

【Answer】
程序运行结果为:
第一个:1
第二个:undefined
第三个:{x: 1}

155. 无重复字符的最长子串

【Question】

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

样例:


  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。



【Answer】


var lengthOfLongestSubstring = function(s: string) {
    let list = s.split("");
    let son = [];
    let max = [];
    for (let i = 0; i < list.length; i++) {
        let current = list[i];
        let index = son.indexOf(current);
        if (index === -1) {
            son.push(current);
        } else {
            let sameIndex = i - son.length + index;
            if (son.length > max.length) {
                max = [...son];
            }
            son = son.slice(sameIndex + 1, son.length);
            son.push(current);
        }
    }
    return max.length;
};



156. 列举一个近期做的最能体现设计能力的项目

【Question】 请举出一个你近期做的项目,项目需要最能体现设计能力, 请从以下角度说明:

  1. 项目描述
  2. 技术选型
  3. 模块化
  4. 模块之间通信
  5. 工程化
  6. 前后端数据流

【Answer】
这是一个开放式的工程设计题目,没有固定答案,评分参考评分标准

157. 实现一个 JSONP

【Question】 函数签名如下:

function jsonp(url, callback) {
  // TODO
}

【Answer】
主要考察如何处理第二个参数 `callback` 的问题,
加分项比如超时处理 onerror 的处理, xss 考虑等等

```
const kCallBackMap = {};
function uuid() {
  return ...;
}

function jsonp(url, callback) {
  const callbackId = uuid();
  url += 'callback=' + callbackId;
	window[calbackId] = callback;
	
	const script = document.createElement('script');
	script.src = url;
	document.head.appendChild(script);
}
```

158. 请谈一谈JAVAscript的作用域和this

【Question】

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

【Answer】
```
window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

主要考察javascript的作用域和this指向。作用域是静态的,声明时确定;this是动态的,运行时确定。
```

159. 请问CSS position有哪些定位方式

【Question】 CSS position有哪些定位方式,每种方式是如何定位的?

【Answer】
### position取值
relative, fixed,absolute和staic、sticky 5种
### 定位方式
*  staic-默认位置;元素会像通常那样流入页面。顶部,底部,左,右,z-index属性不适用。  
*  relative-元素的位置相对于自身进行调整,而不改变布局(从而为未被定位的元素留下一个空白)。  
*  absolute-该元素从页面的流中移除,并相对于其最近位置的祖先定位(非static)在指定位置,如果有的话,或者与初始包含块相对。绝对定位的框可以有边距,并且不会与其他边距折叠。这些元素不影响其他元素的位置。  
*  fixed元素是定位在相对于窗口。  
*  sticky,是相对定位和固定定位的混合。该元素被视为相对位置,直到它越过指定的阈值,此时它被视为固定位置。  


160. 请介绍一下Oauth2.0 的认证过程

【Question】 如题

【Answer】
可以参考 http://www.jianshu.com/p/0db71eb445c8 或者 
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 的答案,
回答的一个重点是 code(授权码)仅一次有效,并且要有失效时间,而且很短,比如一分钟,
因为浏览器收到会立刻跳转。
还有就是服务端可以根据 code 结合相应的 sercet 去获取 token,要说清楚。

161. express中间件的原理

【Question】

express中间件的实现原理 并给出实现

【Answer】
主要考察候选人对中间件的理解 参考代码 ``` export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } ``` koa中间件主要使用 generator和promise可参考https://github.com/tj/co

162. 实现es6字符串模板方法sprintf

【Question】


const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
	name: 'Yiming Zhang',
	city: 'FuJian',
});
console.log(result); // My name is Yiming Zhang,I'm from FuJian


【Answer】


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);



163. 登录表单设计/扫码登录/第三方登录

【Question】

  1. 请实现一个登录表单
  2. 用GET方法行不行?csrf是什么?如何防御?
  3. cookie-sesssion的工作机制
  4. 你已经登录产品的App端,要在web实现扫码登录,该如何设计?
  5. 接入第三方登录(如微信),如何设计?

【Answer】
1. 正确书写html
2. 正确回答GET和POST的区别,从语义、弊端、安全等方面。csrf的防御:token,samesite,referer校验(弊端)等
3. 正确理解cookie-session的工作机制,sessionId的设计,存储
4. 考察对司空见惯的扫码登录,是否有思考其实现。正确设计 Client/Server/App 三方流程,设计二维码存储的内容,client通知有轮训或websocket等解决方案
5. 正确理解 Client/Server/App/Weixin Server 四方流程,理解oauth2协议

164. 作用域以及变量提升

【Question】

请写出下题的结果:

var a = 1; 
function b() { 
    a = 10; 
    return; 
    function a() {} 
} 
b(); 
console.log(a);   

【Answer】
结果:1

165. setTimeout 和 Promise

【Question】

请写出程序的输出内容

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function(resolve) {
  console.log(2);
  for(var i=0 ; i < 10000 ; i++) {
    if (i == 9999) {
      resolve();
    }
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);


【Answer】

正确答案:2 3 5 4 1。重点关注:候选人是否把 2 写在第一位,以及 4 和 1 的顺序。


166. requestIdleCallback和requestAnimationFrame有什么区别?

【Question】

requestIdleCallback和requestAnimationFrame有什么区别?

【Answer】

requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。

我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了。

一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调。

由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了。


167. 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Question】 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Answer】
`
Array.prototype.findDuplicate = function (n) {
    var results = [];
    if (typeof n != 'number' || isNaN(n)) {
        return results;
    }
    
    var itemFreqs = {};
    this.forEach(function (item) {
        if (!itemFreqs[item]) {
            itemFreqs[item] = 0;
        }
        itemFreqs[item] ++;
    });
    
    for (var item in itemFreqs) {
        if (itemFreqs[item] >= n) {
            results.push(item);
        }
    }
    
    return results;
}

`

168. 请回答DOM中对应创建、移除、追加、复制、查找节点的方法是什么?

【Question】 考察候选人对原生dom操作的方法的理解和掌握熟练程度

【Answer】
1.  创建新节点
	*  createDocumentFragment() //创建一个DOM片段
	*  createElement() //创建一个具体的元素
	*  createTextNode() //创建一个文本节点

1.  克隆节点
*  cloneNode()

1. 添加节点
*  appendChild()
*  insertBefore()

1. 移除节点
*  removeChild()

1. 替换节点
*  replaceChild()

1. 查找节点
*  querySelector()
*  querySelectorAll()
*  getElementById()
*  getElementsByName()
*  getElementsByTagName()



169. 请描述如何用原生JS实现数字的货币格式化

【Question】

# 如何用原生JS实现数字的货币格式化,例如数字6123456789格式化后为6,123,456,789,不低于两种方法。

【Answer】

方法一: (6123456789).toLocaleString('en-US') // 6,123,456,789


方法二: (6123456789).toString().split('').reverse().join('').replace(/\d{3}/g,function($1){return $1+','}).split('').reverse().join('')



170. let,const,var的区别

【Question】 请说明一下let,const,var的区别 并回答如下代码会不会报错

const a = {};
a.test = 1;

【Answer】
考察候选人对es6变量声明的理解
1. let声明的变量拥有块级作用域
2. let声明的全局变量不是全局对象的属性
3. let不能重新声明变量
4. const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

上面代码只是针对a的引用 并不会报错

171. 如何实现链式调用

【Question】 请实现函数 a, b, c,使调用方式为 a().b().c() 时,结果为输出 a b c。 如果上面问题回答出来了,并且是在 a 函数内部 return Object 实现, 那么可以补充问下如何能够实现让三个函数任意链式顺序调用。 如 a().c().b() 或 b().a().c() 。

【Answer】
这道题主要就是考察面试者对 JavaScript 的 Object 概念理解是否清晰,
最好的答案是直接将 a b c 三个函数挂载到 runtime 中的某个全局变量中,比如可以是 window。
然后在每个函数内 return window 就可以了。
当然,也可以按照第一道题目的顺序,分别在相应函数内 return 下个函数,但是这样做无法调换顺序。

172. 实现千位分隔符

【Question】 给一个数字,比如:1234567.90,转化成:1,234,567.90

【Answer】
```js
function commafy(num) {
  return num && num
      .toString()
      .replace(/^\d+/, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','));
}
console.log(commafy(1234567.90)); //1,234,567.90
```

173. 编写javascript深度克隆函数deepClone

【Question】 编写javascript深度克隆函数deepClone

【Answer】
```javascript
function deepClone(obj) {
    var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }

        return new RegExp(obj.source, flags.join(''));
    }

    var result = Array.isArray(obj) ? [] :
        obj.constructor ? new obj.constructor() : {};

    for (var key in obj ) {
        result[key] = deepClone(obj[key]);
    }

    return result;
}

function A() {
    this.a = a;
}

var a = {
    name: 'qiu',
    birth: new Date(),
    pattern: /qiu/gim,
    container: document.body,
    hobbys: ['book', new Date(), /aaa/gim, 111]
};

var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
```

174. 请谈谈你对JS单线程以及setTimeout的理解

【Question】

setTimeout(function() {
	setTimeout(function() { console.log(1) }, 100)
	console.log(2)
	setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
	console.log(4)
}, 100)
console.log(5)

请说出上面代码的输出顺序以及原因?如果吧4改为101ms呢?

【Answer】
正确顺序为:5 2 3 4 1
如果4改为101ms则执行顺序还是不变
原因:
1.  JS单线程
2. setTimeout不在当前eventloop。且执行顺序依赖入队顺序。setTimeout 0是放入下一个loop的队尾
3. 虽然4和1都是100ms延迟的标记,但是4先入队列。
4. setTimeout的time是个标记,会在eventloop循环去检测,符合条件的执行,不符合条件的延后到下一个eventloop,这执行过程本身又有时间,因此尽管101>100,但是在一个执行周期内,他们都会被触发,4先入队所以不变

175. async & forEach 考察

【Question】 以下代码的运行结果

const list = [1, 2, 3];
const square = num =&gt; {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(num * num);
        }, 1000);
    });
}
function test() {
    list.forEach(async x =&gt; {
        const res = await square(x);
        console.log(res);
    });
}
test()

如果希望每隔1s输出一个结果,应该如何改造?

【Answer】
1s 后输出 1 4 9  
改为 for 循环:
```javascript
async function test() {
    for (let x of list) {
        const res = await square(x);
        console.log(res)
    }
}
```


176. css单位的百分比

【Question】 给一个div设置它父级div的宽度是100px,然后再设置它的padding-top为20%。
问现在的div有多高?如果父级元素定位是absolute呢?

【Answer】
现有div的高度等于自身高度+父级块的宽度*20%,如果父级元素定位是absolute,结果不变;
当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!

177. NodeJS实现简单的HTTP代理和隧道代理

【Question】 Web代理一般包括普通的HTTP代理和隧道代理,谈谈理解。 NodeJS实现一个简单的HTTP代理,如在本地 8888 端口开启 HTTP 代理服务,修改浏览器的 HTTP 代理为 127.0.0.1:8888 后再访问 HTTP 网站,代理可以正常工作 对隧道代理了解多少,能否实现?

【Answer】
http普通代理:HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。
```
// http 普通代理
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(8888, '0.0.0.0');
```
隧道代理:HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发
```
const http = require('http');
const net = require('net');
const url = require('url');

function connect(cReq, cSock) {
  const u = url.parse('http://' + cReq.url);

  const pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer().on('connect', connect).listen(8888, '0.0.0.0');
```
合二为一
```
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

function connect(cReq, cSock) {
  var u = url.parse('http://' + cReq.url);

  var pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer()
  .on('request', request)
  .on('connect', connect)
  .listen(8888, '0.0.0.0');
```
需要注意的是,大部分浏览器配完隧道代理,默认只会让https走隧道代理,http如果需要走隧道代理,还需要写个Nodejs的验证
```
const options = {
  hostname: '127.0.0.1',
  port: 8888,
  path: 'toutiao.com:80',
  method: 'CONNECT'
};

const req = http.request(options);

req.on('connect', function(res, socket) {
  socket.write('GET / HTTP/1.1\r\n' +
    'Host: toutiao.com\r\n' +
    'Connection: Close\r\n' +
    '\r\n');

  socket.on('data', function(chunk) {
    console.log(chunk.toString());
  });

  socket.on('end', function() {
    console.log('socket end.');
  });
});

req.end();
```

178. 假设一个网页嵌入一个iframe,如何更改iframe内dom样式?

【Question】 假设一个网页嵌入一个iframe,如何更改这个iframe内dom样式

【Answer】
区分同源和不同源解决方案,同源可以通过document.getElementById('iframeId').contentWindow.document,
不同源:分iframe的嵌入的页面是否自己可控,可控可以通过postMessage方式更改,iframe页面监听message事件;如果页面不可控,应该无解。
可以追问iframe有同源策略限制,举个例子说明

179. 数组随机排序

【Question】

var arr=[1,2,3,4,5,6]

【Answer】
方法一、
```javascript
arr.map(item=>{
    return {
        value:item,
        key:Math.random()
    }
})
.sort((a,b)=>a.key-b.key)
.map(item=>item.value)
```
方法二、
```
var arrayToRand = (arr) => {
    for(let i=0; i

180. js事件模型

【Question】 浏览器的事件模型?在当前的事件模型中,哪些事件可以冒泡,哪些不会冒泡,为什么?不冒泡的元素,如何来实现事件代理?

【Answer】
考察浏览器事件模型,看看是不是了解事件模型背后的设计意图。

浏览器开发团队遇到的问题:页面上哪一部分会拥有某个特定的事件?比如单击一个嵌套的同心div,那么到底哪一个div会拥有这个点击事件?实际上难以确定点击者的意图,团队给出的解决方式是所有div都将拥有这个事件,于是产生了事件流模型。如上一个问题所述,“事件”的概念在GUI编程中如此之重要,而这种流式模型能给予其很大的灵活性和控制
对于能精确确定意图的(这种冒泡的话一般也会带来问题,比如mouseleave),或者不可能产生嵌套的媒体类元素,冒泡就不是必须的;对于不冒泡的元素,可以在捕获阶段代理,DOM2级规范addEventListener的第三个参数

181. 请列举说明几个在web中实现长连接的技术方案或手段

【Question】 本地主要考察候选人对长连接技术的概念理解和区分,如果能回答答出大致的名词可以继续追问一些具体的激技术实现细节和存在的优缺点等等。

【Answer】
参考答案:
1. https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet/12855533#12855533
1. https://blog.csdn.net/liang0000zai/article/details/40537059

* Long Polling
* Server-Sent Events
* Websockets
* Comet

182. 函数作用域

【Question】 用代码实现JavaScript中Function的bind方法的polyfill

【Answer】
```
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                                 ? this
                                 : oThis || this,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```

183. CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Question】 CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Answer】
  - `content-box` 默认值,width内容宽度
	- `border-box` width 包含`padding`和`border`

184. JS的new操作符具体做了什么

【Question】 JS的new操作符具体做了什么,描述一下,最好可以体现在代码上

【Answer】
```
function A() {
  this.name = 'a';
  this.getName = function() {
    return this.name;
  }
}
var a = new A();

var aa = new Object();
aa.__proto__ = A.prototype;
A.call(aa);
// 还有最后一步,如果发现A返回的是一个Object类(非primitive类型),则直接返回A的返回值,否则把aa返回出去
```

185. JS编码二叉树的实现与遍历

【Question】 JS编码实现一个二叉树的构造函数,包括节点类Node,树类BST,插入节点函数insert, 并且满足 1.左子节点的值 < 父节点的值 <= 右子节点的值 2.可以实现先序,中序,后续遍历

【Answer】
```
// 二叉树
function BST() {
  this.root = null;
}

BST.prototype.insert = function(data) {
  var n = new Node(data, null, null);
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    for (;;) {
      if (data < current.data) {
        if (current.left === null) {
          current.left = n;
          break;
        } else {
          current = current.left;
        }
      } else {
        if (current.right === null) {
          current.right = n;
          break;
        } else {
          current = current.right;
        }
      }
    }
  }
}

// 先序遍历
BST.prototype.preOrder = function(node) {
  if (node !== null) {
    console.log(node.show() + " ");
    this.preOrder(node.left);
    this.preOrder(node.right);
  }
}

// 中序遍历
BST.prototype.inOrder = function(node) {
  if (node !== null) {
    this.inOrder(node.left);
    console.log(node.show() + " ");
    this.inOrder(node.right);
  }
}

// 后序遍历
BST.prototype.postOrder = function(node) {
  if (node !== null) {
    this.postOrder(node.left);
    this.postOrder(node.right);
    console.log(node.show() + " ");
  }
}

// 节点对象
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = function() {
    return this.data;
  }
}

// 测试代码
var bst = new BST();
var nums = [10, 3, 18, 2, 4, 13, 21, 9, 8, 9];
for (var i = 0; i < nums.length; i++) {
  bst.insert(nums[i]);
}
bst.preOrder(bst.root);
bst.inOrder(bst.root);
bst.postOrder(bst.root);
```

186. 简述一下src与href的区别

【Question】 描述一下html中的src与href的区别和使用场景是什么

【Answer】
基本答案:src用于指向外部资源的位置替换当前元素,href用于在当前文档和引用资源之间确立联系。
1.  src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;
在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。
这也是为什么将js脚本放在底部而不是头部。
 
1.  href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
这也是为什么建议使用link方式来加载css,而不是使用@import方式。

187. js运行机制

【Question】 下面一段代码的输出:

(function() {
  console.log('this is the start');
  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });
  console.log('this is just a message');
  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);
  console.log('this is the end');
})();

【Answer】
因为前端编程基本属于「Event-driven programming」范式,这是GUI之类的交互式程序的基础,区别于传统的批处理式编程。一个页面上的交互行为,基本都是由用户发起的,然而用户的行为意图是难以预测的,所以需要异步的驱动机制来应对
因此有进一步问题:
平时都说JS是单线程执行的,那它是如何实现非阻塞式执行页面JS的?
考察对EventLoop概念的理解,核心是会在调用栈之外建立一个Event Table。可以将Event Table想象成一个电话注册本:调用栈会告诉event table注册一些特定的函数,并且在指定事件发生时会调用他们。当这些指定事件发生时,event table仅仅是简单地把要调用的函数移入Event Queue中去。event queue提供了一个简单等待区域,函数在此区域内等待被移入调用栈进行调用。 『究竟什么情况下,event queue中的函数才会被移入调用栈中?』。实际上,JavaScript 遵从一个简单的法则:存在一个监控进程不断检查调用栈是否为空,当调用栈为空的时候,检查事件队列(event queue)中是否有待调用的函数。如果事件队列中存在待调用的函数,队列头部的函数被移入调用栈执行。如果事件队列为空,监控进程就保持轮询状态。 这意味着js中的定时器的精度,实际上是没有保障的,你写一个setTimeout(function(){ do xxxx}, 1000); 并没办法保证它刚好是在1000ms之后调用,因为之前的代码执行可能非常耗时,也可能事件队列中有其他事件排在前面。 这样就出现了题目中的情况。 更多可参考:http://metaphor.space/2016/04/26/javascript-event-loop/; https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop;还有《你不知道的Javascript中卷》141页~143页,事件循环章节 值得一提的是:我们平常说JS是单线程执行的,但浏览器不是,浏览器是多线程的,有的线程负责网络请求,有的负责渲染页面等;不要搞混了 另外,ES6给JS带来了新的特性,比如加入了可以创建多线程的worker,以及更精准控制事件调度的Promise

188. 请问for of和for in的区别

【Question】 for of和for in的区别? for of可以用在普通对象上吗?

【Answer】
考察候选人对for 循环的理解 以及对es6中的for of和iterator理解

for in不多做解释了 for of主要是对实现了 Symbol.iterator 接口进行遍历

自定义for of
```
var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
```

189. 字符串的排列组合计算

【Question】 输入一个字符串,打印出该字符串中字符的所有排列的情况。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba.

function calc(str){
    var strArray = str.split('');
    // 补全代码
  }
  console.log(calc('ab')) // ['a','b']  ['b','a']

【Answer】
```javascript
function calc(str){
    var strArray = str.split('');
    var path = [];
    var docalc = function(array){
      if(array.length===1){
        path.push(array[0]);
        console.log(path);
        path.pop();
        return;
      }
      for(var i=0;i

190. DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Question】 DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Answer】
*  不冒泡的事件有blur、focus、load、unload、abort、error、mouseenter、mouseleave、resize
*  每个 event 都有一个event.bubbles属性,通过该属性可知是否冒泡

191. JavaScript实现对象深拷贝方法

【Question】 编码实现JavaScript实现对象深拷贝

【Answer】
var clone = function(v) {  
  var o = v.constructor === Array ? [] : {};  
  for (var i in v) {  
    o[i] = typeof v[i] === "Object" ? clone(v[i]) : v[i];  
  }  
  return o;  
}  

192. 故障分析-HTTPS证书不被信任

【Question】

如下图,在不同的设备上,同时访问同一个域名,一个设备显示证书不被信任,另一个设备正常,再使用多个其他设备访问,依然正常。分析可能的原因?以及需要获取的进一步的信息?

正常的设备

ssl_success.png<p>异常的设备</p>ssl_error.png<p>
</p>

【Answer】

需要进行的进一步的操作:

1) 查看证书详情:路径/SN/哈希值

2) 查看DNS解析结果

3) 查看系统时间/版本/浏览器版本

可能的原因:

1) 代理工具/安全软硬件

2) DNS劫持/路由劫持

3) 时间偏差

4) 操作系统/浏览器版本差异


193. 请实现一个CodingMan函数实现以下功能

【Question】


实现一个CodingMan,可以按照以下方式调用:
CodingMan(“Hank”)输出:
Hi! This is Hank!

CodingMan(“Hank”).sleep(10).eat(“dinner”)
输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

CodingMan(“Hank”).eat(“dinner”).eat(“supper”)
输出
Hi This is Hank!
Eat dinner~
Eat supper~

CodingMan(“Hank”).sleepFirst(5).eat(“supper”)
输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。


【Answer】


class _CodingMan {
    constructor(name) {
        this.tasks = [];
        const task = () => {
            console.log(`Hi! This is ${name}`);
            this.next();
        }
        this.tasks.push(task);
        setTimeout(() => {               // 把 this.next() 放到调用栈清空之后执行
            this.next();
        }, 0);
    }

    next() {
        const task = this.tasks.shift(); // 取第一个任务执行
        task && task();
    }

    sleep(time) {
        this._sleepWrapper(time, false);
        return this;                     // 链式调用
    }

    sleepFirst(time) {
        this._sleepWrapper(time, true);
        return this;
    }

    _sleepWrapper(time, first) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                this.next();
            }, time * 1000)
        }
        if (first) {
            this.tasks.unshift(task);     // 放到任务队列顶部
        } else {
            this.tasks.push(task);        // 放到任务队列尾部
        }
    }

    eat(name) {
        const task = () => {
            console.log(`Eat ${name}`);
            this.next();
        }
        this.tasks.push(task);
        return this;
    }
}

function CodingMan(name) {
    return new _CodingMan(name);
}



194. 实现如下函数add,使如下执行都等于9

【Question】


add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】

// 较通用的实现

function currying(fn, length) {

 length = length || fn.length;

 return function (...args) {

  return args.length >= length

   ? fn.apply(this, args)

   : currying(fn.bind(this, ...args), length - args.length) 

 }

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 通用实现2

function currying(fn, length) {

return function(...args) {

if (args.length >= length) {

return args.slice(0, length).reduce((t, i) => t += i);

}

return function(..._args) {

return add.apply(null, [...args, ..._args]);

}

}

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 直接的实现

function add(...args) {

if (args.length >= 3) {

return args.slice(0, 3).reduce((t,i) => t += i);

}

return function(..._args) {

return add(args.concat(_args));

}

}


195. 介绍一下你了解的 WebSocket

【Question】 简单介绍一下 WebSocket,ws 协议和 http 协议的关系是什么,WebSocket 如何校验权限? WebSocket 如何实现 SSL 协议的安全连接?

【Answer】
WebSocket 是基于 http 的,所以建立 WebSocket 连接前,
浏览器会通过 http 的方式请求服务器建立连接,
这个时候可以通过 http  的权限校验方式来校验 WebSocket,比如设置 Cookie。
同理,WebSocket 实现 SSL 协议也同 https 类似,会升级为 wss 连接。
另外,当然也可以在 WebSocket 中还可以通过加密或者 token 等方式,实现自己额外的加密传输和权限判断方式。
更多可参考 https://security.tencent.com/index.php/blog/msg/119


196. 请谈谈iframe有哪些缺点?

【Question】 iframe通常有哪些用途,主要缺点是什么

【Answer】
(1)iframe会阻塞主页面的Onload事件;
(2)搜索引擎的检索程序无法解读这种页面,不利于SEO;
(3)iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
(4)页面简的通信问题
使用iframe之前需要考虑这(1)(3)两个缺点。
如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

197. 请简述JAVAScript事件模型和事件代理

【Question】 简述一下JavaScript事件模型和事件代理,事件代理有哪些优点?

【Answer】
## 事件模型
事件三个阶段:事件捕获,目标,事件冒泡(低版本ie不支持捕获阶段)
## 事件代理及优点: 
把事件委托到其父对象上,借助事件冒泡机制,实现对节点的事件代理。  
### 优点  
*  可以大量节省内存占用,减少事件注册
*  当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

198. 根据id从多叉树里面查找出对应的节点的name

【Question】


一个树形的数据(如下数据),面试官给你一个id,然后拿到对应的name?
  var cityData = [
      {
        id: 1,
        name: '广东省',
        children: [
          {
            id: 11,
            name: '深圳',
            children: [
              {
                id: 111,
                name: '宝安',
                children: [
                  {
                    id: 1111,
                    name: '西乡',
                    children:[
                      {
                        id: 11111,
                        name: '坪洲',
                        children:[]
                      },
                      {
                        id: 11112,
                        name: '灵芝',
                        children:[]
                      }
                    ]
                  },
                  {
                    id: 1112,
                    name: '南山',
                    children:[
                      {
                        id: 11121,
                        name: '科技园',
                        children:[]
                      }
                    ]
                  }
                ]
              },
              {
                id: 112,
                name: '福田',
                children: []
              }
            ]
          },
          {
            id: 12,
            name: '广州',
            children: [
              {
                id: 122,
                name: '白云区',
                children: [
                  {
                    id: 1222,
                    name: '白云区',
                    children: []
                  }
                ]
              },
              {
                id: 122,
                name: '珠海区',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 2,
        name: '湖南省',
        children: []
      }
    ];


【Answer】


主要考查深度/广度优先遍历,递归算法
方法1:递归

let result = ''

// 递归实现
const recursion = (cityData, id) => {
  // cityData数据为空的时候直接返回
  if (!cityData || !cityData.length) return;
  // 常规循环cityData
  for (let i = 0, len = cityData.length; i < len; i++) {
    const childs = cityData[i].children;
    
    // 如果匹配到id的话,就是我们要的结果
    if (cityData[i].id === id) {
      result = cityData[i].name
    }
    // 如果还有子节点,执行递归
    if(childs && childs.length > 0){
      recursion(childs, id);
    }
  }
  return result
};

const r = recursion(cityData, 11112);
console.log(r) // 灵芝


方法2:广度优先遍历
let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定义一个数据栈
  let stack = [];

  let item = null;

  //先将第一层节点放入栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 将数据栈的第一个取出来
    item = stack.shift();
    // 如果符合就赋值给result
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

let r1 = range(cityData, 11112);

console.log(r1) // 灵芝


方法3:深度优先遍历
let result = ''

const deep = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 先定义一个数据栈
  let stack = []
  let item = null

  //先将第一层节点放入数据栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i])
  }
  // 循环
  while (stack.length) {
    item = stack.shift()
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈顶
    if (item.children && item.children.length) {
      // 注意这里调换了顺序
      stack = item.children.concat(stack);
    }
  }
  return result
};

let r3 = deep(cityData, 11112)
console.log(r3) // 灵芝


方法4:正则

const regular = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 数据转成字符串
  let cityStr = JSON.stringify(cityData)
  // 定义正则
  let reg = new RegExp(`"id":${id},"name":"([^\\x00-\\xff]+)",`)
  // 取到正则的子字符串并返回
  return (cityStr.match(reg))[1]
}

let r4 = regular(cityData, 11112);

console.log(r4) // 灵芝



199. js浮点运算

【Question】 console.info(0.7+0.1)会得到什么

【Answer】
输出0.799999


200. macro micro 任务队列(async/await版)

【Question】

async function async1() {

 console.log('async1 start');

 await async2();

 console.log('async1 end');

}

async function async2() {

 console.log('async2 start');

 return new Promise((resolve, reject) => {

  resolve();

  console.log('async2 promise');

 })

}

console.log('script start');

setTimeout(function() {

 console.log('setTimeout');

}, 0);  

async1();

new Promise(function(resolve) {

 console.log('promise1');

 resolve();

}).then(function() {

 console.log('promise2');

}).then(function() {

 console.log('promise3');

});

console.log('script end');

【Answer】

chrome 和 node 都是以下顺序



201. JS实现一个带并发限制的异步调度器

【Question】

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4// 一开始,1、2两个任务进入队列// 500ms时,2完成,输出2,任务3进队// 800ms时,3完成,输出3,任务4进队// 1000ms时,1完成,输出1// 1200ms时,4完成,输出4

【Answer】
class Scheduler {
concurrency = 2
running = 0
queue = []
add(task) {
return new Promise(resolve => {
this.queue.push({
taskGenerator: task,
resolve
})
this.schedule()
})
}
schedule() {
while (this.queue.length > 0 && this.running < this.concurrency) {
const curTask = this.queue.shift()
this.running += 1
curTask.taskGenerator().then(result => {
this.running -= 1
curTask.resolve(result)
this.schedule()
})
}
}
}

202. 写一个加法函数(sum),使他可以同时支持sum(x,y)和sum(x)(y)两种调用方式。

【Question】

写一个按照下面两种方式都能正常调用的 sum 方法
```javascript
console.log(sum(2,3)); // 输出5
console.log(sum(2)(3)); // 输出5
```

【Answer】
答案一
function sum(a,b){
if(b) {
return a+b
}else{
return function(c){
return a+c
}
}
}
答案二
function sum(){
var arg=arguments
if(arg.length==2) {
return arg[0]+arg[1];
}else{
return function(c){
return arg[0]+c
}
}
}

203. ES5,ES6中this指向考察

【Question】

  1. 以下代码输出什么结果,this.name中this指向什么:
    window.name = 'ByteDance';
    function A () {
    this.name = 123;
    }
    A.prototype.getA = function(){
     console.log(this);
     return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    
  2. 如何使funcA()返回undefined?
  3. 下面ES6中又会发生什么,this是什么?
    window.name = 'ByteDance';
    class A {
     constructor() {
      	this.name = 123;
     }
     getA() { 
       console.log(this);
         return this.name + 1; 
     }
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    

【Answer】
1. 输出`Bytedance1`, this指向widnow;
2. 正确使用applay / call;
3. 发生异常:Uncaught TypeError: Cannot read property 'name' of undefined,this为undefined;

204. 请问什么是跨域?跨域请求资源有几哪种方式?

【Question】 何为跨域?跨域请求资源有几哪种方式?

【Answer】
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
跨域请求资源的方式主要有:  
(1)JSONP 动态创建script标签  
但缺点是只支持get请求,并且很难判断请求是否失败(一般通过判断请求是否超时)。  
(2)Proxy代理  
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。  
(3)CORS跨域  
是现代浏览器提供的一种跨域请求资源的方法,需要客户端和服务器端的同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信  
服务响应头返回,Access-Control-Allow-Origin: *

205. 简述React Fiber原理

【Question】

试描述React Fiber的原理。

【Answer】

官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。

之前React的更新过程是同步的,所有更新逻辑会在一帧之内完成,如果组件过于复杂则会导致更新时间超过一帧,其他事务包括用户输入都会被延迟响应,从而引发卡顿。如下图:


破解方式——分片。

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

实现使用的API:requestIdleCallback

Q.为什么引入Fiber架构?原架构有何不足?
A.原架构采用递归遍历方式来更新DOM树,一旦开始,即占用主线程,无法中断,这在页面上会引起问题,如input输入后页面卡顿等

Q.Fiber如何解决该问题
A.时间分片和暂停

Q.Fiber如何实现?
A.使用链表结构,将递归遍历更改为循环遍历,然后配合requestIdleCallback API,实现任务拆分、中断和恢复

Q.Fiber如何实现比较?
A.双缓冲技术,在diff过程中创建新的DOM Tree,diff完成之后生成EffectList,即需要更新的地方,之后进入commit阶段,该阶段不允许中断。

Q.React Hook基于Fiber架构,hook的复用是如何实现的?
A.hook的数据存在于Fiber节点的数据结构中,具体为memoizedState中,该字段中存储了所有hook相关的信息,https://www.jianshu.com/p/d6244228a427 (重要)



206. 请简要描述ES6 module require、exports以及module.exports的区别

【Question】 考察候选人对es6,commonjs等js模块化标准的区别和理解

【Answer】
* CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
* ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
* CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
* export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
* ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性
* 混合使用介绍:https://github.com/ShowJoy-com/showjoy-blog/issues/39

207. 浏览器缓存机制考察

【Question】 浏览器缓存机制考察,包括cache-control , etag, expire, last-modify-time 以及 200 from cache、304

【Answer】
1、cache-control 和 expire 在浏览器端控制  Cache-Control的max-age>expire
2、etag 和 last-modify-time主 要服务器端对比使用

208. 版本号排序

【Question】 versions是一个项目的版本号列表,因多人维护,不规则

var versions=['1.45.0','1.5','6','3.3.3.3.3.3.3']

要求从小到大排序,注意’1.45’比’1.5’大

var sorted=['1.5','1.45.0','3.3.3.3.3.3','6']

【Answer】
```javascript
function sortVersion(arr) {
    return arr.sort((a, b) => {
        const arrA = a.split('.')
        const arrB = b.split('.')
        for (let i = 0; i < arrA.length; i++) {
            if (arrA[i] === undefined) {
                return -1
            } else if (arrB[i] === undefined) {
                return 1
            } else if (parseInt(arrA[i]) === parseInt(arrB[i])) {
                continue
            } else {
                return parseInt(arrA[i]) > parseInt(arrB[i])
            }
        }
    })
}
```

209. JS限流调度器

【Question】

实现JS限流调度器,方法add接收一个返回Promise的函数,同时执行的任务数量不能超过两个。

class Scheduler {
    async add(promiseFunc: () => Promise<void>): Promise<void> {
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
// log: 2 3 1 4


【Answer】


class Scheduler {
    constructor() {
        this.concurrency = 0
        this.queue = []
    }
    async add(promiseFunc) {
        if (this.concurrency >= 2) {
            return new Promise(r => {
                this.queue.push(() => promiseFunc().then(r))
            })
        }
        this.concurrency += 1
        await promiseFunc()
        this.concurrency -= 1
        let next = this.queue.shift()
        if (next) {
            this.add(next)
        }
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)



210. 实现一个简单的Event类(观察者模式)

【Question】

请实现一个观察者模式,拥有四个方法on,off,once和trigger


const Event = {

on() {} // 绑定

off() {} // 解绑

once() {} // 绑定一次

trigger() {} // 触发事件

};

【Answer】

```javascript function Event() { if (!(this instanceof Event)) { return new Event(); } this._callbacks = {}; } Event.prototype.on = function (type, handler) { this_callbacks = this._callbacks || {}; this._callbacks[type] = this.callbacks[type] || []; this._callbacks[type].push(handler); return this; }; Event.prototype.off = function (type, handler) { var list = this._callbacks[type]; if (list) { for (var i = list.length; i >= 0; --i) { if (list[i] === handler) { list.splice(i, 1); } } } return this; }; Event.prototype.trigger = function (type, data) { var list = this._callbacks[type]; if (list) { for (var i = 0, len = list.length; i < len; ++i) { list[i].call(this, data); } } }; Event.prototype.once = function (type, handler) { var self = this; function wrapper() { handler.apply(self, arguments); self.off(type, wrapper); } this.on(type, wrapper); return this; }; ```


【Question】 请说明 cookie、sessionStorage、localStorage 之间的区别、以及在你项目中的应用?

【Answer】
 a) cookie,HTTP Cookie(也叫Web cookie或者浏览器Cookie)是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。比较经典的,可以它用来确定两次请求是否来自于同一个浏览器,从而能够确认和保持用户的登录状态。Cookie的使用使得基于无状态的HTTP协议上记录稳定的状态信息成为了可能。
b) sessionStorage,为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
c) localStorage,localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

区别:
localStorage、sessionStorage 是 Web Storage Api 的组成 API,其为了解决 Cookie 的一些缺陷,服务端 Set 的 cookie 每次会携带在本域下所有的请求上,对性能有损耗。SessionStorage 存储有个期限,当关闭浏览器后就不再存在,但 localStorage 依然存在,需要明确删除。


212. 请简述js浏览器事件循环机制

【Question】


【Answer】

浏览器 Event Loop 是 HTML 中定义的规范,Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

  • JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

  • 同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

  • Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

  • 定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

  • 宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。


213. 何为https?https和http2有什么关系?

【Question】 简要描述HTTPS的安全机制,以及在web服务工程实践中需要注意的问题;描述http2的基本机制

【Answer】
HTTPS是指建立在安全的传输层(通常是tls/ssl)上的HTTP协议,通过对服务器的证书的认证,解决中间人攻击等问题。
证书(certificate)由客户端信任的的证书机构(CA)颁发,通过common name或SAN对服务进行描述;客户端通过CA的根证书对证书进行校验,并将请求域名和证书的common name/DNS域名进行验证,以检验证书的有效性。
目前,很多web api如Notification/web rpc/Service Worker等,都要求必须使用https。
在工程实践中,https存在以下需要注意的问题:
  - js/css等资源必须以https形式加载,否则浏览器将拒绝执行,所以CDN必须完成对https的支持
	- 非https请求的图片等资源不会携带referer
	
	http2是http协议的一个新版本,既可以明文传输也可以在https中使用。浏览器和服务器通过tls的ALPN/SNI等机制可以进行协议协商,决定使用什么协议

214. 用数组的reduce方法实现map方法

【Question】 用数组的reduce方法实现map方法

【Answer】
```
// 代码实现
Array.prototype.map2 = function(f) {
  return this.reduce(function(result, x, index, arr) {
    result.push(f(x, index));
    return result;
  }, []);
}

// 测试代码
var res = [1, 3, 5, 7].map2(function(item, idx){
  return item * 2;
});
console.log(res);
```

215. js异步操作与计算题

【Question】

for (var i = 0; i < 6; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

>1. console.log(new Date, i);得到的结果是什么? >1. 怎样优化,可以变成: 0 -> 1 -> 2 -> 3-> 4 ->5 >1. 如果继续优化,实现console.log(new Date, i);代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4(sleep),之后再暂停5秒,然后输出5, 实现结果类似: >1. 2017-08-31T04:38:23: 0 <— start IIFE >1. 2017-08-31T04:38:24: 1 <— sleep 1s >1. 2017-08-31T04:38:25: 2 <— sleep 1s >1. 2017-08-31T04:38:26: 3 <— sleep 1s >1. 2017-08-31T04:38:27: 4 <— sleep 5s >1. 2017-08-31T04:38:32: 5

【Answer】
1. 属于结果是暂停1S,然后输出6个6,setTimeout属于异步执行
1. 实现0-> 1 -> 2 -> 3-> 4 ->5,用闭包或者var改成let
1. 模拟编程中的sleep实现,参考答案:
```
// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeoutMS) => new Promise((resolve) => {
  setTimeout(resolve, timeoutMS)
});
(async () => {  // 声明即执行的 async 函数表达式
  for (let i = 0; i < 6; i++) {
      if (i < 5) {
        console.log(new Date(), i)
        await sleep(1000)
      } else {
        await sleep(4000)
        console.log(new Date(), i)
      }
    }
})()
```

216. 简单的实现Promise.all

【Question】



function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000);
    })
}
PromiseAll([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

Promise.all([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err=>{
    console.log(err)
})


【Answer】


function PromiseAll(list) {

    return new Promise((resolve, reject) => {

        let count = 0;

        let len = list.length;

        let result = [];

        list.forEach((item,index) => {

            item.then(res => {

                count++;

                result[index] = res;

                if (count === len) {

                    resolve(result);

                }

            }).catch(err => {

                reject(err)

            })

        })

    })

}



217. ES6 import的原理

【Question】 请描述ES6 import的原理以及与commonjs的require的区别

【Answer】
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

218. 不借助变量交换两个数

【Question】 var a = 1, b = 2; function swap(a,b){ …. } swap(a,b) console.log(a, b) // 2,1

【Answer】
方法一、
```
function swap(a,b){
  b=b-a;
  a=a+b;
  b=a-b;
  return [a,b]
}
```
方法二、
```
function swap(a,b){
  return [a, b] = [b, a]
}
```
方法三、
```
function swap(a,b){
  var a=a^b;
  var b=b^a;
  var a=a^b;
	return [a,b]
}
```

219. 实现垂直居中

【Question】


    <div id="block">        
    </div>

id为block的元素不定高不定宽,请实现它在浏览器窗口的居中显示。

【Answer】
```css
#block {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
```

220. 请回答当我们在使用new操作符时,它在对象操作的过程中具体做了什么

【Question】 考察候选人对原型链操作和js对象的理解

【Answer】
1. 简单回答:
1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
1. 属性和方法被加入到 this 引用的对象中。
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
```javascript
function Animal(name) {
      this.name = name;
}
  Animal.prototype.run = function() {
      console.log(this.name + 'can run...');
}
var cat = new Animal('cat'); //    
new Animal('cat')=function(){
let obj={}; //       
obj.__proto__=Animal.prototype; // obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,'cat');//   this        
}
```



221. css3实现多行文字截断处理

【Question】 用css分别实现单行截断和多行截断字符串,最后以…为结尾

【Answer】
单行:
```
.text-overflow ( @class ){
    .@{class} {
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
    }
}
```
多行:
```
.multi-text-overflow ( @class, @line ){
    .@{class} {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        display: box;
        -webkit-line-clamp: @line;
        -webkit-box-orient: vertical;
    }
}
```

222. 请介绍react diff算法和策略

【Question】 react的diff算法和策略了解多少,为什么react的diff性能好,遵循什么样的策略可以把 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

【Answer】
React分别对 tree diff、component diff 以及 element diff做了算法优化,
做了一些假设
1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
3.对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
tree diff:React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较
component diff:
a.如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
b.如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
c.对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff
element diff:
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,减少增加和删除
详见:https://zhuanlan.zhihu.com/p/20346379

223. 函数科里化

【Question】

实现如下函数add,使如下执行都等于9

add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】


function curry(fn) {
  return function res(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return res.apply(this, args.concat(args2));
      }
    }
  }
}



224. 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?应该怎么解决?

【Question】 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?怎么解决?

【Answer】
考察一下JS中整数的安全范围的概念,在头条经常会遇到长整型到前端被截断的问题,需要补一个字符串形式的id供前端使用。
主要会涉及到JS中的最大安全整数问题
https://segmentfault.com/a/1190000002608050

225. JavaScript this 考察

【Question】

下面代码输出的结果是什么?

var length = 10;

function fn() {

 return this.length+1;

}

var obj = {

 length: 5,

 test1: function() {

  return fn();

 }

};

obj.test2=fn;

//下面代码输出是什么

console.log(obj.test1())

console.log(fn()===obj.test2())

【Answer】

11, false(11===6)


226. requestAnimationFrame 和 setTimeout 的区别

【Question】 requestAnimationFrame 和 setTimeout 都可以用来实现动画,它们的区别是什么

【Answer】
1. 执行频率不同,前者按照屏幕刷新频率执行,后者自行控制,可能有无用开销(执行频率小于刷新频率,即1帧执行多次)
2. 前者在页面不可见时,会停止执行(省电),后者在页面不可见时仍会执行,带来不必要开销


227. 编码-js高阶函数考察

【Question】

实现一个repeat方法,要求如下:


// 需要实现的函数

function repeat (func, times, wait) {

// 补全

}


// 使下面调用代码能正常工作

const repeatFunc = repeat(console.log, 4, 3000);

repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒


【Answer】

考点1:能意识到repeat返回的是一个函数,知道参数怎么传递。

考点2:setTimeout的时间,微任务


参考答案

function repeat(fn, times, wait) {

if(typeof times !== 'number') return;

if(typeof wait !== 'number') return;

return function(str){

for(let i = 0; i < times; i++){

setTimeout(()=>{

fn(str)

}, i * wait)

}

}


228. Vue框架中组件消息通信方式

【Question】 考察候选人对Vue框架的消息通信方式了解程度:

  1. vue父子组件通信方式?
  2. 非父子组件通信方式?
  3. 前两问OK,追问:当一个父组件与子组件中间隔着很多层组件怎么办?

【Answer】
1. 父子组件通信方式
在Vue中,父子组件的关系可以总结为props down, events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。

2. 非父子组件通信
两个独立的组件之间通信,可以借助一个空的Vue实例作为中央事件总线,空实例相当于代理人的形式进行消息监听或触发

3. 父子之间层级过多时
当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。
这块如果了解开源的Element组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以`[组件名, 消息名, 参数]`三元组进行消息传递,降低长链传播成本;
具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

229. 什么是 XSS,怎么造成的,有什么防御方法?

【Question】 考察面试者对于 XSS 是否了解,是否足够重视。

【Answer】
XSS 就是在 web 中能够通过某种方式产生执行任意 JavaScript 脚本的情况,
最常见的一种情况就是将用户的输入,直接放到当前 runtime 中,比如用户输入直接放到页面的 html 里面,
立刻显示出来。
XSS 实际上是非常危险的,因为理论上讲,如果能够执行 JavaScript,实际上攻击者可以做任何事情。
简单的就是输出点什么,偷偷 cookie,或者结合 CSRF 攻击,或者让浏览器跳转一下,
复杂点的甚至可以改掉当前整个页面,伪造一切用户看到东西,危害无穷。
如果这种输入存储到数据库中,就会变成一个永久型的 XSS,危害就更大了。
防止 XSS 最简单的就是使用各种框架,如 React、Vuejs 等,对用户输入进行 html 转义。
另外,服务端要设置 httpOnly 的 header,防止 JavaScript 操作 cookie。
当然,服务端也可以对输入进行转义或者过滤监测。

230. webpack插件编写

【Question】

  1. 有用过webpack么?说说该工具的优缺点?
  2. 有开发过webpack插件么?
  3. 假如要在构建过程中去除掉html中的一些字符,如何编写这个插件?

【Answer】
webpack优缺点:
* 概念牛,但文档差,使用起来费劲
* 模块化,让我们可以把复杂的程序细化为小的文件
* require机制强大,一切文件介资源
* 代码分隔
* 丰富的插件,解决less、sass编译

开发插件的两个关键点Compiler和Compilation:
* compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。
* compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

插件编写可参考:https://doc.webpack-china.org/development/how-to-write-a-plugin

231. 如何实现微信扫码登录?

【Question】 综合题,考察网络、前端、认证等多方面知识

【Answer】
参考答案:
https://zhuanlan.zhihu.com/p/22032787
具体步骤:
1. 用户 A 访问微信网页版,微信服务器为这个会话生成一个全局唯一的 ID,上面的 URL 中 obsbQ-Dzag== 就是这个 ID,此时系统并不知道访问者是谁。
2. 用户A打开自己的手机微信并扫描这个二维码,并提示用户是否确认登录。
3. 手机上的微信是登录状态,用户点击确认登录后,手机上的微信客户端将微信账号和这个扫描得到的 ID 一起提交到服务器
4. 服务器将这个 ID 和用户 A 的微信号绑定在一起,并通知网页版微信,这个 ID 对应的微信号为用户 A,网页版微信加载用户 A 的微信信息,至此,扫码登录全部流程完成

232. 设计类似 Vue.js 双向绑定功能的核心逻辑“监听对象属性变化”功能

【Question】 实现一个类,可以监听对象属性的值变化。加分项:考虑对象存在值为数组或对象的属性。

	class Observe {
		constructor(data: Object) {
		}
		// 监听属性变更
		$on() {
		}
		// 触发属性变更事件
		$emit() {
		}
	}
	const data = new Observer({
		a: 1
	});
	coonsole.log(data.a) // console: 1
	data.$on('a', (newValue, oldValue) =&gt; {
		// this === data
		console.log(newValue, oldValue);
	});
	data.a = 2 // console: 2 1

【Answer】
待补充

233. 请简要描述

【Question】

【Answer】
### 作用:
defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
### 区别:
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

234. 原型链、this指针、自有属性考察

【Question】

var a= function () { this.b =3; }
var c = new a();
a.protorype.b = 9;
var b = 7;
a();

问:

console.log(b);
console.log(c.b); 

分别输出什么?

【Answer】
- 第一个 `b = 3`
- 第二个 `c.b = 3`

【Question】

如题

【Answer】
cookie 在浏览器中存在,并且每个请求都会带上,而且是可以设置失效时间的,失效前就会有效。 session 是一次会话有效,也就是说,浏览器关闭 session 就会失效。 其实这道题目考察的一个重点是在于,session 这种机制是怎么在浏览器中实现的呢? 实际上 session 的实现也是在浏览器中放了一个 cookie,失效时间是一次会话失效。

236. JS异步队列macrotask和microtask

【Question】

console.log('begin')
setTimeout(() =&gt; {
	console.log('setTimeout 1')
	Promise.resolve().then(() =&gt; {
		console.log('promise 1')
		setTimeout(() =&gt; {
			console.log('setTimeout2 between promise1&amp;2')
		})
	}).then(() =&gt; {
		console.log('promise 2')
	})
}, 0)
console.log('end')

【Answer】
```
begin
end
setTimeout 1
promise 1
promise 2
setTimeout2 between promise1&2
```

237. 如何理解虚拟DOM?

【Question】 如何理解虚拟DOM?

【Answer】
对虚拟dom和diff算法中的一些细节理解与考察,[https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13)

238. 如何判断一个 JS 对象为空对象

【Question】 如何判断一个 JS 对象为空对象 ?空对象形如{}

【Answer】
1. 使用 `for in`
	```javascript
	function isEmptyObject(obj){
  	for(var key in obj){
    	return false
		};
		return true
	};
	```
2. 通过 JSON.stringify 方法来判断
	```javascript
	if(JSON.stringify({}) === '{}'){
		console.log('empty obj');
	}
	```
3. 使用 ES6 增加的 Object.keys()
	```javascript
	if(Object.keys(obj).length === 0){
		console.log('empty obj');
	}
	```

239. 什么是闭包?实现每隔1秒输出数组中的一个数字

【Question】 解释下js中的闭包概念,解释OK,给出编程题目考察基本功

【Answer】
```js
function fun(arr) {
    var i, len;
    for (i = 0, len = arr.length; i < len; i++) {
      (function(i){
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
}
```

240. promise运行过程解答

【Question】 如下代码的运行结果是什么?

 process.nextTick(() =&gt; {console.log('nextTick')})
Promise.resolve().then(()=&gt; {console.log('promise1');}).then(()=&gt; {
  console.log('promise2');
});
setImmediate(() =&gt; {console.log('setImmediate')})
console.log('end') 

【Answer】
1. end -> nextTick -> promise1 -> promise2-> setImmediate
1. process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。
1. 事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

241. 请简述常见web安全及防护原理

【Question】 常见web安全及防护原理,请举例说明。

【Answer】
1、SQL注入原理  
		就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点  
1. 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2. 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。  
2、XSS原理及防范  
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者JavaScript代码。
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法  
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

3、CSRF原理及防范  
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法

242. 数字格式化问题:1234567890 –> 1,234,567,890

【Question】 数字格式化问题,将1234567890 –> 1,234,567,890

【Answer】
非正则实现
```javascript
let test = '1234567890'
function formatCash(str) {
  let arr = []
  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))
    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))
  }
  return arr.join(',')
}
console.log(formatCash(test)) // 1,234,567,890
```
正则实现
```javascript
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
console.log(format) // 1,234,567,890
```

243. 模拟实现loadash中的_.get()函数,实现如下传入参数取值效果

【Question】

function get() {
  // 请补全函数参数和实现逻辑
}
const obj = { selector: { to: { toutiao: 'FE coder' } }, target: [1, 2, { name: 'byted' }] };
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')

//  输出结果:
// ['FE coder', 1, 'byted']

【Answer】
```javascript
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
```
1. Use Array.map() for each selector
2. String.replace() to replace square brackets with dots
3. String.split('.') to split each selector
4. Array.filter() to remove empty values
5. Array.reduce() to get the value indicated by it

244. 合并两个有序数组

【Question】 合并两个有序数组

【Answer】
```
function mergeSortedArray(a, b){
  var merged = [], 
      aElm = a[0],
      bElm = b[0],
      i = 1,
      j = 1;
  if(a.length ==0)
    return b;
  if(b.length ==0)
    return a;
  while(aElm || bElm){
   if((aElm && !bElm) || aElm < bElm){
     merged.push(aElm);
     aElm = a[i++];
   }   
   else {
     merged.push(bElm);
     bElm = b[j++];
   }
  }
  return merged;
}
```
验证
```
mergeSortedArray([2,5,6,9], [1,2,3,29]);
结果 [1, 2, 2, 3, 5, 6, 9, 29]
```

245. 进行CSRF漏洞扫描的原理和防御方式是什么?

【Question】 如题

【Answer】
CSRF 就是在用户不知情的情况下,发出了请求,让用户做了不该做的操作。
举个例子,比如你的一个网站中有个 img 标签,src 指向的是微博关注某人的接口,
那么当用户访问你的网站时,就会在微博上关注那个人,而且这个操作用户是不知情的。
因为 img src 发出的跨域请求,也是会携带 cookie 的,所以如果用户在微博登录过,
那么就会带有微博的登录授权。同理,如果是其他操作,可能也存在这种漏洞,比较危险的情况就是付款。
一般会采用 CSRF token 的方式防御,就是关键请求得要换取一个一次有效的 token 才有权限。


246. 判断一个字符串是否是回文字符串

【Question】 判断一个字符串是否是回文字符串,回文字符串是对称字符串的形式,例如:did,eve, dad, level

【Answer】
```
function isPalindrome(str){
  var i, len = str.length;
  for(i=0; i isPalindrome('madam')
  = true
> isPalindrome('toyota')
  = false
```

247. box-sizing 实践

【Question】


<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 10px;
        height: 10px;
        border: 1px solid red;
        margin: 2px;
        padding: 2px;
        background: blue;
      }

      #borderBox {
        box-sizing: border-box;
      }

      #contentBox {
        box-sizing: content-box;
      }
    </style>
  </head>
  <body>
    <div>请问下面两个 div 元素,蓝色区域的宽高各是多少像素?</div>
    <div id="borderBox" class="box"></div>
    <div id="contentBox" class="box"></div>
  </body>
</html>


【Answer】

borderBox:10px(width) - 1px(border) * 2 = 8px

contentBox 10px(width) + 2px(padding) *2 = 14px


答题要点:除了验证候选人是否真正了解 box-sizing 之外,也考察候选人是否了解 background 会影响元素的 padding 区域,而不影响 margin 区域这个特点


248. 链式调用+延迟计算

【Question】

写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....)


console.log(sum(1,2,3)(4)) => 输出 10



考察链式调用,闭包,延迟计算,函数toStirng/valueOf




【Answer】


function sum(...args) {
  function next(...innerArgs) {
    args.push(...innerArgs);
    return next;
  }
  next.valueOf = next.toString = () => {
    return args.reduce((r, c) => r + c, 0);
  };

  return next;
}



249. 请描述micro task 与 macro task的区别及应用

【Question】


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');


【Answer】

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout


250. 数组flat函数设计

【Question】 设计一个flat函数将如下数组arr=[1,2,[‘3’,4,’5’,[6,[7,8],9]]]输出为1,2,’3’,4,’5’,6,7,8,9。至少写出两种方法,要求不能改变数组中的原始数据类型

【Answer】
*  方法一:递归
```javascript
function flat(array) {
    var result = [];
    var each = function(arr) {
      arr.forEach(item => {
        if (item instanceof Array) {
          each(item);
        } else {
          result.push(item);
        }
      });
    };
    each(array);
    return result;
  }
var arr=[1,2,['3',4,'5',[6,[7,8],9]]];flat(arr).forEach(item=>{console.log(item)})

```
*  方法二:toString(格式转换),无法保证类型
```javascript
Array.prototype.toString = function() {
  return this.join(',');
};
console.log([1,2,[3,4,[5,6,7]]]+'');
```
*  方法三:Iterator
```javascript
Array.prototype[Symbol.iterator] = function() {
  let arr = [].concat(this),
    index = 0;
  let getFirst=function(array){
    let first=array[0];
    if(first instanceof Array){
      return getFirst(array[0])
    }else if(first!==undefined){
      return array.shift()
    }else{
      return ''
    }
  }
  return {
    next: function() {
      let item=getFirst(arr);
      if(item){
        return {
          value:item,
          done:false
        }
      }else{
        return {
          done:true
        }
      }
    }
  }
}
var t=[1,2,['3',4,'5',[6,[7,8],9]]];
for(let i of t){console.log(i)}
```

【Question】 基础题考察 cookie 和 localStorage 的理解。

【Answer】
存储在 Cookie 中每个 request 都会带上,而放在 localStorage 中,仅有浏览器中会存储。

252. 请说说HTML的Meta标签的用途,并列举一些常用的meta标签

【Question】

【Answer】
考察对网页结构和语义的理解 

```
The HTML  element represents metadata that cannot be represented by other HTML meta-related elements, like , , 

253. 说说前端优化?图片懒加载原理是什么?

【Question】

  • 考察前端的一些优化方式
  • 图片懒加载原理

【Answer】
1. 优化手段:雅虎的34条优化手段,比如:代码压缩、减少请求、cdn、缓存
2. 图片懒加载原理:img标签设置占位属性(data-src),存储真正的图片地址;原src设置占位图片地址;当图片(快)进入用户可视区域的时候进行地址替换;

254. 请谈谈你对ES6的箭头函数的理解

【Question】

var func1 = x =&gt; x;
var func2 = x =&gt; {x}; 
var func3 = x =&gt; ({x});
console.log(func1(1));
console.log(func2(1));
console.log(func3(1));

请写出程序运行结果。

【Answer】
程序运行结果为:
第一个:1
第二个:undefined
第三个:{x: 1}

255. 无重复字符的最长子串

【Question】

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

样例:


  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。



【Answer】


var lengthOfLongestSubstring = function(s: string) {
    let list = s.split("");
    let son = [];
    let max = [];
    for (let i = 0; i < list.length; i++) {
        let current = list[i];
        let index = son.indexOf(current);
        if (index === -1) {
            son.push(current);
        } else {
            let sameIndex = i - son.length + index;
            if (son.length > max.length) {
                max = [...son];
            }
            son = son.slice(sameIndex + 1, son.length);
            son.push(current);
        }
    }
    return max.length;
};



256. 列举一个近期做的最能体现设计能力的项目

【Question】 请举出一个你近期做的项目,项目需要最能体现设计能力, 请从以下角度说明:

  1. 项目描述
  2. 技术选型
  3. 模块化
  4. 模块之间通信
  5. 工程化
  6. 前后端数据流

【Answer】
这是一个开放式的工程设计题目,没有固定答案,评分参考评分标准

257. 实现一个 JSONP

【Question】 函数签名如下:

function jsonp(url, callback) {
  // TODO
}

【Answer】
主要考察如何处理第二个参数 `callback` 的问题,
加分项比如超时处理 onerror 的处理, xss 考虑等等

```
const kCallBackMap = {};
function uuid() {
  return ...;
}

function jsonp(url, callback) {
  const callbackId = uuid();
  url += 'callback=' + callbackId;
	window[calbackId] = callback;
	
	const script = document.createElement('script');
	script.src = url;
	document.head.appendChild(script);
}
```

258. 请谈一谈JAVAscript的作用域和this

【Question】

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

【Answer】
```
window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

主要考察javascript的作用域和this指向。作用域是静态的,声明时确定;this是动态的,运行时确定。
```

259. 请问CSS position有哪些定位方式

【Question】 CSS position有哪些定位方式,每种方式是如何定位的?

【Answer】
### position取值
relative, fixed,absolute和staic、sticky 5种
### 定位方式
*  staic-默认位置;元素会像通常那样流入页面。顶部,底部,左,右,z-index属性不适用。  
*  relative-元素的位置相对于自身进行调整,而不改变布局(从而为未被定位的元素留下一个空白)。  
*  absolute-该元素从页面的流中移除,并相对于其最近位置的祖先定位(非static)在指定位置,如果有的话,或者与初始包含块相对。绝对定位的框可以有边距,并且不会与其他边距折叠。这些元素不影响其他元素的位置。  
*  fixed元素是定位在相对于窗口。  
*  sticky,是相对定位和固定定位的混合。该元素被视为相对位置,直到它越过指定的阈值,此时它被视为固定位置。  


260. 请介绍一下Oauth2.0 的认证过程

【Question】 如题

【Answer】
可以参考 http://www.jianshu.com/p/0db71eb445c8 或者 
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 的答案,
回答的一个重点是 code(授权码)仅一次有效,并且要有失效时间,而且很短,比如一分钟,
因为浏览器收到会立刻跳转。
还有就是服务端可以根据 code 结合相应的 sercet 去获取 token,要说清楚。

261. express中间件的原理

【Question】

express中间件的实现原理 并给出实现

【Answer】
主要考察候选人对中间件的理解 参考代码 ``` export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } ``` koa中间件主要使用 generator和promise可参考https://github.com/tj/co

262. 实现es6字符串模板方法sprintf

【Question】


const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
	name: 'Yiming Zhang',
	city: 'FuJian',
});
console.log(result); // My name is Yiming Zhang,I'm from FuJian


【Answer】


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);



263. 登录表单设计/扫码登录/第三方登录

【Question】

  1. 请实现一个登录表单
  2. 用GET方法行不行?csrf是什么?如何防御?
  3. cookie-sesssion的工作机制
  4. 你已经登录产品的App端,要在web实现扫码登录,该如何设计?
  5. 接入第三方登录(如微信),如何设计?

【Answer】
1. 正确书写html
2. 正确回答GET和POST的区别,从语义、弊端、安全等方面。csrf的防御:token,samesite,referer校验(弊端)等
3. 正确理解cookie-session的工作机制,sessionId的设计,存储
4. 考察对司空见惯的扫码登录,是否有思考其实现。正确设计 Client/Server/App 三方流程,设计二维码存储的内容,client通知有轮训或websocket等解决方案
5. 正确理解 Client/Server/App/Weixin Server 四方流程,理解oauth2协议

264. 作用域以及变量提升

【Question】

请写出下题的结果:

var a = 1; 
function b() { 
    a = 10; 
    return; 
    function a() {} 
} 
b(); 
console.log(a);   

【Answer】
结果:1

265. setTimeout 和 Promise

【Question】

请写出程序的输出内容

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function(resolve) {
  console.log(2);
  for(var i=0 ; i < 10000 ; i++) {
    if (i == 9999) {
      resolve();
    }
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);


【Answer】

正确答案:2 3 5 4 1。重点关注:候选人是否把 2 写在第一位,以及 4 和 1 的顺序。


266. requestIdleCallback和requestAnimationFrame有什么区别?

【Question】

requestIdleCallback和requestAnimationFrame有什么区别?

【Answer】

requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。

我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了。

一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调。

由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了。


267. 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Question】 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Answer】
`
Array.prototype.findDuplicate = function (n) {
    var results = [];
    if (typeof n != 'number' || isNaN(n)) {
        return results;
    }
    
    var itemFreqs = {};
    this.forEach(function (item) {
        if (!itemFreqs[item]) {
            itemFreqs[item] = 0;
        }
        itemFreqs[item] ++;
    });
    
    for (var item in itemFreqs) {
        if (itemFreqs[item] >= n) {
            results.push(item);
        }
    }
    
    return results;
}

`

268. 请回答DOM中对应创建、移除、追加、复制、查找节点的方法是什么?

【Question】 考察候选人对原生dom操作的方法的理解和掌握熟练程度

【Answer】
1.  创建新节点
	*  createDocumentFragment() //创建一个DOM片段
	*  createElement() //创建一个具体的元素
	*  createTextNode() //创建一个文本节点

1.  克隆节点
*  cloneNode()

1. 添加节点
*  appendChild()
*  insertBefore()

1. 移除节点
*  removeChild()

1. 替换节点
*  replaceChild()

1. 查找节点
*  querySelector()
*  querySelectorAll()
*  getElementById()
*  getElementsByName()
*  getElementsByTagName()



269. 请描述如何用原生JS实现数字的货币格式化

【Question】

# 如何用原生JS实现数字的货币格式化,例如数字6123456789格式化后为6,123,456,789,不低于两种方法。

【Answer】

方法一: (6123456789).toLocaleString('en-US') // 6,123,456,789


方法二: (6123456789).toString().split('').reverse().join('').replace(/\d{3}/g,function($1){return $1+','}).split('').reverse().join('')



270. let,const,var的区别

【Question】 请说明一下let,const,var的区别 并回答如下代码会不会报错

const a = {};
a.test = 1;

【Answer】
考察候选人对es6变量声明的理解
1. let声明的变量拥有块级作用域
2. let声明的全局变量不是全局对象的属性
3. let不能重新声明变量
4. const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

上面代码只是针对a的引用 并不会报错

271. 如何实现链式调用

【Question】 请实现函数 a, b, c,使调用方式为 a().b().c() 时,结果为输出 a b c。 如果上面问题回答出来了,并且是在 a 函数内部 return Object 实现, 那么可以补充问下如何能够实现让三个函数任意链式顺序调用。 如 a().c().b() 或 b().a().c() 。

【Answer】
这道题主要就是考察面试者对 JavaScript 的 Object 概念理解是否清晰,
最好的答案是直接将 a b c 三个函数挂载到 runtime 中的某个全局变量中,比如可以是 window。
然后在每个函数内 return window 就可以了。
当然,也可以按照第一道题目的顺序,分别在相应函数内 return 下个函数,但是这样做无法调换顺序。

272. 实现千位分隔符

【Question】 给一个数字,比如:1234567.90,转化成:1,234,567.90

【Answer】
```js
function commafy(num) {
  return num && num
      .toString()
      .replace(/^\d+/, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','));
}
console.log(commafy(1234567.90)); //1,234,567.90
```

273. 编写javascript深度克隆函数deepClone

【Question】 编写javascript深度克隆函数deepClone

【Answer】
```javascript
function deepClone(obj) {
    var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }

        return new RegExp(obj.source, flags.join(''));
    }

    var result = Array.isArray(obj) ? [] :
        obj.constructor ? new obj.constructor() : {};

    for (var key in obj ) {
        result[key] = deepClone(obj[key]);
    }

    return result;
}

function A() {
    this.a = a;
}

var a = {
    name: 'qiu',
    birth: new Date(),
    pattern: /qiu/gim,
    container: document.body,
    hobbys: ['book', new Date(), /aaa/gim, 111]
};

var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
```

274. 请谈谈你对JS单线程以及setTimeout的理解

【Question】

setTimeout(function() {
	setTimeout(function() { console.log(1) }, 100)
	console.log(2)
	setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
	console.log(4)
}, 100)
console.log(5)

请说出上面代码的输出顺序以及原因?如果吧4改为101ms呢?

【Answer】
正确顺序为:5 2 3 4 1
如果4改为101ms则执行顺序还是不变
原因:
1.  JS单线程
2. setTimeout不在当前eventloop。且执行顺序依赖入队顺序。setTimeout 0是放入下一个loop的队尾
3. 虽然4和1都是100ms延迟的标记,但是4先入队列。
4. setTimeout的time是个标记,会在eventloop循环去检测,符合条件的执行,不符合条件的延后到下一个eventloop,这执行过程本身又有时间,因此尽管101>100,但是在一个执行周期内,他们都会被触发,4先入队所以不变

275. async & forEach 考察

【Question】 以下代码的运行结果

const list = [1, 2, 3];
const square = num =&gt; {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(num * num);
        }, 1000);
    });
}
function test() {
    list.forEach(async x =&gt; {
        const res = await square(x);
        console.log(res);
    });
}
test()

如果希望每隔1s输出一个结果,应该如何改造?

【Answer】
1s 后输出 1 4 9  
改为 for 循环:
```javascript
async function test() {
    for (let x of list) {
        const res = await square(x);
        console.log(res)
    }
}
```


276. css单位的百分比

【Question】 给一个div设置它父级div的宽度是100px,然后再设置它的padding-top为20%。
问现在的div有多高?如果父级元素定位是absolute呢?

【Answer】
现有div的高度等于自身高度+父级块的宽度*20%,如果父级元素定位是absolute,结果不变;
当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!

277. NodeJS实现简单的HTTP代理和隧道代理

【Question】 Web代理一般包括普通的HTTP代理和隧道代理,谈谈理解。 NodeJS实现一个简单的HTTP代理,如在本地 8888 端口开启 HTTP 代理服务,修改浏览器的 HTTP 代理为 127.0.0.1:8888 后再访问 HTTP 网站,代理可以正常工作 对隧道代理了解多少,能否实现?

【Answer】
http普通代理:HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。
```
// http 普通代理
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(8888, '0.0.0.0');
```
隧道代理:HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发
```
const http = require('http');
const net = require('net');
const url = require('url');

function connect(cReq, cSock) {
  const u = url.parse('http://' + cReq.url);

  const pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer().on('connect', connect).listen(8888, '0.0.0.0');
```
合二为一
```
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

function connect(cReq, cSock) {
  var u = url.parse('http://' + cReq.url);

  var pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer()
  .on('request', request)
  .on('connect', connect)
  .listen(8888, '0.0.0.0');
```
需要注意的是,大部分浏览器配完隧道代理,默认只会让https走隧道代理,http如果需要走隧道代理,还需要写个Nodejs的验证
```
const options = {
  hostname: '127.0.0.1',
  port: 8888,
  path: 'toutiao.com:80',
  method: 'CONNECT'
};

const req = http.request(options);

req.on('connect', function(res, socket) {
  socket.write('GET / HTTP/1.1\r\n' +
    'Host: toutiao.com\r\n' +
    'Connection: Close\r\n' +
    '\r\n');

  socket.on('data', function(chunk) {
    console.log(chunk.toString());
  });

  socket.on('end', function() {
    console.log('socket end.');
  });
});

req.end();
```

278. 假设一个网页嵌入一个iframe,如何更改iframe内dom样式?

【Question】 假设一个网页嵌入一个iframe,如何更改这个iframe内dom样式

【Answer】
区分同源和不同源解决方案,同源可以通过document.getElementById('iframeId').contentWindow.document,
不同源:分iframe的嵌入的页面是否自己可控,可控可以通过postMessage方式更改,iframe页面监听message事件;如果页面不可控,应该无解。
可以追问iframe有同源策略限制,举个例子说明

279. 数组随机排序

【Question】

var arr=[1,2,3,4,5,6]

【Answer】
方法一、
```javascript
arr.map(item=>{
    return {
        value:item,
        key:Math.random()
    }
})
.sort((a,b)=>a.key-b.key)
.map(item=>item.value)
```
方法二、
```
var arrayToRand = (arr) => {
    for(let i=0; i

280. js事件模型

【Question】 浏览器的事件模型?在当前的事件模型中,哪些事件可以冒泡,哪些不会冒泡,为什么?不冒泡的元素,如何来实现事件代理?

【Answer】
考察浏览器事件模型,看看是不是了解事件模型背后的设计意图。

浏览器开发团队遇到的问题:页面上哪一部分会拥有某个特定的事件?比如单击一个嵌套的同心div,那么到底哪一个div会拥有这个点击事件?实际上难以确定点击者的意图,团队给出的解决方式是所有div都将拥有这个事件,于是产生了事件流模型。如上一个问题所述,“事件”的概念在GUI编程中如此之重要,而这种流式模型能给予其很大的灵活性和控制
对于能精确确定意图的(这种冒泡的话一般也会带来问题,比如mouseleave),或者不可能产生嵌套的媒体类元素,冒泡就不是必须的;对于不冒泡的元素,可以在捕获阶段代理,DOM2级规范addEventListener的第三个参数

281. 请列举说明几个在web中实现长连接的技术方案或手段

【Question】 本地主要考察候选人对长连接技术的概念理解和区分,如果能回答答出大致的名词可以继续追问一些具体的激技术实现细节和存在的优缺点等等。

【Answer】
参考答案:
1. https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet/12855533#12855533
1. https://blog.csdn.net/liang0000zai/article/details/40537059

* Long Polling
* Server-Sent Events
* Websockets
* Comet

282. 函数作用域

【Question】 用代码实现JavaScript中Function的bind方法的polyfill

【Answer】
```
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                                 ? this
                                 : oThis || this,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```

283. CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Question】 CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Answer】
  - `content-box` 默认值,width内容宽度
	- `border-box` width 包含`padding`和`border`

284. JS的new操作符具体做了什么

【Question】 JS的new操作符具体做了什么,描述一下,最好可以体现在代码上

【Answer】
```
function A() {
  this.name = 'a';
  this.getName = function() {
    return this.name;
  }
}
var a = new A();

var aa = new Object();
aa.__proto__ = A.prototype;
A.call(aa);
// 还有最后一步,如果发现A返回的是一个Object类(非primitive类型),则直接返回A的返回值,否则把aa返回出去
```

285. JS编码二叉树的实现与遍历

【Question】 JS编码实现一个二叉树的构造函数,包括节点类Node,树类BST,插入节点函数insert, 并且满足 1.左子节点的值 < 父节点的值 <= 右子节点的值 2.可以实现先序,中序,后续遍历

【Answer】
```
// 二叉树
function BST() {
  this.root = null;
}

BST.prototype.insert = function(data) {
  var n = new Node(data, null, null);
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    for (;;) {
      if (data < current.data) {
        if (current.left === null) {
          current.left = n;
          break;
        } else {
          current = current.left;
        }
      } else {
        if (current.right === null) {
          current.right = n;
          break;
        } else {
          current = current.right;
        }
      }
    }
  }
}

// 先序遍历
BST.prototype.preOrder = function(node) {
  if (node !== null) {
    console.log(node.show() + " ");
    this.preOrder(node.left);
    this.preOrder(node.right);
  }
}

// 中序遍历
BST.prototype.inOrder = function(node) {
  if (node !== null) {
    this.inOrder(node.left);
    console.log(node.show() + " ");
    this.inOrder(node.right);
  }
}

// 后序遍历
BST.prototype.postOrder = function(node) {
  if (node !== null) {
    this.postOrder(node.left);
    this.postOrder(node.right);
    console.log(node.show() + " ");
  }
}

// 节点对象
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = function() {
    return this.data;
  }
}

// 测试代码
var bst = new BST();
var nums = [10, 3, 18, 2, 4, 13, 21, 9, 8, 9];
for (var i = 0; i < nums.length; i++) {
  bst.insert(nums[i]);
}
bst.preOrder(bst.root);
bst.inOrder(bst.root);
bst.postOrder(bst.root);
```

286. 简述一下src与href的区别

【Question】 描述一下html中的src与href的区别和使用场景是什么

【Answer】
基本答案:src用于指向外部资源的位置替换当前元素,href用于在当前文档和引用资源之间确立联系。
1.  src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;
在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。
这也是为什么将js脚本放在底部而不是头部。
 
1.  href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
这也是为什么建议使用link方式来加载css,而不是使用@import方式。

287. js运行机制

【Question】 下面一段代码的输出:

(function() {
  console.log('this is the start');
  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });
  console.log('this is just a message');
  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);
  console.log('this is the end');
})();

【Answer】
因为前端编程基本属于「Event-driven programming」范式,这是GUI之类的交互式程序的基础,区别于传统的批处理式编程。一个页面上的交互行为,基本都是由用户发起的,然而用户的行为意图是难以预测的,所以需要异步的驱动机制来应对
因此有进一步问题:
平时都说JS是单线程执行的,那它是如何实现非阻塞式执行页面JS的?
考察对EventLoop概念的理解,核心是会在调用栈之外建立一个Event Table。可以将Event Table想象成一个电话注册本:调用栈会告诉event table注册一些特定的函数,并且在指定事件发生时会调用他们。当这些指定事件发生时,event table仅仅是简单地把要调用的函数移入Event Queue中去。event queue提供了一个简单等待区域,函数在此区域内等待被移入调用栈进行调用。 『究竟什么情况下,event queue中的函数才会被移入调用栈中?』。实际上,JavaScript 遵从一个简单的法则:存在一个监控进程不断检查调用栈是否为空,当调用栈为空的时候,检查事件队列(event queue)中是否有待调用的函数。如果事件队列中存在待调用的函数,队列头部的函数被移入调用栈执行。如果事件队列为空,监控进程就保持轮询状态。 这意味着js中的定时器的精度,实际上是没有保障的,你写一个setTimeout(function(){ do xxxx}, 1000); 并没办法保证它刚好是在1000ms之后调用,因为之前的代码执行可能非常耗时,也可能事件队列中有其他事件排在前面。 这样就出现了题目中的情况。 更多可参考:http://metaphor.space/2016/04/26/javascript-event-loop/; https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop;还有《你不知道的Javascript中卷》141页~143页,事件循环章节 值得一提的是:我们平常说JS是单线程执行的,但浏览器不是,浏览器是多线程的,有的线程负责网络请求,有的负责渲染页面等;不要搞混了 另外,ES6给JS带来了新的特性,比如加入了可以创建多线程的worker,以及更精准控制事件调度的Promise

288. 请问for of和for in的区别

【Question】 for of和for in的区别? for of可以用在普通对象上吗?

【Answer】
考察候选人对for 循环的理解 以及对es6中的for of和iterator理解

for in不多做解释了 for of主要是对实现了 Symbol.iterator 接口进行遍历

自定义for of
```
var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
```

289. 字符串的排列组合计算

【Question】 输入一个字符串,打印出该字符串中字符的所有排列的情况。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba.

function calc(str){
    var strArray = str.split('');
    // 补全代码
  }
  console.log(calc('ab')) // ['a','b']  ['b','a']

【Answer】
```javascript
function calc(str){
    var strArray = str.split('');
    var path = [];
    var docalc = function(array){
      if(array.length===1){
        path.push(array[0]);
        console.log(path);
        path.pop();
        return;
      }
      for(var i=0;i

290. DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Question】 DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Answer】
*  不冒泡的事件有blur、focus、load、unload、abort、error、mouseenter、mouseleave、resize
*  每个 event 都有一个event.bubbles属性,通过该属性可知是否冒泡

291. JavaScript实现对象深拷贝方法

【Question】 编码实现JavaScript实现对象深拷贝

【Answer】
var clone = function(v) {  
  var o = v.constructor === Array ? [] : {};  
  for (var i in v) {  
    o[i] = typeof v[i] === "Object" ? clone(v[i]) : v[i];  
  }  
  return o;  
}  

292. 故障分析-HTTPS证书不被信任

【Question】

如下图,在不同的设备上,同时访问同一个域名,一个设备显示证书不被信任,另一个设备正常,再使用多个其他设备访问,依然正常。分析可能的原因?以及需要获取的进一步的信息?

正常的设备

ssl_success.png<p>异常的设备</p>ssl_error.png<p>
</p>

【Answer】

需要进行的进一步的操作:

1) 查看证书详情:路径/SN/哈希值

2) 查看DNS解析结果

3) 查看系统时间/版本/浏览器版本

可能的原因:

1) 代理工具/安全软硬件

2) DNS劫持/路由劫持

3) 时间偏差

4) 操作系统/浏览器版本差异


293. 请实现一个CodingMan函数实现以下功能

【Question】


实现一个CodingMan,可以按照以下方式调用:
CodingMan(“Hank”)输出:
Hi! This is Hank!

CodingMan(“Hank”).sleep(10).eat(“dinner”)
输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

CodingMan(“Hank”).eat(“dinner”).eat(“supper”)
输出
Hi This is Hank!
Eat dinner~
Eat supper~

CodingMan(“Hank”).sleepFirst(5).eat(“supper”)
输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。


【Answer】


class _CodingMan {
    constructor(name) {
        this.tasks = [];
        const task = () => {
            console.log(`Hi! This is ${name}`);
            this.next();
        }
        this.tasks.push(task);
        setTimeout(() => {               // 把 this.next() 放到调用栈清空之后执行
            this.next();
        }, 0);
    }

    next() {
        const task = this.tasks.shift(); // 取第一个任务执行
        task && task();
    }

    sleep(time) {
        this._sleepWrapper(time, false);
        return this;                     // 链式调用
    }

    sleepFirst(time) {
        this._sleepWrapper(time, true);
        return this;
    }

    _sleepWrapper(time, first) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                this.next();
            }, time * 1000)
        }
        if (first) {
            this.tasks.unshift(task);     // 放到任务队列顶部
        } else {
            this.tasks.push(task);        // 放到任务队列尾部
        }
    }

    eat(name) {
        const task = () => {
            console.log(`Eat ${name}`);
            this.next();
        }
        this.tasks.push(task);
        return this;
    }
}

function CodingMan(name) {
    return new _CodingMan(name);
}



294. 实现如下函数add,使如下执行都等于9

【Question】


add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】

// 较通用的实现

function currying(fn, length) {

 length = length || fn.length;

 return function (...args) {

  return args.length >= length

   ? fn.apply(this, args)

   : currying(fn.bind(this, ...args), length - args.length) 

 }

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 通用实现2

function currying(fn, length) {

return function(...args) {

if (args.length >= length) {

return args.slice(0, length).reduce((t, i) => t += i);

}

return function(..._args) {

return add.apply(null, [...args, ..._args]);

}

}

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 直接的实现

function add(...args) {

if (args.length >= 3) {

return args.slice(0, 3).reduce((t,i) => t += i);

}

return function(..._args) {

return add(args.concat(_args));

}

}


295. 介绍一下你了解的 WebSocket

【Question】 简单介绍一下 WebSocket,ws 协议和 http 协议的关系是什么,WebSocket 如何校验权限? WebSocket 如何实现 SSL 协议的安全连接?

【Answer】
WebSocket 是基于 http 的,所以建立 WebSocket 连接前,
浏览器会通过 http 的方式请求服务器建立连接,
这个时候可以通过 http  的权限校验方式来校验 WebSocket,比如设置 Cookie。
同理,WebSocket 实现 SSL 协议也同 https 类似,会升级为 wss 连接。
另外,当然也可以在 WebSocket 中还可以通过加密或者 token 等方式,实现自己额外的加密传输和权限判断方式。
更多可参考 https://security.tencent.com/index.php/blog/msg/119


296. 请谈谈iframe有哪些缺点?

【Question】 iframe通常有哪些用途,主要缺点是什么

【Answer】
(1)iframe会阻塞主页面的Onload事件;
(2)搜索引擎的检索程序无法解读这种页面,不利于SEO;
(3)iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
(4)页面简的通信问题
使用iframe之前需要考虑这(1)(3)两个缺点。
如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

297. 请简述JAVAScript事件模型和事件代理

【Question】 简述一下JavaScript事件模型和事件代理,事件代理有哪些优点?

【Answer】
## 事件模型
事件三个阶段:事件捕获,目标,事件冒泡(低版本ie不支持捕获阶段)
## 事件代理及优点: 
把事件委托到其父对象上,借助事件冒泡机制,实现对节点的事件代理。  
### 优点  
*  可以大量节省内存占用,减少事件注册
*  当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

298. 根据id从多叉树里面查找出对应的节点的name

【Question】


一个树形的数据(如下数据),面试官给你一个id,然后拿到对应的name?
  var cityData = [
      {
        id: 1,
        name: '广东省',
        children: [
          {
            id: 11,
            name: '深圳',
            children: [
              {
                id: 111,
                name: '宝安',
                children: [
                  {
                    id: 1111,
                    name: '西乡',
                    children:[
                      {
                        id: 11111,
                        name: '坪洲',
                        children:[]
                      },
                      {
                        id: 11112,
                        name: '灵芝',
                        children:[]
                      }
                    ]
                  },
                  {
                    id: 1112,
                    name: '南山',
                    children:[
                      {
                        id: 11121,
                        name: '科技园',
                        children:[]
                      }
                    ]
                  }
                ]
              },
              {
                id: 112,
                name: '福田',
                children: []
              }
            ]
          },
          {
            id: 12,
            name: '广州',
            children: [
              {
                id: 122,
                name: '白云区',
                children: [
                  {
                    id: 1222,
                    name: '白云区',
                    children: []
                  }
                ]
              },
              {
                id: 122,
                name: '珠海区',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 2,
        name: '湖南省',
        children: []
      }
    ];


【Answer】


主要考查深度/广度优先遍历,递归算法
方法1:递归

let result = ''

// 递归实现
const recursion = (cityData, id) => {
  // cityData数据为空的时候直接返回
  if (!cityData || !cityData.length) return;
  // 常规循环cityData
  for (let i = 0, len = cityData.length; i < len; i++) {
    const childs = cityData[i].children;
    
    // 如果匹配到id的话,就是我们要的结果
    if (cityData[i].id === id) {
      result = cityData[i].name
    }
    // 如果还有子节点,执行递归
    if(childs && childs.length > 0){
      recursion(childs, id);
    }
  }
  return result
};

const r = recursion(cityData, 11112);
console.log(r) // 灵芝


方法2:广度优先遍历
let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定义一个数据栈
  let stack = [];

  let item = null;

  //先将第一层节点放入栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 将数据栈的第一个取出来
    item = stack.shift();
    // 如果符合就赋值给result
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

let r1 = range(cityData, 11112);

console.log(r1) // 灵芝


方法3:深度优先遍历
let result = ''

const deep = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 先定义一个数据栈
  let stack = []
  let item = null

  //先将第一层节点放入数据栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i])
  }
  // 循环
  while (stack.length) {
    item = stack.shift()
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈顶
    if (item.children && item.children.length) {
      // 注意这里调换了顺序
      stack = item.children.concat(stack);
    }
  }
  return result
};

let r3 = deep(cityData, 11112)
console.log(r3) // 灵芝


方法4:正则

const regular = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 数据转成字符串
  let cityStr = JSON.stringify(cityData)
  // 定义正则
  let reg = new RegExp(`"id":${id},"name":"([^\\x00-\\xff]+)",`)
  // 取到正则的子字符串并返回
  return (cityStr.match(reg))[1]
}

let r4 = regular(cityData, 11112);

console.log(r4) // 灵芝



299. js浮点运算

【Question】 console.info(0.7+0.1)会得到什么

【Answer】
输出0.799999


300. macro micro 任务队列(async/await版)

【Question】

async function async1() {

 console.log('async1 start');

 await async2();

 console.log('async1 end');

}

async function async2() {

 console.log('async2 start');

 return new Promise((resolve, reject) => {

  resolve();

  console.log('async2 promise');

 })

}

console.log('script start');

setTimeout(function() {

 console.log('setTimeout');

}, 0);  

async1();

new Promise(function(resolve) {

 console.log('promise1');

 resolve();

}).then(function() {

 console.log('promise2');

}).then(function() {

 console.log('promise3');

});

console.log('script end');

【Answer】

chrome 和 node 都是以下顺序



301. JS实现一个带并发限制的异步调度器

【Question】

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4// 一开始,1、2两个任务进入队列// 500ms时,2完成,输出2,任务3进队// 800ms时,3完成,输出3,任务4进队// 1000ms时,1完成,输出1// 1200ms时,4完成,输出4

【Answer】
class Scheduler {
concurrency = 2
running = 0
queue = []
add(task) {
return new Promise(resolve => {
this.queue.push({
taskGenerator: task,
resolve
})
this.schedule()
})
}
schedule() {
while (this.queue.length > 0 && this.running < this.concurrency) {
const curTask = this.queue.shift()
this.running += 1
curTask.taskGenerator().then(result => {
this.running -= 1
curTask.resolve(result)
this.schedule()
})
}
}
}

302. 写一个加法函数(sum),使他可以同时支持sum(x,y)和sum(x)(y)两种调用方式。

【Question】

写一个按照下面两种方式都能正常调用的 sum 方法
```javascript
console.log(sum(2,3)); // 输出5
console.log(sum(2)(3)); // 输出5
```

【Answer】
答案一
function sum(a,b){
if(b) {
return a+b
}else{
return function(c){
return a+c
}
}
}
答案二
function sum(){
var arg=arguments
if(arg.length==2) {
return arg[0]+arg[1];
}else{
return function(c){
return arg[0]+c
}
}
}

303. ES5,ES6中this指向考察

【Question】

  1. 以下代码输出什么结果,this.name中this指向什么:
    window.name = 'ByteDance';
    function A () {
    this.name = 123;
    }
    A.prototype.getA = function(){
     console.log(this);
     return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    
  2. 如何使funcA()返回undefined?
  3. 下面ES6中又会发生什么,this是什么?
    window.name = 'ByteDance';
    class A {
     constructor() {
      	this.name = 123;
     }
     getA() { 
       console.log(this);
         return this.name + 1; 
     }
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    

【Answer】
1. 输出`Bytedance1`, this指向widnow;
2. 正确使用applay / call;
3. 发生异常:Uncaught TypeError: Cannot read property 'name' of undefined,this为undefined;

304. 请问什么是跨域?跨域请求资源有几哪种方式?

【Question】 何为跨域?跨域请求资源有几哪种方式?

【Answer】
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
跨域请求资源的方式主要有:  
(1)JSONP 动态创建script标签  
但缺点是只支持get请求,并且很难判断请求是否失败(一般通过判断请求是否超时)。  
(2)Proxy代理  
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。  
(3)CORS跨域  
是现代浏览器提供的一种跨域请求资源的方法,需要客户端和服务器端的同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信  
服务响应头返回,Access-Control-Allow-Origin: *

305. 简述React Fiber原理

【Question】

试描述React Fiber的原理。

【Answer】

官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。

之前React的更新过程是同步的,所有更新逻辑会在一帧之内完成,如果组件过于复杂则会导致更新时间超过一帧,其他事务包括用户输入都会被延迟响应,从而引发卡顿。如下图:


破解方式——分片。

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

实现使用的API:requestIdleCallback

Q.为什么引入Fiber架构?原架构有何不足?
A.原架构采用递归遍历方式来更新DOM树,一旦开始,即占用主线程,无法中断,这在页面上会引起问题,如input输入后页面卡顿等

Q.Fiber如何解决该问题
A.时间分片和暂停

Q.Fiber如何实现?
A.使用链表结构,将递归遍历更改为循环遍历,然后配合requestIdleCallback API,实现任务拆分、中断和恢复

Q.Fiber如何实现比较?
A.双缓冲技术,在diff过程中创建新的DOM Tree,diff完成之后生成EffectList,即需要更新的地方,之后进入commit阶段,该阶段不允许中断。

Q.React Hook基于Fiber架构,hook的复用是如何实现的?
A.hook的数据存在于Fiber节点的数据结构中,具体为memoizedState中,该字段中存储了所有hook相关的信息,https://www.jianshu.com/p/d6244228a427 (重要)



306. 请简要描述ES6 module require、exports以及module.exports的区别

【Question】 考察候选人对es6,commonjs等js模块化标准的区别和理解

【Answer】
* CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
* ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
* CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
* export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
* ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性
* 混合使用介绍:https://github.com/ShowJoy-com/showjoy-blog/issues/39

307. 浏览器缓存机制考察

【Question】 浏览器缓存机制考察,包括cache-control , etag, expire, last-modify-time 以及 200 from cache、304

【Answer】
1、cache-control 和 expire 在浏览器端控制  Cache-Control的max-age>expire
2、etag 和 last-modify-time主 要服务器端对比使用

308. 版本号排序

【Question】 versions是一个项目的版本号列表,因多人维护,不规则

var versions=['1.45.0','1.5','6','3.3.3.3.3.3.3']

要求从小到大排序,注意’1.45’比’1.5’大

var sorted=['1.5','1.45.0','3.3.3.3.3.3','6']

【Answer】
```javascript
function sortVersion(arr) {
    return arr.sort((a, b) => {
        const arrA = a.split('.')
        const arrB = b.split('.')
        for (let i = 0; i < arrA.length; i++) {
            if (arrA[i] === undefined) {
                return -1
            } else if (arrB[i] === undefined) {
                return 1
            } else if (parseInt(arrA[i]) === parseInt(arrB[i])) {
                continue
            } else {
                return parseInt(arrA[i]) > parseInt(arrB[i])
            }
        }
    })
}
```

309. JS限流调度器

【Question】

实现JS限流调度器,方法add接收一个返回Promise的函数,同时执行的任务数量不能超过两个。

class Scheduler {
    async add(promiseFunc: () => Promise<void>): Promise<void> {
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
// log: 2 3 1 4


【Answer】


class Scheduler {
    constructor() {
        this.concurrency = 0
        this.queue = []
    }
    async add(promiseFunc) {
        if (this.concurrency >= 2) {
            return new Promise(r => {
                this.queue.push(() => promiseFunc().then(r))
            })
        }
        this.concurrency += 1
        await promiseFunc()
        this.concurrency -= 1
        let next = this.queue.shift()
        if (next) {
            this.add(next)
        }
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)



310. 实现一个简单的Event类(观察者模式)

【Question】

请实现一个观察者模式,拥有四个方法on,off,once和trigger


const Event = {

on() {} // 绑定

off() {} // 解绑

once() {} // 绑定一次

trigger() {} // 触发事件

};

【Answer】

```javascript function Event() { if (!(this instanceof Event)) { return new Event(); } this._callbacks = {}; } Event.prototype.on = function (type, handler) { this_callbacks = this._callbacks || {}; this._callbacks[type] = this.callbacks[type] || []; this._callbacks[type].push(handler); return this; }; Event.prototype.off = function (type, handler) { var list = this._callbacks[type]; if (list) { for (var i = list.length; i >= 0; --i) { if (list[i] === handler) { list.splice(i, 1); } } } return this; }; Event.prototype.trigger = function (type, data) { var list = this._callbacks[type]; if (list) { for (var i = 0, len = list.length; i < len; ++i) { list[i].call(this, data); } } }; Event.prototype.once = function (type, handler) { var self = this; function wrapper() { handler.apply(self, arguments); self.off(type, wrapper); } this.on(type, wrapper); return this; }; ```


【Question】 请说明 cookie、sessionStorage、localStorage 之间的区别、以及在你项目中的应用?

【Answer】
 a) cookie,HTTP Cookie(也叫Web cookie或者浏览器Cookie)是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。比较经典的,可以它用来确定两次请求是否来自于同一个浏览器,从而能够确认和保持用户的登录状态。Cookie的使用使得基于无状态的HTTP协议上记录稳定的状态信息成为了可能。
b) sessionStorage,为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
c) localStorage,localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

区别:
localStorage、sessionStorage 是 Web Storage Api 的组成 API,其为了解决 Cookie 的一些缺陷,服务端 Set 的 cookie 每次会携带在本域下所有的请求上,对性能有损耗。SessionStorage 存储有个期限,当关闭浏览器后就不再存在,但 localStorage 依然存在,需要明确删除。


312. 请简述js浏览器事件循环机制

【Question】


【Answer】

浏览器 Event Loop 是 HTML 中定义的规范,Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

  • JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

  • 同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

  • Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

  • 定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

  • 宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。


313. 何为https?https和http2有什么关系?

【Question】 简要描述HTTPS的安全机制,以及在web服务工程实践中需要注意的问题;描述http2的基本机制

【Answer】
HTTPS是指建立在安全的传输层(通常是tls/ssl)上的HTTP协议,通过对服务器的证书的认证,解决中间人攻击等问题。
证书(certificate)由客户端信任的的证书机构(CA)颁发,通过common name或SAN对服务进行描述;客户端通过CA的根证书对证书进行校验,并将请求域名和证书的common name/DNS域名进行验证,以检验证书的有效性。
目前,很多web api如Notification/web rpc/Service Worker等,都要求必须使用https。
在工程实践中,https存在以下需要注意的问题:
  - js/css等资源必须以https形式加载,否则浏览器将拒绝执行,所以CDN必须完成对https的支持
	- 非https请求的图片等资源不会携带referer
	
	http2是http协议的一个新版本,既可以明文传输也可以在https中使用。浏览器和服务器通过tls的ALPN/SNI等机制可以进行协议协商,决定使用什么协议

314. 用数组的reduce方法实现map方法

【Question】 用数组的reduce方法实现map方法

【Answer】
```
// 代码实现
Array.prototype.map2 = function(f) {
  return this.reduce(function(result, x, index, arr) {
    result.push(f(x, index));
    return result;
  }, []);
}

// 测试代码
var res = [1, 3, 5, 7].map2(function(item, idx){
  return item * 2;
});
console.log(res);
```

315. js异步操作与计算题

【Question】

for (var i = 0; i < 6; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

>1. console.log(new Date, i);得到的结果是什么? >1. 怎样优化,可以变成: 0 -> 1 -> 2 -> 3-> 4 ->5 >1. 如果继续优化,实现console.log(new Date, i);代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4(sleep),之后再暂停5秒,然后输出5, 实现结果类似: >1. 2017-08-31T04:38:23: 0 <— start IIFE >1. 2017-08-31T04:38:24: 1 <— sleep 1s >1. 2017-08-31T04:38:25: 2 <— sleep 1s >1. 2017-08-31T04:38:26: 3 <— sleep 1s >1. 2017-08-31T04:38:27: 4 <— sleep 5s >1. 2017-08-31T04:38:32: 5

【Answer】
1. 属于结果是暂停1S,然后输出6个6,setTimeout属于异步执行
1. 实现0-> 1 -> 2 -> 3-> 4 ->5,用闭包或者var改成let
1. 模拟编程中的sleep实现,参考答案:
```
// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeoutMS) => new Promise((resolve) => {
  setTimeout(resolve, timeoutMS)
});
(async () => {  // 声明即执行的 async 函数表达式
  for (let i = 0; i < 6; i++) {
      if (i < 5) {
        console.log(new Date(), i)
        await sleep(1000)
      } else {
        await sleep(4000)
        console.log(new Date(), i)
      }
    }
})()
```

316. 简单的实现Promise.all

【Question】



function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000);
    })
}
PromiseAll([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

Promise.all([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err=>{
    console.log(err)
})


【Answer】


function PromiseAll(list) {

    return new Promise((resolve, reject) => {

        let count = 0;

        let len = list.length;

        let result = [];

        list.forEach((item,index) => {

            item.then(res => {

                count++;

                result[index] = res;

                if (count === len) {

                    resolve(result);

                }

            }).catch(err => {

                reject(err)

            })

        })

    })

}



317. ES6 import的原理

【Question】 请描述ES6 import的原理以及与commonjs的require的区别

【Answer】
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

318. 不借助变量交换两个数

【Question】 var a = 1, b = 2; function swap(a,b){ …. } swap(a,b) console.log(a, b) // 2,1

【Answer】
方法一、
```
function swap(a,b){
  b=b-a;
  a=a+b;
  b=a-b;
  return [a,b]
}
```
方法二、
```
function swap(a,b){
  return [a, b] = [b, a]
}
```
方法三、
```
function swap(a,b){
  var a=a^b;
  var b=b^a;
  var a=a^b;
	return [a,b]
}
```

319. 实现垂直居中

【Question】


    <div id="block">        
    </div>

id为block的元素不定高不定宽,请实现它在浏览器窗口的居中显示。

【Answer】
```css
#block {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
```

320. 请回答当我们在使用new操作符时,它在对象操作的过程中具体做了什么

【Question】 考察候选人对原型链操作和js对象的理解

【Answer】
1. 简单回答:
1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
1. 属性和方法被加入到 this 引用的对象中。
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
```javascript
function Animal(name) {
      this.name = name;
}
  Animal.prototype.run = function() {
      console.log(this.name + 'can run...');
}
var cat = new Animal('cat'); //    
new Animal('cat')=function(){
let obj={}; //       
obj.__proto__=Animal.prototype; // obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,'cat');//   this        
}
```



321. css3实现多行文字截断处理

【Question】 用css分别实现单行截断和多行截断字符串,最后以…为结尾

【Answer】
单行:
```
.text-overflow ( @class ){
    .@{class} {
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
    }
}
```
多行:
```
.multi-text-overflow ( @class, @line ){
    .@{class} {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        display: box;
        -webkit-line-clamp: @line;
        -webkit-box-orient: vertical;
    }
}
```

322. 请介绍react diff算法和策略

【Question】 react的diff算法和策略了解多少,为什么react的diff性能好,遵循什么样的策略可以把 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

【Answer】
React分别对 tree diff、component diff 以及 element diff做了算法优化,
做了一些假设
1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
3.对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
tree diff:React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较
component diff:
a.如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
b.如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
c.对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff
element diff:
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,减少增加和删除
详见:https://zhuanlan.zhihu.com/p/20346379

323. 函数科里化

【Question】

实现如下函数add,使如下执行都等于9

add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】


function curry(fn) {
  return function res(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return res.apply(this, args.concat(args2));
      }
    }
  }
}



324. 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?应该怎么解决?

【Question】 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?怎么解决?

【Answer】
考察一下JS中整数的安全范围的概念,在头条经常会遇到长整型到前端被截断的问题,需要补一个字符串形式的id供前端使用。
主要会涉及到JS中的最大安全整数问题
https://segmentfault.com/a/1190000002608050

325. JavaScript this 考察

【Question】

下面代码输出的结果是什么?

var length = 10;

function fn() {

 return this.length+1;

}

var obj = {

 length: 5,

 test1: function() {

  return fn();

 }

};

obj.test2=fn;

//下面代码输出是什么

console.log(obj.test1())

console.log(fn()===obj.test2())

【Answer】

11, false(11===6)


326. requestAnimationFrame 和 setTimeout 的区别

【Question】 requestAnimationFrame 和 setTimeout 都可以用来实现动画,它们的区别是什么

【Answer】
1. 执行频率不同,前者按照屏幕刷新频率执行,后者自行控制,可能有无用开销(执行频率小于刷新频率,即1帧执行多次)
2. 前者在页面不可见时,会停止执行(省电),后者在页面不可见时仍会执行,带来不必要开销


327. 编码-js高阶函数考察

【Question】

实现一个repeat方法,要求如下:


// 需要实现的函数

function repeat (func, times, wait) {

// 补全

}


// 使下面调用代码能正常工作

const repeatFunc = repeat(console.log, 4, 3000);

repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒


【Answer】

考点1:能意识到repeat返回的是一个函数,知道参数怎么传递。

考点2:setTimeout的时间,微任务


参考答案

function repeat(fn, times, wait) {

if(typeof times !== 'number') return;

if(typeof wait !== 'number') return;

return function(str){

for(let i = 0; i < times; i++){

setTimeout(()=>{

fn(str)

}, i * wait)

}

}


328. Vue框架中组件消息通信方式

【Question】 考察候选人对Vue框架的消息通信方式了解程度:

  1. vue父子组件通信方式?
  2. 非父子组件通信方式?
  3. 前两问OK,追问:当一个父组件与子组件中间隔着很多层组件怎么办?

【Answer】
1. 父子组件通信方式
在Vue中,父子组件的关系可以总结为props down, events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。

2. 非父子组件通信
两个独立的组件之间通信,可以借助一个空的Vue实例作为中央事件总线,空实例相当于代理人的形式进行消息监听或触发

3. 父子之间层级过多时
当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。
这块如果了解开源的Element组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以`[组件名, 消息名, 参数]`三元组进行消息传递,降低长链传播成本;
具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

329. 什么是 XSS,怎么造成的,有什么防御方法?

【Question】 考察面试者对于 XSS 是否了解,是否足够重视。

【Answer】
XSS 就是在 web 中能够通过某种方式产生执行任意 JavaScript 脚本的情况,
最常见的一种情况就是将用户的输入,直接放到当前 runtime 中,比如用户输入直接放到页面的 html 里面,
立刻显示出来。
XSS 实际上是非常危险的,因为理论上讲,如果能够执行 JavaScript,实际上攻击者可以做任何事情。
简单的就是输出点什么,偷偷 cookie,或者结合 CSRF 攻击,或者让浏览器跳转一下,
复杂点的甚至可以改掉当前整个页面,伪造一切用户看到东西,危害无穷。
如果这种输入存储到数据库中,就会变成一个永久型的 XSS,危害就更大了。
防止 XSS 最简单的就是使用各种框架,如 React、Vuejs 等,对用户输入进行 html 转义。
另外,服务端要设置 httpOnly 的 header,防止 JavaScript 操作 cookie。
当然,服务端也可以对输入进行转义或者过滤监测。

330. webpack插件编写

【Question】

  1. 有用过webpack么?说说该工具的优缺点?
  2. 有开发过webpack插件么?
  3. 假如要在构建过程中去除掉html中的一些字符,如何编写这个插件?

【Answer】
webpack优缺点:
* 概念牛,但文档差,使用起来费劲
* 模块化,让我们可以把复杂的程序细化为小的文件
* require机制强大,一切文件介资源
* 代码分隔
* 丰富的插件,解决less、sass编译

开发插件的两个关键点Compiler和Compilation:
* compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。
* compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

插件编写可参考:https://doc.webpack-china.org/development/how-to-write-a-plugin

331. 如何实现微信扫码登录?

【Question】 综合题,考察网络、前端、认证等多方面知识

【Answer】
参考答案:
https://zhuanlan.zhihu.com/p/22032787
具体步骤:
1. 用户 A 访问微信网页版,微信服务器为这个会话生成一个全局唯一的 ID,上面的 URL 中 obsbQ-Dzag== 就是这个 ID,此时系统并不知道访问者是谁。
2. 用户A打开自己的手机微信并扫描这个二维码,并提示用户是否确认登录。
3. 手机上的微信是登录状态,用户点击确认登录后,手机上的微信客户端将微信账号和这个扫描得到的 ID 一起提交到服务器
4. 服务器将这个 ID 和用户 A 的微信号绑定在一起,并通知网页版微信,这个 ID 对应的微信号为用户 A,网页版微信加载用户 A 的微信信息,至此,扫码登录全部流程完成

332. 设计类似 Vue.js 双向绑定功能的核心逻辑“监听对象属性变化”功能

【Question】 实现一个类,可以监听对象属性的值变化。加分项:考虑对象存在值为数组或对象的属性。

	class Observe {
		constructor(data: Object) {
		}
		// 监听属性变更
		$on() {
		}
		// 触发属性变更事件
		$emit() {
		}
	}
	const data = new Observer({
		a: 1
	});
	coonsole.log(data.a) // console: 1
	data.$on('a', (newValue, oldValue) =&gt; {
		// this === data
		console.log(newValue, oldValue);
	});
	data.a = 2 // console: 2 1

【Answer】
待补充

333. 请简要描述

【Question】

【Answer】
### 作用:
defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
### 区别:
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

334. 原型链、this指针、自有属性考察

【Question】

var a= function () { this.b =3; }
var c = new a();
a.protorype.b = 9;
var b = 7;
a();

问:

console.log(b);
console.log(c.b); 

分别输出什么?

【Answer】
- 第一个 `b = 3`
- 第二个 `c.b = 3`

【Question】

如题

【Answer】
cookie 在浏览器中存在,并且每个请求都会带上,而且是可以设置失效时间的,失效前就会有效。 session 是一次会话有效,也就是说,浏览器关闭 session 就会失效。 其实这道题目考察的一个重点是在于,session 这种机制是怎么在浏览器中实现的呢? 实际上 session 的实现也是在浏览器中放了一个 cookie,失效时间是一次会话失效。

336. JS异步队列macrotask和microtask

【Question】

console.log('begin')
setTimeout(() =&gt; {
	console.log('setTimeout 1')
	Promise.resolve().then(() =&gt; {
		console.log('promise 1')
		setTimeout(() =&gt; {
			console.log('setTimeout2 between promise1&amp;2')
		})
	}).then(() =&gt; {
		console.log('promise 2')
	})
}, 0)
console.log('end')

【Answer】
```
begin
end
setTimeout 1
promise 1
promise 2
setTimeout2 between promise1&2
```

337. 如何理解虚拟DOM?

【Question】 如何理解虚拟DOM?

【Answer】
对虚拟dom和diff算法中的一些细节理解与考察,[https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13)

338. 如何判断一个 JS 对象为空对象

【Question】 如何判断一个 JS 对象为空对象 ?空对象形如{}

【Answer】
1. 使用 `for in`
	```javascript
	function isEmptyObject(obj){
  	for(var key in obj){
    	return false
		};
		return true
	};
	```
2. 通过 JSON.stringify 方法来判断
	```javascript
	if(JSON.stringify({}) === '{}'){
		console.log('empty obj');
	}
	```
3. 使用 ES6 增加的 Object.keys()
	```javascript
	if(Object.keys(obj).length === 0){
		console.log('empty obj');
	}
	```

339. 什么是闭包?实现每隔1秒输出数组中的一个数字

【Question】 解释下js中的闭包概念,解释OK,给出编程题目考察基本功

【Answer】
```js
function fun(arr) {
    var i, len;
    for (i = 0, len = arr.length; i < len; i++) {
      (function(i){
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
}
```

340. promise运行过程解答

【Question】 如下代码的运行结果是什么?

 process.nextTick(() =&gt; {console.log('nextTick')})
Promise.resolve().then(()=&gt; {console.log('promise1');}).then(()=&gt; {
  console.log('promise2');
});
setImmediate(() =&gt; {console.log('setImmediate')})
console.log('end') 

【Answer】
1. end -> nextTick -> promise1 -> promise2-> setImmediate
1. process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。
1. 事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

341. 请简述常见web安全及防护原理

【Question】 常见web安全及防护原理,请举例说明。

【Answer】
1、SQL注入原理  
		就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点  
1. 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2. 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。  
2、XSS原理及防范  
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者JavaScript代码。
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法  
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

3、CSRF原理及防范  
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法

342. 数字格式化问题:1234567890 –> 1,234,567,890

【Question】 数字格式化问题,将1234567890 –> 1,234,567,890

【Answer】
非正则实现
```javascript
let test = '1234567890'
function formatCash(str) {
  let arr = []
  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))
    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))
  }
  return arr.join(',')
}
console.log(formatCash(test)) // 1,234,567,890
```
正则实现
```javascript
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
console.log(format) // 1,234,567,890
```

343. 模拟实现loadash中的_.get()函数,实现如下传入参数取值效果

【Question】

function get() {
  // 请补全函数参数和实现逻辑
}
const obj = { selector: { to: { toutiao: 'FE coder' } }, target: [1, 2, { name: 'byted' }] };
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')

//  输出结果:
// ['FE coder', 1, 'byted']

【Answer】
```javascript
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
```
1. Use Array.map() for each selector
2. String.replace() to replace square brackets with dots
3. String.split('.') to split each selector
4. Array.filter() to remove empty values
5. Array.reduce() to get the value indicated by it

344. 合并两个有序数组

【Question】 合并两个有序数组

【Answer】
```
function mergeSortedArray(a, b){
  var merged = [], 
      aElm = a[0],
      bElm = b[0],
      i = 1,
      j = 1;
  if(a.length ==0)
    return b;
  if(b.length ==0)
    return a;
  while(aElm || bElm){
   if((aElm && !bElm) || aElm < bElm){
     merged.push(aElm);
     aElm = a[i++];
   }   
   else {
     merged.push(bElm);
     bElm = b[j++];
   }
  }
  return merged;
}
```
验证
```
mergeSortedArray([2,5,6,9], [1,2,3,29]);
结果 [1, 2, 2, 3, 5, 6, 9, 29]
```

345. 进行CSRF漏洞扫描的原理和防御方式是什么?

【Question】 如题

【Answer】
CSRF 就是在用户不知情的情况下,发出了请求,让用户做了不该做的操作。
举个例子,比如你的一个网站中有个 img 标签,src 指向的是微博关注某人的接口,
那么当用户访问你的网站时,就会在微博上关注那个人,而且这个操作用户是不知情的。
因为 img src 发出的跨域请求,也是会携带 cookie 的,所以如果用户在微博登录过,
那么就会带有微博的登录授权。同理,如果是其他操作,可能也存在这种漏洞,比较危险的情况就是付款。
一般会采用 CSRF token 的方式防御,就是关键请求得要换取一个一次有效的 token 才有权限。


346. 判断一个字符串是否是回文字符串

【Question】 判断一个字符串是否是回文字符串,回文字符串是对称字符串的形式,例如:did,eve, dad, level

【Answer】
```
function isPalindrome(str){
  var i, len = str.length;
  for(i=0; i isPalindrome('madam')
  = true
> isPalindrome('toyota')
  = false
```

347. box-sizing 实践

【Question】


<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 10px;
        height: 10px;
        border: 1px solid red;
        margin: 2px;
        padding: 2px;
        background: blue;
      }

      #borderBox {
        box-sizing: border-box;
      }

      #contentBox {
        box-sizing: content-box;
      }
    </style>
  </head>
  <body>
    <div>请问下面两个 div 元素,蓝色区域的宽高各是多少像素?</div>
    <div id="borderBox" class="box"></div>
    <div id="contentBox" class="box"></div>
  </body>
</html>


【Answer】

borderBox:10px(width) - 1px(border) * 2 = 8px

contentBox 10px(width) + 2px(padding) *2 = 14px


答题要点:除了验证候选人是否真正了解 box-sizing 之外,也考察候选人是否了解 background 会影响元素的 padding 区域,而不影响 margin 区域这个特点


348. 链式调用+延迟计算

【Question】

写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....)


console.log(sum(1,2,3)(4)) => 输出 10



考察链式调用,闭包,延迟计算,函数toStirng/valueOf




【Answer】


function sum(...args) {
  function next(...innerArgs) {
    args.push(...innerArgs);
    return next;
  }
  next.valueOf = next.toString = () => {
    return args.reduce((r, c) => r + c, 0);
  };

  return next;
}



349. 请描述micro task 与 macro task的区别及应用

【Question】


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');


【Answer】

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout


350. 数组flat函数设计

【Question】 设计一个flat函数将如下数组arr=[1,2,[‘3’,4,’5’,[6,[7,8],9]]]输出为1,2,’3’,4,’5’,6,7,8,9。至少写出两种方法,要求不能改变数组中的原始数据类型

【Answer】
*  方法一:递归
```javascript
function flat(array) {
    var result = [];
    var each = function(arr) {
      arr.forEach(item => {
        if (item instanceof Array) {
          each(item);
        } else {
          result.push(item);
        }
      });
    };
    each(array);
    return result;
  }
var arr=[1,2,['3',4,'5',[6,[7,8],9]]];flat(arr).forEach(item=>{console.log(item)})

```
*  方法二:toString(格式转换),无法保证类型
```javascript
Array.prototype.toString = function() {
  return this.join(',');
};
console.log([1,2,[3,4,[5,6,7]]]+'');
```
*  方法三:Iterator
```javascript
Array.prototype[Symbol.iterator] = function() {
  let arr = [].concat(this),
    index = 0;
  let getFirst=function(array){
    let first=array[0];
    if(first instanceof Array){
      return getFirst(array[0])
    }else if(first!==undefined){
      return array.shift()
    }else{
      return ''
    }
  }
  return {
    next: function() {
      let item=getFirst(arr);
      if(item){
        return {
          value:item,
          done:false
        }
      }else{
        return {
          done:true
        }
      }
    }
  }
}
var t=[1,2,['3',4,'5',[6,[7,8],9]]];
for(let i of t){console.log(i)}
```

【Question】 基础题考察 cookie 和 localStorage 的理解。

【Answer】
存储在 Cookie 中每个 request 都会带上,而放在 localStorage 中,仅有浏览器中会存储。

352. 请说说HTML的Meta标签的用途,并列举一些常用的meta标签

【Question】

【Answer】
考察对网页结构和语义的理解 

```
The HTML  element represents metadata that cannot be represented by other HTML meta-related elements, like , , 

353. 说说前端优化?图片懒加载原理是什么?

【Question】

  • 考察前端的一些优化方式
  • 图片懒加载原理

【Answer】
1. 优化手段:雅虎的34条优化手段,比如:代码压缩、减少请求、cdn、缓存
2. 图片懒加载原理:img标签设置占位属性(data-src),存储真正的图片地址;原src设置占位图片地址;当图片(快)进入用户可视区域的时候进行地址替换;

354. 请谈谈你对ES6的箭头函数的理解

【Question】

var func1 = x =&gt; x;
var func2 = x =&gt; {x}; 
var func3 = x =&gt; ({x});
console.log(func1(1));
console.log(func2(1));
console.log(func3(1));

请写出程序运行结果。

【Answer】
程序运行结果为:
第一个:1
第二个:undefined
第三个:{x: 1}

355. 无重复字符的最长子串

【Question】

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

样例:


  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。



【Answer】


var lengthOfLongestSubstring = function(s: string) {
    let list = s.split("");
    let son = [];
    let max = [];
    for (let i = 0; i < list.length; i++) {
        let current = list[i];
        let index = son.indexOf(current);
        if (index === -1) {
            son.push(current);
        } else {
            let sameIndex = i - son.length + index;
            if (son.length > max.length) {
                max = [...son];
            }
            son = son.slice(sameIndex + 1, son.length);
            son.push(current);
        }
    }
    return max.length;
};



356. 列举一个近期做的最能体现设计能力的项目

【Question】 请举出一个你近期做的项目,项目需要最能体现设计能力, 请从以下角度说明:

  1. 项目描述
  2. 技术选型
  3. 模块化
  4. 模块之间通信
  5. 工程化
  6. 前后端数据流

【Answer】
这是一个开放式的工程设计题目,没有固定答案,评分参考评分标准

357. 实现一个 JSONP

【Question】 函数签名如下:

function jsonp(url, callback) {
  // TODO
}

【Answer】
主要考察如何处理第二个参数 `callback` 的问题,
加分项比如超时处理 onerror 的处理, xss 考虑等等

```
const kCallBackMap = {};
function uuid() {
  return ...;
}

function jsonp(url, callback) {
  const callbackId = uuid();
  url += 'callback=' + callbackId;
	window[calbackId] = callback;
	
	const script = document.createElement('script');
	script.src = url;
	document.head.appendChild(script);
}
```

358. 请谈一谈JAVAscript的作用域和this

【Question】

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

【Answer】
```
window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

主要考察javascript的作用域和this指向。作用域是静态的,声明时确定;this是动态的,运行时确定。
```

359. 请问CSS position有哪些定位方式

【Question】 CSS position有哪些定位方式,每种方式是如何定位的?

【Answer】
### position取值
relative, fixed,absolute和staic、sticky 5种
### 定位方式
*  staic-默认位置;元素会像通常那样流入页面。顶部,底部,左,右,z-index属性不适用。  
*  relative-元素的位置相对于自身进行调整,而不改变布局(从而为未被定位的元素留下一个空白)。  
*  absolute-该元素从页面的流中移除,并相对于其最近位置的祖先定位(非static)在指定位置,如果有的话,或者与初始包含块相对。绝对定位的框可以有边距,并且不会与其他边距折叠。这些元素不影响其他元素的位置。  
*  fixed元素是定位在相对于窗口。  
*  sticky,是相对定位和固定定位的混合。该元素被视为相对位置,直到它越过指定的阈值,此时它被视为固定位置。  


360. 请介绍一下Oauth2.0 的认证过程

【Question】 如题

【Answer】
可以参考 http://www.jianshu.com/p/0db71eb445c8 或者 
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 的答案,
回答的一个重点是 code(授权码)仅一次有效,并且要有失效时间,而且很短,比如一分钟,
因为浏览器收到会立刻跳转。
还有就是服务端可以根据 code 结合相应的 sercet 去获取 token,要说清楚。

361. express中间件的原理

【Question】

express中间件的实现原理 并给出实现

【Answer】
主要考察候选人对中间件的理解 参考代码 ``` export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } ``` koa中间件主要使用 generator和promise可参考https://github.com/tj/co

362. 实现es6字符串模板方法sprintf

【Question】


const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
	name: 'Yiming Zhang',
	city: 'FuJian',
});
console.log(result); // My name is Yiming Zhang,I'm from FuJian


【Answer】


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);



363. 登录表单设计/扫码登录/第三方登录

【Question】

  1. 请实现一个登录表单
  2. 用GET方法行不行?csrf是什么?如何防御?
  3. cookie-sesssion的工作机制
  4. 你已经登录产品的App端,要在web实现扫码登录,该如何设计?
  5. 接入第三方登录(如微信),如何设计?

【Answer】
1. 正确书写html
2. 正确回答GET和POST的区别,从语义、弊端、安全等方面。csrf的防御:token,samesite,referer校验(弊端)等
3. 正确理解cookie-session的工作机制,sessionId的设计,存储
4. 考察对司空见惯的扫码登录,是否有思考其实现。正确设计 Client/Server/App 三方流程,设计二维码存储的内容,client通知有轮训或websocket等解决方案
5. 正确理解 Client/Server/App/Weixin Server 四方流程,理解oauth2协议

364. 作用域以及变量提升

【Question】

请写出下题的结果:

var a = 1; 
function b() { 
    a = 10; 
    return; 
    function a() {} 
} 
b(); 
console.log(a);   

【Answer】
结果:1

365. setTimeout 和 Promise

【Question】

请写出程序的输出内容

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function(resolve) {
  console.log(2);
  for(var i=0 ; i < 10000 ; i++) {
    if (i == 9999) {
      resolve();
    }
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);


【Answer】

正确答案:2 3 5 4 1。重点关注:候选人是否把 2 写在第一位,以及 4 和 1 的顺序。


366. requestIdleCallback和requestAnimationFrame有什么区别?

【Question】

requestIdleCallback和requestAnimationFrame有什么区别?

【Answer】

requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。

我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了。

一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调。

由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了。


367. 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Question】 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Answer】
`
Array.prototype.findDuplicate = function (n) {
    var results = [];
    if (typeof n != 'number' || isNaN(n)) {
        return results;
    }
    
    var itemFreqs = {};
    this.forEach(function (item) {
        if (!itemFreqs[item]) {
            itemFreqs[item] = 0;
        }
        itemFreqs[item] ++;
    });
    
    for (var item in itemFreqs) {
        if (itemFreqs[item] >= n) {
            results.push(item);
        }
    }
    
    return results;
}

`

368. 请回答DOM中对应创建、移除、追加、复制、查找节点的方法是什么?

【Question】 考察候选人对原生dom操作的方法的理解和掌握熟练程度

【Answer】
1.  创建新节点
	*  createDocumentFragment() //创建一个DOM片段
	*  createElement() //创建一个具体的元素
	*  createTextNode() //创建一个文本节点

1.  克隆节点
*  cloneNode()

1. 添加节点
*  appendChild()
*  insertBefore()

1. 移除节点
*  removeChild()

1. 替换节点
*  replaceChild()

1. 查找节点
*  querySelector()
*  querySelectorAll()
*  getElementById()
*  getElementsByName()
*  getElementsByTagName()



369. 请描述如何用原生JS实现数字的货币格式化

【Question】

# 如何用原生JS实现数字的货币格式化,例如数字6123456789格式化后为6,123,456,789,不低于两种方法。

【Answer】

方法一: (6123456789).toLocaleString('en-US') // 6,123,456,789


方法二: (6123456789).toString().split('').reverse().join('').replace(/\d{3}/g,function($1){return $1+','}).split('').reverse().join('')



370. let,const,var的区别

【Question】 请说明一下let,const,var的区别 并回答如下代码会不会报错

const a = {};
a.test = 1;

【Answer】
考察候选人对es6变量声明的理解
1. let声明的变量拥有块级作用域
2. let声明的全局变量不是全局对象的属性
3. let不能重新声明变量
4. const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

上面代码只是针对a的引用 并不会报错

371. 如何实现链式调用

【Question】 请实现函数 a, b, c,使调用方式为 a().b().c() 时,结果为输出 a b c。 如果上面问题回答出来了,并且是在 a 函数内部 return Object 实现, 那么可以补充问下如何能够实现让三个函数任意链式顺序调用。 如 a().c().b() 或 b().a().c() 。

【Answer】
这道题主要就是考察面试者对 JavaScript 的 Object 概念理解是否清晰,
最好的答案是直接将 a b c 三个函数挂载到 runtime 中的某个全局变量中,比如可以是 window。
然后在每个函数内 return window 就可以了。
当然,也可以按照第一道题目的顺序,分别在相应函数内 return 下个函数,但是这样做无法调换顺序。

372. 实现千位分隔符

【Question】 给一个数字,比如:1234567.90,转化成:1,234,567.90

【Answer】
```js
function commafy(num) {
  return num && num
      .toString()
      .replace(/^\d+/, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','));
}
console.log(commafy(1234567.90)); //1,234,567.90
```

373. 编写javascript深度克隆函数deepClone

【Question】 编写javascript深度克隆函数deepClone

【Answer】
```javascript
function deepClone(obj) {
    var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }

        return new RegExp(obj.source, flags.join(''));
    }

    var result = Array.isArray(obj) ? [] :
        obj.constructor ? new obj.constructor() : {};

    for (var key in obj ) {
        result[key] = deepClone(obj[key]);
    }

    return result;
}

function A() {
    this.a = a;
}

var a = {
    name: 'qiu',
    birth: new Date(),
    pattern: /qiu/gim,
    container: document.body,
    hobbys: ['book', new Date(), /aaa/gim, 111]
};

var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
```

374. 请谈谈你对JS单线程以及setTimeout的理解

【Question】

setTimeout(function() {
	setTimeout(function() { console.log(1) }, 100)
	console.log(2)
	setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
	console.log(4)
}, 100)
console.log(5)

请说出上面代码的输出顺序以及原因?如果吧4改为101ms呢?

【Answer】
正确顺序为:5 2 3 4 1
如果4改为101ms则执行顺序还是不变
原因:
1.  JS单线程
2. setTimeout不在当前eventloop。且执行顺序依赖入队顺序。setTimeout 0是放入下一个loop的队尾
3. 虽然4和1都是100ms延迟的标记,但是4先入队列。
4. setTimeout的time是个标记,会在eventloop循环去检测,符合条件的执行,不符合条件的延后到下一个eventloop,这执行过程本身又有时间,因此尽管101>100,但是在一个执行周期内,他们都会被触发,4先入队所以不变

375. async & forEach 考察

【Question】 以下代码的运行结果

const list = [1, 2, 3];
const square = num =&gt; {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(num * num);
        }, 1000);
    });
}
function test() {
    list.forEach(async x =&gt; {
        const res = await square(x);
        console.log(res);
    });
}
test()

如果希望每隔1s输出一个结果,应该如何改造?

【Answer】
1s 后输出 1 4 9  
改为 for 循环:
```javascript
async function test() {
    for (let x of list) {
        const res = await square(x);
        console.log(res)
    }
}
```


376. css单位的百分比

【Question】 给一个div设置它父级div的宽度是100px,然后再设置它的padding-top为20%。
问现在的div有多高?如果父级元素定位是absolute呢?

【Answer】
现有div的高度等于自身高度+父级块的宽度*20%,如果父级元素定位是absolute,结果不变;
当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!

377. NodeJS实现简单的HTTP代理和隧道代理

【Question】 Web代理一般包括普通的HTTP代理和隧道代理,谈谈理解。 NodeJS实现一个简单的HTTP代理,如在本地 8888 端口开启 HTTP 代理服务,修改浏览器的 HTTP 代理为 127.0.0.1:8888 后再访问 HTTP 网站,代理可以正常工作 对隧道代理了解多少,能否实现?

【Answer】
http普通代理:HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。
```
// http 普通代理
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(8888, '0.0.0.0');
```
隧道代理:HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发
```
const http = require('http');
const net = require('net');
const url = require('url');

function connect(cReq, cSock) {
  const u = url.parse('http://' + cReq.url);

  const pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer().on('connect', connect).listen(8888, '0.0.0.0');
```
合二为一
```
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

function connect(cReq, cSock) {
  var u = url.parse('http://' + cReq.url);

  var pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer()
  .on('request', request)
  .on('connect', connect)
  .listen(8888, '0.0.0.0');
```
需要注意的是,大部分浏览器配完隧道代理,默认只会让https走隧道代理,http如果需要走隧道代理,还需要写个Nodejs的验证
```
const options = {
  hostname: '127.0.0.1',
  port: 8888,
  path: 'toutiao.com:80',
  method: 'CONNECT'
};

const req = http.request(options);

req.on('connect', function(res, socket) {
  socket.write('GET / HTTP/1.1\r\n' +
    'Host: toutiao.com\r\n' +
    'Connection: Close\r\n' +
    '\r\n');

  socket.on('data', function(chunk) {
    console.log(chunk.toString());
  });

  socket.on('end', function() {
    console.log('socket end.');
  });
});

req.end();
```

378. 假设一个网页嵌入一个iframe,如何更改iframe内dom样式?

【Question】 假设一个网页嵌入一个iframe,如何更改这个iframe内dom样式

【Answer】
区分同源和不同源解决方案,同源可以通过document.getElementById('iframeId').contentWindow.document,
不同源:分iframe的嵌入的页面是否自己可控,可控可以通过postMessage方式更改,iframe页面监听message事件;如果页面不可控,应该无解。
可以追问iframe有同源策略限制,举个例子说明

379. 数组随机排序

【Question】

var arr=[1,2,3,4,5,6]

【Answer】
方法一、
```javascript
arr.map(item=>{
    return {
        value:item,
        key:Math.random()
    }
})
.sort((a,b)=>a.key-b.key)
.map(item=>item.value)
```
方法二、
```
var arrayToRand = (arr) => {
    for(let i=0; i

380. js事件模型

【Question】 浏览器的事件模型?在当前的事件模型中,哪些事件可以冒泡,哪些不会冒泡,为什么?不冒泡的元素,如何来实现事件代理?

【Answer】
考察浏览器事件模型,看看是不是了解事件模型背后的设计意图。

浏览器开发团队遇到的问题:页面上哪一部分会拥有某个特定的事件?比如单击一个嵌套的同心div,那么到底哪一个div会拥有这个点击事件?实际上难以确定点击者的意图,团队给出的解决方式是所有div都将拥有这个事件,于是产生了事件流模型。如上一个问题所述,“事件”的概念在GUI编程中如此之重要,而这种流式模型能给予其很大的灵活性和控制
对于能精确确定意图的(这种冒泡的话一般也会带来问题,比如mouseleave),或者不可能产生嵌套的媒体类元素,冒泡就不是必须的;对于不冒泡的元素,可以在捕获阶段代理,DOM2级规范addEventListener的第三个参数

381. 请列举说明几个在web中实现长连接的技术方案或手段

【Question】 本地主要考察候选人对长连接技术的概念理解和区分,如果能回答答出大致的名词可以继续追问一些具体的激技术实现细节和存在的优缺点等等。

【Answer】
参考答案:
1. https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet/12855533#12855533
1. https://blog.csdn.net/liang0000zai/article/details/40537059

* Long Polling
* Server-Sent Events
* Websockets
* Comet

382. 函数作用域

【Question】 用代码实现JavaScript中Function的bind方法的polyfill

【Answer】
```
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                                 ? this
                                 : oThis || this,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```

383. CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Question】 CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Answer】
  - `content-box` 默认值,width内容宽度
	- `border-box` width 包含`padding`和`border`

384. JS的new操作符具体做了什么

【Question】 JS的new操作符具体做了什么,描述一下,最好可以体现在代码上

【Answer】
```
function A() {
  this.name = 'a';
  this.getName = function() {
    return this.name;
  }
}
var a = new A();

var aa = new Object();
aa.__proto__ = A.prototype;
A.call(aa);
// 还有最后一步,如果发现A返回的是一个Object类(非primitive类型),则直接返回A的返回值,否则把aa返回出去
```

385. JS编码二叉树的实现与遍历

【Question】 JS编码实现一个二叉树的构造函数,包括节点类Node,树类BST,插入节点函数insert, 并且满足 1.左子节点的值 < 父节点的值 <= 右子节点的值 2.可以实现先序,中序,后续遍历

【Answer】
```
// 二叉树
function BST() {
  this.root = null;
}

BST.prototype.insert = function(data) {
  var n = new Node(data, null, null);
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    for (;;) {
      if (data < current.data) {
        if (current.left === null) {
          current.left = n;
          break;
        } else {
          current = current.left;
        }
      } else {
        if (current.right === null) {
          current.right = n;
          break;
        } else {
          current = current.right;
        }
      }
    }
  }
}

// 先序遍历
BST.prototype.preOrder = function(node) {
  if (node !== null) {
    console.log(node.show() + " ");
    this.preOrder(node.left);
    this.preOrder(node.right);
  }
}

// 中序遍历
BST.prototype.inOrder = function(node) {
  if (node !== null) {
    this.inOrder(node.left);
    console.log(node.show() + " ");
    this.inOrder(node.right);
  }
}

// 后序遍历
BST.prototype.postOrder = function(node) {
  if (node !== null) {
    this.postOrder(node.left);
    this.postOrder(node.right);
    console.log(node.show() + " ");
  }
}

// 节点对象
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = function() {
    return this.data;
  }
}

// 测试代码
var bst = new BST();
var nums = [10, 3, 18, 2, 4, 13, 21, 9, 8, 9];
for (var i = 0; i < nums.length; i++) {
  bst.insert(nums[i]);
}
bst.preOrder(bst.root);
bst.inOrder(bst.root);
bst.postOrder(bst.root);
```

386. 简述一下src与href的区别

【Question】 描述一下html中的src与href的区别和使用场景是什么

【Answer】
基本答案:src用于指向外部资源的位置替换当前元素,href用于在当前文档和引用资源之间确立联系。
1.  src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;
在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。
这也是为什么将js脚本放在底部而不是头部。
 
1.  href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
这也是为什么建议使用link方式来加载css,而不是使用@import方式。

387. js运行机制

【Question】 下面一段代码的输出:

(function() {
  console.log('this is the start');
  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });
  console.log('this is just a message');
  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);
  console.log('this is the end');
})();

【Answer】
因为前端编程基本属于「Event-driven programming」范式,这是GUI之类的交互式程序的基础,区别于传统的批处理式编程。一个页面上的交互行为,基本都是由用户发起的,然而用户的行为意图是难以预测的,所以需要异步的驱动机制来应对
因此有进一步问题:
平时都说JS是单线程执行的,那它是如何实现非阻塞式执行页面JS的?
考察对EventLoop概念的理解,核心是会在调用栈之外建立一个Event Table。可以将Event Table想象成一个电话注册本:调用栈会告诉event table注册一些特定的函数,并且在指定事件发生时会调用他们。当这些指定事件发生时,event table仅仅是简单地把要调用的函数移入Event Queue中去。event queue提供了一个简单等待区域,函数在此区域内等待被移入调用栈进行调用。 『究竟什么情况下,event queue中的函数才会被移入调用栈中?』。实际上,JavaScript 遵从一个简单的法则:存在一个监控进程不断检查调用栈是否为空,当调用栈为空的时候,检查事件队列(event queue)中是否有待调用的函数。如果事件队列中存在待调用的函数,队列头部的函数被移入调用栈执行。如果事件队列为空,监控进程就保持轮询状态。 这意味着js中的定时器的精度,实际上是没有保障的,你写一个setTimeout(function(){ do xxxx}, 1000); 并没办法保证它刚好是在1000ms之后调用,因为之前的代码执行可能非常耗时,也可能事件队列中有其他事件排在前面。 这样就出现了题目中的情况。 更多可参考:http://metaphor.space/2016/04/26/javascript-event-loop/; https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop;还有《你不知道的Javascript中卷》141页~143页,事件循环章节 值得一提的是:我们平常说JS是单线程执行的,但浏览器不是,浏览器是多线程的,有的线程负责网络请求,有的负责渲染页面等;不要搞混了 另外,ES6给JS带来了新的特性,比如加入了可以创建多线程的worker,以及更精准控制事件调度的Promise

388. 请问for of和for in的区别

【Question】 for of和for in的区别? for of可以用在普通对象上吗?

【Answer】
考察候选人对for 循环的理解 以及对es6中的for of和iterator理解

for in不多做解释了 for of主要是对实现了 Symbol.iterator 接口进行遍历

自定义for of
```
var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
```

389. 字符串的排列组合计算

【Question】 输入一个字符串,打印出该字符串中字符的所有排列的情况。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba.

function calc(str){
    var strArray = str.split('');
    // 补全代码
  }
  console.log(calc('ab')) // ['a','b']  ['b','a']

【Answer】
```javascript
function calc(str){
    var strArray = str.split('');
    var path = [];
    var docalc = function(array){
      if(array.length===1){
        path.push(array[0]);
        console.log(path);
        path.pop();
        return;
      }
      for(var i=0;i

390. DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Question】 DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Answer】
*  不冒泡的事件有blur、focus、load、unload、abort、error、mouseenter、mouseleave、resize
*  每个 event 都有一个event.bubbles属性,通过该属性可知是否冒泡

391. JavaScript实现对象深拷贝方法

【Question】 编码实现JavaScript实现对象深拷贝

【Answer】
var clone = function(v) {  
  var o = v.constructor === Array ? [] : {};  
  for (var i in v) {  
    o[i] = typeof v[i] === "Object" ? clone(v[i]) : v[i];  
  }  
  return o;  
}  

392. 故障分析-HTTPS证书不被信任

【Question】

如下图,在不同的设备上,同时访问同一个域名,一个设备显示证书不被信任,另一个设备正常,再使用多个其他设备访问,依然正常。分析可能的原因?以及需要获取的进一步的信息?

正常的设备

ssl_success.png<p>异常的设备</p>ssl_error.png<p>
</p>

【Answer】

需要进行的进一步的操作:

1) 查看证书详情:路径/SN/哈希值

2) 查看DNS解析结果

3) 查看系统时间/版本/浏览器版本

可能的原因:

1) 代理工具/安全软硬件

2) DNS劫持/路由劫持

3) 时间偏差

4) 操作系统/浏览器版本差异


393. 请实现一个CodingMan函数实现以下功能

【Question】


实现一个CodingMan,可以按照以下方式调用:
CodingMan(“Hank”)输出:
Hi! This is Hank!

CodingMan(“Hank”).sleep(10).eat(“dinner”)
输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

CodingMan(“Hank”).eat(“dinner”).eat(“supper”)
输出
Hi This is Hank!
Eat dinner~
Eat supper~

CodingMan(“Hank”).sleepFirst(5).eat(“supper”)
输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。


【Answer】


class _CodingMan {
    constructor(name) {
        this.tasks = [];
        const task = () => {
            console.log(`Hi! This is ${name}`);
            this.next();
        }
        this.tasks.push(task);
        setTimeout(() => {               // 把 this.next() 放到调用栈清空之后执行
            this.next();
        }, 0);
    }

    next() {
        const task = this.tasks.shift(); // 取第一个任务执行
        task && task();
    }

    sleep(time) {
        this._sleepWrapper(time, false);
        return this;                     // 链式调用
    }

    sleepFirst(time) {
        this._sleepWrapper(time, true);
        return this;
    }

    _sleepWrapper(time, first) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                this.next();
            }, time * 1000)
        }
        if (first) {
            this.tasks.unshift(task);     // 放到任务队列顶部
        } else {
            this.tasks.push(task);        // 放到任务队列尾部
        }
    }

    eat(name) {
        const task = () => {
            console.log(`Eat ${name}`);
            this.next();
        }
        this.tasks.push(task);
        return this;
    }
}

function CodingMan(name) {
    return new _CodingMan(name);
}



394. 实现如下函数add,使如下执行都等于9

【Question】


add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】

// 较通用的实现

function currying(fn, length) {

 length = length || fn.length;

 return function (...args) {

  return args.length >= length

   ? fn.apply(this, args)

   : currying(fn.bind(this, ...args), length - args.length) 

 }

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 通用实现2

function currying(fn, length) {

return function(...args) {

if (args.length >= length) {

return args.slice(0, length).reduce((t, i) => t += i);

}

return function(..._args) {

return add.apply(null, [...args, ..._args]);

}

}

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 直接的实现

function add(...args) {

if (args.length >= 3) {

return args.slice(0, 3).reduce((t,i) => t += i);

}

return function(..._args) {

return add(args.concat(_args));

}

}


395. 介绍一下你了解的 WebSocket

【Question】 简单介绍一下 WebSocket,ws 协议和 http 协议的关系是什么,WebSocket 如何校验权限? WebSocket 如何实现 SSL 协议的安全连接?

【Answer】
WebSocket 是基于 http 的,所以建立 WebSocket 连接前,
浏览器会通过 http 的方式请求服务器建立连接,
这个时候可以通过 http  的权限校验方式来校验 WebSocket,比如设置 Cookie。
同理,WebSocket 实现 SSL 协议也同 https 类似,会升级为 wss 连接。
另外,当然也可以在 WebSocket 中还可以通过加密或者 token 等方式,实现自己额外的加密传输和权限判断方式。
更多可参考 https://security.tencent.com/index.php/blog/msg/119


396. 请谈谈iframe有哪些缺点?

【Question】 iframe通常有哪些用途,主要缺点是什么

【Answer】
(1)iframe会阻塞主页面的Onload事件;
(2)搜索引擎的检索程序无法解读这种页面,不利于SEO;
(3)iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
(4)页面简的通信问题
使用iframe之前需要考虑这(1)(3)两个缺点。
如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

397. 请简述JAVAScript事件模型和事件代理

【Question】 简述一下JavaScript事件模型和事件代理,事件代理有哪些优点?

【Answer】
## 事件模型
事件三个阶段:事件捕获,目标,事件冒泡(低版本ie不支持捕获阶段)
## 事件代理及优点: 
把事件委托到其父对象上,借助事件冒泡机制,实现对节点的事件代理。  
### 优点  
*  可以大量节省内存占用,减少事件注册
*  当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

398. 根据id从多叉树里面查找出对应的节点的name

【Question】


一个树形的数据(如下数据),面试官给你一个id,然后拿到对应的name?
  var cityData = [
      {
        id: 1,
        name: '广东省',
        children: [
          {
            id: 11,
            name: '深圳',
            children: [
              {
                id: 111,
                name: '宝安',
                children: [
                  {
                    id: 1111,
                    name: '西乡',
                    children:[
                      {
                        id: 11111,
                        name: '坪洲',
                        children:[]
                      },
                      {
                        id: 11112,
                        name: '灵芝',
                        children:[]
                      }
                    ]
                  },
                  {
                    id: 1112,
                    name: '南山',
                    children:[
                      {
                        id: 11121,
                        name: '科技园',
                        children:[]
                      }
                    ]
                  }
                ]
              },
              {
                id: 112,
                name: '福田',
                children: []
              }
            ]
          },
          {
            id: 12,
            name: '广州',
            children: [
              {
                id: 122,
                name: '白云区',
                children: [
                  {
                    id: 1222,
                    name: '白云区',
                    children: []
                  }
                ]
              },
              {
                id: 122,
                name: '珠海区',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 2,
        name: '湖南省',
        children: []
      }
    ];


【Answer】


主要考查深度/广度优先遍历,递归算法
方法1:递归

let result = ''

// 递归实现
const recursion = (cityData, id) => {
  // cityData数据为空的时候直接返回
  if (!cityData || !cityData.length) return;
  // 常规循环cityData
  for (let i = 0, len = cityData.length; i < len; i++) {
    const childs = cityData[i].children;
    
    // 如果匹配到id的话,就是我们要的结果
    if (cityData[i].id === id) {
      result = cityData[i].name
    }
    // 如果还有子节点,执行递归
    if(childs && childs.length > 0){
      recursion(childs, id);
    }
  }
  return result
};

const r = recursion(cityData, 11112);
console.log(r) // 灵芝


方法2:广度优先遍历
let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定义一个数据栈
  let stack = [];

  let item = null;

  //先将第一层节点放入栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 将数据栈的第一个取出来
    item = stack.shift();
    // 如果符合就赋值给result
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

let r1 = range(cityData, 11112);

console.log(r1) // 灵芝


方法3:深度优先遍历
let result = ''

const deep = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 先定义一个数据栈
  let stack = []
  let item = null

  //先将第一层节点放入数据栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i])
  }
  // 循环
  while (stack.length) {
    item = stack.shift()
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈顶
    if (item.children && item.children.length) {
      // 注意这里调换了顺序
      stack = item.children.concat(stack);
    }
  }
  return result
};

let r3 = deep(cityData, 11112)
console.log(r3) // 灵芝


方法4:正则

const regular = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 数据转成字符串
  let cityStr = JSON.stringify(cityData)
  // 定义正则
  let reg = new RegExp(`"id":${id},"name":"([^\\x00-\\xff]+)",`)
  // 取到正则的子字符串并返回
  return (cityStr.match(reg))[1]
}

let r4 = regular(cityData, 11112);

console.log(r4) // 灵芝



399. js浮点运算

【Question】 console.info(0.7+0.1)会得到什么

【Answer】
输出0.799999


400. macro micro 任务队列(async/await版)

【Question】

async function async1() {

 console.log('async1 start');

 await async2();

 console.log('async1 end');

}

async function async2() {

 console.log('async2 start');

 return new Promise((resolve, reject) => {

  resolve();

  console.log('async2 promise');

 })

}

console.log('script start');

setTimeout(function() {

 console.log('setTimeout');

}, 0);  

async1();

new Promise(function(resolve) {

 console.log('promise1');

 resolve();

}).then(function() {

 console.log('promise2');

}).then(function() {

 console.log('promise3');

});

console.log('script end');

【Answer】

chrome 和 node 都是以下顺序



401. JS实现一个带并发限制的异步调度器

【Question】

JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4// 一开始,1、2两个任务进入队列// 500ms时,2完成,输出2,任务3进队// 800ms时,3完成,输出3,任务4进队// 1000ms时,1完成,输出1// 1200ms时,4完成,输出4

【Answer】
class Scheduler {
concurrency = 2
running = 0
queue = []
add(task) {
return new Promise(resolve => {
this.queue.push({
taskGenerator: task,
resolve
})
this.schedule()
})
}
schedule() {
while (this.queue.length > 0 && this.running < this.concurrency) {
const curTask = this.queue.shift()
this.running += 1
curTask.taskGenerator().then(result => {
this.running -= 1
curTask.resolve(result)
this.schedule()
})
}
}
}

402. 写一个加法函数(sum),使他可以同时支持sum(x,y)和sum(x)(y)两种调用方式。

【Question】

写一个按照下面两种方式都能正常调用的 sum 方法
```javascript
console.log(sum(2,3)); // 输出5
console.log(sum(2)(3)); // 输出5
```

【Answer】
答案一
function sum(a,b){
if(b) {
return a+b
}else{
return function(c){
return a+c
}
}
}
答案二
function sum(){
var arg=arguments
if(arg.length==2) {
return arg[0]+arg[1];
}else{
return function(c){
return arg[0]+c
}
}
}

403. ES5,ES6中this指向考察

【Question】

  1. 以下代码输出什么结果,this.name中this指向什么:
    window.name = 'ByteDance';
    function A () {
    this.name = 123;
    }
    A.prototype.getA = function(){
     console.log(this);
     return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    
  2. 如何使funcA()返回undefined?
  3. 下面ES6中又会发生什么,this是什么?
    window.name = 'ByteDance';
    class A {
     constructor() {
      	this.name = 123;
     }
     getA() { 
       console.log(this);
         return this.name + 1; 
     }
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    

【Answer】
1. 输出`Bytedance1`, this指向widnow;
2. 正确使用applay / call;
3. 发生异常:Uncaught TypeError: Cannot read property 'name' of undefined,this为undefined;

404. 请问什么是跨域?跨域请求资源有几哪种方式?

【Question】 何为跨域?跨域请求资源有几哪种方式?

【Answer】
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
跨域请求资源的方式主要有:  
(1)JSONP 动态创建script标签  
但缺点是只支持get请求,并且很难判断请求是否失败(一般通过判断请求是否超时)。  
(2)Proxy代理  
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。  
(3)CORS跨域  
是现代浏览器提供的一种跨域请求资源的方法,需要客户端和服务器端的同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信  
服务响应头返回,Access-Control-Allow-Origin: *

405. 简述React Fiber原理

【Question】

试描述React Fiber的原理。

【Answer】

官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。

之前React的更新过程是同步的,所有更新逻辑会在一帧之内完成,如果组件过于复杂则会导致更新时间超过一帧,其他事务包括用户输入都会被延迟响应,从而引发卡顿。如下图:


破解方式——分片。

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

实现使用的API:requestIdleCallback

Q.为什么引入Fiber架构?原架构有何不足?
A.原架构采用递归遍历方式来更新DOM树,一旦开始,即占用主线程,无法中断,这在页面上会引起问题,如input输入后页面卡顿等

Q.Fiber如何解决该问题
A.时间分片和暂停

Q.Fiber如何实现?
A.使用链表结构,将递归遍历更改为循环遍历,然后配合requestIdleCallback API,实现任务拆分、中断和恢复

Q.Fiber如何实现比较?
A.双缓冲技术,在diff过程中创建新的DOM Tree,diff完成之后生成EffectList,即需要更新的地方,之后进入commit阶段,该阶段不允许中断。

Q.React Hook基于Fiber架构,hook的复用是如何实现的?
A.hook的数据存在于Fiber节点的数据结构中,具体为memoizedState中,该字段中存储了所有hook相关的信息,https://www.jianshu.com/p/d6244228a427 (重要)



406. 请简要描述ES6 module require、exports以及module.exports的区别

【Question】 考察候选人对es6,commonjs等js模块化标准的区别和理解

【Answer】
* CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
* ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
* CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
* export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
* ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性
* 混合使用介绍:https://github.com/ShowJoy-com/showjoy-blog/issues/39

407. 浏览器缓存机制考察

【Question】 浏览器缓存机制考察,包括cache-control , etag, expire, last-modify-time 以及 200 from cache、304

【Answer】
1、cache-control 和 expire 在浏览器端控制  Cache-Control的max-age>expire
2、etag 和 last-modify-time主 要服务器端对比使用

408. 版本号排序

【Question】 versions是一个项目的版本号列表,因多人维护,不规则

var versions=['1.45.0','1.5','6','3.3.3.3.3.3.3']

要求从小到大排序,注意’1.45’比’1.5’大

var sorted=['1.5','1.45.0','3.3.3.3.3.3','6']

【Answer】
```javascript
function sortVersion(arr) {
    return arr.sort((a, b) => {
        const arrA = a.split('.')
        const arrB = b.split('.')
        for (let i = 0; i < arrA.length; i++) {
            if (arrA[i] === undefined) {
                return -1
            } else if (arrB[i] === undefined) {
                return 1
            } else if (parseInt(arrA[i]) === parseInt(arrB[i])) {
                continue
            } else {
                return parseInt(arrA[i]) > parseInt(arrB[i])
            }
        }
    })
}
```

409. JS限流调度器

【Question】

实现JS限流调度器,方法add接收一个返回Promise的函数,同时执行的任务数量不能超过两个。

class Scheduler {
    async add(promiseFunc: () => Promise<void>): Promise<void> {
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)
// log: 2 3 1 4


【Answer】


class Scheduler {
    constructor() {
        this.concurrency = 0
        this.queue = []
    }
    async add(promiseFunc) {
        if (this.concurrency >= 2) {
            return new Promise(r => {
                this.queue.push(() => promiseFunc().then(r))
            })
        }
        this.concurrency += 1
        await promiseFunc()
        this.concurrency -= 1
        let next = this.queue.shift()
        if (next) {
            this.add(next)
        }
    }
}

const scheduler = new Scheduler()
const timeout = (time) => {
    return new Promise(r => setTimeout(r, time))
}
const addTask = (time, order) => {
    scheduler.add(() => timeout(time))
        .then(() => console.log(order))
}

addTask(1000, 1)
addTask(500, 2)
addTask(300, 3)
addTask(400, 4)



410. 实现一个简单的Event类(观察者模式)

【Question】

请实现一个观察者模式,拥有四个方法on,off,once和trigger


const Event = {

on() {} // 绑定

off() {} // 解绑

once() {} // 绑定一次

trigger() {} // 触发事件

};

【Answer】

```javascript function Event() { if (!(this instanceof Event)) { return new Event(); } this._callbacks = {}; } Event.prototype.on = function (type, handler) { this_callbacks = this._callbacks || {}; this._callbacks[type] = this.callbacks[type] || []; this._callbacks[type].push(handler); return this; }; Event.prototype.off = function (type, handler) { var list = this._callbacks[type]; if (list) { for (var i = list.length; i >= 0; --i) { if (list[i] === handler) { list.splice(i, 1); } } } return this; }; Event.prototype.trigger = function (type, data) { var list = this._callbacks[type]; if (list) { for (var i = 0, len = list.length; i < len; ++i) { list[i].call(this, data); } } }; Event.prototype.once = function (type, handler) { var self = this; function wrapper() { handler.apply(self, arguments); self.off(type, wrapper); } this.on(type, wrapper); return this; }; ```


【Question】 请说明 cookie、sessionStorage、localStorage 之间的区别、以及在你项目中的应用?

【Answer】
 a) cookie,HTTP Cookie(也叫Web cookie或者浏览器Cookie)是服务器发送到用户浏览器并保存在浏览器上的一块数据,它会在浏览器下一次发起请求时被携带并发送到服务器上。比较经典的,可以它用来确定两次请求是否来自于同一个浏览器,从而能够确认和保持用户的登录状态。Cookie的使用使得基于无状态的HTTP协议上记录稳定的状态信息成为了可能。
b) sessionStorage,为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
c) localStorage,localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。

区别:
localStorage、sessionStorage 是 Web Storage Api 的组成 API,其为了解决 Cookie 的一些缺陷,服务端 Set 的 cookie 每次会携带在本域下所有的请求上,对性能有损耗。SessionStorage 存储有个期限,当关闭浏览器后就不再存在,但 localStorage 依然存在,需要明确删除。


412. 请简述js浏览器事件循环机制

【Question】


【Answer】

浏览器 Event Loop 是 HTML 中定义的规范,Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

  • JS 调用栈

JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

  • 同步任务、异步任务

JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

  • Event Loop

调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

  • 定时器

定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

  • 宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。


413. 何为https?https和http2有什么关系?

【Question】 简要描述HTTPS的安全机制,以及在web服务工程实践中需要注意的问题;描述http2的基本机制

【Answer】
HTTPS是指建立在安全的传输层(通常是tls/ssl)上的HTTP协议,通过对服务器的证书的认证,解决中间人攻击等问题。
证书(certificate)由客户端信任的的证书机构(CA)颁发,通过common name或SAN对服务进行描述;客户端通过CA的根证书对证书进行校验,并将请求域名和证书的common name/DNS域名进行验证,以检验证书的有效性。
目前,很多web api如Notification/web rpc/Service Worker等,都要求必须使用https。
在工程实践中,https存在以下需要注意的问题:
  - js/css等资源必须以https形式加载,否则浏览器将拒绝执行,所以CDN必须完成对https的支持
	- 非https请求的图片等资源不会携带referer
	
	http2是http协议的一个新版本,既可以明文传输也可以在https中使用。浏览器和服务器通过tls的ALPN/SNI等机制可以进行协议协商,决定使用什么协议

414. 用数组的reduce方法实现map方法

【Question】 用数组的reduce方法实现map方法

【Answer】
```
// 代码实现
Array.prototype.map2 = function(f) {
  return this.reduce(function(result, x, index, arr) {
    result.push(f(x, index));
    return result;
  }, []);
}

// 测试代码
var res = [1, 3, 5, 7].map2(function(item, idx){
  return item * 2;
});
console.log(res);
```

415. js异步操作与计算题

【Question】

for (var i = 0; i < 6; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

>1. console.log(new Date, i);得到的结果是什么? >1. 怎样优化,可以变成: 0 -> 1 -> 2 -> 3-> 4 ->5 >1. 如果继续优化,实现console.log(new Date, i);代码执行时,立即输出 0,之后每隔 1 秒依次输出 1,2,3,4(sleep),之后再暂停5秒,然后输出5, 实现结果类似: >1. 2017-08-31T04:38:23: 0 <— start IIFE >1. 2017-08-31T04:38:24: 1 <— sleep 1s >1. 2017-08-31T04:38:25: 2 <— sleep 1s >1. 2017-08-31T04:38:26: 3 <— sleep 1s >1. 2017-08-31T04:38:27: 4 <— sleep 5s >1. 2017-08-31T04:38:32: 5

【Answer】
1. 属于结果是暂停1S,然后输出6个6,setTimeout属于异步执行
1. 实现0-> 1 -> 2 -> 3-> 4 ->5,用闭包或者var改成let
1. 模拟编程中的sleep实现,参考答案:
```
// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeoutMS) => new Promise((resolve) => {
  setTimeout(resolve, timeoutMS)
});
(async () => {  // 声明即执行的 async 函数表达式
  for (let i = 0; i < 6; i++) {
      if (i < 5) {
        console.log(new Date(), i)
        await sleep(1000)
      } else {
        await sleep(4000)
        console.log(new Date(), i)
      }
    }
})()
```

416. 简单的实现Promise.all

【Question】



function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000);
    })
}
PromiseAll([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

Promise.all([fn1(), fn2()]).then(res => {
    console.log(res)
}).catch(err=>{
    console.log(err)
})


【Answer】


function PromiseAll(list) {

    return new Promise((resolve, reject) => {

        let count = 0;

        let len = list.length;

        let result = [];

        list.forEach((item,index) => {

            item.then(res => {

                count++;

                result[index] = res;

                if (count === len) {

                    resolve(result);

                }

            }).catch(err => {

                reject(err)

            })

        })

    })

}



417. ES6 import的原理

【Question】 请描述ES6 import的原理以及与commonjs的require的区别

【Answer】
CommonJS模块的是一个值的拷贝,而ES6模块输出的是值的引用。
ES6模块加载的机制,与CommonJS模块完全不同。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。
CommonJS模块输出的是被输出值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值,换句话说,ES6的输入有点像Unix系统的“符号连接”,原始值变了,import输入的值也会跟着变。因此,ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

418. 不借助变量交换两个数

【Question】 var a = 1, b = 2; function swap(a,b){ …. } swap(a,b) console.log(a, b) // 2,1

【Answer】
方法一、
```
function swap(a,b){
  b=b-a;
  a=a+b;
  b=a-b;
  return [a,b]
}
```
方法二、
```
function swap(a,b){
  return [a, b] = [b, a]
}
```
方法三、
```
function swap(a,b){
  var a=a^b;
  var b=b^a;
  var a=a^b;
	return [a,b]
}
```

419. 实现垂直居中

【Question】


    <div id="block">        
    </div>

id为block的元素不定高不定宽,请实现它在浏览器窗口的居中显示。

【Answer】
```css
#block {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
```

420. 请回答当我们在使用new操作符时,它在对象操作的过程中具体做了什么

【Question】 考察候选人对原型链操作和js对象的理解

【Answer】
1. 简单回答:
1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
1. 属性和方法被加入到 this 引用的对象中。
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
```javascript
function Animal(name) {
      this.name = name;
}
  Animal.prototype.run = function() {
      console.log(this.name + 'can run...');
}
var cat = new Animal('cat'); //    
new Animal('cat')=function(){
let obj={}; //       
obj.__proto__=Animal.prototype; // obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,'cat');//   this        
}
```



421. css3实现多行文字截断处理

【Question】 用css分别实现单行截断和多行截断字符串,最后以…为结尾

【Answer】
单行:
```
.text-overflow ( @class ){
    .@{class} {
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
    }
}
```
多行:
```
.multi-text-overflow ( @class, @line ){
    .@{class} {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        display: box;
        -webkit-line-clamp: @line;
        -webkit-box-orient: vertical;
    }
}
```

422. 请介绍react diff算法和策略

【Question】 react的diff算法和策略了解多少,为什么react的diff性能好,遵循什么样的策略可以把 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

【Answer】
React分别对 tree diff、component diff 以及 element diff做了算法优化,
做了一些假设
1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
3.对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
tree diff:React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较
component diff:
a.如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
b.如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
c.对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff
element diff:
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,减少增加和删除
详见:https://zhuanlan.zhihu.com/p/20346379

423. 函数科里化

【Question】

实现如下函数add,使如下执行都等于9

add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】


function curry(fn) {
  return function res(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return res.apply(this, args.concat(args2));
      }
    }
  }
}



424. 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?应该怎么解决?

【Question】 如果数据库中采用64位长整型存储一个数据的id,前端通过api拿到这个id的话,会有什么问题?怎么解决?

【Answer】
考察一下JS中整数的安全范围的概念,在头条经常会遇到长整型到前端被截断的问题,需要补一个字符串形式的id供前端使用。
主要会涉及到JS中的最大安全整数问题
https://segmentfault.com/a/1190000002608050

425. JavaScript this 考察

【Question】

下面代码输出的结果是什么?

var length = 10;

function fn() {

 return this.length+1;

}

var obj = {

 length: 5,

 test1: function() {

  return fn();

 }

};

obj.test2=fn;

//下面代码输出是什么

console.log(obj.test1())

console.log(fn()===obj.test2())

【Answer】

11, false(11===6)


426. requestAnimationFrame 和 setTimeout 的区别

【Question】 requestAnimationFrame 和 setTimeout 都可以用来实现动画,它们的区别是什么

【Answer】
1. 执行频率不同,前者按照屏幕刷新频率执行,后者自行控制,可能有无用开销(执行频率小于刷新频率,即1帧执行多次)
2. 前者在页面不可见时,会停止执行(省电),后者在页面不可见时仍会执行,带来不必要开销


427. 编码-js高阶函数考察

【Question】

实现一个repeat方法,要求如下:


// 需要实现的函数

function repeat (func, times, wait) {

// 补全

}


// 使下面调用代码能正常工作

const repeatFunc = repeat(console.log, 4, 3000);

repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒


【Answer】

考点1:能意识到repeat返回的是一个函数,知道参数怎么传递。

考点2:setTimeout的时间,微任务


参考答案

function repeat(fn, times, wait) {

if(typeof times !== 'number') return;

if(typeof wait !== 'number') return;

return function(str){

for(let i = 0; i < times; i++){

setTimeout(()=>{

fn(str)

}, i * wait)

}

}


428. Vue框架中组件消息通信方式

【Question】 考察候选人对Vue框架的消息通信方式了解程度:

  1. vue父子组件通信方式?
  2. 非父子组件通信方式?
  3. 前两问OK,追问:当一个父组件与子组件中间隔着很多层组件怎么办?

【Answer】
1. 父子组件通信方式
在Vue中,父子组件的关系可以总结为props down, events up。父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息。

2. 非父子组件通信
两个独立的组件之间通信,可以借助一个空的Vue实例作为中央事件总线,空实例相当于代理人的形式进行消息监听或触发

3. 父子之间层级过多时
当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。
这块如果了解开源的Element组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以`[组件名, 消息名, 参数]`三元组进行消息传递,降低长链传播成本;
具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

429. 什么是 XSS,怎么造成的,有什么防御方法?

【Question】 考察面试者对于 XSS 是否了解,是否足够重视。

【Answer】
XSS 就是在 web 中能够通过某种方式产生执行任意 JavaScript 脚本的情况,
最常见的一种情况就是将用户的输入,直接放到当前 runtime 中,比如用户输入直接放到页面的 html 里面,
立刻显示出来。
XSS 实际上是非常危险的,因为理论上讲,如果能够执行 JavaScript,实际上攻击者可以做任何事情。
简单的就是输出点什么,偷偷 cookie,或者结合 CSRF 攻击,或者让浏览器跳转一下,
复杂点的甚至可以改掉当前整个页面,伪造一切用户看到东西,危害无穷。
如果这种输入存储到数据库中,就会变成一个永久型的 XSS,危害就更大了。
防止 XSS 最简单的就是使用各种框架,如 React、Vuejs 等,对用户输入进行 html 转义。
另外,服务端要设置 httpOnly 的 header,防止 JavaScript 操作 cookie。
当然,服务端也可以对输入进行转义或者过滤监测。

430. webpack插件编写

【Question】

  1. 有用过webpack么?说说该工具的优缺点?
  2. 有开发过webpack插件么?
  3. 假如要在构建过程中去除掉html中的一些字符,如何编写这个插件?

【Answer】
webpack优缺点:
* 概念牛,但文档差,使用起来费劲
* 模块化,让我们可以把复杂的程序细化为小的文件
* require机制强大,一切文件介资源
* 代码分隔
* 丰富的插件,解决less、sass编译

开发插件的两个关键点Compiler和Compilation:
* compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。
* compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

插件编写可参考:https://doc.webpack-china.org/development/how-to-write-a-plugin

431. 如何实现微信扫码登录?

【Question】 综合题,考察网络、前端、认证等多方面知识

【Answer】
参考答案:
https://zhuanlan.zhihu.com/p/22032787
具体步骤:
1. 用户 A 访问微信网页版,微信服务器为这个会话生成一个全局唯一的 ID,上面的 URL 中 obsbQ-Dzag== 就是这个 ID,此时系统并不知道访问者是谁。
2. 用户A打开自己的手机微信并扫描这个二维码,并提示用户是否确认登录。
3. 手机上的微信是登录状态,用户点击确认登录后,手机上的微信客户端将微信账号和这个扫描得到的 ID 一起提交到服务器
4. 服务器将这个 ID 和用户 A 的微信号绑定在一起,并通知网页版微信,这个 ID 对应的微信号为用户 A,网页版微信加载用户 A 的微信信息,至此,扫码登录全部流程完成

432. 设计类似 Vue.js 双向绑定功能的核心逻辑“监听对象属性变化”功能

【Question】 实现一个类,可以监听对象属性的值变化。加分项:考虑对象存在值为数组或对象的属性。

	class Observe {
		constructor(data: Object) {
		}
		// 监听属性变更
		$on() {
		}
		// 触发属性变更事件
		$emit() {
		}
	}
	const data = new Observer({
		a: 1
	});
	coonsole.log(data.a) // console: 1
	data.$on('a', (newValue, oldValue) =&gt; {
		// this === data
		console.log(newValue, oldValue);
	});
	data.a = 2 // console: 2 1

【Answer】
待补充

433. 请简要描述

【Question】

【Answer】
### 作用:
defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
### 区别:
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

434. 原型链、this指针、自有属性考察

【Question】

var a= function () { this.b =3; }
var c = new a();
a.protorype.b = 9;
var b = 7;
a();

问:

console.log(b);
console.log(c.b); 

分别输出什么?

【Answer】
- 第一个 `b = 3`
- 第二个 `c.b = 3`

【Question】

如题

【Answer】
cookie 在浏览器中存在,并且每个请求都会带上,而且是可以设置失效时间的,失效前就会有效。 session 是一次会话有效,也就是说,浏览器关闭 session 就会失效。 其实这道题目考察的一个重点是在于,session 这种机制是怎么在浏览器中实现的呢? 实际上 session 的实现也是在浏览器中放了一个 cookie,失效时间是一次会话失效。

436. JS异步队列macrotask和microtask

【Question】

console.log('begin')
setTimeout(() =&gt; {
	console.log('setTimeout 1')
	Promise.resolve().then(() =&gt; {
		console.log('promise 1')
		setTimeout(() =&gt; {
			console.log('setTimeout2 between promise1&amp;2')
		})
	}).then(() =&gt; {
		console.log('promise 2')
	})
}, 0)
console.log('end')

【Answer】
```
begin
end
setTimeout 1
promise 1
promise 2
setTimeout2 between promise1&2
```

437. 如何理解虚拟DOM?

【Question】 如何理解虚拟DOM?

【Answer】
对虚拟dom和diff算法中的一些细节理解与考察,[https://github.com/livoras/blog/issues/13](https://github.com/livoras/blog/issues/13)

438. 如何判断一个 JS 对象为空对象

【Question】 如何判断一个 JS 对象为空对象 ?空对象形如{}

【Answer】
1. 使用 `for in`
	```javascript
	function isEmptyObject(obj){
  	for(var key in obj){
    	return false
		};
		return true
	};
	```
2. 通过 JSON.stringify 方法来判断
	```javascript
	if(JSON.stringify({}) === '{}'){
		console.log('empty obj');
	}
	```
3. 使用 ES6 增加的 Object.keys()
	```javascript
	if(Object.keys(obj).length === 0){
		console.log('empty obj');
	}
	```

439. 什么是闭包?实现每隔1秒输出数组中的一个数字

【Question】 解释下js中的闭包概念,解释OK,给出编程题目考察基本功

【Answer】
```js
function fun(arr) {
    var i, len;
    for (i = 0, len = arr.length; i < len; i++) {
      (function(i){
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
}
```

440. promise运行过程解答

【Question】 如下代码的运行结果是什么?

 process.nextTick(() =&gt; {console.log('nextTick')})
Promise.resolve().then(()=&gt; {console.log('promise1');}).then(()=&gt; {
  console.log('promise2');
});
setImmediate(() =&gt; {console.log('setImmediate')})
console.log('end') 

【Answer】
1. end -> nextTick -> promise1 -> promise2-> setImmediate
1. process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。
1. 事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

441. 请简述常见web安全及防护原理

【Question】 常见web安全及防护原理,请举例说明。

【Answer】
1、SQL注入原理  
		就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点  
1. 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2. 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。  
2、XSS原理及防范  
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者JavaScript代码。
看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法  
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

3、CSRF原理及防范  
CSRF的防御
服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
通过验证码的方法

442. 数字格式化问题:1234567890 –> 1,234,567,890

【Question】 数字格式化问题,将1234567890 –> 1,234,567,890

【Answer】
非正则实现
```javascript
let test = '1234567890'
function formatCash(str) {
  let arr = []
  for (let i = 1; i < str.length; i++) {
    if (str.length % 3 && i == 1)
      arr.push(str.substr(0, str.length % 3))
    if (i % 3 === 0)
      arr.push(str.substr(i - 2, 3))
  }
  return arr.join(',')
}
console.log(formatCash(test)) // 1,234,567,890
```
正则实现
```javascript
let test1 = '1234567890'
let format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
console.log(format) // 1,234,567,890
```

443. 模拟实现loadash中的_.get()函数,实现如下传入参数取值效果

【Question】

function get() {
  // 请补全函数参数和实现逻辑
}
const obj = { selector: { to: { toutiao: 'FE coder' } }, target: [1, 2, { name: 'byted' }] };
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')

//  输出结果:
// ['FE coder', 1, 'byted']

【Answer】
```javascript
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
```
1. Use Array.map() for each selector
2. String.replace() to replace square brackets with dots
3. String.split('.') to split each selector
4. Array.filter() to remove empty values
5. Array.reduce() to get the value indicated by it

444. 合并两个有序数组

【Question】 合并两个有序数组

【Answer】
```
function mergeSortedArray(a, b){
  var merged = [], 
      aElm = a[0],
      bElm = b[0],
      i = 1,
      j = 1;
  if(a.length ==0)
    return b;
  if(b.length ==0)
    return a;
  while(aElm || bElm){
   if((aElm && !bElm) || aElm < bElm){
     merged.push(aElm);
     aElm = a[i++];
   }   
   else {
     merged.push(bElm);
     bElm = b[j++];
   }
  }
  return merged;
}
```
验证
```
mergeSortedArray([2,5,6,9], [1,2,3,29]);
结果 [1, 2, 2, 3, 5, 6, 9, 29]
```

445. 进行CSRF漏洞扫描的原理和防御方式是什么?

【Question】 如题

【Answer】
CSRF 就是在用户不知情的情况下,发出了请求,让用户做了不该做的操作。
举个例子,比如你的一个网站中有个 img 标签,src 指向的是微博关注某人的接口,
那么当用户访问你的网站时,就会在微博上关注那个人,而且这个操作用户是不知情的。
因为 img src 发出的跨域请求,也是会携带 cookie 的,所以如果用户在微博登录过,
那么就会带有微博的登录授权。同理,如果是其他操作,可能也存在这种漏洞,比较危险的情况就是付款。
一般会采用 CSRF token 的方式防御,就是关键请求得要换取一个一次有效的 token 才有权限。


446. 判断一个字符串是否是回文字符串

【Question】 判断一个字符串是否是回文字符串,回文字符串是对称字符串的形式,例如:did,eve, dad, level

【Answer】
```
function isPalindrome(str){
  var i, len = str.length;
  for(i=0; i isPalindrome('madam')
  = true
> isPalindrome('toyota')
  = false
```

447. box-sizing 实践

【Question】


<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 10px;
        height: 10px;
        border: 1px solid red;
        margin: 2px;
        padding: 2px;
        background: blue;
      }

      #borderBox {
        box-sizing: border-box;
      }

      #contentBox {
        box-sizing: content-box;
      }
    </style>
  </head>
  <body>
    <div>请问下面两个 div 元素,蓝色区域的宽高各是多少像素?</div>
    <div id="borderBox" class="box"></div>
    <div id="contentBox" class="box"></div>
  </body>
</html>


【Answer】

borderBox:10px(width) - 1px(border) * 2 = 8px

contentBox 10px(width) + 2px(padding) *2 = 14px


答题要点:除了验证候选人是否真正了解 box-sizing 之外,也考察候选人是否了解 background 会影响元素的 padding 区域,而不影响 margin 区域这个特点


448. 链式调用+延迟计算

【Question】

写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....)


console.log(sum(1,2,3)(4)) => 输出 10



考察链式调用,闭包,延迟计算,函数toStirng/valueOf




【Answer】


function sum(...args) {
  function next(...innerArgs) {
    args.push(...innerArgs);
    return next;
  }
  next.valueOf = next.toString = () => {
    return args.reduce((r, c) => r + c, 0);
  };

  return next;
}



449. 请描述micro task 与 macro task的区别及应用

【Question】


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');


【Answer】

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout


450. 数组flat函数设计

【Question】 设计一个flat函数将如下数组arr=[1,2,[‘3’,4,’5’,[6,[7,8],9]]]输出为1,2,’3’,4,’5’,6,7,8,9。至少写出两种方法,要求不能改变数组中的原始数据类型

【Answer】
*  方法一:递归
```javascript
function flat(array) {
    var result = [];
    var each = function(arr) {
      arr.forEach(item => {
        if (item instanceof Array) {
          each(item);
        } else {
          result.push(item);
        }
      });
    };
    each(array);
    return result;
  }
var arr=[1,2,['3',4,'5',[6,[7,8],9]]];flat(arr).forEach(item=>{console.log(item)})

```
*  方法二:toString(格式转换),无法保证类型
```javascript
Array.prototype.toString = function() {
  return this.join(',');
};
console.log([1,2,[3,4,[5,6,7]]]+'');
```
*  方法三:Iterator
```javascript
Array.prototype[Symbol.iterator] = function() {
  let arr = [].concat(this),
    index = 0;
  let getFirst=function(array){
    let first=array[0];
    if(first instanceof Array){
      return getFirst(array[0])
    }else if(first!==undefined){
      return array.shift()
    }else{
      return ''
    }
  }
  return {
    next: function() {
      let item=getFirst(arr);
      if(item){
        return {
          value:item,
          done:false
        }
      }else{
        return {
          done:true
        }
      }
    }
  }
}
var t=[1,2,['3',4,'5',[6,[7,8],9]]];
for(let i of t){console.log(i)}
```

【Question】 基础题考察 cookie 和 localStorage 的理解。

【Answer】
存储在 Cookie 中每个 request 都会带上,而放在 localStorage 中,仅有浏览器中会存储。

452. 请说说HTML的Meta标签的用途,并列举一些常用的meta标签

【Question】

【Answer】
考察对网页结构和语义的理解 

```
The HTML  element represents metadata that cannot be represented by other HTML meta-related elements, like , , 

453. 说说前端优化?图片懒加载原理是什么?

【Question】

  • 考察前端的一些优化方式
  • 图片懒加载原理

【Answer】
1. 优化手段:雅虎的34条优化手段,比如:代码压缩、减少请求、cdn、缓存
2. 图片懒加载原理:img标签设置占位属性(data-src),存储真正的图片地址;原src设置占位图片地址;当图片(快)进入用户可视区域的时候进行地址替换;

454. 请谈谈你对ES6的箭头函数的理解

【Question】

var func1 = x =&gt; x;
var func2 = x =&gt; {x}; 
var func3 = x =&gt; ({x});
console.log(func1(1));
console.log(func2(1));
console.log(func3(1));

请写出程序运行结果。

【Answer】
程序运行结果为:
第一个:1
第二个:undefined
第三个:{x: 1}

455. 无重复字符的最长子串

【Question】

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

样例:


  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。



【Answer】


var lengthOfLongestSubstring = function(s: string) {
    let list = s.split("");
    let son = [];
    let max = [];
    for (let i = 0; i < list.length; i++) {
        let current = list[i];
        let index = son.indexOf(current);
        if (index === -1) {
            son.push(current);
        } else {
            let sameIndex = i - son.length + index;
            if (son.length > max.length) {
                max = [...son];
            }
            son = son.slice(sameIndex + 1, son.length);
            son.push(current);
        }
    }
    return max.length;
};



456. 列举一个近期做的最能体现设计能力的项目

【Question】 请举出一个你近期做的项目,项目需要最能体现设计能力, 请从以下角度说明:

  1. 项目描述
  2. 技术选型
  3. 模块化
  4. 模块之间通信
  5. 工程化
  6. 前后端数据流

【Answer】
这是一个开放式的工程设计题目,没有固定答案,评分参考评分标准

457. 实现一个 JSONP

【Question】 函数签名如下:

function jsonp(url, callback) {
  // TODO
}

【Answer】
主要考察如何处理第二个参数 `callback` 的问题,
加分项比如超时处理 onerror 的处理, xss 考虑等等

```
const kCallBackMap = {};
function uuid() {
  return ...;
}

function jsonp(url, callback) {
  const callbackId = uuid();
  url += 'callback=' + callbackId;
	window[calbackId] = callback;
	
	const script = document.createElement('script');
	script.src = url;
	document.head.appendChild(script);
}
```

458. 请谈一谈JAVAscript的作用域和this

【Question】

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();
obj1.say();
obj2.say();
obj1.say = say;
obj1.say();
obj1.say = obj2.say;
obj1.say();

【Answer】
```
window
window

1-1
1-2

2-1
2-2

window
1-2

2-1
1-2

主要考察javascript的作用域和this指向。作用域是静态的,声明时确定;this是动态的,运行时确定。
```

459. 请问CSS position有哪些定位方式

【Question】 CSS position有哪些定位方式,每种方式是如何定位的?

【Answer】
### position取值
relative, fixed,absolute和staic、sticky 5种
### 定位方式
*  staic-默认位置;元素会像通常那样流入页面。顶部,底部,左,右,z-index属性不适用。  
*  relative-元素的位置相对于自身进行调整,而不改变布局(从而为未被定位的元素留下一个空白)。  
*  absolute-该元素从页面的流中移除,并相对于其最近位置的祖先定位(非static)在指定位置,如果有的话,或者与初始包含块相对。绝对定位的框可以有边距,并且不会与其他边距折叠。这些元素不影响其他元素的位置。  
*  fixed元素是定位在相对于窗口。  
*  sticky,是相对定位和固定定位的混合。该元素被视为相对位置,直到它越过指定的阈值,此时它被视为固定位置。  


460. 请介绍一下Oauth2.0 的认证过程

【Question】 如题

【Answer】
可以参考 http://www.jianshu.com/p/0db71eb445c8 或者 
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 的答案,
回答的一个重点是 code(授权码)仅一次有效,并且要有失效时间,而且很短,比如一分钟,
因为浏览器收到会立刻跳转。
还有就是服务端可以根据 code 结合相应的 sercet 去获取 token,要说清楚。

461. express中间件的原理

【Question】

express中间件的实现原理 并给出实现

【Answer】
主要考察候选人对中间件的理解 参考代码 ``` export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } ``` koa中间件主要使用 generator和promise可参考https://github.com/tj/co

462. 实现es6字符串模板方法sprintf

【Question】


const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
	name: 'Yiming Zhang',
	city: 'FuJian',
});
console.log(result); // My name is Yiming Zhang,I'm from FuJian


【Answer】


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);



463. 登录表单设计/扫码登录/第三方登录

【Question】

  1. 请实现一个登录表单
  2. 用GET方法行不行?csrf是什么?如何防御?
  3. cookie-sesssion的工作机制
  4. 你已经登录产品的App端,要在web实现扫码登录,该如何设计?
  5. 接入第三方登录(如微信),如何设计?

【Answer】
1. 正确书写html
2. 正确回答GET和POST的区别,从语义、弊端、安全等方面。csrf的防御:token,samesite,referer校验(弊端)等
3. 正确理解cookie-session的工作机制,sessionId的设计,存储
4. 考察对司空见惯的扫码登录,是否有思考其实现。正确设计 Client/Server/App 三方流程,设计二维码存储的内容,client通知有轮训或websocket等解决方案
5. 正确理解 Client/Server/App/Weixin Server 四方流程,理解oauth2协议

464. 作用域以及变量提升

【Question】

请写出下题的结果:

var a = 1; 
function b() { 
    a = 10; 
    return; 
    function a() {} 
} 
b(); 
console.log(a);   

【Answer】
结果:1

465. setTimeout 和 Promise

【Question】

请写出程序的输出内容

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function(resolve) {
  console.log(2);
  for(var i=0 ; i < 10000 ; i++) {
    if (i == 9999) {
      resolve();
    }
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);


【Answer】

正确答案:2 3 5 4 1。重点关注:候选人是否把 2 写在第一位,以及 4 和 1 的顺序。


466. requestIdleCallback和requestAnimationFrame有什么区别?

【Question】

requestIdleCallback和requestAnimationFrame有什么区别?

【Answer】

requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。

我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了。

一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。

假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调。

由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了。


467. 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Question】 请为所有数组对象添加一个findDuplicate(n)方法,用于返回该数组中出现频率>=n的元素列表

【Answer】
`
Array.prototype.findDuplicate = function (n) {
    var results = [];
    if (typeof n != 'number' || isNaN(n)) {
        return results;
    }
    
    var itemFreqs = {};
    this.forEach(function (item) {
        if (!itemFreqs[item]) {
            itemFreqs[item] = 0;
        }
        itemFreqs[item] ++;
    });
    
    for (var item in itemFreqs) {
        if (itemFreqs[item] >= n) {
            results.push(item);
        }
    }
    
    return results;
}

`

468. 请回答DOM中对应创建、移除、追加、复制、查找节点的方法是什么?

【Question】 考察候选人对原生dom操作的方法的理解和掌握熟练程度

【Answer】
1.  创建新节点
	*  createDocumentFragment() //创建一个DOM片段
	*  createElement() //创建一个具体的元素
	*  createTextNode() //创建一个文本节点

1.  克隆节点
*  cloneNode()

1. 添加节点
*  appendChild()
*  insertBefore()

1. 移除节点
*  removeChild()

1. 替换节点
*  replaceChild()

1. 查找节点
*  querySelector()
*  querySelectorAll()
*  getElementById()
*  getElementsByName()
*  getElementsByTagName()



469. 请描述如何用原生JS实现数字的货币格式化

【Question】

# 如何用原生JS实现数字的货币格式化,例如数字6123456789格式化后为6,123,456,789,不低于两种方法。

【Answer】

方法一: (6123456789).toLocaleString('en-US') // 6,123,456,789


方法二: (6123456789).toString().split('').reverse().join('').replace(/\d{3}/g,function($1){return $1+','}).split('').reverse().join('')



470. let,const,var的区别

【Question】 请说明一下let,const,var的区别 并回答如下代码会不会报错

const a = {};
a.test = 1;

【Answer】
考察候选人对es6变量声明的理解
1. let声明的变量拥有块级作用域
2. let声明的全局变量不是全局对象的属性
3. let不能重新声明变量
4. const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

上面代码只是针对a的引用 并不会报错

471. 如何实现链式调用

【Question】 请实现函数 a, b, c,使调用方式为 a().b().c() 时,结果为输出 a b c。 如果上面问题回答出来了,并且是在 a 函数内部 return Object 实现, 那么可以补充问下如何能够实现让三个函数任意链式顺序调用。 如 a().c().b() 或 b().a().c() 。

【Answer】
这道题主要就是考察面试者对 JavaScript 的 Object 概念理解是否清晰,
最好的答案是直接将 a b c 三个函数挂载到 runtime 中的某个全局变量中,比如可以是 window。
然后在每个函数内 return window 就可以了。
当然,也可以按照第一道题目的顺序,分别在相应函数内 return 下个函数,但是这样做无法调换顺序。

472. 实现千位分隔符

【Question】 给一个数字,比如:1234567.90,转化成:1,234,567.90

【Answer】
```js
function commafy(num) {
  return num && num
      .toString()
      .replace(/^\d+/, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','));
}
console.log(commafy(1234567.90)); //1,234,567.90
```

473. 编写javascript深度克隆函数deepClone

【Question】 编写javascript深度克隆函数deepClone

【Answer】
```javascript
function deepClone(obj) {
    var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }

        return new RegExp(obj.source, flags.join(''));
    }

    var result = Array.isArray(obj) ? [] :
        obj.constructor ? new obj.constructor() : {};

    for (var key in obj ) {
        result[key] = deepClone(obj[key]);
    }

    return result;
}

function A() {
    this.a = a;
}

var a = {
    name: 'qiu',
    birth: new Date(),
    pattern: /qiu/gim,
    container: document.body,
    hobbys: ['book', new Date(), /aaa/gim, 111]
};

var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
```

474. 请谈谈你对JS单线程以及setTimeout的理解

【Question】

setTimeout(function() {
	setTimeout(function() { console.log(1) }, 100)
	console.log(2)
	setTimeout(function() { console.log(3) }, 0)
}, 0)
setTimeout(function () {
	console.log(4)
}, 100)
console.log(5)

请说出上面代码的输出顺序以及原因?如果吧4改为101ms呢?

【Answer】
正确顺序为:5 2 3 4 1
如果4改为101ms则执行顺序还是不变
原因:
1.  JS单线程
2. setTimeout不在当前eventloop。且执行顺序依赖入队顺序。setTimeout 0是放入下一个loop的队尾
3. 虽然4和1都是100ms延迟的标记,但是4先入队列。
4. setTimeout的time是个标记,会在eventloop循环去检测,符合条件的执行,不符合条件的延后到下一个eventloop,这执行过程本身又有时间,因此尽管101>100,但是在一个执行周期内,他们都会被触发,4先入队所以不变

475. async & forEach 考察

【Question】 以下代码的运行结果

const list = [1, 2, 3];
const square = num =&gt; {
    return new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
            resolve(num * num);
        }, 1000);
    });
}
function test() {
    list.forEach(async x =&gt; {
        const res = await square(x);
        console.log(res);
    });
}
test()

如果希望每隔1s输出一个结果,应该如何改造?

【Answer】
1s 后输出 1 4 9  
改为 for 循环:
```javascript
async function test() {
    for (let x of list) {
        const res = await square(x);
        console.log(res)
    }
}
```


476. css单位的百分比

【Question】 给一个div设置它父级div的宽度是100px,然后再设置它的padding-top为20%。
问现在的div有多高?如果父级元素定位是absolute呢?

【Answer】
现有div的高度等于自身高度+父级块的宽度*20%,如果父级元素定位是absolute,结果不变;
当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!

477. NodeJS实现简单的HTTP代理和隧道代理

【Question】 Web代理一般包括普通的HTTP代理和隧道代理,谈谈理解。 NodeJS实现一个简单的HTTP代理,如在本地 8888 端口开启 HTTP 代理服务,修改浏览器的 HTTP 代理为 127.0.0.1:8888 后再访问 HTTP 网站,代理可以正常工作 对隧道代理了解多少,能否实现?

【Answer】
http普通代理:HTTP 客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive),同时向服务器发送请求,并将收到的响应转发给客户端。
```
// http 普通代理
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(8888, '0.0.0.0');
```
隧道代理:HTTP 客户端通过 CONNECT 方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP 连接,并对客户端和服务器之间的后继数据进行盲转发
```
const http = require('http');
const net = require('net');
const url = require('url');

function connect(cReq, cSock) {
  const u = url.parse('http://' + cReq.url);

  const pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer().on('connect', connect).listen(8888, '0.0.0.0');
```
合二为一
```
const http = require('http');
const net = require('net');
const url = require('url');

function request(cReq, cRes) {
  const u = url.parse(cReq.url);

  const options = {
    hostname: u.hostname,
    port: u.port || 80,
    path: u.path,
    method: cReq.method,
    headers: cReq.headers
  };

  const pReq = http.request(options, pRes => {
    cRes.writeHead(pRes.statusCode, pRes.headers);
    pRes.pipe(cRes);
  }).on('error', function(e) {
    cRes.end();
  });

  cReq.pipe(pReq);
}

function connect(cReq, cSock) {
  var u = url.parse('http://' + cReq.url);

  var pSock = net.connect(u.port, u.hostname, function() {
    cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    pSock.pipe(cSock);
  }).on('error', function(e) {
    cSock.end();
  });

  cSock.pipe(pSock);
}

http.createServer()
  .on('request', request)
  .on('connect', connect)
  .listen(8888, '0.0.0.0');
```
需要注意的是,大部分浏览器配完隧道代理,默认只会让https走隧道代理,http如果需要走隧道代理,还需要写个Nodejs的验证
```
const options = {
  hostname: '127.0.0.1',
  port: 8888,
  path: 'toutiao.com:80',
  method: 'CONNECT'
};

const req = http.request(options);

req.on('connect', function(res, socket) {
  socket.write('GET / HTTP/1.1\r\n' +
    'Host: toutiao.com\r\n' +
    'Connection: Close\r\n' +
    '\r\n');

  socket.on('data', function(chunk) {
    console.log(chunk.toString());
  });

  socket.on('end', function() {
    console.log('socket end.');
  });
});

req.end();
```

478. 假设一个网页嵌入一个iframe,如何更改iframe内dom样式?

【Question】 假设一个网页嵌入一个iframe,如何更改这个iframe内dom样式

【Answer】
区分同源和不同源解决方案,同源可以通过document.getElementById('iframeId').contentWindow.document,
不同源:分iframe的嵌入的页面是否自己可控,可控可以通过postMessage方式更改,iframe页面监听message事件;如果页面不可控,应该无解。
可以追问iframe有同源策略限制,举个例子说明

479. 数组随机排序

【Question】

var arr=[1,2,3,4,5,6]

【Answer】
方法一、
```javascript
arr.map(item=>{
    return {
        value:item,
        key:Math.random()
    }
})
.sort((a,b)=>a.key-b.key)
.map(item=>item.value)
```
方法二、
```
var arrayToRand = (arr) => {
    for(let i=0; i

480. js事件模型

【Question】 浏览器的事件模型?在当前的事件模型中,哪些事件可以冒泡,哪些不会冒泡,为什么?不冒泡的元素,如何来实现事件代理?

【Answer】
考察浏览器事件模型,看看是不是了解事件模型背后的设计意图。

浏览器开发团队遇到的问题:页面上哪一部分会拥有某个特定的事件?比如单击一个嵌套的同心div,那么到底哪一个div会拥有这个点击事件?实际上难以确定点击者的意图,团队给出的解决方式是所有div都将拥有这个事件,于是产生了事件流模型。如上一个问题所述,“事件”的概念在GUI编程中如此之重要,而这种流式模型能给予其很大的灵活性和控制
对于能精确确定意图的(这种冒泡的话一般也会带来问题,比如mouseleave),或者不可能产生嵌套的媒体类元素,冒泡就不是必须的;对于不冒泡的元素,可以在捕获阶段代理,DOM2级规范addEventListener的第三个参数

481. 请列举说明几个在web中实现长连接的技术方案或手段

【Question】 本地主要考察候选人对长连接技术的概念理解和区分,如果能回答答出大致的名词可以继续追问一些具体的激技术实现细节和存在的优缺点等等。

【Answer】
参考答案:
1. https://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet/12855533#12855533
1. https://blog.csdn.net/liang0000zai/article/details/40537059

* Long Polling
* Server-Sent Events
* Websockets
* Comet

482. 函数作用域

【Question】 用代码实现JavaScript中Function的bind方法的polyfill

【Answer】
```
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP
                                 ? this
                                 : oThis || this,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```

483. CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Question】 CSS 属性 box-sizing的值有哪些?分别有什么含义?

【Answer】
  - `content-box` 默认值,width内容宽度
	- `border-box` width 包含`padding`和`border`

484. JS的new操作符具体做了什么

【Question】 JS的new操作符具体做了什么,描述一下,最好可以体现在代码上

【Answer】
```
function A() {
  this.name = 'a';
  this.getName = function() {
    return this.name;
  }
}
var a = new A();

var aa = new Object();
aa.__proto__ = A.prototype;
A.call(aa);
// 还有最后一步,如果发现A返回的是一个Object类(非primitive类型),则直接返回A的返回值,否则把aa返回出去
```

485. JS编码二叉树的实现与遍历

【Question】 JS编码实现一个二叉树的构造函数,包括节点类Node,树类BST,插入节点函数insert, 并且满足 1.左子节点的值 < 父节点的值 <= 右子节点的值 2.可以实现先序,中序,后续遍历

【Answer】
```
// 二叉树
function BST() {
  this.root = null;
}

BST.prototype.insert = function(data) {
  var n = new Node(data, null, null);
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    for (;;) {
      if (data < current.data) {
        if (current.left === null) {
          current.left = n;
          break;
        } else {
          current = current.left;
        }
      } else {
        if (current.right === null) {
          current.right = n;
          break;
        } else {
          current = current.right;
        }
      }
    }
  }
}

// 先序遍历
BST.prototype.preOrder = function(node) {
  if (node !== null) {
    console.log(node.show() + " ");
    this.preOrder(node.left);
    this.preOrder(node.right);
  }
}

// 中序遍历
BST.prototype.inOrder = function(node) {
  if (node !== null) {
    this.inOrder(node.left);
    console.log(node.show() + " ");
    this.inOrder(node.right);
  }
}

// 后序遍历
BST.prototype.postOrder = function(node) {
  if (node !== null) {
    this.postOrder(node.left);
    this.postOrder(node.right);
    console.log(node.show() + " ");
  }
}

// 节点对象
function Node(data, left, right) {
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = function() {
    return this.data;
  }
}

// 测试代码
var bst = new BST();
var nums = [10, 3, 18, 2, 4, 13, 21, 9, 8, 9];
for (var i = 0; i < nums.length; i++) {
  bst.insert(nums[i]);
}
bst.preOrder(bst.root);
bst.inOrder(bst.root);
bst.postOrder(bst.root);
```

486. 简述一下src与href的区别

【Question】 描述一下html中的src与href的区别和使用场景是什么

【Answer】
基本答案:src用于指向外部资源的位置替换当前元素,href用于在当前文档和引用资源之间确立联系。
1.  src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;
在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。
这也是为什么将js脚本放在底部而不是头部。
 
1.  href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。
这也是为什么建议使用link方式来加载css,而不是使用@import方式。

487. js运行机制

【Question】 下面一段代码的输出:

(function() {
  console.log('this is the start');
  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });
  console.log('this is just a message');
  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);
  console.log('this is the end');
})();

【Answer】
因为前端编程基本属于「Event-driven programming」范式,这是GUI之类的交互式程序的基础,区别于传统的批处理式编程。一个页面上的交互行为,基本都是由用户发起的,然而用户的行为意图是难以预测的,所以需要异步的驱动机制来应对
因此有进一步问题:
平时都说JS是单线程执行的,那它是如何实现非阻塞式执行页面JS的?
考察对EventLoop概念的理解,核心是会在调用栈之外建立一个Event Table。可以将Event Table想象成一个电话注册本:调用栈会告诉event table注册一些特定的函数,并且在指定事件发生时会调用他们。当这些指定事件发生时,event table仅仅是简单地把要调用的函数移入Event Queue中去。event queue提供了一个简单等待区域,函数在此区域内等待被移入调用栈进行调用。 『究竟什么情况下,event queue中的函数才会被移入调用栈中?』。实际上,JavaScript 遵从一个简单的法则:存在一个监控进程不断检查调用栈是否为空,当调用栈为空的时候,检查事件队列(event queue)中是否有待调用的函数。如果事件队列中存在待调用的函数,队列头部的函数被移入调用栈执行。如果事件队列为空,监控进程就保持轮询状态。 这意味着js中的定时器的精度,实际上是没有保障的,你写一个setTimeout(function(){ do xxxx}, 1000); 并没办法保证它刚好是在1000ms之后调用,因为之前的代码执行可能非常耗时,也可能事件队列中有其他事件排在前面。 这样就出现了题目中的情况。 更多可参考:http://metaphor.space/2016/04/26/javascript-event-loop/; https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop;还有《你不知道的Javascript中卷》141页~143页,事件循环章节 值得一提的是:我们平常说JS是单线程执行的,但浏览器不是,浏览器是多线程的,有的线程负责网络请求,有的负责渲染页面等;不要搞混了 另外,ES6给JS带来了新的特性,比如加入了可以创建多线程的worker,以及更精准控制事件调度的Promise

488. 请问for of和for in的区别

【Question】 for of和for in的区别? for of可以用在普通对象上吗?

【Answer】
考察候选人对for 循环的理解 以及对es6中的for of和iterator理解

for in不多做解释了 for of主要是对实现了 Symbol.iterator 接口进行遍历

自定义for of
```
var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
```

489. 字符串的排列组合计算

【Question】 输入一个字符串,打印出该字符串中字符的所有排列的情况。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba.

function calc(str){
    var strArray = str.split('');
    // 补全代码
  }
  console.log(calc('ab')) // ['a','b']  ['b','a']

【Answer】
```javascript
function calc(str){
    var strArray = str.split('');
    var path = [];
    var docalc = function(array){
      if(array.length===1){
        path.push(array[0]);
        console.log(path);
        path.pop();
        return;
      }
      for(var i=0;i

490. DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Question】 DOM中哪些事件是不冒泡的,如何判断事件是不是可以冒泡?

【Answer】
*  不冒泡的事件有blur、focus、load、unload、abort、error、mouseenter、mouseleave、resize
*  每个 event 都有一个event.bubbles属性,通过该属性可知是否冒泡

491. JavaScript实现对象深拷贝方法

【Question】 编码实现JavaScript实现对象深拷贝

【Answer】
var clone = function(v) {  
  var o = v.constructor === Array ? [] : {};  
  for (var i in v) {  
    o[i] = typeof v[i] === "Object" ? clone(v[i]) : v[i];  
  }  
  return o;  
}  

492. 故障分析-HTTPS证书不被信任

【Question】

如下图,在不同的设备上,同时访问同一个域名,一个设备显示证书不被信任,另一个设备正常,再使用多个其他设备访问,依然正常。分析可能的原因?以及需要获取的进一步的信息?

正常的设备

ssl_success.png<p>异常的设备</p>ssl_error.png<p>
</p>

【Answer】

需要进行的进一步的操作:

1) 查看证书详情:路径/SN/哈希值

2) 查看DNS解析结果

3) 查看系统时间/版本/浏览器版本

可能的原因:

1) 代理工具/安全软硬件

2) DNS劫持/路由劫持

3) 时间偏差

4) 操作系统/浏览器版本差异


493. 请实现一个CodingMan函数实现以下功能

【Question】


实现一个CodingMan,可以按照以下方式调用:
CodingMan(“Hank”)输出:
Hi! This is Hank!

CodingMan(“Hank”).sleep(10).eat(“dinner”)
输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

CodingMan(“Hank”).eat(“dinner”).eat(“supper”)
输出
Hi This is Hank!
Eat dinner~
Eat supper~

CodingMan(“Hank”).sleepFirst(5).eat(“supper”)
输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。


【Answer】


class _CodingMan {
    constructor(name) {
        this.tasks = [];
        const task = () => {
            console.log(`Hi! This is ${name}`);
            this.next();
        }
        this.tasks.push(task);
        setTimeout(() => {               // 把 this.next() 放到调用栈清空之后执行
            this.next();
        }, 0);
    }

    next() {
        const task = this.tasks.shift(); // 取第一个任务执行
        task && task();
    }

    sleep(time) {
        this._sleepWrapper(time, false);
        return this;                     // 链式调用
    }

    sleepFirst(time) {
        this._sleepWrapper(time, true);
        return this;
    }

    _sleepWrapper(time, first) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                this.next();
            }, time * 1000)
        }
        if (first) {
            this.tasks.unshift(task);     // 放到任务队列顶部
        } else {
            this.tasks.push(task);        // 放到任务队列尾部
        }
    }

    eat(name) {
        const task = () => {
            console.log(`Eat ${name}`);
            this.next();
        }
        this.tasks.push(task);
        return this;
    }
}

function CodingMan(name) {
    return new _CodingMan(name);
}



494. 实现如下函数add,使如下执行都等于9

【Question】


add(2,3,4)=9
add(2)(3,4)=9
add(2)(3)(4)=9
add(2,3)(4)=9


【Answer】

// 较通用的实现

function currying(fn, length) {

 length = length || fn.length;

 return function (...args) {

  return args.length >= length

   ? fn.apply(this, args)

   : currying(fn.bind(this, ...args), length - args.length) 

 }

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 通用实现2

function currying(fn, length) {

return function(...args) {

if (args.length >= length) {

return args.slice(0, length).reduce((t, i) => t += i);

}

return function(..._args) {

return add.apply(null, [...args, ..._args]);

}

}

}

function add(...args) {

return args.reduce((t, i) => t+=i)

}

var newAdd = currying(add, 3)


// 直接的实现

function add(...args) {

if (args.length >= 3) {

return args.slice(0, 3).reduce((t,i) => t += i);

}

return function(..._args) {

return add(args.concat(_args));

}

}


495. 介绍一下你了解的 WebSocket

【Question】 简单介绍一下 WebSocket,ws 协议和 http 协议的关系是什么,WebSocket 如何校验权限? WebSocket 如何实现 SSL 协议的安全连接?

【Answer】
WebSocket 是基于 http 的,所以建立 WebSocket 连接前,
浏览器会通过 http 的方式请求服务器建立连接,
这个时候可以通过 http  的权限校验方式来校验 WebSocket,比如设置 Cookie。
同理,WebSocket 实现 SSL 协议也同 https 类似,会升级为 wss 连接。
另外,当然也可以在 WebSocket 中还可以通过加密或者 token 等方式,实现自己额外的加密传输和权限判断方式。
更多可参考 https://security.tencent.com/index.php/blog/msg/119


496. 请谈谈iframe有哪些缺点?

【Question】 iframe通常有哪些用途,主要缺点是什么

【Answer】
(1)iframe会阻塞主页面的Onload事件;
(2)搜索引擎的检索程序无法解读这种页面,不利于SEO;
(3)iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
(4)页面简的通信问题
使用iframe之前需要考虑这(1)(3)两个缺点。
如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

497. 请简述JAVAScript事件模型和事件代理

【Question】 简述一下JavaScript事件模型和事件代理,事件代理有哪些优点?

【Answer】
## 事件模型
事件三个阶段:事件捕获,目标,事件冒泡(低版本ie不支持捕获阶段)
## 事件代理及优点: 
把事件委托到其父对象上,借助事件冒泡机制,实现对节点的事件代理。  
### 优点  
*  可以大量节省内存占用,减少事件注册
*  当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

498. 根据id从多叉树里面查找出对应的节点的name

【Question】


一个树形的数据(如下数据),面试官给你一个id,然后拿到对应的name?
  var cityData = [
      {
        id: 1,
        name: '广东省',
        children: [
          {
            id: 11,
            name: '深圳',
            children: [
              {
                id: 111,
                name: '宝安',
                children: [
                  {
                    id: 1111,
                    name: '西乡',
                    children:[
                      {
                        id: 11111,
                        name: '坪洲',
                        children:[]
                      },
                      {
                        id: 11112,
                        name: '灵芝',
                        children:[]
                      }
                    ]
                  },
                  {
                    id: 1112,
                    name: '南山',
                    children:[
                      {
                        id: 11121,
                        name: '科技园',
                        children:[]
                      }
                    ]
                  }
                ]
              },
              {
                id: 112,
                name: '福田',
                children: []
              }
            ]
          },
          {
            id: 12,
            name: '广州',
            children: [
              {
                id: 122,
                name: '白云区',
                children: [
                  {
                    id: 1222,
                    name: '白云区',
                    children: []
                  }
                ]
              },
              {
                id: 122,
                name: '珠海区',
                children: []
              }
            ]
          }
        ]
      },
      {
        id: 2,
        name: '湖南省',
        children: []
      }
    ];


【Answer】


主要考查深度/广度优先遍历,递归算法
方法1:递归

let result = ''

// 递归实现
const recursion = (cityData, id) => {
  // cityData数据为空的时候直接返回
  if (!cityData || !cityData.length) return;
  // 常规循环cityData
  for (let i = 0, len = cityData.length; i < len; i++) {
    const childs = cityData[i].children;
    
    // 如果匹配到id的话,就是我们要的结果
    if (cityData[i].id === id) {
      result = cityData[i].name
    }
    // 如果还有子节点,执行递归
    if(childs && childs.length > 0){
      recursion(childs, id);
    }
  }
  return result
};

const r = recursion(cityData, 11112);
console.log(r) // 灵芝


方法2:广度优先遍历
let result = ''

const range = (cityData, id) => {
  if (!cityData || !cityData.length) return;
  // 定义一个数据栈
  let stack = [];

  let item = null;

  //先将第一层节点放入栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i]);
  }

  while (stack.length) {
    // 将数据栈的第一个取出来
    item = stack.shift();
    // 如果符合就赋值给result
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈底
    if (item.children && item.children.length) {
      stack = stack.concat(item.children);
    }
  }
  return result
};

let r1 = range(cityData, 11112);

console.log(r1) // 灵芝


方法3:深度优先遍历
let result = ''

const deep = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 先定义一个数据栈
  let stack = []
  let item = null

  //先将第一层节点放入数据栈
  for (var i = 0, len = cityData.length; i < len; i++) {
    stack.push(cityData[i])
  }
  // 循环
  while (stack.length) {
    item = stack.shift()
    if (item.id === id) {
      result = item.name
    }
    //如果该节点有子节点,继续添加进入栈顶
    if (item.children && item.children.length) {
      // 注意这里调换了顺序
      stack = item.children.concat(stack);
    }
  }
  return result
};

let r3 = deep(cityData, 11112)
console.log(r3) // 灵芝


方法4:正则

const regular = (cityData, id) => {
  // 没有数据直接返回
  if (!cityData || !cityData.length) return;
  // 数据转成字符串
  let cityStr = JSON.stringify(cityData)
  // 定义正则
  let reg = new RegExp(`"id":${id},"name":"([^\\x00-\\xff]+)",`)
  // 取到正则的子字符串并返回
  return (cityStr.match(reg))[1]
}

let r4 = regular(cityData, 11112);

console.log(r4) // 灵芝



499. js浮点运算

【Question】 console.info(0.7+0.1)会得到什么

【Answer】
输出0.799999


500. macro micro 任务队列(async/await版)

【Question】

async function async1() {

 console.log('async1 start');

 await async2();

 console.log('async1 end');

}

async function async2() {

 console.log('async2 start');

 return new Promise((resolve, reject) => {

  resolve();

  console.log('async2 promise');

 })

}

console.log('script start');

setTimeout(function() {

 console.log('setTimeout');

}, 0);  

async1();

new Promise(function(resolve) {

 console.log('promise1');

 resolve();

}).then(function() {

 console.log('promise2');

}).then(function() {

 console.log('promise3');

});

console.log('script end');

【Answer】

chrome 和 node 都是以下顺序



Search

    Table of Contents