ES6学习总结

ES6学习总结
1.ES6的新特性
1.1不一样的变量声明:增加了块级作用域 增加了let和const两个变量类型 规定了只有声明这个变量以后才可以使用 如果没有声明就使用会爆referenceError
1.2模板字符串:基本的字符串格式化。将表达式嵌入字符串中进行拼接,用${}来界定 反引号(``)
1.3箭头函数 使用括号包裹参数,跟随一个=> 紧接着是函数体 箭头函数最直观的三个特点 不需要function关键字来创建函数 省略return关键字 继承当前上下文的this关键字 ps当你的参数只有一个的时候可以省略掉括号
1.4 函数的参数默认值(default)
1.5 Spread/Rest操作符
1.6二进制和八进制字面量 通过在数字前面添加0o 或者0O 即可转换成为八进制
1.7 对象和数组解构
1.8 对象超类 ES6 语序在对象中使用super方法
1.9 for…of 和 for…in for…of用来遍历一个迭代器 for…in 哟用来遍历对象中的属性
1.10 ES6支持class语法,不过ES6 的class不是新的对象继承模型 他只是原型链的语法糖表现形式 函数中用static关键词定义构造函数的方法和属性

2.作用域let const
2.1块级作用域
let实际上为JavaScript新增了块级作用域 ES6允许块级作用域的任意嵌套,外层作用域无法读取内层作用域的变量 内层作用域可以定义外层作用域的同名变量
ES6 规定块级作用域内可以声明函数 声明的函数语句行为类似LET 在块级作用域之外不可引用 但是考虑到环境导致的行为差异太大,应该避免在块级作用域里面声明函数 如果确实需要也应该写成函数表达式而不是函数声明语句
块级作用域不返回值 即外部无法获取块级作用域里面的值除非里面的值是全局变量
ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立
2.2Let
ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
let变量一定要在声明后使用 否则会报错
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错
let不允许在相同的作用域内重复声明同一个变量
2.3Const
声明一个只读的常量 一旦声明这个常量的值就不能改变 这就意味着const一旦声明变量就必须立即初始化 不能留到以后赋值 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
对于复合型变量 变量名不指向数据 而是指向数据所在的地址 const命令指示保证变量名指向的地址必变,并不保证该地址的数据不变 所以将一个对象声明为常量必须非常小心
ES6 一共有6中声明变量的方法
3、顶层对象属性
顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。
ES6规定var命令和function命令声明的全局变量依旧是顶层对象的属性 可以用window.几来调用 let和const class命令声明的全局变量不属于顶层对象的属性
同一段代码为了能够在各种环境都能取到顶层对象,现在一般使用this变量 但是有局限性
全局环境中 this会返回顶层对象,但是Node和ES6模块中this 返回的是当前模块
函数里面的this 如果函数不是作为对象的方法运行 而是淡出作为函数运行 this会指向顶层模块 但是严格模式下 this会返回undefined
4、解构赋值
ES6允许按照一定的模式,从数组和对象中提取值 对变量进行复制 这被称为解构 本质上这种模式属于“匹配模式” 只要等号两边的模式相同 左边的变量就会被赋予相应的值 如 var [a,b,c] = [1,2,3] 解构不成功的几种情况 1.左右两边不相等 2.右边不是可以遍历的结构
解构不仅适用于var命令 还适用于let 和const命令
解构赋值允许指定默认值 var [foo = true] = [] foo//true
解构不仅可以用于数组还可用于对象 数组的元素是按次序排列的 变量的取值由他的位置决定 但是对象的属性没有次序 变量必须与属性同名才能取到正确的值
var x;
{x}={x:1};
这样的写法会报错 因为x之前已经被声明过 JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误 只有不将大括号写在首行 避免JavaScript将其解释称代码块才能解决这个问题 可以改成如下写法
var x;
({x}={x:1})
字符串也可以解构 当时用这种模式时 字符串被转换成了一个类似数组的对象
const [a,b,c,d,e]=‘hello’;
a//h
b//e
c//l
d//l
e//o
类似数组的对象都会有一个length属性 因此还可以对这个属性解构赋值
let {length:len} = ‘hello’
len //5
解构赋值的规则是 只要等号右边不是对象就先转换成对象再进行赋值 由于undefined和null 不能被转换成对象 所以对他们进行解构赋值会报错
在解构赋值中要谨慎使用圆括号 以下三种不能使用解构赋值的情况
1)、变量声明语句中,不能使用圆括号 比如 var [(b)]=[1]
2)、函数参数中,模式不能使用圆括号
3)、赋值语句中不能将整个模式或者嵌套模式中的一层放在圆括号中
可以使用圆括号的情况只有一种 赋值语句的而非模式部分
[(b)] =[3]
({p:(d)}={})
变量解构赋值的用途:
1)、交换变量的值 [x,y]=[y,x]
2)、从函数返回多个值 例如
//返回一个数组
function example(){
return [1,2,3];
}
var [a,b,c] = example();
//返回一个对象
function example(){
return{
foo:1,
bar:2
};
}
var {foo,bar } = example();
3)、函数参数的定义
解构赋值可以方便的将一组参数与变量名对应起来
function f([x,y,z]){…}
f([1,2,3]);
4)、提取JSON数据
var jsonData ={
id:3502,
status:‘OK’,
data:[877,900]
};
let {id,status,data:number} = jsonData;
console.log(id,status,number);
5)、函数参数的默认值
6)、遍历map解构
var map = new Map();
map.set(‘first’,‘Hello’);
map.set(‘second’,‘world’);

for (let [key,value] of map){
console.log(key + “is” + value);
}
7)、输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require(“source-map”);
5、字符串扩展
字符的Unicode表示法
codePoinAt()
这个方法能够正确处理4个字节存储的字符 返回一个字符的马甸 该方法的参数是在字符在字符串中的位置(从0开始) codePoinAt()方法会正确返回32位的UTF-16字符的马甸 对于那些两个字节存储的常规字符 返回结果与charCodeAt()方法相同codePointAt方法返回的是马甸的十进制 如果想要十六进制的值可以用toSting方法转换一下 codePointAt()方法是测试一个字符是由一个字节还是两个字节组成的最简单方法
string.fromCodePoint()
String.fromCharCode不能识别大于0xFFFF的码点ES6提供了String.fromCodePoint()方法弥补了不足
fromCodePoint方法定义在String对象上,而codePointAt方法定义在字符串的实例对象上。

ES6为字符串添加了遍历器接口使得字符串也可以被for…of遍历
for(let codePoint of ‘foo’){
console.log(codePoint)
}
at()
nomalize()
传统上,JavaScript只有indexOf()方法,可以用来确定一个字符串是否包含在另一个字符串中
includes():返回布尔值,表示是否找到了参数字符串
startsWith():返回布尔值,表示参数字符串是否在源字符串头部
endsWith():返回布尔值,表示参数字符串是否在原字符串尾部
以上三个方法都支持第二个参数 表示开始搜索的位置 但是endWith()的行为与其他两个方法有所不同 它针对的是前n个字符,而其他两个方法针对从第n个位置直到字符串结束
repeat() 该方法返回一个新字符串,表示将原字符串重复n次 带参数 ‘x’.repeat(3);//xxx 参数如果是小数会被取整 但是如果参数是负数或者infinity会报错 如果是0-1之间的小数则等同于0 参数NaN等同于0
padStart(),padEnd()
ES6推出了字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或者尾部补全 padStart()用于头部补全,padEnd()用于尾部补全 ‘x’.padStart(5,‘ab’);//ababx ‘x’.padEnd(5,‘ab’);//xabab 如果省略第二个参数 则会用空格补全长度 padStart() 的常见用途是为数值补全指定位数,另一个用途是提示字符串格式 ‘12’.padStart(10,‘YYYY-MM-DD’) //YYYY-MM-12
模板字符串
模板字符串是增强版的字符串,用反引号(``)标识,它可以当成普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
//普通字符串
in javascript\nis a line-feed
//多行字符串
inJavaScript this is not legal
console.log(string text line 1 srting text line 2);
//字符串中嵌入变量
var name =“Bob”,time = “tody”;
Hello ${name},how are you ${time}?
在使用过程中在使用多行字符串的时候往往会为了保证代码整齐进行换行和缩进操作,但是该缩进会被认为是字符串的一部分 如何解决这个问题?
1、传统字符串拼接+换行符
var a = This is template string.\n
+Even though each line is indented to keep the\n
优点:简单易懂
缺点:拼接繁琐容易出错,没有充分利用字符串模板的优势
2、字符串替换
var a = (this is a template string. Even though each line is indented to keep the code neat and tidy,the white space used to indent is not in the resulting string).replace(/^ /gm,’’);
优点:充分利用了模板字符串
缺点:空格数不一致容易导致替换出错,对文本入侵性可能会替换掉不该替换的内容
3、变量替换
const N = “\n”;
var a = this is a template string.${N} Even though each line is indented to keep the${N} code neat and tidy,the white space used to indent ${N} is not in the resulting string
优点:简单易懂且充分利用模板字符串的特性
缺点:需要专门维护换行符变量
如果是想消除字符串首尾的空格可以用trim方法
$(’#list’).html(`

<ul>
<li>first</li>
<li>first</li>
</ul>

.trim()); 模板字符串中嵌入变量需要将变量名写在${}中,大括号内部可以放入任意的JavaScript表达式可以进行运算以及引用对象属性,大括号里面的值如果不是字符串,将按照一般的规则转换成字符串,比如大括号里面的是个对象则默认调用toString方法 var x=1; var y=2; x + {x}+ x+{y}= x + y ‘ / / 1 + 2 = 3 v a r o b j = x : 1 , y : 2 ; ‘ {x+y}`//1+2=3 var obj = {x:1,y:2}; ` x+y//1+2=3varobj=x:1,y:2;{obj.x + obj.y}//3 模板字符串之中还能调用函数 function fn(){ return "Hello Woeld";}foo KaTeX parse error: Expected 'EOF', got '\n' at position 155: …串 string.raw`Hi\̲n̲{2+3}`
// “Hi\n5”

