ES6学习
2016-05-23 10:56:25 80 举报
AI智能生成
JavaScript 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 条评论
下一页