关于前端:来自大厂-10-前端面试题附答案整理版

22次阅读

共计 17181 个字符,预计需要花费 43 分钟才能阅读完成。

跨域计划

很多种办法,但万变不离其宗,都是为了搞定同源策略。重用的有 jsonpiframecorsimgHTML5 postMessage等等。其中用到 html 标签进行跨域的原理就是 html 不受同源策略影响。但只是承受 Get 的申请形式,这个得分明。

延长 1:img iframe script 来发送跨域申请有什么优缺点?

1. iframe

  • 长处:跨域结束之后 DOM 操作和相互之间的 JavaScript 调用都是没有问题的
  • 毛病:1. 若后果要以 URL 参数传递,这就意味着在后果数据量很大的时候须要宰割传递,巨烦。2. 还有一个是 iframe 自身带来的,母页面和 iframe 自身的交互自身就有安全性限度。

2. script

  • 长处:能够间接返回 json 格局的数据,不便解决
  • 毛病:只承受 GET 申请形式

3. 图片 ping

  • 长处:能够拜访任何url,个别用来进行点击追踪,做页面剖析罕用的办法
  • 毛病:不能拜访响应文本,只能监听是否响应

延长 2:配合 webpack 进行反向代理?

webpackdevServer 选项外面提供了一个 proxy 的参数供开发人员进行反向代理

'/api': {
  target: 'http://www.example.com', // your target host
  changeOrigin: true, // needed for virtual hosted sites
  pathRewrite: {'^/api': ''  // rewrite path}
},

而后再配合 http-proxy-middleware 插件对 api 申请地址进行代理

const express = require('express');
const proxy = require('http-proxy-middleware');
// proxy api requests
const exampleProxy = proxy(options); // 这里的 options 就是 webpack 外面的 proxy 选项对应的每个选项

// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);

而后再用 nginx 把容许跨域的源地址增加到报头外面即可

说到 nginx,能够再谈谈 CORS 配置,大抵如下

location / {if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';  
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type';  
    add_header 'Access-Control-Max-Age' 86400;  
    add_header 'Content-Type' 'text/plain charset=UTF-8';  
    add_header 'Content-Length' 0;  
    return 200;  
  }
}

什么是物理像素,逻辑像素和像素密度,为什么在挪动端开发时须要用到 @3x, @2x 这种图片?

以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV 元素宽度为 414px,这个 DIV 就会填满手机的宽度;

而如果有一把尺子来理论测量这部手机的物理像素,理论为 1242*2688 物理像素;通过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素 = 3 个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。

对于图片来说,为了保障其不失真,1 个图片像素至多要对应一个物理像素,如果原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片能力保障 1 个物理像素至多对应一个图片像素,能力不失真。当然,也能够针对所有屏幕,都只提供最高清图片。尽管低密度屏幕用不到那么多图片像素,而且会因为下载多余的像素造成带宽节约和下载提早,但从后果上说能保障图片在所有屏幕上都不会失真。

还能够应用 CSS 媒体查问来判断不同的像素密度,从而抉择不同的图片:

my-image {background: (low.png); }
@media only screen and (min-device-pixel-ratio: 1.5) {#my-image { background: (high.png); }
}

应用 clear 属性革除浮动的原理?

应用 clear 属性革除浮动,其语法如下:

clear:none|left|right|both

如果单看字面意思,clear:left 是“革除左浮动”,clear:right 是“革除右浮动”,实际上,这种解释是有问题的,因为浮动始终还在,并没有革除。

官网对 clear 属性解释:“元素盒子的边不能和后面的浮动元素相邻”,对元素设置 clear 属性是为了防止浮动元素对该元素的影响,而不是革除掉浮动。