6、扩展函数
ES6可以直接为函数的参数指定默认值
function log(x,y=‘world’)
{
console.log(x,y);
}
log(‘Hello’)//Hello Word
log(‘Hello’,‘China’);//hello China
log(‘Hello’,’’)//hello
参数变量是默认声明的 所以不能用let 或者const再次声明
参数默认值可以与解构赋值默认值结合使用
通常情况下,定义了默认值的参数是尾参数 ,如果是非尾部的参数设置默认值 这个参数是没办法省略的
当非尾部的参数设置了默认值时 除非显式的 输入undefined,才会触发默认值 但是输入null就不会触发
指定了默认值以后 length属性将失真 length属性的返回值等于函数参数个数减去指定了默认值的参数的个数
(function (a,b,c=3){}).length //2 因为length属性的定义是该函数预期传入的参数个数 某个参数指定默认值以后 预期传入的参数就不包括这个参数了,同理 rest参数就也不会计入length属性
(function(a,b=9,c,d)).length //1
作用域
如果参数的默认值是一个变量 则该变量所处的作用域,与其他变量的作用域规则是一样的,即现实当前函数的作用域,然后才是全局作用域

利用参数默认值可以指定某个参数不得省略,如果省略就会抛出错误
rest参数
ES6引入rest参数(形式为“…变量名”),用于获取函数多余的参数 这样就不需要arguments对象了 rest参数搭配的变量是一个数组,该变量将多余的参数放入到数组当中
function add(…values){
let sum =0;
for(var val of values){
sum+=val;
}
return sum;
}
add(2,5,3)//10
以上代码的add函数是一个求和函数,利用rest参数,可以向该函数传入任意数目的参数 注意 rest参数之后不能再有其他参数 否则会报错
7、扩展运算符
扩展运算符(spread)是三个点(…) 它好比rest参数的逆运算 将一个数组转为用逗号分割的参数序列
console.log(…[1,2,3])//1 2 3
该运算主要用于函数调用
function push(array,…item){
array.push(…items);
}
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了
function f(x,y,z){//…}
var arg=[1,2,3];
f(…arg);
7.1合并数组
扩展运算符提供了数组合并的新写法
var arr1=[‘a’,‘b’];
var arr2=[‘c’,‘d’];
var arr3=[‘e’];
//ES5的数组合并
arr1.concat(arr2,arr3)
//ES6 的数组合并
[…arr1,…arr2…arr3]
7.2扩展运算符可以与解构赋值结合起来用于生成数组
//Es5
a=list[0],rest = list.slice(1)
//Es6
[a,…rest]=list
另外的例子
const [first,…rest]=[1,2,3,4,5,6]
first//1
rest//[2,3,4,5,6]
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错
7.3函数的返回值
JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或者对象。扩展运算符提供了解决这个问题的一种变通方法
var dateFields = readDateFields(database);//从数据库取出一行数据
var d = new Date(…dateFields );//通过扩展运算符直接将其传入构造函数Date
7.4字符串
扩展运算符还可以将字符串转成真正的数组
[…‘hello’]
//[“h”,“e”,“l”,“l”,“o”]
利用扩展运算符可以真正的识别32位的Unicode字符 如下写法
function length(str){
return […str].length;
}
length(‘x/uD83D/uDE80y’)//3
凡是涉及到32位Unicode字符的函数,最好都用扩展运算符改写
7.5实现了letrator接口的对象
任何lterator接口的对象,都可以用扩展运算符转为真正的数组,但是只限于部署lterator接口的类似数组的对象
7.6Map和Set结构 Generator函数
扩展运算符内部调用的是数据结构的lterator接口,因此只要具有lterator接口的对象都可以使用扩展运算符,比如Map结构
let map=new Map ([
[1,‘one’],
[2,‘two’],
[3,‘three’],
]);
let arr = […map.keys()];//[1,2,3]

Genertator函数运行后 返回一个遍历器对象,因此也可以使用扩展运算符
var go =function*(){
yield 1;
yield 2;
yield 3;
}
[…go()] //[1,2,3]
8、严格模式
1.严格模式通过抛出错误来消除了一些原有静默错误。
2.严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
3.严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设置为严格模式,否则会报错
9、name属性
函数的name属性,返回该函数的函数名,Es6规定 如果讲一个匿名函数赋值给一个变量,,name属性会返回实际的函数名
10、箭头函数
ES6允许使用“箭头”(=>)定义函数。如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分,如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
由于大括号被解释称为代码块,如果箭头函数返回的是一个对象 那么必须在对象外面加上小括号
箭头函数的一个用处是简化回调函数例如
正常函数写法
[1,2,3].map(function(x){
return xx;
})
箭头函数
[1,2,3].map(x => x
x);
ps使用注意点
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
箭头函数可以嵌套
箭头函数绑定this
函数绑定运算符是两个并排的双冒号(::) 双冒号左边是一个对象右边是一个函数 该运算符会自动将左边的对象,作为上下文环境即this对象 绑定到右边的函数上
如果双冒号左边为空,右边是一个对象额方法,则等于将该方法绑定在该对象上面
1、属性的扩展
ES6允许直接写入变量和函数作为对象的属性和方法
function f(x,y){
return {x,y}
}
//等同于
function f(x,y){
return {x:x,y:y}
}
也允许方法的简写
var o = {
method(){
return “Hello”;
}
}
//等同于
var o = {
mehod:function(){
return “Hello”
}
}
这种写法用于函数的返回值会非常方便
CommonJS模块输出变量,就会非常合适使用简洁写法
var ms={}
function getItem(key){
return key in ms ?ms[key] :null;
}
function setItem(key,value){
ms[key] = value;
}
function clear(){
ms={};
}
module.exports = {getItem,setItem,clear};
注意间接写法的属性名总是字符串
如果某个方法是Generator函数 那么前面需要加上星号
var obj ={
*m(){
yield ‘Hello World’;
}
}
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。如果使用了取值函数,则会在方法名前加上get。如果是存值函数,方法名的前面会加上set。有两种特殊情况:bind方法创造的函数,name属性返回“bound”加上原函数的名字;Function构造函数创造的函数,name属性返回“anonymous”。如果对象的方法是一个Symbol值,那么name属性返回的是这个Symbol值的描述。
1、1Object.is()
Object.is() 判断两个值是否相同。如果下列任何一项成立,则两个值相同:

  • 两个值都是 undefined
  • 两个值都是 null
  • 两个值都是 true 或者都是 false
  • 两个值是由相同个数的字符按照相同的顺序组成的字符串
  • 两个值指向同一个对象
  • 两个值都是数字并且都是正零 +0都是负零 -0都是 NaN
  • 都是除零和 NaN 外的其它同一个数字
    1、2Object.assign(target,source)
    Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
    Object.assign方法的第一个参数是目标对象,后面的参数都是源对象;如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
    Object.assign方法的用途
    1.2.1为对象添加属性
    1.2.2为对象添加方法
    1.2.3克隆对象
    function clone(origin){
    return Object.assign({},origin);
    }
    以上代码将原始对象拷贝到一个空对象 但是只能克隆对象自身的值,不能克隆它继承的值 如果要克隆继承的值
    function clone(origin){
    let originProto = Object.getPrototypeOf(origin);
    return Object.assign(Object.create(originProto),origin);
    }
    1.2.4合并多个对象
    1.2.5为属性指定默认值
    1、3属性的可枚举性
    对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。描述对象的enumerable属性,称为”可枚举性“,如果该属性为false,就表示某些操作会忽略当前属性。
    到目前为止有四个操作会忽略enumerable为false的属性
  • for…in循环:只遍历对象自身的和继承的可枚举属性
  • Object.keys():返回对象自身的所有可枚举的属性的键名
  • JSON.stringify():值串行化对象自身的可枚举的属性
  • Object.assign():会忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性

这四个操作中只有for…in会返回继承的属性,操作中引入继承的属性会让问题复杂化,大多数的时候尽量不要用for…in 循环而是用Object.keys()来代替
1、4属性的遍历
ES6一共有5种方法可以遍历对象的属性。
1.for…in 循环遍历对象自身的和继承的可枚举属性(不包含Symbol属性)
2.Object.keys(obj) 返回一个数组,包括对象自身的(不含继承)所有的可枚举属性(不含Symbol属性)
3.Object.getOwnPropertyNames(obj) 返回一个数组,包含对象自身的所有属性(不包含Symbol属性 但是包括不可枚举属性)
4.Reflect.ownKeys(obj) 返回一个数组,包含对象自身的所有属性,不管是属性名函数Symbol或者字符串,也不管是否可枚举
以上五种方法的遍历次序规则为
首先遍历所有属性名为数值的属性,按照数字排序
其次遍历所有属性名为字符串的属性,按照生成时间排序
最后遍历所有属性名为Symbol值得属性,按照生成时间排序
2、Symbol
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分
Symbol值作为对象属性名时,不能用点运算符
var mySymbal = Symbol();
var a={};
a.mySymbol = 'Hello '//undefined 这是表示a的一个属性命名为mySymbol的属性值为‘Hello’这是的mySymbol不是Symbol类型的值 而是一个字符串
a[mySymbol]=‘Hello’//这时才是给类型为Symbol的属性名赋值为‘Hello’
在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。
Symbol类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
注意,Symbol值作为属性名时,该属性还是公开属性,不是私有属性
Object.getOwnPropertyNames(obj)方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
1.5.1内置的Symbol值

  • Symbol.hasInstance
    指向一个内部方法,当其他对象使用instanceof元素符 判断是否是该对象的实例时,会调用这个方法

  • Symbol.isConcatSpreadable
    等于一个布尔值,小时该对象使用Array.prototype.concat()时,是否可以展开对于一个类来说,Symbol.isConcatSpreadable属性必须写成实例的属性。

  • Symbol.species
    指向一个方法 该对象作为构造函数创造实例时会调用这个方法即如果this.constructor[Symbol.species]存在,就会使用这个属性作为构造函数,来创造新的实例对象。

  • Symbol.match

  • Symbol.replace

  • Symbol.search

  • Symbol.split

  • Symbol.iterator

  • Symbol.toPrimitive

  • Symbol.toStringTag

  • Symbol.unscopables

3、Set和Map结构
1、set
1.1基本用法
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。
var set = new Set([1,2,3,4,4]);
[…set]
//[1,2,3,4]
Set 可以用来去除数组的重复成员
[…new Set(array)]
向Set加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。两个对象总是不相等的
1.2set实例的属性和方法
Set结构的实例有一下属性

  • Set.prototype.constructor:构造函数,默认就是Set函数
  • Set.prototype.size:返回Set实例的成员总数
    Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)
    四个操作方法
    1.add(value):添加某个值,返回set结构本身
    2.delete(value):删除某个值,返回一个布尔值,表示删除成功
    3.has(value):返回一个布尔值,表示该值是否为Set成员
    4.clear():清除所有成员,没有返回值

