1.ES6特性
1.1 import 和 require 区别
require是执行函数然后赋值给左边的过程, 无论 module.exports 赋值的是匿名函数 | 函数 | Object
,require 都会把它直接获取到 , 该函数没有考虑 default
而import 是编译时的解构过程,必须放在文件开头,
export
不允许接匿名函数,即禁止a = ()=>1 || {sth:sth}; export a
或export function(){}
,可替换为 export {a}
和export function a(){}
,
它区分default 和非 default :
不同的export | 对应的 import 方式 | 结果 |
---|---|---|
export default {a,b} |
import a from './xx' |
log : {a:a,b:b} |
export {a}; export {b}; |
import {a,b} from './xx' |
a,b 是两个不同的变量, 解构了 |
import * as a from './xx' |
{ a: [Function: a], b: [Function: b], default: { a: [Function: a], b: [Function: b] } } |
require 输出的是一个值的复制,而 import 输出的是值的只读引用,类似于Unix
的符号链接,会随着原始值变化
循环加载时二者的表现不同,require 模块遇到循环加载时返回的时当前已经执行的部分的值,而import 因为返回的是值的引用,只要引用存在就能执行.
1.2 正则表达式
/a+/y
es6中新增加了y
作为粘连(sticky)修饰符, 和 g 一样都是全局匹配, 但 y 要求每次匹配都都从上一次匹配成功的紧挨着的位置开始
1.2.1 具名组匹配
var e = /(?\d{4})-(?\d{2})/
var a = e.exec('2019-01'))
console.log(a.groups) // { year:"2019", month:"01" }
//也可以解构赋值
let { groups:{ year, month} } = a
//字符串替换时, 使用 $<组名> 来引用具名组
'2019-02'.replace(e, '$-$') // 02-2019
//正则表达式内部引用具名组匹配, 下面两个式子等价
; /(\w+)!(\1)/.exec('abc!abc') // ["abc!abc", "abc", "abc", index: 0, input: "abc!abc", groups: undefined]
; /(?\w+)!(\k)/.exec('abc!abc') //["abc!abc", "abc", "abc", index: 0, input: "abc!abc", groups: {…}]
//由上式可知, js 的正则可以匹配如上述的上下文相关文法了
node8不支持. 但 chrome 和 node10 已支持
实在有想法的人也可以用正则表达式匹配回文字符串, 但这样匹配实质上等于/(.)(.)(.)\3\2\1/
, 也就是说对字符串长度有要求, 且可读性较差, 不如str.split('').reverse().join('') === str
1.3 Math 数学运算扩展
-
8**2 == 64
加入了指数运算符 -
Math.sinh(x)等
加入了双曲函数方法
1.4 特殊的条件表达式
Object.is 和 ===
的区别:
+0 === -1 //true
NaN === NaN // false
typeof null === "object" // true
Object.is(+0,-0) // false
Object.is(NaN, NaN) // true
1.5 Symbol
-
Symbol
是 js 第7种数据类型, 它是一种类似于字符串的原始类型, 不是对象, 不能添加属性. -
Symbol
的出发点是用于表示独一无二的值, 常用在 Object 的 key 值. 因此Symbol('123') !== Symbol('123')
-
Symbol
值不能和其它类型的值进行运算 -
Symbol
值可以显示转化为字符串String(Symbol('1'))
或布尔Boolean(Symbol())
, 但不能转化为数值 -
Symbol.for('foo')
全局搜索以该字符串作为名称的 Symbol 值, 反向的有Symbol.keyFor(sym)
- Symbol.toStringTag , 这个属性可以用于定制
[object Object]
或[object Array]
中 object 后面的字符串
1.6 Set
- Set 的创建
new Set([1,2,3]);
而new Set(1,2,3)
不行,因为第一个参数必须部署了iterable
接口 - Set 的属性: 数组的
arr.length
而set 有set.size
- Set 的方法:
-
add
delete
has
clear
forEach
- map 和 filter
let set = new Set([1,2,3]) set = new Set([...set].map(x => x*2) //或 filter(x=>x%2==0)
-
- Set 的应用:
- 数组去重
let unique = [...new Set([1,2,2,3,3,4])]
- 集合求 并集
let union = new Set([...a,...b])
- 集合求 交集
let intersect = new Set([...a]).filter(x=>b.has(x))
- 集合求 差集
let difference = new Set([...a]).filter(x=> !b.has(x))
- 数组去重
1.8 Map
- Map 构造
new Map([ ['key','value'], ['key2','value'] ])
要求输入参数具有 Iterator 接口并且每个成员都是一个双元素数组 - Map 属性
size
- Map 方法
set
get
has
delete
clear
forEach
- Map 和 Set 都有的方法
keys()
values()
entries()
, 都返回遍历器对象, 同时对 map 和 set 使用Object.keys
不会得到想要的结果
1.8.1 WeakMap 典型应用:为 DOM 注册事件的 listener
let myWeakMap = new WeakMap()
let el = document.getElementById('logo')
myWeakMap.set(el, { timesClicked : 0 } )
el.onclick = function(){
myWeakMap.get(el).timesClicked++
}
这样的好处是当该 DOM 被删除时,对应的计数器变量也能被释放内存
附: 主动触发事件:
var click = new MouseEvent('click')
el.dispatchEvent(click)
1.9 Proxy
1.9.1 Proxy 可以拦截的操作
-
get
,set
,has
(拦截propKey in proxy
的操作),deleteProperty
(拦截delete target[key]
) -
ownKeys
(只有 Object.keys()返回对象的可遍历属性,其它接口都返回全部属性,包括:Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()
-
apply(target,object,args)
拦截函数调用操作,包括f()
,f.call(o,...args)
,f.apply()
-
construct
拦截 new 操作
1.9.2 proxy和Object.defineProperties区别
- proxy创建了一个新的对象
- proxy对this的处理 在Proxy 代理的情况下,目标对象内部的 this 会指向 Proxy 代理
- proxy支持的属性更多
答:后者定义get
set
writeable
value
enumerable
configurable
而 Proxy 和后者的交集只有 get 和 set - proxy 定义 get 和 set 时的
propKey
可以是参数, 而后者必须是一个指定的字符串, 参数也比较少(set : function (newValue) { }
)
1.9.3 Proxy应用
- P238 拦截数组读取负数索引(defineProperty 无法实现)
function proxyArray(target) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
},
set(target,propKey,value){
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return target[propKey] = value
}
};
return new Proxy(target, handler);
}
let arr = proxyArray([1,2,3]);
arr[-1] // 3
1.10 Reflect 能否还原proxy或defineP的get和set?
答: 不能
1.11 Promise
1.11.1 Promise.all()
var p = Promise.all([p1,p2,p3])
处理逻辑类似于&&运算, 只有p1 p2 p3都变成 Fulfilled.
相对的,Promise.race()
就类似于或运算
1.12 Iterator
1.12.1 关键词
it.next()
// { value : '', done : false }
一般部署了 next 接口的对象就可以视作 Iterator
部署了[Symbol.iterator]接口的对象就可以被遍历
1.12.2 Object.keys和values和Iterator有关吗?for in呢?说到底,哪些有关,哪些无关?
- 和
o[Symbol.iterator]
有关:[...o]
,for of
,yield* [1,2]
,Array.from()
- 和
原生[Symbol.iterator]
有关:
o.keys(),o.values(),o.entries()
- 无关:
for in
,Object.keys(a),Object.values(a),Object.entries(a)
测试代码
var a = [1,2,3]
a[Symbol.iterator] = ()=>{ return { next: ()=>{ return {value:1,done:Math.random()>0.3}} } }
[...a] // 可能为[], [1], [1,1]
1.12.3 实现类python的range函数
优点: 相对创建数组来说更节省内存
- 原生 Iterator 实现 next 的方法:
function range(start,stop){
var o = { value:start,stop:stop }
o[Symbol.iterator] = function(){
return {
next:()=>{
if(this.value < this.stop){
return { done:false, value:this.value++ }
}else { return { done:true } }
}
}
}
return o
}
for(let i of range(0,5)){
console.log(i) // 0,1,2,3,4
}
- 使用 Generator 的话更简洁:
function range(start,stop){
return {
*[Symbol.iterator](){
for(let i=start;i
1.12.4 为 Object 添加类似 map 的遍历接口
Object.prototype[Symbol.iterator] = function * () {
for(let i in this){
yield [i,this[i]]
}
}
var o = { 'Li': { age:23 }, 'Wang' : { age:24 } } ;
[...o].filter(x=>x[1].age<=23) // [['Li',{age:23}]]
1.13 Generator 和 Iterator 的关系
Generator 函数就是 Iterator 遍历器生成函数, Generator 函数执行后, 返回一个遍历器对象,该对象本身也具有[Symbol.iterator]属性,执行后返回自身
next()方法的参数会作为上一条 yield 语句的返回值而存在
1.13.1 fibonacci
function * fibonacci(){
let [prev,curr] = [0,1]
while(1) {
[prev,curr] = [curr, prev+curr]
yield prev
}
}
for(let n of fibonacci()) {
if(n>10) break;
console.log(n); // 1,1,2,3,5,8
}
1.14 async await
一比较就会发现,async函数就是将 Generator 函数的星号(*
)替换成async,将yield
替换成await
,另外它内置了执行器: spawn 函数(不用使用co
模块了)
2. 函数节流throttle
和函数去抖 debounce
参考掘金 知乎专栏
定义:
css-tricks.com
实现:
function throttle(fn, interval = 300) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval);
};
}
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
如果设置16.7ms 的话,更流畅的方法:
requestAnimationFrame()
The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
可以将它看做一个钩子,刚好卡在浏览器重绘前向我们的操作伸出橄榄枝。
函数 curry 化
Function.prototype.curry = function(){
var args = arguments, that = this
return function( ){
return that.call(this, ...args, ...arguments)
}
}
使用 js 添加 css 样式表
1. 在head
中插入新的link
/**
>在中使用标签引入一个外部样式文件
@param {string} url - css文件的链接
*/
function includeLinkStyle(url) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
document.getElementsByTagName("head")[0].appendChild(link);
}
缺点: 会被屏蔽掉. 原因参考Content Security Policy 入门教程
2. addStyle
var nums = document.styleSheets.length;
var position = document.styleSheets[nums - 1].cssRules.length
/**
>添加一条 css 规则
@param {string} str - css样式
*/
function addStyle(str) {
document.styleSheets[nums - 1].insertRule(str, position++);
}
要求 Content-Security-Policy 的 script 相关规则如下:content-security-policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'
即
- 允许执行页面内嵌的