还须要留神 clear 属性指的是元素盒子的边不能和后面的浮动元素相邻,留神这里“后面的”3 个字,也就是 clear 属性对“前面的”浮动元素是充耳不闻的。思考到 float 属性要么是 left,要么是 right,不可能同时存在,同时因为 clear 属性对“前面的”浮动元素充耳不闻,因而,当 clear:left 无效的时候,clear:right 必然有效,也就是此时 clear:left 等同于设置 clear:both;同样地,clear:right 如果无效也是等同于设置 clear:both。由此可见,clear:left 和 clear:right 这两个申明就没有任何应用的价值,至多在 CSS 世界中是如此,间接应用 clear:both 吧。

个别应用伪元素的形式革除浮动:

.clear::after{content:'';  display: block;   clear:both;}

clear 属性只有块级元素才无效的,而::after 等伪元素默认都是内联程度,这就是借助伪元素革除浮动影响时须要设置 display 属性值的起因。

+ 操作符什么时候用于字符串的拼接?

依据 ES5 标准,如果某个操作数是字符串或者可能通过以下步骤转换为字符串的话,+ 将进行拼接操作。如果其中一个操作数是对象(包含数组),则首先对其调用 ToPrimitive 形象操作,该形象操作再调用 [[DefaultValue]],以数字作为上下文。如果不能转换为字符串,则会将其转换为数字类型来进行计算。

简略来说就是,如果 + 的其中一个操作数是字符串(或者通过以上步骤最终失去字符串),则执行字符串拼接,否则执行数字加法。

那么对于除了加法的运算符来说,只有其中一方是数字,那么另一方就会被转为数字。

数组有哪些原生办法?

  • 数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 办法能够指定转换为字符串时的分隔符。
  • 数组尾部操作的办法 pop() 和 push(),push 办法能够传入多个参数。
  • 数组首部操作的办法 shift() 和 unshift() 重排序的办法 reverse() 和 sort(),sort() 办法能够传入一个函数来进行比拟,传入前后两个值,如果返回值为负数,则替换两个参数的地位。
  • 数组连贯的办法 concat(),返回的是拼接好的数组,不影响原数组。
  • 数组截取方法 slice(),用于截取数组中的一部分返回,不影响原数组。
  • 数组插入方法 splice(),影响原数组查找特定项的索引的办法,indexOf() 和 lastIndexOf() 迭代办法 every()、some()、filter()、map() 和 forEach() 办法
  • 数组归并办法 reduce() 和 reduceRight() 办法

JavaScript 有哪些内置对象

全局的对象(global objects)或称规范内置对象,不要和 “ 全局对象(global object)” 混同。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其余对象能够由用户的脚本创立或由宿主程序提供。

规范内置对象的分类:

(1)值属性,这些全局属性返回一个简略值,这些值没有本人的属性和办法。例如 Infinity、NaN、undefined、null 字面量

(2)函数属性,全局函数能够间接调用,不须要在调用时指定所属对象,执行完结后会将后果间接返回给调用者。例如 eval()、parseFloat()、parseInt() 等

(3)根本对象,根本对象是定义或应用其余对象的根底。根本对象包含个别对象、函数对象和谬误对象。例如 Object、Function、Boolean、Symbol、Error 等

(4)数字和日期对象,用来示意数字、日期和执行数学计算的对象。例如 Number、Math、Date

(5)字符串,用来示意和操作字符串的对象。例如 String、RegExp

(6)可索引的汇合对象,这些对象示意依照索引值来排序的数据汇合,包含数组和类型数组,以及类数组构造的对象。例如 Array

(7)应用键的汇合对象,这些汇合对象在存储数据时会应用到键,反对依照插入程序来迭代元素。
例如 Map、Set、WeakMap、WeakSet

(8)矢量汇合,SIMD 矢量汇合中的数据会被组织为一个数据序列。
例如 SIMD 等

(9)结构化数据,这些对象用来示意和操作结构化的缓冲区数据,或应用 JSON 编码的数据。例如 JSON 等

(10)管制形象对象
例如 Promise、Generator 等