Array.from 可以吧Set结构转换成数组 这就提供了另一种去除重复成员的刚发
function dedupe(array){
return Array.from(new Set(array));
}
dedupe([1,2,3,3]);//1,2,3
四个遍历操作
1.keys():返回键名的遍历器
2.values():返回键值的遍历器
3.entries():返回键值对的遍历器
4.forEach():使用回调函数遍历每个成员
Set的遍历顺序就是插入顺序,如果使用Set保存一个回调函数列表,调用时就能保证按照添加顺序调用
keys() values() entries方法返回的都是遍历器对象 由于set解构没有键名只有键值 所以keys方法和value方法的行为是一致的
Set结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。这意味着,可以省略values方法,直接用for…of循环遍历Set
let set=new Set([1,2,3,4]);
for(let x of set){
console.log(x);
}
//1 2 3 4
Set结构的forEach方法是对每个成员执行某种操作,没有返回值
let set = new Set([1,2,3]);
set.forEach((value,key)=>console.log (value*2))
//2,4,6
遍历的应用
扩展运算符(…)内部使用for…of循环,所以也可以用于Set结构。
let set = new Set([1,2,3,4])
let arr = […set];
//[1,2,3]
扩展运算符和Set结构相结合,就可以去除数组的重复成员
数组的Map和Filter方法也可以用于Set结构
2、Map
2.1Map结构的目的和基本用法
传统JavaScript的对象 本质上是键值对的集合,但是只能用字符串当做键 ES6提供了Map数据结构 类似于对象也是键值对的集合 但是键的范围不限于字符串 各种类型的值 包括对象都可以当做键 简而言之 Object提供的是 字符串–值 的对应 Map结构提供的是值—值得对应 是一种更完善的Hash结构实现作为构造函数,Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组如果对同一个键多次赋值,后面的值将覆盖前面的值
2.2实例的属性和操作方法
1)、size属性 返回Map结构的成员总数
2)、set(key,value) set方法设置key所对应的的键值,然后返回整个Map结构,如果key值已经存在,则键值会被更新 否则就新生成改建 set方法返回的是Map本身 因此可以采用链式写法
let map=new Map().set(1,‘a’).set(2,‘b’).set(3,‘c’);
3)、get(key) get方法读取key对应的键值,如果找不到key 会返回undefined
4)、has(key) has方法返回一个布尔值,表示某个键是否在Map数据结构中
5)、delete(key) delete方法删除某个键,返回true 如果删除失败返回false
6)、clear() 清除所有成员,没有返回值
2.3 遍历方法
Map原生提供三个遍历器生成函数和一个遍历方法
keys():返回键名的遍历器
values():返回键值的遍历器
enteries():返回所有成员的遍历器
forEach():遍历Map的所有成员
ps Map的遍历顺序就是插入顺序
结合数组的map方法、filter方法,可以实现Map的遍历和过滤(Map本身没有map和filter方法)。
let map0=new Map().set(1,‘a’).set(2,‘b’).set(3,‘c’);
let map1 = new Map ( [ …map0 ] . map ( (k,v) => [k *2,’_’+v ] ) );
let map2 = new Map( [ …map0 ] . filter ( (k,v) => k<3 ) );
2.4与其他数据结构的互相转换
1)、Map转换为数组 使用扩展运算符(…)
let myMap = new Map().set(true,7).set({foo:3},[‘abc’]);
[…myMap]// [ [true,7] , [ {foo:3} , [‘abc’] ] ]
2)、数组转为Map 将数组传入Map构造函数,就可以转为Map
new Map ( [ [ true,7] ,[ { foo:3} , [ ‘abc’ ] ] ] );
//Map{ true => 7 , Object { foo : 3} =>[‘abc’] }
3)、Map转为对象 如果所有Map的键都是字符串,它可以转为对象
4、Proxy和Reflect
Proxy (代理器)可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
ES6原生提供的Proxy构造函数,用来生成Proxy实例
var proxy = new Proxy ( target , handler );
第一个参数target表示要拦截的目标对象
第二个参数 handler用来定制拦截行为

