[关闭]
@wy 2018-09-27T18:01:03.000000Z 字数 4569 阅读 483

在此处输入标题

面试题


async、await介绍一下

async 关键字是用来创建异步函数,调用后,会隐式的返回一个 Promise 对象作为结果。

await 是一个操作符,只能使用在 async function 的异步函数中,作用是暂停当前 async function继续执行,等待 Promise 处理完成。Promise 处理完之后,把resolve函数参数或者异常原因作为 await 表达式的值。

如果在代码中使用了异步函数,它的语法和结构会更像是标准的同步函数。

console.log是异步?

console.log不是标准化的,所以行为是相当未定义的(不是异步,或许是同步执行的,没有明确规定),控制台可能克隆(序列化)您记录的可变对象,或者它将存储对它们的引用。控制台中的初始渲染可能会显示对象的“当前”状态。但是,当展开对象以进一步检查其属性时,控制台很可能只会存储对对象及其属性的引用,并且现在显示它们将显示它们的当前(已经变异的)状态。

参考:https://stackoverflow.com/questions/23392111/console-log-async-or-sync

实现一个bind函数

bind是用来改变函数this指向的,但不像 call 和 apply那样,会把函数直接执行,bind执行后会返回新的函数。所以实现起来不是那么的麻烦。
但是有一点要主要,将来bind返回的函数如果当做构造函数的话,是需要继承原来调用bind的那个函数的。

可以先看下bind的常规用处:

  1. function func(...arg){
  2. console.log('改变this为:',this);
  3. console.log('参数为:',arg);
  4. }
  5. // bind第一个参数是改变this指向,改变为数组,之后传入的是给func函数传递的实参
  6. let f = func.bind([1,2,3],1,2,3);
  7. // 调用f
  8. f(4,5,6); // 结果为:改变this为: [1, 2, 3],同时给func传入参数
  9. // 参数为: [1, 2, 3, 4, 5, 6]

把f当做构造函数,此时要找原型上的方法,依然去func上找:

  1. function func(...arg){
  2. console.log('改变this为:',this);
  3. console.log('参数为:',arg);
  4. }
  5. func.prototype.say = function (){
  6. console.log('会说话');
  7. }
  8. // bind第一个参数是改变this指向,改变为数组,之后传入的是给func函数传递的实参
  9. let f = func.bind([1,2,3],1,2,3);
  10. let instance = new f(); // this不在执行传入的数组,而是实例本身
  11. instance.say(); // 打印:会说话

以上要注意两点:

实现代码如下:

  1. Function.prototype.customeBind = function (argThis,...arg){
  2. let self = this; // this指向调用customeBind的函数
  3. let m = function (){}
  4. let fnBound = function (...arg2){
  5. // 判断是否是new调用创建实例
  6. let that = this instanceof m ? this : argThis;
  7. return self.apply(that,[...arg,...arg2])
  8. }
  9. m.prototype = self.prototype;
  10. fnBound.prototype = new m;
  11. return fnBound;
  12. }

参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

eval是做什么的

作用:会将传入的字符串当做 JavaScript 代码进行执行
语法:eval(string)
说明:string是有效 JavaScript 代码。
安全问题: 当使用不可信任来源的代码时,谨慎使用 eval 解析。
缺点:
1. 可读性差
2. 不利于调试
3. 增加性能消耗
4. 破坏执行作用域

补充说明:

  1. 在以前不能解析JSON字符串的年代,使用eval可以把后端传来的JSON字符串转成对象:
  1. let s = '({"b":20,a:90})'
  2. console.log(eval(s));

现在使用JSON.parse代替。

  1. 破坏执行作用域
  1. var a = 10; // 全局变量
  2. let js = 'var a = 10000; console.log(a);'
  3. function test(){
  4. var b = 30;
  5. eval(js); // 执行后在全局重新定义了变量a
  6. eval('var b = 50;'); // 执行后覆盖局部的变量b
  7. console.log(b); // 输出为50
  8. }
  9. test();
  10. console.log(a); // 结果为10000

执行代码后并不是在局部定义变量,而是在全局定义了变量 a
执行代码后在局部重新定义了b变量,把局部变量覆盖了。

如果手动写动画,你认为最小间隔是多久,为什么?

