ECMAScript6.0(简称es6)
是JavaScript语言下一代的标准,它的目标,是使得JavaScript语言可以用来编写复杂的大型应用,成为企业级的开发语言.
Babel(转码器)
有的浏览器目前还不支持es6语法,所以需要Babel转码器将es6代码转换成es5代码,支持浏览器执行.
- Babel配置文件.babelrc
格式
{
"presets":[
'es2015'
],
"plugins":[]
}
es2015转码规则 将规则加入persets配置中
npm install --save-dev babel-preset-es2015
- 命令行转码 babel-cli
Babel提供babel-cli工具,用于命令行转码,安装命令如下
npm install --global babel-cli
然后,改写package.json.
{
"devDependencies": {
"babel-cli": "^6.0.0"
},
"scripts": {
"build": "babel src -d lib"
},
}
转码时候,执行下面命令.
npm run build
- bable-node
babel-cli自带命令,提供一个支持ES6的REPL环境,支持Node的REPL所有功能,可以直接运行es6代码
babel-node es6.js
- babel-register
babel-register模块改写require命令,为它加上一个钩子,此后每当使用require加载.js、.jsx、.es、
、.es6后缀的文件,就会先用Babel进行转码.
npm install --save-dev babel-register
使用时必须先加载babel-register
require("babel-register")
require("./index.js")
- babel-core
如果某些代码需要调用Babel的API进行转码,就要使用babel-core模块
//安装命令
npm install babel-core --save
在项目中可以调用babel-core
let babel=require('babel-core');
//字符串转码
babel.transform('code',options);
//文件转码(异步)
babel.transformFile('文件地址',options,callback);
//文件转码 (同步)
babel.transformFileSync('文件地址',options)
//Babel AST转码
babel.transformFromAst(ast,code,options)
- babel-polyfill
Babel默认只转换新的JavaScript语法,而不转换新的API,例如Iterator、Gennerator、Set、
Map、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码.这时候需要使用babel-polyfill,为当前环境提供一个垫片.
//安装命令
npm install --save babel-polyfill
在头部引入
import 'babel-polyfill'
//或
require('bable-polyfill')
let和const命令
- let命令
类似于var,但是所声明的变量,只在let命令所在的代码块内有效.
{
let a=10;
var b=1;
}
console.log(a) // ReferenceError: a is not defined.
console.log(b) //1
如果for循环 let声明的变量只在循环体内有效
for(let i=0;i<10;i++){
}
console.log(i)// ReferenceError: i is not defined
不存在变量提升,let不会像var那样会发生变量提升现象,所以,变量声明一定要在声明后使用.
console.log(foo) //输出undefined
console.log(bar) //报错ReferenceError
var foo=2;
let bar=2;
暂时性死区,只要块级作用域存在let命令,它所声明的变量就"绑定"这个区域,不再受外部的影响.
var tmp=123
if(true){
tmp='abc'; //报错ReferenceError
let tmp;
}
"暂时性死区"也意味着typeof,不再是一个百分之百安全的操作.在没有let之前 typeof百分之百安全.
typeof x; //报错ReferenceError
let x;
不允许重复声明,let不允许在相同的作用域内重复声明.
//报错
function (){
let a=10;
var a=1;
}
//报错
function (){
let a=10;
let a=1;
}
function func(arg){
let arg //报错
}
块级作用域与函数声明,es5规定函数不能在块级作用域中声明.
//es5严格模式
'use strict'
if(true){ //报错
function f(){
}
}
//es6严格模式
'use strict'
if(true){ //不报错
function f(){
}
}
es6规定
1.允许在块级作用域内声明函数.
2.函数声明类似于var,即会提升到全局作用域或函数作用域的头部.
3.同时,函数声明还会提升到所在的块级作用域头部.
do表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值.
//在外部没办法拿到t的值,应为块级作用域不会返回值,除非t是全局变量.
{
let t=f();
t=t*t+1;
}
//解决方案变成do表达式,会使x得到整个块级作用域的返回值.
let x=do{
let t=f();
t*t+1;
}
const命令
const 声明一个只读常量,一旦声明,常量的值就不能改变.意味着const一旦声明变量,就立即初始化赋值.
const PI=3.1415;
PI //3.1415
PI=3; //TypeError: Assignment to constant variable.
对于复合类型的变量,指向数据所在的引用地址.操作此变量必须保证引用地址不能改变.
const foo={};
foo.prop='123';
foo.prop // 123
foo={} //报错 TypeError: "foo" is read-only
其余暂时性死区,不能重复声明,不存在变量提升与let类似.
顶层对象属性
顶层对象,在浏览器环境指向的是window对象,在node中指向的是global对象,es5中,顶层对象的属性,与全局变量是等价的.
window.a=1;
a //1
var a=2;
window.a //2
es6全局对象与顶层对象属性脱钩.
let a=1;
window.a //undefined
变量的解构赋值
- 数组的解构赋值
基本用法(es6允许按照一定的模式,从数组和对象中提取值,对比那辆进行解构,以前对变量赋值,只能直接指定值.只要等号两边的模式相同,左边的变量就会被赋予对应的值).
//es5
var a=1;
var b=2;
//es6
let [a,b]=[1,2]
结构例子
let [foo,[bar,baz]]=[1,[2,3]]
let [,,third]=["foo","bar","baz"]
third // "baz"
let [head,...tail]=[1,2,3,4,5];
head //1
tail //2,3,4,5
let [x,y,...z]=[2]
x //2
y //undefined
z // []
//结构不成功,变量undefined
let foo=[];
let [bar,foo]=[1] //foo都等于undefined
//不完全结构例子,等号左边的模式,只匹配为一部分等号右边的数组.这种情况,解构依然可以成功.
let [x,y]=[1,2];
let [z,[b],d]=[1,[2,3],4]
如果等号右边不是数组,那么会报错.
let [f]=1;
let [f]=false;
let [f]=NaN;
let [f]=undefined;
let [f]=null;
let [f]={};
对于Set结构,也可以使用数组的解构赋值.
let [x,y,z]=new Set(['a','b','c'])
x //a
默认值(解构赋值允许指定默认值)
let [foo=true]=[];
foo //ture
[x,y='b']=['a']; //x='a' y='b'
[x,y='b']=['a',undefined] //x='a' y='b' // 如果数组成员不严格等于undefined,默认值不会生效.
//默认值是表达式,那么这个表达式会惰性求值,在用到的时候才会求值.
function f(){
console.log('aaaa')
}
let [x=f()]=[1] //应为x能取到值,所以函数f不会执行
//默认值可以引用解构赋值的其他变量,但该变量必须已经声明.
let [x=1,y=x]=[] //x=1 y=1
let [x=1,y=x]=[2] //x=2 y=2
let [x=1 ,y=x]=[1,2] // x=1 y=2
let [x=y,y=1]=[] // ReferenceError
2.对象结构赋值
//变量与属性同名在能正确赋值.
let {foo,bar}={foo:'zzz',bar:'bbb'};
foo //zzz
let {baz}={foo:'zzz'}
baz // undefined
//如果变量名与属性名不一致必须写成这样
let {foo:baz}={foo:'aaa',bar:'bbb'};
baz // 'aaa'
let obj={first:'hello',last:'world'};
let {first:f,last}=obj
f //'hello'
last //'world'
//对象结构赋值的内部机制,是先找到同名属性,然后在赋值对应的变量,真正赋值的是后者.
let {foo:foo}={foo:'aaa'}
//对于let const变量不能重复声明.
let foo;
let {foo}={foo:1} // SyntaxError: Duplicate declaration "foo"
//成功赋值
let foo;
({foo}={foo:1}) //成功
和数组一样,解构也可以用于嵌套结构对象.
let obj={
p:['hello',{
y:'word'
}
]
}
let { p:[x,{ y } ] }=obj //x='hello' y='word' 注意p是模式不是变量,因此p不被赋值
let node={
loc:{
start:{
line:1,
column:5
}
}
let { loc:{ start:{ line } } }=node
line //1
loc //error: loc is undefined
start //error: start is undefined loc与start都是模式不会被赋值
嵌套赋值
let obj={}
let arr=[]
({foo:obj.prop,bar:arr[0]})={foo:123,bar:true}
obj //{prop:123}
arr // [true]
对象结构指定默认值(默认值生效的条件是,对象的属性值严格等于undefined)
let {x=3}={} //x=3
let {x,y=5}={x:1} //x=1 y=5
let {x,y=3}={} //y=3
let {x,y=x}={x:5} // y=5
如果解构模式是嵌套对象,而且子对象所在的父属性不存在,会报错.
//报错
let {foo:{bar}}={baz:'111'}
//应为foo等于undefined在取子属性就会报错
let _tmp={baz:'baz'};
_tmp.foo.bar //报错
对象的解构赋值,可以方便得将现有对象的方法,赋值到某个变量.
let {log,sin,cos}=Math;
由于数组本质是特殊的对象,因此可以对数组进行对象属性的结构.
let arr=[1,2,3];
let {0:first,[arr.length-1]:last}=arr;
first //1
last //3
3.字符串解构赋值
const [a,b,c,d,e]='hello'
a //h
b //e
类似数组的对象都有一个length属性,英雌还可以对这个属性解构赋值
let {length:len}='hello'
len //5
4.数值和布尔值的解构赋值
解构赋值时,如果等号左边是数值和布尔值,则会将其转换成对象.
let {toString:s}=123;
s===Number.property.toString //true
let {toString:s}=true;
s===Boolean.property.toString //true
解构赋值的规则是,只要等号左边的值不是对象,现将其转换为对象,由于undefined和null无法转换为对象,所以解构赋值会报错
let {prop:x}=undefined //TypeError
let {prop:y}=null //TypeError
5.函数参数的解构赋值
function add([x,y]){
return x+y
}
add([1,2]) //3
[[1,2],[3,4]].map(([a,b])=>{a+b}) //[ 3 ,7]
函数参数也可以使用默认值
function move({x=0,y=0}={}){
return [x,y]
}
move({x:4,y:5}) //[4,5]
move({}) //[0,0]
move() // [0,0]
//下列写法会得到不同结果
function move({x,y}={x:0,y:0}){
return [x,y]
}
move({x:3,y:8}) //[3,8]
move({x:3}) //[3,undefined]
move({}) //[undefined,undefined]
move() //[0,0]
//undefined会触发默认值
[1,undefined,3].map((x='yes')=>x)
//[1,yes,3]
解构赋值用途
1.交换变量
[x,y]=[y,x]
2.从函数中返回多个值
function example(){
return [1,2,3]
}
let [x,y ,z]=example();
3.返回一个对象
function example(){
return {
x:1
}
}
let {x}= example()
4.函数参数定义
function f([x,y,z]){
}
f([1,2,3])
5.提取json
let jsonData={
id:42,
status:'OK',
data:[1,2]
}
let {id,status,data:number}=jsonData;
id //42
status //'OK'
number //[1,2]
6.遍历Map结构
let map=new Map();
map.set('first','hello')
for(let [key,value] of map){
console.log(key+'is'+value)
}
//first is hello
//只想获取键值
for(let [,value] of map){
}
7.输入模块的指定方法
const {SourceNode} =require('source-map')
字符串的扩展
1.字符串接口遍历
for(let codePoint of 'foo'){
console.log(codePoint)
} // "f" "o" "o"
//传统的for循环无法识别这样的码点,for of可以
let text=String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
2.at()
//es5
'abc'.charAt(0) //a
'𠮷'.charAt(0) // "\uD842"
//es6
'abc'.at(0) //a
'𠮷'.at(0) // "𠮷"
- includes(),startWith(),endsWith()
- includes():返回布尔值,表示是否找到了参数字符串
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部
- endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
- 以上方法都支持第二个参数,表示开始搜索位置.
let s='Hello World';
s.startsWith('World',6) //true
s.endsWith('Hello',5) //true
s.includes('Hello',6) // false
4.repeat()
该方法表示将原有字符串重复n次
'x'.repeat(3) //'xxx'
//参数如果是小数,会被取整.
'c'.repeat(2.9) //'cc'
//参数是负数或者Infinity,会报错
'na'.repeat(Infinity) // RangeError
'na'.repeat(-1) // RangeError
//如果参数是0到-1之间的小数,则等同于0.NaN等同于0,字符串则会先转换为数字.
'na'.repeat(-0.9) // ' '
5.padStart() padEnd()
es7推出了字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或尾部补全.padStart头部补全,padEnd尾部补全.(接受2个参数,参数一用来指定字符串的最小长度,参数二用来补全的字符串)
'x'.padStart(5,'ab') //'ababx'
'x'.padStart(4,'ab') // 'abax'
'x'.padEnd(4,'ab') //'xabab'
//如果原有字符串的长度大于等于指定的最小长度,则会返回原有字符串
'xxx'.padStart(2,'ab') //'xxx'
//如果用来补全的字符串与原有字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串.
'abc'.padStart(10,'0123456789') //'0123456abc'
//如果省略第二个参数,则会用空格补全长度
'x'.padStart(4) //' x'
6.模板字符串
let name='zdb'
`My Name is ${name}`
模板编译
//使用<%...%>放置JavaScript代码,使用<%=...%>输出Javascript表达式.
let template=`
<ul>
<%for(let i=0;i<data.length;i++){%>
<li><%= data[i] %></li>
<% } %>
</ul>
`
模板标签
//它紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串.这个被称为"模板标"签功能
alert `123`
//等同于
alert(123)
//如果字符串里有变量,就不是简单的调用了,而是会将模板字符串处理成多个参数,在调用函数.
let a=5;
let b=3;
tag `Hello ${a+b} world ${a*b}`
//等同于
tag (['Hello ',' world ', ' '],15,50)
数值的扩展
Number.isFinite() Number.isNaN()与传统全局的区别在于,传统方法先调用Number()转换,在进行判断.
//Number.isFinite检查一个数据是否为有限的
Number.isFinite(Infinfiy) //false
Number.isFinite('foo') //false
Number.isFinite(true)//false
//Number.isNaN检查一个值是否为NaN,带有隐式转换
Number.isNaN('15') //false
Number.isNaN(NaN)//true
Number.isNaN(true)//false
isFinite("25") //true
Number.isFinite("25")//false
isNaN("NaN") //true
Number.isNaN("NaN")//false
Number.parseInt(),Number.parseFloat(),将全局的parseInit,parseFloat,一直到Number对象上,行为完全保持不变.
Number.parseInit('12.34')//12
Number.parseFloat('123.45#') //123.45
Number.isInteger()用来判断一个值是否为整数
Number.isInteger(25)//true
Number.isInteger(25.0)//true
Number.isInteger(25.1)//false
Number.isInteger("25")//false
Number.isInteger(true)//false
Number.EPSILON在Number对象上,新增一个极小的常量.Number.isSafeInteger()能够表示的整数范围在-2^53 到 2^53之间(不含两个端点),用来判断一个整数是否在安全数范围内.
Math对象的扩展
Math.trunc()用于去除一个数的小数部分,返回整数部分.对于非数值会Number先转换.
Math.trunc(4.1) //4
Math.trunc(-4.1) //-4
Math.trunc(-0.23)//-0
Math.trunc() //NaN
Math.sign()用来判断一个数到底是正数、负数、还是零.
//参数为正数,返回+1
//参数为负数,返回-1
//参数为0,返回0
//参数为-0,返回-0
//其他值,返回NaN
Math.sign(1)//1
Math.sign(-1)//-1
Math.sign(0)//0
Math.sign(-0)//-0
Math.sign('foo'); // NaN
Math.sign(); // NaN
Math.cbrt()用来计算一个数的立方根
Math.cbrt(-1) //-1
Math.cbrt(0)//0
Math.cbrt(2)// 1.2599210498948734
Math.clz32()返回一个数的32位无符号正数形式有多少个前导0
Math.imul()方法返回两个数以32位带符号正数形式相乘的结果,返回的也是一个32位的带符号整数
Math.imul(2,4) //8
Math.imul(-1,8)//-8
Math.imul(-2,-2)
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, 'foo'); // 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)
**指数运算符
2**2 //4
2**3 //8
ES6数组的扩展
Array.from()方法用于将两类对象转换为真正的数组:类似数组的对象和可比案例的对象(包括es6新增的Set和Map).接受第二个参数
let arrayLike={
'0':'a',
'1':'b',
length:2
}
//es5写法
let arr1=[].slice.call(arrayLike)
//es6
let arr2=Array.from(arrayLike)
let ps=document.querySelectorAll('p');
Array.from(ps)
Array.from('hello')//['h','e','l','l','o']
let set=new Set(['a','b']);
Array.from(set) //['a','b']
//扩展运算符...也可以将某些数据结构转换为数组
function foo(){
let args=[...arguments]
}
[...document.querySelectorAll('div')]
Array.from({length:3})
//[undefined,undefined,undefined]
[...{length:3}] //error
//接受第二个参数,用来对每个元素处理,将处理后的值放入返回数组
Array.from([1,2,3],(x)=>x*x)//[1,4,9]
Array.from第三个参数用来绑定this
Array.of()方法将一组值,转换为数组.这个方法主要弥补Array的不足.
Array.of(3,11,8) //[3,11,8]
Array.of(3) //[3]
Array.of(3).length //1
Array.of()//[]
Array.of(undefined)//[undefined]
Array() //[]
Array(3) // [,,,]
Array(3,11,8)//[3,11,8]
//Array.of()方法可以用以下代码实现
function ArrayOf(){
return [].slice.call(arguments)
}
copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。会修改原有数组.接受三个参数.
1.target(必须):从该位置替换数据.
2.start(可选):从该位置开始读取数据,默认为0,如果为负值,表示倒数.
3.end(可选): 到该位置前停止读取数据,默认等于数组长度,如果为负数,表示倒数.
Array.prototype.copyWithin(target,start=0,end=this.length);
[1,2,3,4,5].copyWithin(0,3) //[4,5,3,4,5]
[1,2,3,4,5].copyWithin(0,3,4)//[4,2,3,4,5]
[1,2,3,4,5].copyWithin(0,-2,-1)//[4,2,3,4,5]
数组实例的find()和findIndex()
1.数组实例的find方法,用于找出第一个符合条件的数组成员.参数是一个回调函数,所有数组成员一次执行该回调函数,找到第一个返回值为true的成员,返回该成员,如果没有符合条件的成员,则返回undefined.(三个参数当前值,下标,当前数组)
2.findIndex与find类似,返回第一个符合成员的下标,所有成员不符合返回-1.
[1,4,-5,10].find((x)=>{x<0}) //-5
[1,5,10,15].findIndex(function(value,index,arr)=>{
return value>9 //2
)
//另外这两个方法都可以发现NaN弥补的indexOf不足
[NaN].indexOf(NaN) //-1
[NaN].findIndex(y=>Object.is(NaN,y))
数组实例的fill()
fill方法使用给定值,填充一个数组.(三个参数,填充的值,起始位置,结束位置)
//初始化数组,数组中已有的元素,会被全部替换.
['a','b','c'].fill(7) //[7,7,7]
['a','b','c'].fill(7,1,2) //['a',7,'c']
数组实例的entries(),keys(),values()用于遍历数组,都返回一个遍历对象,可以用for...of循环进行遍历,keys是对键名的遍历,values是对键值得遍历,entries()十岁键值对遍历
for(let index of ['a','b'].keys()){
console.log(index) //0 1
}
for(let elem of ['a','b'].values()){
console.log(elem) //'a' 'b'
}
for(let [index,elem] of ['a','b'].entries()){
console.log(index,elem) //0 'a' 1 'b'
}
//手动next方法进行遍历
let letter=['a','b','c'];
let entries=letter.entries()
console.log(entries.next().value);[0,'a']
数组实例的includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值.两个参数(查找的值,起始坐标[如果是负数,表示倒数的位置上])
[1,2,3].includes(2) //true
[1,2,NaN].includes(NaN) //true
[1,2,3].includes(3,3) //false
[1,2,3].includes(3,-1) //true
数组的空位
数组的空位数组的某一位置没有任何值,比如Array构造函数返回的数组都是空位.
Array(3)[ , , ,] //空位不是undefined,一个位置等于undefined,依然有值.空位没有任何值
0 in [undefined,undefined,undefined] //true
0 in [ , , ,]//false
es5对空位的处理,已经很不一致了,大多数情况下会忽略空位
- forEach(),filter(),every()和some都会跳过空位.
- map()会跳过空位,但保留这个值.
- join()和toString()将空位视为undefined,而undefined和null会被处理成空字符串.
// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x==='a') // true
// some方法
[,'a'].some(x => x !== 'a') // false
// map方法
[,'a'].map(x => 1) // [,1]
// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"
es6明确将空位转换为undefined
Array.from(['a',,'b']) //['a',undefined,'b']
[...['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) //1 1
}
//entries()、key()、values()、find()、和findIndex()会将空位处理成undefined
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
函数的扩展
函数参数默认值
//es5
function log(x,y){
y=y || 'world'
console.log(x,y)
}
log('Hello') //'Hello World'
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
//es6
function log(x,y='world'){
console.log(x,y)
}
log('Hello') //'Hello World'
log('Hello','China') //'Hello China'
log('Hello','') //'Hello'
//参数变量是默认声明,所以不能用let const再次声明.
function foo(x=5){
let x=0 //error
const x=1 //error
}
参数的默认值可以与解构赋值的默认值,结合起来使用.
function foo({x,y=5}){
console.log(x,y)
}
foo({}) // undefined,5
foo({x:1}) //1,5
foo({x:1,y:2}) //1,2
foo() //TypeError: Cannot read property 'x' of undefined
function fetch(url,{body='',methods='GET',headers={}}){
console.log(methods)
}
fetch('http://example.com',{}) //'GET'
fetch('http://example.com')//报错
//上面写法不能忽略第二参数,结合函数参数默认参数可以忽略第二参数
function fetch(url,{methods='GET'}={}){
console.log(methods)
}
fetch('http://example.com') //'GET'
函数指定默认值以后length的属性,将返回没有指定的默认值的参数个数.rest参数也不会计入length属性.
(function(a){}).length //1
(function(a=5){}).length //0
(function(...arg){}).length //0
作用域,如果参数默认值是一个变量,该变量所在的的作用域,与其他变量的作用域规则是一样的,即先当前函数的作用域,然后才是全局作用域.
let x=1;
function f(x,y=x){
console.log(y)
}
f(2) //2
//如果调用时,函数作用域内部的变量x没有生成,结果就会不一样
let x=1;
function f(y=x){
let x=2;
console.log(y)
}
f() //1
如果参数是一个函数,该函数的作用域是其声明时所在的作用域
let foo='outer';
function bar(func=x=>foo){
let foo='inner';
console.log(func()) //'outer'
}
bar()
//上面代码中,函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。这个匿名函数声明时,bar函数的作用域还没有形成,所以匿名函数里面的foo指向外层作用域的foo,输出outer。
应用 利用参数默认值可以指定某一个参数不得忽略,如果省略就抛出一个错误
function throwIfMissing(){
throw new Error('Missing parameter')
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided
}
foo() //Error: Missing parameter
//默认值设为undefined,表示这个参数可以省略
function foo(option=undefined){}
rest参数,用于获取函数的多余参数.注意rest只能是最后一个参数.
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
扩展运算符
console.log(...[1,2,3]) //1 2 3
console.log(1,...[2,3,4],5) // 1 2 3 4 5
[...document.querySelectorAll('div')] //[div,div,div]
//替代数组的apply方法,由于罗占运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数.
//es5
function f(x,y,z){
}
let args=[1,2,3]
f.apply(null,args)
//es6
function f(x,y,z){
}
let args=[1,2,3]
f(...args)
//找出最大值
//es5
Math.max.apply(null,[14,3,77])
//es6
Math.max(...[14,3,77])
//将一个数组添加到另一个数组尾部
//es5
let arr1=[0,1,2];
let arr2=[3,4,5]
Array.prototype.push.apply(arr1,arr2)
//es6
let arr1=[0,1,2];
let arr2=[3,4,5]
arr1.push(...arr2)
//es5
new (Date.bind.apply(Date,[null,2015,1,1]))
//es6
new Date(...[2015,1,1])
扩展运算符的应用
//es5
[1,2].concat(more)
[1,2,...more]
let arr1 = ['a', 'b'];
let arr2 = ['c'];
let arr3 = ['d', 'e'];
// ES5的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
//es5
a=list[0],rest=list.slice(1)
[a,...rest]=list
const [first,...rest]=[1,2,3,4,5]
firts//1
rest//[2,3,4,5]
const [first,...rest]=[]
first //undefined
rest //[]
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
//字符串
[...'hello'] //['h','e','l','l','o']
//能够识别32位的Unicode字符
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
let arr=Array.from(arrayLike) //['a','b','c']
Map和Set结构,Generator函数
let map=new Map([
[1,'one'],
[2,'two'],
[3,'three']
])
let arr=[...map.keys()];//1,2,3
let go=function *(){
yield 1;
yield 2;
yield 3;
}
[...go()] //[1,2,3]
//如果没有iterator接口的对象,使用扩展运算符,将会报错.
let obj={a:1,b:2}
let arr=[...obj] //TypeError: Cannot spread non-iterable object
严格模式
es5函数内部可以设定严格模式
es6函数使用了默认值、解构赋值、或者扩展运算符,不能使用严格模式会报错.
name属性
function foo(){}
foo.name //foo
var func1 = function () {};
// ES5
func1.name // ""
// ES6
func1.name // "func1"
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
箭头函数
- 使用注意点
1.函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象.
2.不可当做构造函数,也就是你说,不可以使用new命令
3.不可以使用arguments对象,该对象在函数体内不存在,可以使用rest参数代替
4.不可使用yield命令,因此箭头函数不能作为Generator函数.
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
//箭头函数可以与变量解构结合使用。
const full = ({ first, last }) => first + ' ' + last;
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
//另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向
(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']
//es5
const pipeline = (...funcs) =>
val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5)
// 12
//es6
const plus1 = a => a + 1;
const mult2 = a => a * 2;
mult2(plus1(5)) //12
绑定this es7提议箭头函数可以绑定this,提出了函数绑定运算符,同来取代call,apply,bind调用.(函数绑定运算符::左边是对象,右边是一个函数)
foo::bar
//等同于
bar.bind(foo)
foo::bar(...argument)
//等同于
bar.apply(foo,argument)
//如果左面为空,右面是一个对象的方法,则等于将该方法绑定在对象上面.
let method=obj::obj.foo;
//等同于
let methods=::obj.foo;
let log=::console.log
//等同于
let log=console.log.bind(console)
对象的扩展
属性的简洁表示
let foo='bar';
let baz={foo}
baz //{foo:'bar'}
//等同于
let baz={foo:foo}
//es6允许在对象之中,直接写变量名。这是属性名为变量名。属性值为变量的值.
function f(x,y){
return {x,y}
}
//等同于
function f(x,y){
reutrn {x:x,y:y}
}
f(1,2) //Object {x:1,y:2}
//方法简写
let o={
methods(){
return 'Hello'
}
}
//等同于
let o={
methods:function(){
return 'Hello'
}
}
let birth='2000/0/1'
let person{
name:'zs',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello(){ console.log('111')}
}
//Generator函数前面需加上*号
let obj={
*m(){
yield 'hello'
}
}
属性名表达式
//方法一
obj.foo=true;
obj['a'+'bc']=123
//es5
let obj={
foo: true,
abc: 123
}
//es6
let propKey='foo';
let obj={
[propKey]:true,
['a'+'bc']:123
}
var lastWord = 'last word';
var a = {
'first word': 'hello',
[lastWord]: 'world'
};
a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
Object.is() ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('foo','foo') //true
Object.is({},{}) //false
//与es5==和===不同之处 +0!==-0 NaN等于自身
+0===-0 //true
NaN===NaN //false
Object.is(+0.-0)//false
Object.is(NaN,NaN) //true
Object.assign(浅拷贝)
//相同的属性名会替换
let target={a:1,b:1}
let source1={b:2,c:1};
Object.assign(target,source)
target //{a:1,b:2,c:3}
let obj={a:1};
Object.assign(obj)===obj //ture
//由于undefined和null无法转换为对象,如果作为参数会报错.
Object.assign(undefined) //报错
Object.assign(null) //报错
//不在首参数就不会报错
let obj={a:1}
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
//其他类型的值(数值,字符串,和布尔值)不在首参数,也不会报错,除了字符串会议数组的形式,拷贝如对象,其他值都不会产生效果.
let v1='abc';
let v2=true
let v3=10
let obj=Object.assign({},v1,v2,v3);
console.log(obj) // {"0":"a","1":"b","2":"c"}
//属性名为Symbol值得属性,也会被Object.assign拷贝
Object.assign({a:'b'},{[Symbol('c')]:'d'})
//{a:'b',Symbol(c):'d'}
//处理数组,但是会把数组视为对象
Object.assign([1,2,3],[4,5]) //[4,5,3]
//常见用途
//(1)为对象添加属性
class Point{
constructor(x,y){
Object.assign(this,{x,y})
}
}
//(2)为对象添加方法
Object.assign(SomeClass.prototype,{
someMethod(arg1,arg2){
}
})
//等同于
SomeClass.prototype.someMethod=function(arg1,arg2){}
//(3)克隆对象
function clone(origin){
return Object.assign({},origin)
}
//不仅克隆对象自身的值,也克隆继承的值。
function clone(origin){
let originProto=Object.getPrototypeOf(origin);
return Object.assign(Object,create(originProto),origin)
}
//(4)为属性指定默认值
const default={
level:0
}
function processContent(options){
options=Object.assign({},default,options)
}
Object.getOwnProrertyDescriptor()获取属性的描述对象.
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }