es6(持续更新)

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

变量的解构赋值

  1. 数组的解构赋值

基本用法(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) // "𠮷"
  1. 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
//  }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容