(11)反射。例如 Reflect、Proxy

(12)国际化,为了反对多语言解决而退出 ECMAScript 的对象。例如 Intl、Intl.Collator 等

(13)WebAssembly

(14)其余。例如 arguments

总结: js 中的内置对象次要指的是在程序执行前存在全局作用域里的由 js 定义的一些全局值属性、函数和用来实例化其余对象的构造函数对象。个别常常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。

ajax、axios、fetch 的区别

(1)AJAX Ajax 即“AsynchronousJavascriptAndXML”(异步 JavaScript 和 XML),是指一种创立交互式网页利用的网页开发技术。它是一种在无需从新加载整个网页的状况下,可能更新局部网页的技术。通过在后盾与服务器进行大量数据交换,Ajax 能够使网页实现异步更新。这意味着能够在不从新加载整个网页的状况下,对网页的某局部进行更新。传统的网页(不应用 Ajax)如果须要更新内容,必须重载整个网页页面。其毛病如下:

  • 自身是针对 MVC 编程,不合乎前端 MVVM 的浪潮
  • 基于原生 XHR 开发,XHR 自身的架构不清晰
  • 不合乎关注拆散(Separation of Concerns)的准则
  • 配置和调用形式十分凌乱,而且基于事件的异步模型不敌对。

(2)Fetch fetch 号称是 AJAX 的替代品,是在 ES6 呈现的,应用了 ES6 中的 promise 对象。Fetch 是基于 promise 设计的。Fetch 的代码构造比起 ajax 简略多。fetch 不是 ajax 的进一步封装,而是原生 js,没有应用 XMLHttpRequest 对象

fetch 的长处:

  • 语法简洁,更加语义化
  • 基于规范 Promise 实现,反对 async/await
  • 更加底层,提供的 API 丰盛(request, response)
  • 脱离了 XHR,是 ES 标准里新的实现形式

fetch 的毛病:

  • fetch 只对网络申请报错,对 400,500 都当做胜利的申请,服务器返回 400,500 错误码时并不会 reject,只有网络谬误这些导致申请不能实现时,fetch 才会被 reject。
  • fetch 默认不会带 cookie,须要增加配置项:fetch(url, {credentials: ‘include’})
  • fetch 不反对 abort,不反对超时管制,应用 setTimeout 及 Promise.reject 的实现的超时管制并不能阻止申请过程持续在后盾运行,造成了流量的节约
  • fetch 没有方法原生监测申请的进度,而 XHR 能够

(3)Axios Axios 是一种基于 Promise 封装的 HTTP 客户端,其特点如下:

  • 浏览器端发动 XMLHttpRequests 申请
  • node 端发动 http 申请
  • 反对 Promise API
  • 监听申请和返回
  • 对申请和返回进行转化
  • 勾销申请
  • 主动转换 json 数据
  • 客户端反对抵挡 XSRF 攻打