多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms

["1","2","3"].map(parseInt)答案是多少?

首先需要了解parseInt的完整用法:
作用:解析一个字符串参数,并返回一个指定基数的十进制的整数
语法:parseInt(string, radix);
说明:radix一个介于2和36之间的整数,标示string字符串的基数。默认这个基数是10

例如:
parseInt('100', 10); 结果为:100
意思是把100通过基数10转成10进制的数字,结果还是100
数字100,从右到左从低位到高位,位置从0开始,依次乘上10为基数的次方,然后加上总和:
1*10^2 + 0*10^1 + 0*10^0 = 100 + 0 + 0 = 100;

parseInt('100', 2); 结果为:4
意思是把1000通过基数2转成10进制的数字,如下操作,把数字转成10进制:
数字100,从右到左从低位到高位,位置从0开始,依次乘上2为基数的次方,然后加上总和:
1*2^2 + 0*2^1 + 0*2^0 = 4 + 0 + 0 = 4;

parseInt('100', 3); 结果为:9
意思是把1000通过基数3转成10进制的数字,如下操作,把数字转成10进制:
数字100,从右到左从低位到高位,位置从0开始,依次乘上3为基数的次方,然后加上总和:
1*3^2 + 0*3^1 + 0*3^0 = 6 + 3 + 0 = 9;

一直到基数为36都成立。。。

但是要注意,传入的数字每一位不能大于等于基数,否则返回NaN,例如:

parseInt('200', 2); 结果为:NaN
parseInt('301', 3); 结果为:NaN

再说map方法的使用,主要是map回调函数的使用

  1. [1,2,3].map((item,index) => {
  2. 这个回调函数接收的是每一项item,和下标index
  3. })

回到题再来看一下,["1","2","3"].map(parseInt)

使用map循环数组,传入parseInt,此时parseInt会接受两个参数,每一项和下标,就类似这样:

["1","2","3"].map(function parseInt(item,index){
...
})

具体到调用parseInt接收的参数:

  1. parseInt('1',0) // 基数不能为0,直接返回前面数字 结果为1
  2. parseInt('2',1) // 基数为1,前面数字位数有大于1的,结果为NaN
  3. parseInt('3',2) // 基数为2,前面数字位数有大于2的,结果为NaN

map返回值是数组,所以最终结果为: [1,NaN,NaN]

ES6新特性

函数默认参数
模板字符串
扩展运算符...
解构赋值
箭头函数 =>
Promise对象解决异步回调
块级作用域的let和const
类class,继承extend
模块化es6 module

参考:http://es6.ruanyifeng.com/

缓存中设置了no-cache、no-store、max-age、private分别代表什么?

Cache-Control 通用消息头被用于在http 请求和响应中通过指定指令来实现缓存机制。
指令不区分大小写,并且具有可选参数,可以用令牌或者带引号的字符串语法。多个指令以逗号分隔。

参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ#%E7%BC%93%E5%AD%98%E6%8E%A7%E5%88%B6

什么是强缓存和协商缓存

务端可以通过响应头里的 Last-Modified(最后修改时间) 或者 ETag(内容特征) 标记实体。浏览器会存下这些标记,并在下次请求时带上 If-Modified-Since: 上次 Last-Modified 的内容 或 If-None-Match: 上次 ETag 的内容,询问服务端资源是否过期。如果服务端发现并没有过期,直接返回一个状态码为 304、正文为空的响应,告知浏览器使用本地缓存;如果资源有更新,服务端返回状态码 200、新的 Last-Modified、Etag 和正文。这个过程被称之为 HTTP 的协商缓存,通常也叫做弱缓存。

Last-Modified的问题:
1. 只能精确到秒,1 秒内的多次变化反映不出来;
2. 在轮询的负载均衡算法中,如果各机器读到的文件修改时间不一致,有缓存无故失效和缓存不更新的风险。

HTTP/1.1 并没有规定 ETag 的生成规则,而一般实现者都是对资源内容做摘要,能解决前面两个问题。

服务端通过响应头告诉浏览器,在什么时间之前(Expires)或在多长时间之内(Cache-Control: Max-age=xxx),不要再请求服务器了。这个机制我们通常称之为 HTTP 的强缓存。

使用 Cache-Control:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注