跨域计划
很多种办法,但万变不离其宗,都是为了搞定同源策略。重用的有
jsonp
、iframe
、cors
、img
、HTML5 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 进行反向代理?
webpack
在 devServer
选项外面提供了一个 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
输入后果 result
是 step3()
的参数 700 + 200
= 900
。doIt()
程序执行了三个步骤,一共用了 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 构造函数承受一个函数作为参数,该函数的两个参数别离是 resolve
和reject
。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作胜利 */){resolve(value);
} else {reject(error);
}
});
个别状况下都会应用 new Promise()
来创立 promise 对象,然而也能够应用 promise.resolve
和promise.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。上面就来看一下这些办法。
- 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
最初的状态,在执行完 then
或catch
指定的回调函数当前,都会执行 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
代码执行过程如下:
- 首先遇到定时器,将其退出到宏工作队列;
- 遇到 Promise,首先执行外面的同步代码,打印出 2,遇到 resolve,将其退出到微工作队列,执行前面同步代码,打印出 3;
- 继续执行 script 中的代码,打印出 7 和 8,至此第一轮代码执行实现;
- 执行微工作队列中的代码,首先打印出 4,如遇到 Promise,执行其中的同步代码,打印出 5,遇到定时器,将其退出到宏工作队列中,此时宏工作队列中有两个定时器;
- 执行宏工作队列中的代码,这里咱们须要留神是的第一个定时器的工夫为 100ms,第二个定时器的工夫为 10ms,所以先执行第二个定时器,打印出 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 时,阐明这个变量曾经没有价值,因而,在在机回收期下次再运行时,这个变量所占有的内存空间就会被释放出来。
- 这种办法会引起 循环援用 的问题:例如:
obj1
和obj2
通过属性进行互相援用,两个对象的援用次数都是 2。当应用循环计数时,因为函数执行完后,两个对象都来到作用域,函数执行完结,obj1
和obj2
还将会持续存在,因而它们的援用次数永远不会是 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];
};