HTTP 1.1 和 HTTP 2.0 的区别

  • 二进制协定:HTTP/2 是一个二进制协定。在 HTTP/1.1 版中,报文的头信息必须是文本(ASCII 编码),数据体能够是文本,也能够是二进制。HTTP/2 则是一个彻底的二进制协定,头信息和数据体都是二进制,并且统称为 ” 帧 ”,能够分为头信息帧和数据帧。帧的概念是它实现多路复用的根底。
  • 多路复用: HTTP/2 实现了多路复用,HTTP/2 依然复用 TCP 连贯,然而在一个连贯里,客户端和服务器都能够同时发送多个申请或回应,而且不必依照程序一一发送,这样就防止了 ” 队头梗塞 ”【1】的问题。
  • 数据流: HTTP/2 应用了数据流的概念,因为 HTTP/2 的数据包是不按程序发送的,同一个连贯外面间断的数据包,可能属于不同的申请。因而,必须要对数据包做标记,指出它属于哪个申请。HTTP/2 将每个申请或回应的所有数据包,称为一个数据流。每个数据流都有一个举世无双的编号。数据包发送时,都必须标记数据流 ID,用来辨别它属于哪个数据流。
  • 头信息压缩: HTTP/2 实现了头信息压缩,因为 HTTP 1.1 协定不带状态,每次申请都必须附上所有信息。所以,申请的很多字段都是反复的,比方 Cookie 和 User Agent,截然不同的内容,每次申请都必须附带,这会节约很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制。一方面,头信息应用 gzip 或 compress 压缩后再发送;另一方面,客户端和服务器同时保护一张头信息表,所有字段都会存入这个表,生成一个索引号,当前就不发送同样字段了,只发送索引号,这样就能进步速度了。
  • 服务器推送: HTTP/2 容许服务器未经请求,被动向客户端发送资源,这叫做服务器推送。应用服务器推送提前给客户端推送必要的资源,这样就能够绝对缩小一些延迟时间。这里须要留神的是 http2 下服务器被动推送的是动态资源,和 WebSocket 以及应用 SSE 等形式向客户端发送即时数据的推送是不同的。

【1】队头梗塞:

队头阻塞是由 HTTP 根本的“申请 – 应答”模型所导致的。HTTP 规定报文必须是“一发一收”,这就造成了一个先进先出的“串行”队列。队列里的申请是没有优先级的,只有入队的先后顺序,排在最后面的申请会被最优先解决。如果队首的申请因为解决的太慢耽搁了工夫,那么队列里前面的所有申请也不得不跟着一起期待,后果就是其余的申请承当了不应有的工夫老本,造成了队头梗塞的景象。

intanceof 操作符的实现原理及实现

instanceof 运算符用于判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。

function myInstanceof(left, right) {
  // 获取对象的原型
  let proto = Object.getPrototypeOf(left)
  // 获取构造函数的 prototype 对象
  let prototype = right.prototype; 

  // 判断构造函数的 prototype 对象是否在对象的原型链上
  while (true) {if (!proto) return false;
    if (proto === prototype) return true;
    // 如果没有找到,就持续从其原型上找,Object.getPrototypeOf 办法用来获取指定对象的原型
    proto = Object.getPrototypeOf(proto);
  }
}

对 AJAX 的了解,实现一个 AJAX 申请

AJAX 是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新以后网页的对应局部,而不必刷新整个网页。

创立 AJAX 申请的步骤:

  • 创立一个 XMLHttpRequest 对象。
  • 在这个对象上 应用 open 办法创立一个 HTTP 申请,open 办法所须要的参数是申请的办法、申请的地址、是否异步和用户的认证信息。
  • 在发动申请前,能够为这个对象 增加一些信息和监听函数。比如说能够通过 setRequestHeader 办法来为申请增加头信息。还能够为这个对象增加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变动时会触发 onreadystatechange 事件,能够通过设置监听函数,来解决申请胜利后的后果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接管实现,这个时候能够通过判断申请的状态,如果状态是 2xx 或者 304 的话则代表返回失常。这个时候就能够通过 response 中的数据来对页面进行更新了。
  • 当对象的属性和监听函数设置实现后,最初调 用 sent 办法来向服务器发动申请,能够传入参数作为发送的数据体。
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创立 Http 申请
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
  // 当申请胜利时
  if (this.status === 200) {handle(this.response);
  } else {console.error(this.statusText);
  }
};
// 设置申请失败时的监听函数
xhr.onerror = function() {console.error(this.statusText);
};
// 设置申请头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 申请
xhr.send(null);

应用 Promise 封装 AJAX:

