ES6知识点学习笔记分享
2022-10-13 10:40:07 0 举报
AI智能生成
ES6知识点学习笔记分享
作者其他创作
大纲/内容
注意事项
in 运算符
判断对象是否为数组/对象的元素/属性: 格式:(变量 in 对象), 当“对象”为数组时,“变量”指的是数组的“索引”; 当“对象”为对象是,“变量”指的是对象的“属性”。 var arr = ["a","b","2","3","str"]; var result = ("b" in arr); var result1 = (4 in arr);
... 扩展运算符
数组中的空位处理
数组包含空位map方法遍历,空位是会跳过的。
由于空位的处理规则非常不统一,所以建议避免出现空位。
Function构造函数
Let和const命令
在ES6之前,只有全局作用域和函数作用域(在函数内声明的变量不会影响到函数外的变量, 同时函数外的范围也影响不到函数内部的变量)
var的变量作用域在代码块之外都能用,全局变量
只在Let命令所在的代码块内有效
为什么:1.内层变量可能会覆盖外层变量2.用来计数的循环变量泄露为全局变量(循环结束还不消失,还能继续调用到)
很合适使用循环计数器
不存在"变量提升"现象
暂时性死去:如果块级作用域内存在let命令,他所生命的变量就绑定这个区域,不在接受外部的影响
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在,当时不可获取, 自由等到声明变量的那一行代码出现,才可以获取和使用该变量
不允许重复声明:Let不允许在相同作用域内重复声明同一个变量
外层代码不受内层代码的影响,外层作用域无法读取到内层作用域变量.而var却会受影响
ES6允许块级作用域任意嵌套
块级作用域外部无法调用块级作用域内部定义的函数
const用来声明常量.一旦声明就不可以改变
一旦声明就必须初始化,不能留到以后赋值,不然就报错
const的作用域和let相同,自在声明所在的块级作用域内有效
同样存在暂时性死区,常量同样不提升,只能在声明后使用,在同一块级作用域内不可以重复声明变量
对于复合类型的变量(如{},[] 对象),变量名不指向数据,而是指向数据所在的地址. const命令只是保证变量名指向的地址不变,并不能保证其中的数据不变.
如果要想使复合变量的内容也不能够变化,可以使用Object.freeze方法
ES5只有两种声明变量的方式,var命令和function命令,ES6除了添加let和const命令还有import命令和class命令
跨模块调用变量:需要用到import 关键字
全局对象属性:在ES5中,全局对象的属性与全局变量是等价的
在node模块中,全局变量必须使用global变量
变量的解构赋值
解构赋值允许存在默认值
var [foo=true]=[]
数组的解构赋值(适用于let const var)
只要某种数据结构具有Iterator接口,都可以采用数组的形式解构赋值,基本都是采用iterator对象来赋值,如果是数组那么也是转化为iterator来处理
let [a,b,c]=[1,2,3],就相当于给a=1,b=2,c=3
let [x,,y]=['foo','bar','baz'],就相当于给x='foo',y='baz'
let[head,...tail]=[1,2,3,4],相当于head=1,tail=[2,3,4]
对象的解构赋值
对象的解构与数组有一个重要的不同.数组的元素是按照次序排列的,变量的取值由她的位置决定,而对象没有次序,变量必须与属性同名,才能取到值
var {foo,bar}={foo:'aaa',bar:'bbb'}
var {foo,bar}={foo:'aaa',bar:'bbb'} 相当于var {foo:foo,bar:bar}={foo:'aaa',bar:'bbb'}的简写 baz //aaa 也就是说对象的解构赋值的内部机制,是先找到同名属性,然后再赋值给对应的变量。真正被赋值的是后者,而不是前者。
其实是通过属性名来区分,然后来挨个的分配变量的。
一旦赋值的变量以前申明过,就不能重新申明。
解构也可以用于嵌套结构的对象
let obj={} let arr=[] ({foo:obj.prop,bar:arr[0]}={foo:123,bar:true}) obj //{prop:123} arr //[true]
对象的解构也可以有默认值
var {x=3}={} var {x,y=5}={x:1} var {message:msg='something went wrong'}={} x//1 y//5 msg// "something went wrong" var a={}; ({name:a.name,age:a.age,address:a.address}={name:'jiangliqing',age:'24',address:'suzhoukejixueyuan'})
默认值的条件是对象严格等于undefined
var {x=3}={x:undefined} //x =3 var {x=3}={x:null}//x=null
如果解析失败,,变量的值就等于undefined
var {foo}={bar:'baz'}
字符串的解构赋值
const [a,b,c,d,e]='hello' a//'h' b//'e' c//'l' d//'l' e//'o'
let {length:len}='hello' len //5
数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值或布尔值,则会先转为对象 规则:只要等号右边的值不是对象,就先将其转为对象。由于undefined和null无法转为对象,所以对他们进行解构赋值都会报错
let {toString:s}=123 s===Number.prototype.toString //true let {toString:s}=true s===boolean.prototype.toString //true
函数参数的解构赋值(函数的参数也是可以解构赋值)
function add([x,y]){ return x+y } add([1,2]) [[1,2],[3,4]].map(([a,b])=>a+b) //[3,7]
函数参数的解构赋值也可以使用默认值
functionmove({x=0,y=0}={}){ return [x,y ] }
圆括号问题
如果模式种出现圆括号怎么处理。ES6的规则是,只要有可能导致解构歧义,就不得使用圆括号。
变量声明语句中,模式不能带有圆括号
var [(a)]=[1] //报错 var{x:(c)}={} //报错 var {o:({p:p})}={o:{p:2}}//报错
函数参数中,模式不能带有圆括号
function f([(z)]){ return z;}
不能将整个模式或嵌套模式中的一层放在圆括号中。
({p:a})={p:42}
([a])=[5]
可以使用圆括号的情况
赋值语句的非模式部分可以使用圆括号
[(b)]=[3] ({p:(d)}={}) [(parseInt.prop)]=[3] //以上都正确
用途
可以用来变换变量的值
[x,y]=[x,y]
函数可以一次性返回多个值
function example(){ return [1,2,3]} var [a,b,c]=example()
function example(){ return {foo:1,bar:2}} var {foo,bar}=example()
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
function f([x,y,z]){...} f([1,2,3])
function f({x,y,z}){...} f({z:3,y:2,x:1})
提取JSON中的数据尤其有用
var jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData;
函数参数的默认值
指定参数的默认值,就避免了在函数体内部再写 var foo = config.foo || 'default foo';这样的语句。
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config }) { // ... do stuff };
遍历map解构
任何部署了Iterator接口的对象,都可以用for...of循环遍历。
var map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + " is " + value); }
输入模块的指定方法,其他的方法可以不用加载
加载模块时,往往需要指定输入那些方法。解构赋值使得输入语句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map"); //这样的话就只加载 SourceMapConsumer和SourceNode 方法
字符串的扩展
字符串的Unicode表示法
JavaScript允许采用\uxxxx形式表示一个字符,xxxx为码点, 如果xxxx超过ffff范围之外就要用双字节的形式表达,把码点放入{}中包起来, 就能正确解读该字符,如\u{41}\u{42}\u{43} //"ABC"
codePointAt()
JavaScript认为UTF-16格式存储,每个字符固定为2字节, 如果超出需要4个字节存储,JavaScript就认为他们是2个字符
ES6提供了codePointAt方法,能够正确处理4个字节存储的字符, codePointAt方法会正确的返回32位的UTF-16字符的码点 charCodeAt方法只能返回16位的utf-8字符的码点(32bit位置会被拆分了) (for of 循环,可以正确识别的32位的UTF-16字符)
var a = "\uD842\uDFB7" var b = "\u{20BB7}" a.codePointAt(0).toString() //134071 b.codePointAt(0).toString()//134071
var a = "\uD842\uDFB7a" for (let ch of a) { console.log(ch.codePointAt(0).toString(16)) } //20bb7 //61
可以用来测试一个字符由2个字节还是4个字节
function is32Bit(c) { return c.codePointAt(0) > 0xffff }
String.formCodePoint()
String.fromCharCode()从码点返回对应字符,当是这个方法不能识别32位的UTF-16字符(Unicode编号大于0xffff,不然会发生溢出截断)
String.fromCodePoint() 从码点返回对应字符,这个方法可以返回4位字节的Unicode码
String.fromCodePoint(0x78,0x1f680,0x79) 如果是多参数的话,那么会将他们变成一个字符串
字符串的遍历器接口
字符串可以使用for ...of循环遍历.这个遍历器最大的优点是可以识别大于0xffff的码点, 传统的for循环无法识别这样的码点.
at()
ES5对字符串提供了charAt()方法,返回字符串给定位置字符, 但是不能识别码点大于0xFFFF的字符,ES6给出了at()方法, 可以识别Unicode编号大于0xffff字符,返回正确的字符
'abc'.charAt(0) // "a" '𠮷'.charAt(0) // "\uD842"
normalize()
ES6提供字符串实例的normalize()方法,(可以有一个参数,这个参数有四个值可选) 用来将字符的不同表示方法统一为同样的形式,这称为Unicode正规化。
参数值:NFC,标准等价合成,返回多个简单字符的合成字符, 视觉和语义上的等价
参数值:NFD,标准等价分解,在标准等价的前提下, 返回合成字符分解出的多个简单字符
NFKC:兼容等价合成,指的是语义上等价,视觉上不等价
NFKD:兼容等价分解,在兼容等价的前提下, 返回合成字符分解出的多个简单字符
includes() startsWith() endsWith()
传统上,JS只有indexOf方法可用来确定一个字符串是否包含在另一个字符串中 ES6又提供了3种新方法.三种方法都支持第二个参数,表示开始搜索的位置
includes():返回boolean,表示是否找到了参数字符串
startsWith():返回boolean,表示参数是否在源字符串的头部
endsWith():返回boolean,表示参数字符是否在源字符串的尾部
repeat()
返回一个新字符串表示将原字符串重复n次, 如果参数的内容的字符串,会先将字符串转化数字
'jiang'.repeat('2')//jiangjiang
padStart() padEnd()
ES7退出了字符串补全长度的功能,如果每个字符串未达到指定长度,会在头部或尾部补全, 如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。 padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。如果省略第二个参数,则会用空格补全长度。
padStart用于头部补全
'x'.padStart(5,'ab')//ababx 'x'.padStart(4,'ab')//abax
padEnd用于在尾部补全
'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'
模板字符串
模板字符串是增强版的字符串,用反引号`标识,她可以当做普通字符串使用, 也可以用来定义多行字符串,或者在字符串中嵌入变量, 所有使用空格和缩进都会被保留在输出中, 变量中需要嵌入变量需写在${}中,大括号内可以放入任意的JavaScript表达式,可进行运算,引用对象属性, 如果大括号中的值不是字符串,将按照一般的规则转为字符串。 如果模板字符串中的变量没有声明,将报错。
普通字符串:`In JavaScript '\n' is a line-feed.`
多行字符串:`In JavaScript this is not legal.`
字符串中嵌入变量: var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`
模板字符串中还可以调用方法 function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。 // 写法一 let str = 'return ' + '`Hello ${name}!`'; let func = new Function('name', str); func('Jack') // "Hello Jack!" // 写法二 let str = '(name) => `Hello ${name}!`'; let func = eval.call(null, str); func('Jack') // "Hello Jack!"
模板编译
待看
标签模板
模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面, 该函数将被调用来处理这个模板字符串。 标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
alert`123` // 等同于 alert(123)
var a = 5; var b = 10; function tag(s, v1, v2) { console.log(s[0]);//"Hello" console.log(s[1]);//"world" console.log(s[2]);//"" console.log(v1);//15 console.log(v2);//50 return "OK";//OK }
标签模板的一个重要应用,就是过滤HTML字符串,防止恶意内容
String.raw()
方法可以作为处理模板字符串的基本方法,它会将所有变量替换, 而且对斜杠进行转义,方便下一步作为字符串来使用。
var name = 'jiangliqing' var age = 24 var address = 'suzhou keji xueyuan' console.log(`name ${name} ,age ${age},address ${address}`) console.log(String.raw`name ${name} ,age ${age},address ${address}`)
正则的扩展
构造函数
在ES5中,RegExp构造函数只能接受字符串作为参数, ES6允许RegExp构造函数接受正则表达式作为参数, 如果构造函数的第2个参数指定修饰符,就会忽略原有的修饰符,用新的
var reg=new RegExp("xyz","i"l) //等价于 var regex=/xyz/i
字符串的正则方法
字符串对象共有4个方法可以使用正则表达式: match();re;lace();search();split();
u修饰符
ES6 对正则表达式添加了u修饰符(和i g 一样是修饰符),来表示Unicode模式, 用来正确处理大于\uffff的Unicode字符.可以正确处理4个字节的UTF-16编码
/^\uD83D/.test(\uD83D\uDC2A) //ES5中不用U模式会解析成两个字符,所以true /^\uD83D/u.test(\uD83D\uDC2A)//用了U模式,会解析成一个字,为false
点字符
. 字符会匹配换行意外的任意字符,但是在大于0xffffd Unicode字符就不能识别了, 这时候需要加上U模式茶能识别
var s="吉" /^.$/.test(s)//false /^.$/u.test(s)//true
Unicode字符表示法
ES6新增了使用大括号表示Unicode字符的表示法, 在正则表达式中必须加U修饰符才能识别到
/\u{61}/.test('a') //false /\u{61}/u.test('a')//true 不加U这会匹配连续的61个u字符
量词
使用u修饰符后,所有量词都会正确识别大于码点大于0xFFFF的Unicode字符。
/a{2}/.test('aa') // true /a{2}/u.test('aa') // true /𠮷{2}/.test('𠮷𠮷') // false /𠮷{2}/u.test('𠮷𠮷') // true
预定义模式
u修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF的Unicode字符。
/^\S$/.test('𠮷') // false /^\S$/u.test('𠮷') // true
y修饰符
y修饰符的作用与g修饰符类似,也是全局匹配, 后一次匹配都从上一次匹配成功的下一个位置开始。 不同之处在于,g修饰符只要剩余位置中存在匹配就可, 而y修饰符确保匹配必须从剩余的第一个位置就必须是匹配的, 这也就是“粘连”的涵义。
'#x#'.split(/#/y) // [ '', 'x#' ] '##'.split(/#/y) // [ '', '', '' ]
sticky
与y修饰符相匹配,ES6的正则对象多了sticky属性,表示是否设置了y修饰符
var r = /hello\d/y; r.sticky // true
flags属性
ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符
// ES5的source属性 // 返回正则表达式的正文 /abc/ig.source // "abc"
// ES6的flags属性 // 返回正则表达式的修饰符 /abc/ig.flags // 'gi'
RegExp.escape()
待看
后行断言
待看
数值的扩展
二进制和八进制数值表示法
0o或者0O作为八进制的前缀
如果需要将0B或者0x前缀的字符串数值转为10进制,要用Number方法
Number('0xff') Number('0o10')
0b或0B作为二进制的前缀
Number.isFinite(),Number.isNan()
传统方法先调用Number()将非数值的值转为数值,再进行判断, 而这两个新方法只对数值有效,非数值一律返回false。
Number.isFinite():用来检查一个数值是否是非无穷
Number.isFinite(0.8)//true Number.isFinite(Infinity)//ture Number.isFinite(true)//false
Number.isNaN():用来价差一个数值是否为NaN
Number.isNaN(NaN)//true Number.isNaN(15)//false Number.isNan(true/0)//true Number.isNan(9/Nan)//true
Number.parseInt(), Number.parseFloat()
ES6将全局方法parseInt()和parseFloat(), 移植到Number对象上面,行为完全保持不变。
// ES5的写法 parseInt('12.34') // 12 parseFloat('123.45#') // 123.45 // ES6的写法 Number.parseInt('12.34') // 12 Number.parseFloat('123.45#') // 123.45
Number.isInteger()
用来判断一个值是否为整数。需要注意的是,在JavaScript内部, 整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger("15") // false Number.isInteger(true) // false
Number.EPSILON
引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。
function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON; } withinErrorMargin(0.1 + 0.2, 0.3) // true withinErrorMargin(0.2 + 0.2, 0.3) // false
Number.EPSILON的实质是一个可以接受的误差范围。
5.551115123125783e-17 < Number.EPSILON // true
Number.isSafeInteger()
JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
Math.pow(2,53) Number.MAX_SAFE_INTEGER 9007199254740992 9007199254740991
ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限
Number.isSafeInteger(9007199254740993) // false Number.isSafeInteger(990)
9007199254740993不是一个安全整数,这个数超出了精度范围, 在计算机内部,以9007199254740992的形式储存。
Math对象的扩展
Math.trunc()
方法用于去除一个数的小数部分,返回整数部分 对于非数值,Math.trunc内部使用Number方法将其先转为数值。 对于空值和无法截取整数的值,返回NaN。
Math.trunc(4.1) // 4 Math.trunc(4.9) // 4
Math.trunc('123.456')
Math.sign()
Math.sign方法用来判断一个数到底是正数、负数、还是零。
参数为正数,返回+1; 参数为负数,返回-1; 参数为0,返回0; 参数为-0,返回-0; 其他值,返回NaN
Math.cbrt()
Math.cbrt方法用于计算一个数的立方根。
Math.cbrt(1) // 1
Math.clz32()
方法返回一个数的32位无符号整数形式有多少个前导0,就是32为二进制前面有多少个0 对于小数,Math.clz32()只考虑整数部分
Math.clz32(0)//32 Math.clz32(1000)//22
Math.clz32(1<<1)//30 Math.clz32(1<<2)//29
Math.imul()
方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。
Math.imul(2, 4) // 8 Math.imul(-1, 8) // -8 Math.imul(-2, -2) // 4
Math.fround()
方法返回一个数的单精度浮点数形式。
Math.fround(0) // 0 Math.fround(1) // 1 Math.fround(1.337) // 1.3370000123977661 Math.fround(1.5) // 1.5 Math.fround(NaN) // NaN
Math.hypot()
方法返回所有参数的平方和的平方根。
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); // 7.0710678118654755 Math.hypot(); // 0 Math.hypot(NaN); // NaN Math.hypot(3, 4, 'foo'); // NaN
四个和对数相关的方法
Math.expm1(x)返回ex - 1,即Math.exp(x) - 1。
Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。
Math.log10(x)返回以10为底的x的对数。如果x小于0,则返回NaN。
Math.log2(x)返回以2为底的x的对数。如果x小于0,则返回NaN。
ES6新增了6个三角函数方法。
Math.sinh(x) 返回x的双曲正弦(hyperbolic sine) Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine) Math.tanh(x) 返回x的双曲正切(hyperbolic tangent) Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine) Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine) Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
指数运算符
ES7新增了一个指数运算符(**),目前Babel转码器已经支持。 指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。
2 ** 2 // 4 2 ** 3 // 8
let a = 2; a **= 2; // 等同于 a = a * a; let b = 3; b **= 3; // 等同于 b = b * b * b;
数组的扩展
Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。 只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组。
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 };
// ES5的写法 var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c'] // ES6的写法 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组。
// arguments对象 function foo() { var args = [...arguments]; } // NodeList对象 [...document.querySelectorAll('div')]
因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
Array.from({ length: 3 }); // [ undefined, undefined, undefinded ]
Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from([1, 2, 3], (x) => x * x)
Array.from([1, , 2, , 3], (n) => n || 0) // [1, 0, 2, 0, 3]
Array.of()
方法用于将一组值转为数组
Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array.of(3).length // 1
Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
数组实例的copyWithin()
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员), 然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
上面代码表示将从3号位直到数组结束的成员(4和5), 复制到从0号位开始的位置,结果覆盖了原来的1和2。 [1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
数组实例的find() findIndex()
find()
数组实例的find方法,用于找出第一个符合条件的数组成员。
它的参数是一个回调函数,所有数组成员依次执行该回调函数, 直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员, 则返回undefined。
[1, 4, -5, 10].find((n) => n < 0) // -5
[1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10
findIndex()
返回第一个符合条件的数组成员的位置
[1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2
这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。
另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。
因为数组IndexOf()查找元素的时候不能够发现NaN
[NaN].indexOf(NaN) // -1 [NaN].findIndex(y => Object.is(NaN, y)) // 0
数组实例的fill()
fill方法使用给定值,填充一个数组。 上面代码表明,fill方法用于空数组的初始化非常方便。
数组中已有的元素,会被全部抹去。
['a', 'b', 'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7]
fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
数组实例的entries(),keys()和values()
ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象
keys()
keys()是对键名的遍历
for (let index of ['a', 'b'].keys()) { console.log(index); } // 0 // 1
values()
values()是对键值的遍历
for (let elem of ['a', 'b'].values()) { console.log(elem); } // 'a' // 'b'
entries()
entries()是对键值对的遍历
for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"
数组实例的includes()
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。 该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数, 则表示倒数的位置,如果这时它大于数组长度,则会重置为从0开始。
[1, 2, NaN].includes(NaN); // true
数组的空位
数组的空位指,数组的某一个位置没有任何值。比如, Array构造函数返回的数组都是空位。 Array(3) // [, , ,]
注意,空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值
ES6则是明确将空位转为undefined。 Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。
Array.from(['a',,'b']) // [ "a", undefined, "b" ]
扩展运算符(...)也会将空位转为undefined。
[...['a',,'b']] // [ "a", undefined, "b" ]
copyWithin()会连空位一起拷贝。
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
fill()会将空位视为正常的数组位置。
new Array(3).fill('a') // ["a","a","a"]
for...of循环也会遍历空位。
let arr = [, ,]; for (let i of arr) { console.log(1); }
entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。
数组推导
允许通过现有数组生成新数组,目前只支持数组 //node 不能用
var a1=[1,2,3,4] var a2=[for(i of a1) i*2] a2 //[2,4,6,8]
函数的扩展
函数参数的默认值,即直接写在参数定义的后面。
用法: 参数变量是默认声明的,所以不能用let或const再次声
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello
与解构赋值默认值结合使用
参数默认值可以与解构赋值的默认值,结合起来使用。
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined, 5 foo({x: 1}) // 1, 5 foo() // TypeError: Cannot read property 'x' of undefined 如果函数foo调用时参数不是对象,变量x和y就不会生成,从而报错。 也就是说参数不能省略
function fetch(url, { method = 'GET' } = {}) { console.log(method); } fetch('http://example.com') // "GET"
函数的length属性
用来获取函数的参数个数(不包含默认值的参数个数), 如果函数的参数加入了默认参数,那么Length将失真
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2
因为Length属性的含义是,该函数预期传入的参数个数
(function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1
如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
参数的作用域
如果参数默认值是一个变量,而不是一个固定值, 那么该变量所处的作用域与其他变量的作用域规则是一样的, 先是当前作用域在是全局作用域
如果函数A的参数默认值是函数B,由于函数的作用域是其声明时所在的作用域,那么函数B的作用域不是函数A
let foo = 'outer'; let f = x => foo; function bar(func = f) { let foo = 'inner'; console.log(func()); // outer } bar();
应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误, 也就是说参数的默认值不是在定义时候执行,而是在运行时执行.
function throwIfMissing() { throw new Error('Missing parameter'); } function foo(mustBeProvided = throwIfMissing()) { return mustBeProvided; } foo() // Error: Missing parameter
可以将参数默认值设置为undefined,表明这个参数可以省略
rest参数
ES6总引入了rest参数(...变量名),用户获取函数的多余参数, 这样就不需要使用arguments对象了,rest参数搭配的变量是一个数组, 将多余的参数放入其中.
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); } var a = []; push(a, 1, 2, 3)
rest参数之后不能再有其他参数了
函数的length不包括rest属性
(function(a) {}).length // 1 (function(...a) {}).length // 0 (function(a, ...b) {}).length // 1
扩展运算符
扩展运算符(spread)是三个点(...)。 它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
只能是参数序列
console.log(...[1, 2, 3])
该运算符主要用于函数调用。
function f(v, w, x, y, z) { } var args = [0, 1]; f(-1, ...args, 2, ...[3]);
替代数组的apply方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
// ES5的写法 Math.max.apply(null, [14, 3, 77]) // ES6的写法 Math.max(...[14, 3, 77]) // 等同于 Math.max(14, 3, 77);
// ES5的写法 var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; Array.prototype.push.apply(arr1, arr2); // ES6的写法 var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; arr1.push(...arr2);
应用
合并数组
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more]
[...arr1, ...arr2, ...arr3]
与解构赋值结合
const [first, ...rest] = [1, 2, 3, 4, 5]; first // 1 rest // [2, 3, 4, 5]
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...butLast, last] = [1, 2, 3, 4, 5]; // 报错 const [first, ...middle, last] = [1, 2, 3, 4, 5]; // 报错
字符串
扩展运算符可以将字符串转为真正的数组,能够识别32位的unicode码
[...'hello'] // [ "h", "e", "l", "l", "o" ]
类似数组的对象
任何类似数组的对象都可以用扩展运算符转为真正的数组
map和set解构,Generator函数
扩展运算符内部调用的是数据结构的Iterator接口,只要具备Iterator接口的对象,都可以使用扩展运算符
let map = new Map([[1,'one'],[2,'two'],[3,'three']]);
Generator函数运行后返回一个遍历器对象,也可以使用扩展运算符
子主题
name属性
函数的name属性返回该函数的函数名.
function foo(){} console.log(foo.name)
如果将一个匿名函数赋值给一个变量,ES5的name属性, 会返回空字符串,而ES6的name属性会返回实际的函数名。
var func1 = function () {}; // ES5 func1.name // "" // ES6 func1.name // "func1"
如果将一个具名函数赋值给一个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字。
const bar = function baz() {}; // ES5 bar.name // "baz" // ES6 bar.name // "baz"
Function构造函数返回的函数实例,name属性的值为“anonymous"
(new Function).name // "anonymous"
bind返回的函数,name属性值会加上“bound ”前缀。
function foo() {}; foo.bind({}).name // "bound foo" (function(){}).bind({}).name // "bound "
箭头函数
ES6允许使用箭头(=>)定义函数
对象的扩展
属性的简洁表示法
ES6允许直接写入变量和函数,作为对象的属性和方法
var foo = 'bar'; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo};
上面代码表明,ES6允许在对象之中,只写属性名, 不写属性值。这时,属性值等于属性名所代表的变量。
function f(x, y) { return {x, y}; } // 等同于 function f(x, y) { return {x: x, y: y}; } f(1, 2) // Object {x: 1, y: 2}
function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint() // {x:1, y:10}
除了属性简写,方法也可以简写。
var o = { method() { return "Hello!"; } }; // 等同于 var o = { method: function() { return "Hello!"; } };
属性的赋值器(setter)和取值器(getter),也可以采用这种方法
var cart = { _wheels: 4, get wheels () { return this._wheels; }, set wheels (value) { if (value < this._wheels) { throw new Error('数值太小了!'); } this._wheels = value; } }
属性名表达式
JS语言定义对象属性,有两种方法 obj.foo=true obj['a'+'bc']=123
如果用表达式作为属性名,这是要将表达式放在括号内
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 };
表达式还可以用于定义方法名
let obj = { ['h'+'ello']() { return 'hi'; } };
属性名表达式与简洁表示法,不能同时使用,会报错
// 报错 var foo = 'bar'; var bar = 'abc'; var baz = { [foo] };
方法的name属性
函数name属性,返回函数名, 对象的方法也是函数,也有name属性
var person = { sayName() { console.log(this.name); }, get firstName() { return "Nicholas"; } }; person.sayName.name // "sayName" person.firstName.name // "get firstName"
bind方法创造的函数,name属性返回“bound”加上原函数的名字; Function构造函数创造的函数,name属性返回“anonymous”。
(new Function()).name // "anonymous" var doSomething = function() { // ... }; doSomething.bind().name // "bound doSomething"
如果对象的方法是一个Symbol值, 那么name属性返回的是这个Symbol值的描述。
const key1 = Symbol('description'); const key2 = Symbol(); let obj = { [key1]() {}, [key2]() {}, }; obj[key1].name // "[description]" obj[key2].name // ""
Object.is()
ES5中比较是否相等, == 会转换类型在比较 === 严格相等 Object.is()是用来比较是否严格相等, 和===比较基本一致
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign
Object.assign方法用于对象的合并, 将源对象(source)的所有可枚举属性, 复制到目标对象(target)。
如果目标对象与源对象有同名属性, 或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果只有一个参数,Object.assign会直接返回该参数。
var obj = {a: 1}; Object.assign(obj) === obj // true
如果该参数不是对象,则会先转成对象,然后返回。
typeof Object.assign(2) // "object"
子主题
由于undefined和null无法转成对象, 所以如果它们作为参数,就会报错。
Object.assign(undefined) // 报错 Object.assign(null) // 报错
如果非对象参数出现在源对象的位置(即非首参数), 那么处理规则有所不同。首先,这些参数都会转成对象, 如果无法转成对象,就会跳过。这意味着, 如果undefined和null不在首参数,就不会报错。
let obj = {a: 1}; Object.assign(obj, undefined) === obj // true Object.assign(obj, null) === obj // true
字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。
var v1 = 'abc'; var v2 = true; var v3 = 10; var obj = Object.assign({}, v1, v2, v3); console.log(obj); // { "0": "a", "1": "b", "2": "c" }
Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性) ,也不拷贝不可枚举的属性(enumerable: false)。
属性名为Symbol值的属性,也会被Object.assign拷贝。
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' }) // { a: 'b', Symbol(c): 'd' }
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说, 如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
var obj1 = {a: {b: 1}}; var obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
子主题Object.assign可以用来处理数组,但是会把数组视为对象。
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
上面代码中,Object.assign把数组视为属性名为0、1、2的对象, 因此目标数组的0号属性4覆盖了原数组的0号属性1。
用途
为对象添加属性
class Point { constructor(x, y) { Object.assign(this, {x, y}); } }
为对象添加方法
Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); // 等同于下面的写法 SomeClass.prototype.someMethod = function (arg1, arg2) { ··· }; SomeClass.prototype.anotherMethod = function () { ··· };
克隆对象
当时不能克隆对象原型链上面的值
function clone(origin) { return Object.assign({}, origin); }
能克隆对象原型链上面的值
function clone(origin) { let originProto = Object.getPrototypeOf(origin); return Object.assign(Object.create(originProto), origin); }
合并多个对象
const merge = (target, ...sources) => Object.assign(target, ...sources);
const merge = (...sources) => Object.assign({}, ...sources);
为属性指定默认值
const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { let options = Object.assign({}, DEFAULTS, options); }
上面代码中 default是默认值,option对象是用户提供的参数,将他们合并成一个对象
DEFAULTS对象和options对象的所有属性的值,都只能是简单类型, 而不能指向另一个对象。否则,将导致DEFAULTS对象的该属性不起作用。
属性的可枚举型
对象的每个属性都有一个描述对象(Descriptor), 用来控制该属性的行为。
let obj = { foo: 123 }; Object.getOwnPropertyDescriptor(obj, 'foo') // { value: 123, // writable: true, // enumerable: true, // configurable: true }
尽量不要用for...in循环,而用Object.keys()代替。
for...in 循环:只遍历对象自身的和继承的可枚举的属性 Object.keys():返回对象自身的所有可枚举的属性的键名 JSON.stringify():只串行化对象自身的可枚举的属性 Object.assign():只拷贝对象自身的可枚举的属性 Reflect.enumerate():返回所有for...in循环会遍历的属性
属性的遍历
for...in
循环遍历对象自身的和继承的可枚举属性(不包含Symbol属性)
Object.keys(obj)
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象自身的所有Symbol属性。
Reflect.ownKeys(obj)
返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
Reflect.enumerate(obj)
返回一个Iterator对象,遍历对象自身的和继承的所有可枚举属性(不含Symbol属性) ,与for...in循环相同。
以上6种方法遍历对象的属性,都遵守同样的规则
子主题首先遍历所有属性名为数值的属性,按照数字排序。 其次遍历所有属性名为字符串的属性,按照生成时间排序。 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
__proto__属性, Object.setPrototypeOf() ,Object.getPrototypeOf()
__proto__属性
用来读取或设置当前对象的prototype对象,所有浏览器都支持, 该属性没有写入ES6的正文,而是写入了附录,已经广泛的被浏览器支持, 最好不要用改属性,用其他的东西代替如 Object.setPrototypeOf() Object.getPrototypeOf() Object.create()
Object.setPrototypeOf()
用来设置一个对象的prototype对象,推荐使用
// 格式 Object.setPrototypeOf(object, prototype) // 用法 var o = Object.setPrototypeOf({}, null);
Object.getPrototypeOf()
该方法与setPrototypeOf方法配套,用于读取一个对象的prototype对象。
Object.getPrototypeOf(obj);
Object.values(), Object.entries()
Object.keys()
ES5引入了Object.keys方法,返回一个数组, 成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
Object.values()
方法返回一个数组,成员是参数对象自身的(不含继承的) 所有可遍历(enumerable)属性的键值。
var obj = { foo: "bar", baz: 42 }; Object.values(obj) // ["bar", 42]
返回数组的成员顺序,是按照数值小到大
Object.values会过滤属性名为Symbol值的属性.
如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组
Ojbect.values('foo') //['f','o','o']
如果参数不是对象,会先将其转为对象
Object.entries()
方法返回一个数组,成员是参数对象自身的(不含继承的) 所有可遍历(enumerable)属性的键值对数组。
var obj = { foo: 'bar', baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]
如果原对象的属性名是一个Symbol值,该属性会被省略。
用处:方法的一个用处是,将对象转为真正的Map结构。
var obj = { foo: 'bar', baz: 42 }; var map = new Map(Object.entries(obj)); map // Map { foo: "bar", baz: 42 }
Object.getOwnPropertyDescriptors()
ES5有一个Object.getOwnPropertyDescriptor方法, 返回某个对象属性的描述对象(descriptor)。
var obj = { p: 'a' }; Object.getOwnPropertyDescriptor(obj, 'p') // Object { value: "a", // writable: true, // enumerable: true, // configurable: true // }
Symbol
定义
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型, 前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是Symbol类型
凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
Symbol函数签不能使用new命令,否则会报错,Symbol值不是对象,所以不能添加属性,基本上它是一种类似于字符串的数据类型.
Symbol函数可以接受一个字符串作为参数,表示Symbol实例的描述,主要是为了控制台显示,容易区分
var s1 = Symbol('foo'); var s2 = Symbol('bar'); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)"
注意,Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。
// 没有参数的情况 var s1 = Symbol(); var s2 = Symbol(); s1 === s2 // false // 有参数的情况 var s1 = Symbol("foo"); var s2 = Symbol("foo"); s1 === s2 // false
Symbol值不能与其他类型的值进行运算,会报错
但是,Symbol值可以显式转为字符串。
var sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)'
另外,Symbol值也可以转为布尔值,但是不能转为数值。
var sym = Symbol(); Boolean(sym) // true !sym // false if (sym) { // ... } Number(sym) // TypeError sym + 2 // TypeError
作为属性名的Symbol
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符, 用于对象的属性名,就能保证不会出现同名的属性。 这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
var mySymbol = Symbol(); // 第一种写法 var a = {}; a[mySymbol] = 'Hello!'; // 第二种写法 var a = { [mySymbol]: 'Hello!' }; // 第三种写法 var a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上写法都得到同样结果 a[mySymbol] // "Hello!"
Symbol值作为对象属性名时,不能用点运算符。
var mySymbol = Symbol(); var a = {}; a.mySymbol = 'Hello!'; a[mySymbol] // undefined a['mySymbol'] // "Hello!"
Symbol类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn') }; log(log.levels.DEBUG, 'debug message'); log(log.levels.INFO, 'info message');
const COLOR_RED = Symbol(); const COLOR_GREEN = Symbol(); function getComplement(color) { switch (color) { case COLOR_RED: return COLOR_GREEN; case COLOR_GREEN: return COLOR_RED; default: throw new Error('Undefined color'); } }
消除魔术字符串
属性名的遍历
Symbol作为属性名,该属性不会出现在for...in、for...of循环中, 也不会被Object.keys()、Object.getOwnPropertyNames()返回。 但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法, 可以获取指定对象的所有Symbol属性名。
Symbol.for(),Symbol.keyFor()
Set和Map数据结构
set
定义
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
var s = new Set(); [2, 3, 5, 4, 5, 2, 2].map(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4
Set结构不会添加重复的值。
Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。
也展示了一种去除数组重复成员的方法
两个对象总是不相等的。
let set = new Set(); set.add({}); set.size // 1 set.add({}); set.size // 2
Set实例的属性和方法
size属性,返回实例的成员总数
操作数据方法
add(value)
添加某个值,返回set结构本身
delete(value)
删除某个值,返回boolean
has(value)
返回一个boolean,是否为set成员
clear()
清除所有成员,没有返回值
Array.from方法可以将Set结构转为数组
去除数组重复成员的方法
function dedupe(array) { return Array.from(new Set(array)); } dedupe([1, 1, 2, 3]) // [1, 2, 3]
遍历方法
keys()
返回一个键名的遍历器
values()
返回一个键值的便利器
entries()
返回一个键值对的便利器
forEach()
使用回调函数遍历每个成员,没有返回值
let set = new Set([1, 2, 3]); set.forEach((value, key) => console.log(value * 2) )
for...of循环遍历Set
数组的map和filter方法也可以用于Set了
因此使用Set,可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); // 并集 let union = new Set([...a, ...b]); // [1, 2, 3, 4] // 交集 let intersect = new Set([...a].filter(x => b.has(x))); // [2, 3] // 差集 let difference = new Set([...a].filter(x => !b.has(x))); // [1]
如果想在代码中直接改变遍历改变原来的set内容
// 方法一 let set = new Set([1, 2, 3]); set = new Set([...set].map(val => val * 2)); // set的值是2, 4, 6 // 方法二 let set = new Set([1, 2, 3]); set = new Set(Array.from(set, val => val * 2)); // set的值是2, 4, 6
WeakSet
WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别。
WeakSet的成员只能是对象,而不能是其他类型的值。
垃圾回收机制不考虑WeakSet对该对象的引用
不可遍历
WeakSet是一个构造函数,可以使用new命令创建
作为构造函数,WeakSet可以接受一个数组或类似数组的对象作为参数
WeakSet没有size属性,没有办法遍历它的成员
有三个方法
add(value)
delete(value)
has(value)
map
Object结构提供了“字符串—值”的对应, |Map结构提供了“值—值”的对应, 是一种更完善的Hash结构实现。
var m = new Map(); var o = {p: "Hello World"}; m.set(o, "content") m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
Map也可以接受一个数组作为参数。
var map = new Map([["name", "张三"], ["title", "Author"]]); map.size // 2 map.has("name") // true map.get("name") // "张三" map.has("title") // true map.get("title") // "Author"
如果对同一个键多次赋值,后面的值将覆盖前面的值。
let map = new Map(); map .set(1, 'aaa') .set(1, 'bbb'); map.get(1) // "bbb"
只有对同一个对象的引用,Map结构才将其视为同一个键。 同样的值的两个实例,在Map结构中被视为两个键。 Map的键实际上是跟内存地址绑定的
var map = new Map(); map.set(['a'], 555); map.get(['a']) // undefined
var map = new Map(); var k1 = ['a']; var k2 = ['a']; map .set(k1, 111) .set(k2, 222); map.get(k1) // 111 map.get(k2) // 222
Map的键是一个简单类型的值(数字、字符串、布尔值), 则只要两个值严格相等, Map将其视为一个键,包括0和-0。
let map = new Map(); map.set(NaN, 123); map.get(NaN) // 123 map.set(-0, 123); map.get(+0) // 123
实例的属性和操作方法
size属性
size属性返回Map结构的成员总数
let map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2
set(key, value)
set方法设置key所对应的键值,然后返回整个Map结构。
var m = new Map(); m.set("edition", 6) // 键是字符串 m.set(262, "standard") // 键是数值 m.set(undefined, "nah") // 键是undefined
get(key)
get方法读取key对应的键值,如果找不到key,返回undefined。
var m = new Map(); var hello = function() {console.log("hello");} m.set(hello, "Hello ES6!") // 键是函数 m.get(hello) // Hello ES6!
has(key)
has方法返回一个布尔值,表示某个键是否在Map数据结构中。
delete(key)
delete方法删除某个键,返回true。如果删除失败,返回false。
clear()
clear方法清除所有成员,没有返回值。
遍历方法
keys():返回键名的遍历器。 values():返回键值的遍历器。 entries():返回所有成员的遍历器。 forEach():遍历Map的所有成员。
for (let [key, value] of map.entries()) { console.log(key, value); }
// 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); }
Map结构转为数组结构,比较快速的方法是结合使用扩展运算符(...)。
let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); [...map.keys()] // [1, 2, 3] [...map.values()] // ['one', 'two', 'three'] [...map.entries()] // [[1,'one'], [2, 'two'], [3, 'three']] [...map] // [[1,'one'], [2, 'two'], [3, 'three']]
结合数组的map方法、filter方法, 可以实现Map的遍历和过滤(Map本身没有map和filter方法)。
let map0 = new Map() .set(1, 'a') .set(2, 'b') .set(3, 'c'); let map1 = new Map( [...map0].filter(([k, v]) => k < 3) ); // 产生Map结构 {1 => 'a', 2 => 'b'} let map2 = new Map( [...map0].map(([k, v]) => [k * 2, '_' + v]) ); // 产生Map结构 {2 => '_a', 4 => '_b', 6 => '_c'}
Map还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。
与其他数据结构的互相转换
map转为数组,最方便的就是使用扩展运算符
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']); [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
数组转为map
将数组转入Map构造函数,就可以转为Map。
new Map([[true, 7], [{foo: 3}, ['abc']]]) // Map {true => 7, Object {foo: 3} => ['abc']}
Map转为对象
function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } let myMap = new Map().set('yes', true).set('no', false); strMapToObj(myMap) // { yes: true, no: false }
对象转为map
function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({yes: true, no: false}) // [ [ 'yes', true ], [ 'no', false ] ]
JSON转为Map
Weakmap
结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受其他类型的值作为键名.
异步操作和Async函数
基本概念
Generator函数
Thunk函数
co模块
async函数
Class
Class基本语法
定义“类”的方法的时候,前面不需要加上function这个关键字
类的数据类型就是函数,类本身就指向构造函数。
事实上,类的所有方法都定义在类的prototype属性上面。
在类的实例上面调用方法,其实就是调用原型上的方法。
类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
类的属性名,可以采用表达式。
实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上
与ES5一样,类的所有实例共享一个原型对象。
采用Class表达式,可以写出立即执行的Class。
const MyClass = class { /* ... */ };
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }('张三'); person.sayName(); // "张三"
不存在变量提升
Class的继承
方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。
在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错
上面代码中,实例对象cp同时是ColorPoint和Point两个类的实例,这与ES5的行为完全一致。
原生构造函数的继承
Class的取值函数(getter)和存值函数(setter)
Class的Generator方法
Class的静态方法
Class的静态属性和实例属性
new.target属性
Mixin模式的实现
0 条评论
下一页