5、lterator和for。。of
遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费
ES6规定 一个数据结构只要具有Symbol.iterator属性 就认为是可遍历的
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构
调用Iterator接口的场合
1)、对数组和set结构进行解构赋值的时候默认会调用Symbol.iterator方法
2)、扩展运算符
3)、yield* 后面跟的是个可遍历的结构,它会调用该结构的遍历器接口

6、Generator函数、yield语句
Generator函数是ES6提供的一种异步编程解决方案
从语法上理解 ,是一个状态机 封装了很多个内部状态,执行Generator函数会返回一个遍历器对象,这个遍历器对象可以依次遍历Generator函数内部的每个状态
从形式上理解,Generator函数是一个普通函数 但是有两个特征 1、function关键字与函数名之间有个星号;2、函数体内部使用yield语句,定义不同的内部状态
Generator函数的调用方法也是函数名加上一对圆括号,但是调用这个函数 这个函数并不会执行,返回的也不是函数运行的结果 而是一个指向内部状态的指针对象 也就是遍历器对象;下一步必须调用遍历器对象的next方法使得指针移向下一个状态 。也就是每次调用next方法内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句 也就是说 Generator函数是分段执行的 yield语句是暂停执行的标志,而next方法是恢复执行的
当调用Generator函数时 会返回一个遍历器对象,代表Generator函数的内部指针,以后每调用一次遍历器对象的next方法都会返回两个值 value 和 done;value的值表示当前的内部状态的值 即yield语句后面的值,done属性是一个布尔值 表示是否遍历结束
该函数返回的遍历器对象有throw方法 在内部输出错误
有return方法
Generator函数 不能与new命令一起用 会报错
Generator函数的应用
1)、异步操作的同步化表达 可以吧异步操作写在yield语句里面 等到调用next访法的时候再往后执行
2)、控制流管理
3)、部署iterator接口
4)、Generator可以看作是数据结构,因为Generator函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口