// promise 封装实现:function getJSON(url) {
  // 创立一个 promise 对象
  let promise = new Promise(function(resolve, reject) {let xhr = new XMLHttpRequest();
    // 新建一个 http 申请
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
      // 当申请胜利或失败时,扭转 promise 的状态
      if (this.status === 200) {resolve(this.response);
      } else {reject(new Error(this.statusText));
      }
    };
    // 设置谬误监听函数
    xhr.onerror = function() {reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置申请头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 申请
    xhr.send(null);
  });
  return promise;
}

use strict 是什么意思 ? 应用它区别是什么?

use strict 是一种 ECMAscript5 增加的(严格模式)运行模式,这种模式使得 Javascript 在更严格的条件下运行。设立严格模式的目标如下:

  • 打消 Javascript 语法的不合理、不谨严之处,缩小怪异行为;
  • 打消代码运行的不平安之处,保障代码运行的平安;
  • 进步编译器效率,减少运行速度;
  • 为将来新版本的 Javascript 做好铺垫。

区别:

  • 禁止应用 with 语句。
  • 禁止 this 关键字指向全局对象。
  • 对象不能有重名的属性。

async/await 的劣势

繁多的 Promise 链并不能发现 async/await 的劣势,然而,如果须要解决由多个 Promise 组成的 then 链的时候,劣势就能体现进去了(很有意思,Promise 通过 then 链来解决多层回调的问题,当初又用 async/await 来进一步优化它)。

假如一个业务,分多个步骤实现,每个步骤都是异步的,而且依赖于上一个步骤的后果。依然用 setTimeout 来模仿异步操作:

/** * 传入参数 n,示意这个函数执行的工夫(毫秒)* 执行的后果是 n + 200,这个值将用于下一步骤 */
function takeLongTime(n) {
    return new Promise(resolve => {setTimeout(() => resolve(n + 200), n);
    });
}
function step1(n) {console.log(`step1 with ${n}`);
    return takeLongTime(n);
}
function step2(n) {console.log(`step2 with ${n}`);
    return takeLongTime(n);
}
function step3(n) {console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

当初用 Promise 形式来实现这三个步骤的解决:

function doIt() {console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms

输入后果 resultstep3() 的参数 700 + 200 = 900doIt() 程序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的后果统一。

如果用 async/await 来实现呢,会是这样:

async function doIt() {console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

后果和之前的 Promise 实现是一样的,然而这个代码看起来是不是清晰得多,简直跟同步代码一样

Promise 的根本用法

(1)创立 Promise 对象

Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已胜利)和 rejected(已失败)。

Promise 构造函数承受一个函数作为参数,该函数的两个参数别离是 resolvereject

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作胜利 */){resolve(value);
  } else {reject(error);
  }
});

个别状况下都会应用 new Promise() 来创立 promise 对象,然而也能够应用 promise.resolvepromise.reject这两个办法:

  • Promise.resolve

Promise.resolve(value)的返回值也是一个 promise 对象,能够对返回值进行.then 调用,代码如下:

Promise.resolve(11).then(function(value){console.log(value); // 打印出 11
});

resolve(11)代码中,会让 promise 对象进入确定 (resolve 状态),并将参数 11 传递给前面的 then 所指定的onFulfilled 函数;

创立 promise 对象能够应用 new Promise 的模式创建对象,也能够应用 Promise.resolve(value) 的模式创立 promise 对象;

  • Promise.reject

Promise.reject 也是 new Promise 的快捷模式,也创立一个 promise 对象。代码如下:

Promise.reject(new Error(“我错了,请原谅俺!!”));

就是上面的代码 new Promise 的简略模式:

new Promise(function(resolve,reject){reject(new Error("我错了!"));
});

上面是应用 resolve 办法和 reject 办法:

function testPromise(ready) {return new Promise(function(resolve,reject){if(ready) {resolve("hello world");
    }else {reject("No thanks");
    }
  });
};
// 办法调用
testPromise(true).then(function(msg){console.log(msg);
},function(error){console.log(error);
});

