对 CSS 工程化的了解
CSS 工程化是为了解决以下问题:
- 宏观设计:CSS 代码如何组织、如何拆分、模块构造怎么设计?
- 编码优化:怎么写出更好的 CSS?
- 构建:如何解决我的 CSS,能力让它的打包后果最优?
- 可维护性:代码写完了,如何最小化它后续的变更老本?如何确保任何一个共事都能轻松接手?
以下三个方向都是时下比拟风行的、普适性十分好的 CSS 工程化实际:
- 预处理器:Less、 Sass 等;
- 重要的工程化插件: PostCss;
- Webpack loader 等 。
基于这三个方向,能够衍生出一些具备典型意义的子问题,这里咱们一一来看:
(1)预处理器:为什么要用预处理器?它的呈现是为了解决什么问题?
预处理器,其实就是 CSS 世界的“轮子”。预处理器反对咱们写一种相似 CSS、但理论并不是 CSS 的语言,而后把它编译成 CSS 代码: 那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和原本用 JS 也能够实现所有性能,但最初却写 React 的 jsx 或者 Vue 的模板语法一样——为了爽!要想晓得有了预处理器有多爽,首先要晓得的是传统 CSS 有多不爽。随着前端业务复杂度的进步,前端工程中对 CSS 提出了以下的诉求:
- 宏观设计上:咱们心愿能优化 CSS 文件的目录构造,对现有的 CSS 文件实现复用;
- 编码优化上:咱们心愿能写出构造清晰、扼要易懂的 CSS,须要它具备高深莫测的嵌套层级关系,而不是无差别的一铺到底写法;咱们心愿它具备变量特色、计算能力、循环能力等等更强的可编程性,这样咱们能够少写一些无用的代码;
- 可维护性上:更强的可编程性意味着更优质的代码构造,实现复用意味着更简略的目录构造和更强的拓展能力,这两点如果能做到,天然会带来更强的可维护性。
这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器广泛会具备这样的个性:
- 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系 ;
- 反对定义 css 变量;
- 提供计算函数;
- 容许对代码片段进行 extend 和 mixin;
- 反对循环语句的应用;
- 反对将 CSS 文件模块化,实现复用。
(2)PostCss:PostCss 是如何工作的?咱们在什么场景下会应用 PostCss?
它和预处理器的不同就在于,预处理器解决的是 类CSS,而 PostCss 解决的就是 CSS 自身。Babel 能够将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是相似的事件:它能够编译尚未被浏览器广泛支持的先进的 CSS 语法,还能够主动为一些须要额定兼容的语法减少前缀。更强的是,因为 PostCss 有着弱小的插件机制,反对各种各样的扩大,极大地强化了 CSS 的能力。
PostCss 在业务中的应用场景十分多:
- 进步 CSS 代码的可读性:PostCss 其实能够做相似预处理器能做的工作;
- 当咱们的 CSS 代码须要适配低版本浏览器时,PostCss 的 Autoprefixer 插件能够帮忙咱们主动减少浏览器前缀;
- 容许咱们编写面向未来的 CSS:PostCss 可能帮忙咱们编译 CSS next 代码;
(3)Webpack 能解决 CSS 吗?如何实现? Webpack 能解决 CSS 吗:
- Webpack 在裸奔的状态下,是不能解决 CSS 的,Webpack 自身是一个面向 JavaScript 且只能解决 JavaScript 代码的模块化打包工具;
- Webpack 在 loader 的辅助下,是能够解决 CSS 的。
如何用 Webpack 实现对 CSS 的解决:
- Webpack 中操作 CSS 须要应用的两个要害的 loader:css-loader 和 style-loader
留神,答出“用什么”有时候可能还不够,面试官会狐疑你是不是在背答案,所以你还须要理解每个 loader 都做了什么事件:
- css-loader:导入 CSS 模块,对 CSS 代码进行编译解决;
- style-loader:创立style标签,把 CSS 内容写入标签。
在理论应用中,css-loader 的执行程序肯定要安顿在 style-loader 的后面。因为只有实现了编译过程,才能够对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无奈了解这坨货色的,它会无情报错。
说一下购物车的逻辑?
//vue中购物车逻辑的实现1. 购物车信息用一个数组来存储,数组中保留对象,对象中有id和count属性2. 在vuex中state中增加一个数据 cartList 用来保留这个数组3. 因为商品详情页须要用到退出购物车性能,所以咱们须要提供一个mutation, 用来将购物车信息退出 cartList中4. 退出购物车信息的时候,遵循如下规定: 如果购物车中曾经有了该商品信息,则数量累加,如果没有该商品信息,则新增一个对象5. 在商品详情页,点击退出购物车按钮的时候,调用vuex提供的addToCart这个mutation将以后的商品信息 (id count)传给addTocart this.$store.commit("addToCart", {id: , count:})// js中购物车逻辑的实现1.商品页点击“退出购物车”按钮,触发事件2.事件调用购物车“减少商品”的Js程序(函数、对象办法)3.向Js程序传递传递“商品id”、“商品数量”等数据4.存储“商品id”、“商品数量”到浏览器的localStorage中**展现购物车中的商品******1.关上购物车页面2.从localStorage中取出“商品Id”、“商品数量”等信息。3.调用服务器端“取得商品详情”的接口失去购物车中的商品信息(参数为商品Id)4.将取得的商品信息显示在购物车页面。**实现购物车中商品的购买******1.用户对购物车中的商品实现购买流程,产生购物订单2.革除localStorage中存储的曾经购买的商品信息备注1:购物车中商品存储的数据除了“商品id”、“商品数量”之外,依据产品要求还能够有其余的信息,例如残缺的商品详情(这样就不必掉服务器接口取得详情了)、购物车商品的过期工夫,超过工夫的购物车商品在下次关上网站或者购物车页面时被革除。备注2:购物车商品除了存储在localStorage中,依据产品的需要不同,也能够存储在sessionStorage、cookie、session中,或者间接向服务器接口发动申请存储在服务器上。何种状况应用哪种形式存储、有啥区别请本人剖析。
AJAX
题目形容:利用 XMLHttpRequest 手写 AJAX 实现
实现代码如下:
const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } }; xhr.send(); });};
for...in和for...of的区别
for…of 是ES6新增的遍历形式,容许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下
- for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
- for… in 会遍历对象的整个原型链,性能十分差不举荐应用,而 for … of 只遍历以后对象不会遍历原型链;
- 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包含原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for...in 循环次要是为了遍历对象而生,不适用于遍历数组;for...of 循环能够用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
PWA应用过吗?serviceWorker的应用原理是啥?
渐进式网络应用(PWA)
是谷歌在2015年底提出的概念。基本上算是web应用程序,但在外观和感觉上与原生app
相似。反对PWA
的网站能够提供脱机工作、推送告诉和设施硬件拜访等性能。
Service Worker
是浏览器在后盾独立于网页运行的脚本,它关上了通向不须要网页或用户交互的性能的大门。 当初,它们已包含如推送告诉和后盾同步等性能。 未来,Service Worker
将会反对如定期同步或天文围栏等其余性能。 本教程探讨的外围性能是拦挡和解决网络申请,包含通过程序来治理缓存中的响应。
陈说输出URL回车后的过程
1.读取缓存: 搜寻本身的 DNS 缓存。(如果 DNS 缓存中找到IP 地址就跳过了接下来查找 IP 地址步骤,间接拜访该 IP 地址。)2.DNS 解析:将域名解析成 IP 地址3.TCP 连贯:TCP 三次握手,繁难形容三次握手 客户端:服务端你在么? 服务端:客户端我在,你要连贯我么? 客户端:是的服务端,我要链接。 连贯买通,能够开始申请来4.发送 HTTP 申请5.服务器解决申请并返回 HTTP 报文6.浏览器解析渲染页面7.断开连接:TCP 四次挥手对于第六步浏览器解析渲染页面又能够聊聊如果返回的是html页面依据 HTML 解析出 DOM 树依据 CSS 解析生成 CSS 规定树联合 DOM 树和 CSS 规定树,生成渲染树依据渲染树计算每一个节点的信息依据计算好的信息绘制页面
对媒体查问的了解?
媒体查问由⼀个可选的媒体类型和零个或多个使⽤媒体性能的限度了样式表范畴的表达式组成,例如宽度、⾼度和颜⾊。媒体查问,增加⾃CSS3,容许内容的出现针对⼀个特定范畴的输出设备⽽进⾏裁剪,⽽不用扭转内容自身,适宜web⽹⻚应答不同型号的设施⽽做出对应的响应适配。
媒体查问蕴含⼀个可选的媒体类型和满⾜CSS3标准的条件下,蕴含零个或多个表达式,这些表达式形容了媒体特色,最终会被解析为true或false。如果媒体查问中指定的媒体类型匹配展现⽂档所使⽤的设施类型,并且所有的表达式的值都是true,那么该媒体查问的后果为true。那么媒体查问内的款式将会⽣效。
<!-- link元素中的CSS媒体查问 --> <link rel="stylesheet" media="(max-width: 800px)" href="example.css" /> <!-- 样式表中的CSS媒体查问 --> <style> @media (max-width: 600px) { .facet_sidebar { display: none; } }</style>
简略来说,应用 @media 查问,能够针对不同的媒体类型定义不同的款式。@media 能够针对不同的屏幕尺寸设置不同的款式,特地是须要设置设计响应式的页面,@media 是十分有用的。当重置浏览器大小的过程中,页面也会依据浏览器的宽度和高度从新渲染页面。
TCP和UDP的区别
UDP | TCP | |
---|---|---|
是否连贯 | 无连贯 | 面向连贯 |
是否牢靠 | 不牢靠传输,不应用流量管制和拥塞管制 | 牢靠传输(数据程序和正确性),应用流量管制和拥塞管制 |
连贯对象个数 | 反对一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
实用场景 | 实用于实时利用,例如视频会议、直播 | 实用于要求牢靠传输的利用,例如文件传输 |
new 一个函数产生了什么
结构调用:
- 发明一个全新的对象
- 这个对象会被执行 [[Prototype]] 连贯,将这个新对象的 [[Prototype]] 链接到这个构造函数.prototype 所指向的对象
- 这个新对象会绑定到函数调用的 this
- 如果函数没有返回其余对象,那么 new 表达式中的函数调用会主动返回这个新对象
0.1 + 0.2 === 0.3 嘛?为什么?
JavaScript 应用 Number 类型来示意数字(整数或浮点数),遵循 IEEE 754 规范,通过 64 位来示意一个数字(1 + 11 + 52)
- 1 符号位,0 示意负数,1 示意正数 s
- 11 指数位(e)
- 52 尾数,小数局部(即有效数字)
最大平安数字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1,转换成整数就是 16 位,所以 0.1 === 0.1,是因为通过 toPrecision(16) 去无效位之后,两者是相等的。
在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会产生有限循环,而后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度失落。
所以总结:精度失落可能呈现在进制转换和对阶运算中
compose
题目形容:实现一个 compose 函数
// 用法如下:function fn1(x) { return x + 1;}function fn2(x) { return x + 2;}function fn3(x) { return x + 3;}function fn4(x) { return x + 4;}const a = compose(fn1, fn2, fn3, fn4);console.log(a(1)); // 1+4+3+2+1=11
实现代码如下:
function compose(...fn) { if (!fn.length) return (v) => v; if (fn.length === 1) return fn[0]; return fn.reduce( (pre, cur) => (...args) => pre(cur(...args)) );}
对JSON的了解
JSON 是一种基于文本的轻量级的数据交换格局。它能够被任何的编程语言读取和作为数据格式来传递。
在我的项目开发中,应用 JSON 作为前后端数据交换的形式。在前端通过将一个合乎 JSON 格局的数据结构序列化为
JSON 字符串,而后将它传递到后端,后端通过 JSON 格局的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。
因为 JSON 的语法是基于 js 的,因而很容易将 JSON 和 js 中的对象弄混,然而应该留神的是 JSON 和 js 中的对象不是一回事,JSON 中对象格局更加严格,比如说在 JSON 中属性值不能为函数,不能呈现 NaN 这样的属性值等,因而大多数的 js 对象是不合乎 JSON 对象的格局的。
在 js 中提供了两个函数来实现 js 数据结构和 JSON 格局的转换解决,
- JSON.stringify 函数,通过传入一个合乎 JSON 格局的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不合乎 JSON 格局,那么在序列化的时候会对这些值进行对应的非凡解决,使其符合规范。在前端向后端发送数据时,能够调用这个函数将数据对象转化为 JSON 格局的字符串。
- JSON.parse() 函数,这个函数用来将 JSON 格局的字符串转换为一个 js 数据结构,如果传入的字符串不是规范的 JSON 格局的字符串的话,将会抛出谬误。当从后端接管到 JSON 格局的字符串时,能够通过这个办法来将其解析为一个 js 数据结构,以此来进行数据的拜访。
代码输入后果
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(resolve => { console.log("promise1"); resolve();}).then(function() { console.log("promise2");});console.log('script end')
输入后果如下:
script startasync1 startasync2promise1script endasync1 endpromise2setTimeout
代码执行过程如下:
- 结尾定义了async1和async2两个函数,然而并未执行,执行script中的代码,所以打印出script start;
- 遇到定时器Settimeout,它是一个宏工作,将其退出到宏工作队列;
- 之后执行函数async1,首先打印出async1 start;
- 遇到await,执行async2,打印出async2,并阻断前面代码的执行,将前面的代码退出到微工作队列;
- 而后跳出async1和async2,遇到Promise,打印出promise1;
- 遇到resolve,将其退出到微工作队列,而后执行前面的script代码,打印出script end;
- 之后就该执行微工作队列了,首先打印出async1 end,而后打印出promise2;
- 执行完微工作队列,就开始执行宏工作队列中的定时器,打印出setTimeout。
Unicode、UTF-8、UTF-16、UTF-32的区别?
(1)Unicode
在说Unicode
之前须要先理解一下ASCII
码:ASCII 码(American Standard Code for Information Interchange
)称为美国规范信息替换码。
- 它是基于拉丁字母的一套电脑编码零碎。
- 它定义了一个用于代表常见字符的字典。
- 它蕴含了"A-Z"(蕴含大小写),数据"0-9" 以及一些常见的符号。
- 它是专门为英语而设计的,有128个编码,对其余语言无能为力
ASCII
码能够示意的编码无限,要想示意其余语言的编码,还是要应用Unicode
来示意,能够说Unicode
是ASCII
的超集。
Unicode
全称 Unicode Translation Format
,又叫做对立码、万国码、繁多码。Unicode
是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了对立并且惟一的二进制编码,以满足跨语言、跨平台进行文本转换、解决的要求。
Unicode
的实现形式(也就是编码方式)有很多种,常见的是UTF-8、UTF-16、UTF-32和USC-2。
(2)UTF-8
UTF-8
是应用最宽泛的Unicode
编码方式,它是一种可变长的编码方式,能够是1—4个字节不等,它能够齐全兼容ASCII
码的128个字符。
留神: UTF-8
是一种编码方式,Unicode
是一个字符汇合。
UTF-8
的编码规定:
- 对于单字节的符号,字节的第一位为0,前面的7位为这个字符的
Unicode
编码,因而对于英文字母,它的Unicode
编码和ACSII
编码一样。 - 对于n字节的符号,第一个字节的前n位都是1,第n+1位设为0,前面字节的前两位一律设为10,剩下的没有提及的二进制位,全副为这个符号的
Unicode
码 。
来看一下具体的Unicode
编号范畴与对应的UTF-8
二进制格局 :
编码范畴(编号对应的十进制数) | 二进制格局 |
---|---|
0x00—0x7F (0-127) | 0xxxxxxx |
0x80—0x7FF (128-2047) | 110xxxxx 10xxxxxx |
0x800—0xFFFF (2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000—0x10FFFF (65536以上) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那该如何通过具体的Unicode
编码,进行具体的UTF-8
编码呢?步骤如下:
- 找到该
Unicode
编码的所在的编号范畴,进而找到与之对应的二进制格局 - 将
Unicode
编码转换为二进制数(去掉最高位的0) - 将二进制数从右往左一次填入二进制格局的
X
中,如果有X
未填,就设为0
来看一个理论的例子:
“马” 字的Unicode
编码是:0x9A6C
,整数编号是39532
(1)首选确定了该字符在第三个范畴内,它的格局是 1110xxxx 10xxxxxx 10xxxxxx
(2)39532对应的二进制数为1001 1010 0110 1100
(3)将二进制数填入X中,后果是:11101001 10101001 10101100
(3)UTF-16
1. 立体的概念
在理解UTF-16
之前,先看一下立体的概念: Unicode
编码中有很多很多的字符,它并不是一次性定义的,而是分区进行定义的,每个区寄存65536(216)个字符,这称为一个立体,目前总共有17 个立体。
最后面的一个立体称为根本立体,它的码点从0 — 216-1,写成16进制就是U+0000 — U+FFFF
,那剩下的16个立体就是辅助立体,码点范畴是 U+10000—U+10FFFF
。
2. UTF-16 概念:
UTF-16
也是Unicode
编码集的一种编码模式,把Unicode
字符集的形象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode
字符的码位须要1个或者2个16位长的码元来示意,因而UTF-16
也是用变长字节示意的。
3. UTF-16 编码规定:
- 编号在
U+0000—U+FFFF
的字符(罕用字符集),间接用两个字节示意。 - 编号在
U+10000—U+10FFFF
之间的字符,须要用四个字节示意。
4. 编码辨认
那么问题来了,当遇到两个字节时,怎么晓得是把它当做一个字符还是和前面的两个字节一起当做一个字符呢?
UTF-16
编码必定也思考到了这个问题,在根本立体内,从 U+D800 — U+DFFF
是一个空段,也就是说这个区间的码点不对应任何的字符,因而这些空段就能够用来映射辅助立体的字符。
辅助立体共有 220 个字符位,因而示意这些字符至多须要 20 个二进制位。UTF-16
将这 20 个二进制位分成两半,前 10 位映射在 U+D800 — U+DBFF
,称为高位(H),后 10 位映射在 U+DC00 — U+DFFF
,称为低位(L)。这就相当于,将一个辅助立体的字符拆成了两个根本立体的字符来示意。
因而,当遇到两个字节时,发现它的码点在 U+D800 —U+DBFF
之间,就能够晓得,它前面的两个字节的码点应该在 U+DC00 — U+DFFF
之间,这四个字节必须放在一起进行解读。
5. 举例说明
以 "" 字为例,它的 Unicode
码点为 0x21800
,该码点超出了根本立体的范畴,因而须要用四个字节来示意,步骤如下:
- 首先计算超出局部的后果:
0x21800 - 0x10000
- 将下面的计算结果转为20位的二进制数,有余20位就在后面补0,后果为:
0001000110 0000000000
- 将失去的两个10位二进制数别离对应到两个区间中
U+D800
对应的二进制数为1101100000000000
, 将0001000110
填充在它的后10 个二进制位,失去1101100001000110
,转成 16 进制数为0xD846
。同理,低位为0xDC00
,所以这个字的UTF-16
编码为0xD846 0xDC00
(4) UTF-32
UTF-32
就是字符所对应编号的整数二进制模式,每个字符占四个字节,这个是间接进行转换的。该编码方式占用的贮存空间较多,所以应用较少。
比方“马” 字的Unicode编号是:U+9A6C
,整数编号是39532
,间接转化为二进制:1001 1010 0110 1100
,这就是它的UTF-32编码。
(5)总结
Unicode、UTF-8、UTF-16、UTF-32有什么区别?
Unicode
是编码字符集(字符集),而UTF-8
、UTF-16
、UTF-32
是字符集编码(编码规定);UTF-16
应用变长码元序列的编码方式,相较于定长码元序列的UTF-32
算法更简单,甚至比同样是变长码元序列的UTF-8
也更为简单,因为其引入了独特的代理对这样的代理机制;UTF-8
须要判断每个字节中的结尾标记信息,所以如果某个字节在传送过程中出错了,就会导致前面的字节也会解析出错;而UTF-16
不会判断结尾标记,即便错也只会错一个字符,所以容错能力教强;- 如果字符内容全副英文或英文与其余文字混合,但英文占绝大部分,那么用
UTF-8
就比UTF-16
节俭了很多空间;而如果字符内容全副是中文这样相似的字符或者混合字符中中文占绝大多数,那么UTF-16
就占优势了,能够节俭很多空间;
计算属性和watch有什么区别?以及它们的使用场景?
// 区别 computed 计算属性:依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值产生扭转,下一次获取computed的值时才会从新计算computed的值。 watch 侦听器:更多的是察看的作用,无缓存性,相似与某些数据的监听回调,每当监听的数据变动时都会执行回调进行后续操作//使用场景 当须要进行数值计算,并且依赖与其它数据时,应该应用computed,因为能够利用computed的缓存属性,防止每次获取值时都要从新计算。 当须要在数据变动时执行异步或开销较大的操作时,应该应用watch,应用watch选项容许执行异步操作(拜访一个API),限度执行该操作的频率,并在失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。
代码输入后果
const async1 = async () => { console.log('async1'); setTimeout(() => { console.log('timer1') }, 2000) await new Promise(resolve => { console.log('promise1') }) console.log('async1 end') return 'async1 success'} console.log('script start');async1().then(res => console.log(res));console.log('script end');Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .catch(4) .then(res => console.log(res))setTimeout(() => { console.log('timer2')}, 1000)
输入后果如下:
script startasync1promise1script end1timer2timer1
代码的执行过程如下:
- 首先执行同步带吗,打印出script start;
- 遇到定时器timer1将其退出宏工作队列;
- 之后是执行Promise,打印出promise1,因为Promise没有返回值,所以前面的代码不会执行;
- 而后执行同步代码,打印出script end;
- 继续执行上面的Promise,.then和.catch冀望参数是一个函数,这里传入的是一个数字,因而就会产生值浸透,将resolve(1)的值传到最初一个then,间接打印出1;
- 遇到第二个定时器,将其退出到微工作队列,执行微工作队列,按程序顺次执行两个定时器,然而因为定时器工夫的起因,会在两秒后先打印出timer2,在四秒后打印出timer1。
函数柯里化
什么叫函数柯里化?其实就是将应用多个参数的函数转换成一系列应用一个参数的函数的技术。还不懂?来举个例子。
function add(a, b, c) { return a + b + c}add(1, 2, 3)let addCurry = curry(add)addCurry(1)(2)(3)
当初就是要实现 curry 这个函数,使函数从一次调用传入多个参数变成屡次调用每次传一个参数。
function curry(fn) { let judge = (...args) => { if (args.length == fn.length) return fn(...args) return (...arg) => judge(...args, ...arg) } return judge}
ES6新个性
1.ES6引入来严格模式 变量必须申明后在应用 函数的参数不能有同名属性, 否则报错 不能应用with语句 (说实话我根本没用过) 不能对只读属性赋值, 否则报错 不能应用前缀0示意八进制数,否则报错 (说实话我根本没用过) 不能删除不可删除的数据, 否则报错 不能删除变量delete prop, 会报错, 只能删除属性delete global[prop] eval不会在它的外层作用域引入变量 eval和arguments不能被从新赋值 arguments不会主动反映函数参数的变动 不能应用arguments.caller (说实话我根本没用过) 不能应用arguments.callee (说实话我根本没用过) 禁止this指向全局对象 不能应用fn.caller和fn.arguments获取函数调用的堆栈 (说实话我根本没用过) 减少了保留字(比方protected、static和interface)2.对于let和const新增的变量申明3.变量的解构赋值4.字符串的扩大 includes():返回布尔值,示意是否找到了参数字符串。 startsWith():返回布尔值,示意参数字符串是否在原字符串的头部。 endsWith():返回布尔值,示意参数字符串是否在原字符串的尾部。5.数值的扩大 Number.isFinite()用来查看一个数值是否为无限的(finite)。 Number.isNaN()用来查看一个值是否为NaN。6.函数的扩大 函数参数指定默认值7.数组的扩大 扩大运算符8.对象的扩大 对象的解构9.新增symbol数据类型10.Set 和 Map 数据结构 ES6 提供了新的数据结构 Set。它相似于数组,然而成员的值都是惟一的,没有反复的值。 Set 自身是一个构造函数,用来生成 Set 数据结构。 Map它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。11.Proxy Proxy 能够了解成,在指标对象之前架设一层“拦挡”,外界对该对象的拜访 都必须先通过这层拦挡,因而提供了一种机制,能够对外界的拜访进行过滤和改写。 Proxy 这个词的原意是代理,用在这里示意由它来“代理”某些操作,能够译为“代理器”。 Vue3.0应用了proxy12.Promise Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更正当和更弱小。 特点是: 对象的状态不受外界影响。 一旦状态扭转,就不会再变,任何时候都能够失去这个后果。13.async 函数 async函数对 Generator 函数的区别: (1)内置执行器。 Generator 函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与一般函数截然不同,只有一行。 (2)更好的语义。 async和await,比起星号和yield,语义更分明了。async示意函数里有异步操作,await示意紧跟在前面的表达式须要期待后果。 (3)失常状况下,await命令前面是一个 Promise 对象。如果不是,会被转成一个立刻resolve的 Promise 对象。 (4)返回值是 Promise。 async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象不便多了。你能够用then办法指定下一步的操作。14.Class class跟let、const一样:不存在变量晋升、不能反复申明... ES6 的class能够看作只是一个语法糖,它的绝大部分性能 ES5 都能够做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。15.Module ES6 的模块主动采纳严格模式,不论你有没有在模块头部加上"use strict";。 import和export命令以及export和export default的区别
call apply bind
题目形容:手写 call apply bind 实现
实现代码如下:
Function.prototype.myCall = function (context, ...args) { if (!context || context === null) { context = window; } // 发明惟一的key值 作为咱们结构的context外部办法名 let fn = Symbol(); context[fn] = this; //this指向调用call的函数 // 执行函数并返回后果 相当于把本身作为传入的context的办法进行调用了 return context[fn](...args);};// apply原理统一 只是第二个参数是传入的数组Function.prototype.myApply = function (context, args) { if (!context || context === null) { context = window; } // 发明惟一的key值 作为咱们结构的context外部办法名 let fn = Symbol(); context[fn] = this; // 执行函数并返回后果 return context[fn](...args);};//bind实现要简单一点 因为他思考的状况比拟多 还要波及到参数合并(相似函数柯里化)Function.prototype.myBind = function (context, ...args) { if (!context || context === null) { context = window; } // 发明惟一的key值 作为咱们结构的context外部办法名 let fn = Symbol(); context[fn] = this; let _this = this; // bind状况要简单一点 const result = function (...innerArgs) { // 第一种状况 :若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符应用,则不绑定传入的 this,而是将 this 指向实例化进去的对象 // 此时因为new操作符作用 this指向result实例对象 而result又继承自传入的_this 依据原型链常识可得出以下论断 // this.__proto__ === result.prototype //this instanceof result =>true // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true if (this instanceof _this === true) { // 此时this指向指向result的实例 这时候不须要扭转this指向 this[fn] = _this; this[fn](...[...args, ...innerArgs]); //这里应用es6的办法让bind反对参数合并 } else { // 如果只是作为一般函数调用 那就很简略了 间接扭转this指向为传入的context context[fn](...[...args, ...innerArgs]); } }; // 如果绑定的是构造函数 那么须要继承构造函数原型属性和办法 // 实现继承的形式: 应用Object.create result.prototype = Object.create(this.prototype); return result;};//用法如下// function Person(name, age) {// console.log(name); //'我是参数传进来的name'// console.log(age); //'我是参数传进来的age'// console.log(this); //构造函数this指向实例对象// }// // 构造函数原型的办法// Person.prototype.say = function() {// console.log(123);// }// let obj = {// objName: '我是obj传进来的name',// objAge: '我是obj传进来的age'// }// // 一般函数// function normalFun(name, age) {// console.log(name); //'我是参数传进来的name'// console.log(age); //'我是参数传进来的age'// console.log(this); //一般函数this指向绑定bind的第一个参数 也就是例子中的obj// console.log(this.objName); //'我是obj传进来的name'// console.log(this.objAge); //'我是obj传进来的age'// }// 先测试作为结构函数调用// let bindFun = Person.myBind(obj, '我是参数传进来的name')// let a = new bindFun('我是参数传进来的age')// a.say() //123// 再测试作为一般函数调用// let bindFun = normalFun.myBind(obj, '我是参数传进来的name')// bindFun('我是参数传进来的age')
手写题:数组扁平化
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));