yield语句
由于Generator函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志
yield语句不能出现在普通函数中 否则会报错
yield语句不返回任何值 也不能带参数
next可以带一个参数这个参数就不会被当成上一个yield语句的返回值,通过next这个参数 就有办法在Generator函数开始运行之后,继续向函数体内部注入值 即可以在generator函数运行的任何阶段,从外部向内部注入值,从而调整函数的行为
由于next方法的参数表示上一个yield语句的返回值,所以第一次使用next方法时,不能带有参数。
7、Promise对象
Promise简单来说就是一个容器 里面保存着某个未来才会结束的事件(通常是一个异步操作)
Promise对象有以下两个特点
1)、对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态 Pending(进行中)、Resolved(已完成,又称Fulfiled)和Rejected(已失败)。只有异步操作的结果可以决定当前是那种状态
2)、一旦状态改变,就不会再变 任何时候都可以得到这个结果。Promise对象的状态发生改变只有两种可能:从pending变为Resolved和从pending变为Rejected 只要这两种情况发生,只要状态发生一次改变状态就凝固了 不会再变
Promise也有一些缺点
首先无法取消Promise 一旦新建他就会立即执行,无法中途取消 其次 如果不设置回调函数,Promise内部抛出错误,不会反映到内部。第三 当初与pending状态时,无法得知目前进展到哪个阶段(刚刚开始还是即将完成)
如果某些事件不断的发生,一般来说,使用stram模式比部署Promise是更好的选择