下面的代码的含意是给 testPromise 办法传递一个参数,返回一个 promise 对象,如果为 true 的话,那么调用 promise 对象中的 resolve() 办法,并且把其中的参数传递给前面的 then 第一个函数内,因而打印出“hello world”, 如果为 false 的话,会调用 promise 对象中的 reject() 办法,则会进入 then 的第二个函数内,会打印No thanks

(2)Promise 办法

Promise 有五个罕用的办法:then()、catch()、all()、race()、finally。上面就来看一下这些办法。

  1. then()

当 Promise 执行的内容合乎胜利条件时,调用 resolve 函数,失败就调用 reject 函数。Promise 创立完了,那该如何调用呢?

promise.then(function(value) {// success}, function(error) {// failure});

then办法能够承受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数能够省略。then办法返回的是一个新的 Promise 实例(不是原来那个 Promise 实例)。因而能够采纳链式写法,即 then 办法前面再调用另一个 then 办法。

当要写有程序的异步事件时,须要串行时,能够这样写:

let promise = new Promise((resolve,reject)=>{ajax('first').success(function(res){resolve(res);
    })
})
promise.then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
        })
    })
}).then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
        })
    })
}).then(res=>{})

那当要写的事件没有程序或者关系时,还如何写呢?能够应用all 办法来解决。

2. catch()

Promise 对象除了有 then 办法,还有一个 catch 办法,该办法相当于 then 办法的第二个参数,指向 reject 的回调函数。不过 catch 办法还有一个作用,就是在执行 resolve 回调函数时,如果呈现谬误,抛出异样,不会进行运行,而是进入 catch 办法中。

p.then((data) => {console.log('resolved',data);
},(err) => {console.log('rejected',err);
     }
); 
p.then((data) => {console.log('resolved',data);
}).catch((err) => {console.log('rejected',err);
});

3. all()

all办法能够实现并行任务,它接管一个数组,数组的每一项都是一个 promise 对象。当数组中所有的 promise 的状态都达到 resolved 的时候,all办法的状态就会变成 resolved,如果有一个状态变成了rejected,那么all 办法的状态就会变成rejected

javascript
let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(1);
    },2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
    },1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
    },3000)
});
Promise.all([promise1,promise2,promise3]).then(res=>{console.log(res);
    // 后果为:[1,2,3] 
})

调用 all 办法时的后果胜利的时候是回调函数的参数也是一个数组,这个数组按程序保留着每一个 promise 对象 resolve 执行时的值。

(4)race()

race办法和 all 一样,承受的参数是一个每项都是 promise 的数组,然而与 all 不同的是,当最先执行完的事件执行完之后,就间接返回该 promise 对象的值。如果第一个 promise 对象状态变成 resolved,那本身的状态变成了resolved;反之第一个promise 变成rejected,那本身状态就会变成rejected

let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{reject(1);
    },2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
    },1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
    },3000)
});
Promise.race([promise1,promise2,promise3]).then(res=>{console.log(res);
    // 后果:2
},rej=>{console.log(rej)};
)

那么 race 办法有什么理论作用呢?当要做一件事,超过多长时间就不做了,能够用这个办法来解决:

Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})

5. finally()

finally办法用于指定不论 Promise 对象最初状态如何,都会执行的操作。该办法是 ES2018 引入规范的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

下面代码中,不论 promise 最初的状态,在执行完 thencatch指定的回调函数当前,都会执行 finally 办法指定的回调函数。

上面是一个例子,服务器应用 Promise 解决申请,而后应用 finally 办法关掉服务器。

server.listen(port)
  .then(function () {// ...})
  .finally(server.stop);

finally办法的回调函数不承受任何参数,这意味着没有方法晓得,后面的 Promise 状态到底是 fulfilled 还是 rejected。这表明,finally 办法外面的操作,应该是与状态无关的,不依赖于 Promise 的执行后果。finally实质上是 then 办法的特例:

promise
.finally(() => {// 语句});
// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);

下面代码中,如果不应用 finally 办法,同样的语句须要为胜利和失败两种状况各写一次。有了 finally 办法,则只须要写一次。

手写题:数组扁平化

function flatten(arr) {let result = [];

  for (let i = 0; i < arr.length; i++) {if (Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]));
    } else {result = result.concat(arr[i]);
    }
  }

  return result;
}

const a = [1, [2, [3, 4]]];
console.log(flatten(a));

代码输入后果

setTimeout(function () {console.log(1);
}, 100);

new Promise(function (resolve) {console.log(2);
  resolve();
  console.log(3);
}).then(function () {console.log(4);
  new Promise((resove, reject) => {console.log(5);
    setTimeout(() =>  {console.log(6);
    }, 10);
  })
});
console.log(7);
console.log(8);

输入后果为:

2
3
7
8
4
5
6
1

代码执行过程如下:

  1. 首先遇到定时器,将其退出到宏工作队列;
  2. 遇到 Promise,首先执行外面的同步代码,打印出 2,遇到 resolve,将其退出到微工作队列,执行前面同步代码,打印出 3;
  3. 继续执行 script 中的代码,打印出 7 和 8,至此第一轮代码执行实现;
  4. 执行微工作队列中的代码,首先打印出 4,如遇到 Promise,执行其中的同步代码,打印出 5,遇到定时器,将其退出到宏工作队列中,此时宏工作队列中有两个定时器;
  5. 执行宏工作队列中的代码,这里咱们须要留神是的第一个定时器的工夫为 100ms,第二个定时器的工夫为 10ms,所以先执行第二个定时器,打印出 6;
  6. 此时微工作队列为空,继续执行宏工作队列,打印出 1。

做完这道题目,咱们就须要分外留神,每个定时器的工夫,并不是所有定时器的工夫都为 0 哦。

Number() 的存储空间是多大?如果后盾发送了一个超过最大本人的数字怎么办

Math.pow(2, 53),53 为有效数字,会产生截断,等于 JS 能反对的最大数字。

display 的 block、inline 和 inline-block 的区别

(1)block: 会独占一行,多个元素会另起一行,能够设置 width、height、margin 和 padding 属性;

(2)inline: 元素不会独占一行,设置 width、height 属性有效。但能够设置程度方向的 margin 和 padding 属性,不能设置垂直方向的 padding 和 margin;

(3)inline-block: 将对象设置为 inline 对象,但对象的内容作为 block 对象出现,之后的内联对象会被排列在同一行内。

对于行内元素和块级元素,其特点如下:

(1)行内元素

  • 设置宽高有效;
  • 能够设置程度方向的 margin 和 padding 属性,不能设置垂直方向的 padding 和 margin;
  • 不会主动换行;

(2)块级元素

  • 能够设置宽高;
  • 设置 margin 和 padding 都无效;
  • 能够主动换行;
  • 多个块状,默认排列从上到下。

浏览器的垃圾回收机制

(1)垃圾回收的概念

垃圾回收:JavaScript 代码运行时,须要分配内存空间来贮存变量和值。当变量不在参加运行时,就须要零碎发出被占用的内存空间,这就是垃圾回收。

回收机制

  • Javascript 具备主动垃圾回收机制,会定期对那些不再应用的变量、对象所占用的内存进行开释,原理就是找到不再应用的变量,而后开释掉其占用的内存。
  • JavaScript 中存在两种变量:局部变量和全局变量。全局变量的生命周期会继续要页面卸载;而局部变量申明在函数中,它的生命周期从函数执行开始,直到函数执行完结,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行完结后,这些局部变量不再被应用,它们所占有的空间就会被开释。
  • 不过,当局部变量被内部函数应用时,其中一种状况就是闭包,在函数执行完结后,函数内部的变量仍然指向函数外部的局部变量,此时局部变量仍然在被应用,所以不会回收。

(2)垃圾回收的形式

浏览器通常应用的垃圾回收办法有两种:标记革除,援用计数。1)标记革除

  • 标记革除是浏览器常见的垃圾回收形式,当变量进入执行环境时,就标记这个变量“进入环境”,被标记为“进入环境”的变量是不能被回收的,因为他们正在被应用。当变量来到环境时,就会被标记为“来到环境”,被标记为“来到环境”的变量会被内存开释。
  • 垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。而后,它会去掉环境中的变量以及被环境中的变量援用的标记。而在此之后再被加上标记的变量将被视为筹备删除的变量,起因是环境中的变量曾经无法访问到这些变量了。最初。垃圾收集器实现内存革除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