基本用法
ES6规定Promise对象是一个构造函数,用来生成Promise实例
例如:
var promise = new Promise( function (resolve , reject ){
//some code
if ( /异步操作成功/) {
resolve (value );
} else {
reject( error );
}
});

Promise接受一个函数作为参数 该函数的参数分别是resolve 和 reject 他们是两个函数,由JavaScript引擎提供不用自己部署
resolve 函数的作用是,将Promise对象的状态从未完成变成完成(pending 变为 Resolve) 在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
reject函数的作用是,将Promise对象的状态从未完成变成失败(pending变成reject) 在异步操作失败的时候调用,并将异步操作报出的错误作为参数传递出去
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数
promise.then ( function ( value) {
//success
} ,function ( error) {
//failure
} )
Promise.prototype.then()
Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
Promise.all()
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。Promise.all方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例
组合后的Promise实例的状态有两种可能
1)、都为fulfilled 组合后的就为fulfilled
2)、有一个是reject 则状态就会变成reject 此时第一个被reject的实例的返回值会传递给组合后的实例的回调函数
Promise.race()
该方法同样是将多个Promise实例,包装成一个新的Promise实例。
Promise.resolve()
将现有的对象转换成Promise对象 这个方法的参数有四种情况
1)、参数是一个Promise实例 那么该方法将不会做任何修改原封不动的返回这个实例
2)、参数是一个thenable对象 thenable对象 指的是含有太狠方法的对象,该方法会将这个对象转为Promise对象 然后就立即执行thenable对象的then方法
3)、参数不是具有then方法的对象,或者根本就不是对象 这时该方法会返回一个新的Promise对象状态为resolved
4)、不带任何参数 promise.resolve()方法允许调用时不带任何参数 直接返回一个状态为resolved的Promise对象
Promise.reject()
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。它的参数用法与Promise.resolve方法完全一致
应用
加载图片 当图片加载完成 promise的状态发生改变
Generator函数与Promise的结合 使用Generator函数管理流程 遇到异步操作的时候通常会返回一个Promise对象
8、Asyns
异步编程对JavaScript很重要 因为JavaScript语言的执行环境是单线程的
ES6诞生以前 异步编程的方法大概有以下四种

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象
    基本概念
    异步:简单来说就是一个任务分成两段 先执行一段转而执行其他的任务,等做好了准备再回头执行第二段
    不连续的执行叫做异步 连续的执行叫做同步
    JavaScript语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。它的英语名字callback,直译过来就是"重新调用"
    promise 允许将回调函数的嵌套写成链式调用

Generator函数的数据交换和错误处理
Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。
next方法返回值的value属性,是Generator函数向外输出数据;next方法还可以接受参数,这是向Generator函数体内输入数据。
Async函数

用Generator函数 依次读取两个文件的代码如下

var fs = require ( ’ fs ’ );
var readFile = function ( fileName ) {
return new Promise ( function ( resolve, reject ) {
fs.readFile ( fileName , function ( error ,data ) {
if ( error ) reject ( error );
resolve ( data );
});
} );
} ;
var gen = function* ( ) {
var f1 = yield readFile ( ’ /etc/fstab ’ );
var f2 = yeild readFile ( ’ /ect/shells ’ );
console.log ( f1.toStirng () );
console.log ( f1.toStirng () );
};
写成 async 函数就是如下
var asyncReadFile = async function ( ) {
var f1 = await readFille ( ’ /etc/fstab ’ );
var f2 = await readFile ( ’ /etc/shells ’ );
console.log ( f1.toStirng () );
console.log ( f1.toStirng () );
};

async 函数 对Generator函数的改进主要体现在以下四点

  • 内置执行器 Generator函数的执行必须靠执行器 但是async 函数自带执行器 即async函数的执行跟普通函数一样只需要一行
  • 更好地语义 async和await 比起星号和yield 意义更加清楚 async表示函数里面有异步操作 await表示 紧跟在后面的表达式需要等待结果
  • 更广的适用性
  • 返回值是Promise 这就可以用then进行下一步的操作
    async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。
    语法
    1)、async函数返回的是一个Promise对象
    async函数内部return语句返回的值 会成为then方法回调函数的参数
    async function f ( ) {
    reutrn 'Hello World ';
    }
    f ( ) .then ( v => console.log ( v ) ); //Hello World