2)援用计数

  • 另外一种垃圾回收机制就是援用计数,这个用的绝对较少。援用计数就是跟踪记录每个值被援用的次数。当申明了一个变量并将一个援用类型赋值给该变量时,则这个值的援用次数就是 1。相同,如果蕴含对这个值援用的变量又获得了另外一个值,则这个值的援用次数就减 1。当这个援用次数变为 0 时,阐明这个变量曾经没有价值,因而,在在机回收期下次再运行时,这个变量所占有的内存空间就会被释放出来。
  • 这种办法会引起 循环援用 的问题:例如:obj1obj2 通过属性进行互相援用,两个对象的援用次数都是 2。当应用循环计数时,因为函数执行完后,两个对象都来到作用域,函数执行完结,obj1obj2 还将会持续存在,因而它们的援用次数永远不会是 0,就会引起循环援用。
function fun() {let obj1 = {};
    let obj2 = {};
    obj1.a = obj2; // obj1 援用 obj2
    obj2.a = obj1; // obj2 援用 obj1
}

这种状况下,就要手动开释变量占用的内存:

obj1.a =  null
 obj2.a =  null

(3)缩小垃圾回收

尽管浏览器能够进行垃圾主动回收,然而当代码比较复杂时,垃圾回收所带来的代价比拟大,所以应该尽量减少垃圾回收。

  • 对数组进行优化: 在清空一个数组时,最简略的办法就是给其赋值为[],然而与此同时会创立一个新的空对象,能够将数组的长度设置为 0,以此来达到清空数组的目标。
  • object 进行优化: 对象尽量复用,对于不再应用的对象,就将其设置为 null,尽快被回收。
  • 对函数进行优化: 在循环中的函数表达式,如果能够复用,尽量放在函数的里面。

说一说你用过的 css 布局

gird 布局,layout 布局,flex 布局,双飞翼,圣杯布局等

动静布局求解硬币找零问题

题目形容: 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算能够凑成总金额所需的起码的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

示例 1:输出: coins = [1, 2, 5], amount = 11
输入: 3
解释: 11 = 5 + 5 + 1

示例 2:输出: coins = [2], amount = 3
输入: -1

实现代码如下:

const coinChange = function (coins, amount) {
  // 用于保留每个指标总额对应的最小硬币个数
  const f = [];
  // 提前定义已知状况
  f[0] = 0;
  // 遍历 [1, amount] 这个区间的硬币总额
  for (let i = 1; i <= amount; i++) {
    // 求的是最小值,因而咱们预设为无穷大,确保它肯定会被更小的数更新
    f[i] = Infinity;
    // 循环遍历每个可用硬币的面额
    for (let j = 0; j < coins.length; j++) {
      // 若硬币面额小于指标总额,则问题成立
      if (i - coins[j] >= 0) {
        // 状态转移方程
        f[i] = Math.min(f[i], f[i - coins[j]] + 1);
      }
    }
  }
  // 若指标总额对应的解为无穷大,则意味着没有一个符合条件的硬币总数来更新它,本题无解,返回 -1
  if (f[amount] === Infinity) {return -1;}
  // 若有解,间接返回解的内容
  return f[amount];
};

正文完
 0