async函数内部抛出错误,会导致返回的Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
2)、async函数返回的Promise对象,必须等到内部所有await命令的Promise对象执行完,才会发生状态改变。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
3)、正常情况下,await命令后面是一个Promise对象。如果不是,会被转成一个立即resolve的Promise对象。
4)、如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。
async函数的实现
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。
async函数的用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
async函数使用注意点

  • await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。
  • 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
  • await命令只能用在async函数之中,如果用在普通函数,就会报错
    class基本语法
    ES6 提供了更接近传统语言的写法,引入了类的概念 Class作为对象的模板
    定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
    类的数据类型就是函数,类本身就指向构造函数
    由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。
    prototype对象的constructor属性,直接指向“类”的本身
    Point.prototype.constructor === Point // true
    类的内部所有定义的方法,都是不可枚举的(non-enumerable)
    constructor方法
    constructor方法是类的默认方法,一个类必须包含一个constructor方法 如果没有现实定义,一个空的constructor方法会被默认添加 constructor方法默认返回实例对象(即 this) 完全可以指定返回另一个对象
    类的构造函数,不使用new是没法调用的,会报错
    类的实例对象
    生成类的实例对象的方法就是使用new命令,实例的属性除非显式定义在其本身(即定义在this对象上)否则都是定义在原型上(即class上)
    实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。
    不存在变量提升
    class比存在变量提升(hoist):就是把class提升到代码头部 所以在写代码的时候 必须保证子类在父类之后定义
    Class表达式
    与函数一样 类也可以使用表达式的形式定义
    const MyClass = class Me{
    getClassName(){
    return Me.name;
    }
    };

注意这个类的名字是MyClass 而不是 Me Me只是在Class内部代码可用 指代当前类
如果类的内部没用的话,可以省略Me 简写成以下形式
const MyClass = class{//};
私有方法
ES6不提供私有方法,只能通过变通的方法模拟实现

  • 一种是在命名上加以区别
    class Widget {
    //公有方法
    foo (baz ) {
    this._bar(baz);
    }
    //bar前面的下划线 _bar 表示 这是一个仅限于内部使用的私有方法 但是这种命名是不保险的 在类的外部还是可以调用到这个方法
    _bar (baz) {
    return this.snaf = baz;
    }

}

  • 一种是直接移出模块内
    class Widget {
    //公有方法
    foo (baz ) {
    bar.call(this,baz);
    }
    //…

}
function bar ( baz ) {
return this.snaf = baz;
}

  • 利用Symbol值得唯一性 将一个私有方法的名字命名为Symbol值
    const bar = Symbol(‘bar’);
    const snaf = Symbol(‘snaf’);
    export default class myClass {
    //共有方法
    foo (baz){
    thisbar;
    }
    //私有方法
    bar{
    return this[snaf] = baz;
    }
    };
    this的指向
    类的方法内部如果含有this 它默认指向类的实例对象 ,但是最好不要单独使用 会造成this的指向不明
    严格模式
    类和模块的内部默认就是严格模式 不需要使用use strict指定运行模式
    Name属性
    本质上类就是构造函数的一层包装 所以函数的很多特性都被class继承包括name属性
    name属性总是返回紧跟在class关键字后面的类名

Class的继承
class之间可以通过extends关键字实现继承
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ’ ’ + super.toString(); // 调用父类的toString()
}
}

ColorPoint继承了Point类的所有属性和方法
在ColorPoint中的 constructor方法和toString方法都出现了 super 关键字,它在这里表示父类的构造函数,用来新建父类的this对象
子类必须在constructor方法中调用super方法 否则新建实例的时候会报错 因为子类没有自己的this对象 如果不调用super方法 子类就得不到父类的this对象
ES6的继承机制是 先创造父类的实例对象this (所以必须调用super方法)然后再用子类的构造函数修改this
类的prototype属性和__proto__属性
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
class A{
}
classB extends A {

}

B.proto === A ;//true
B.prototype.proto === A.prototype ; //true
Object.getPrototypeOf()
Object.getPrototypeOf方法可以用来从子类上获取父类,可以使用这个方法判断,一个类是否继承了另一个类
super关键字
即可以当做函数使用 也可以当做对象使用
第一种情况 super 作为函数调用时,作为父类的构造函数 ES6规定 子类的构造函数必须执行一次super函数 super()只能用在子类的构造函数之中,用在其他地方就会报错
第二种情况,super作为对象时,指向父类的原型对象
ES6 规定,通过super调用父类的方法时,super会绑定子类的this

使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。
实例的__proto__属性
子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型
因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为
原生构造函数的继承
原生构造函数是指语言内置的构造函数,通常用来生成数据结构。ECMAScript的原生构造函数大致有下面这些。

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()
    ES6允许继承原生构造函数定义子类,因为ES6是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承
    Class的静态方法
    如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
    父类的静态方法,可以被子类继承
    静态方法也是可以从super对象上调用的
    new.target属性
    new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的
    子类继承父类时,new.target会返回子类。
    注意,在函数外部,使用new.target会报错。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值