let
、const
关键字在 ES6
以前,JavaScript
中变量默认是全局性的,只存在函数级做用域,声明函数曾经是创造做用域的惟一方法。这点和其余编程语言存在差别,其余语言大多数都存在块级做用域。因此在 ES6
中,新提出的 let
和 const
关键字使这个缺陷获得了修复。javascript
if (true) { let a = 'name'; } console.log(a); // ReferenceError: a is not defined
同时还引入的概念 const
,用来定义一个常量,一旦定义之后不能够修改,若是是引用类型,那么能够修改其引用属性,不能够修改其引用。html
const MYNAME = 'liangyin'; MYNAME = 'doke'; // TypeError: Assignment to constant variable. const MYNAME = { first: 'liang' }; MYNAME.last = 'yin'; // {first: "liang", last: "yin"}
有如下几点须要注意:前端
let
和 const
代替 var
const
以防止后期无心覆盖const
定义的变量使用大写形式箭头函数是一种更简单的函数声明方式,能够把它看做是一种语法糖,箭头函数永远是匿名的。java
let add = (a, b) => { return a + b; } // 当后面是表达式(expression)的时候,还能够简写成 let add = (a, b) => a + b; // 等同于 let add = function(a, b) { return a + b; } // 在回调函数中应用 let number = [1, 2, 3]; let doubleNumber = number.map(number => number * 2); console.log(doubleNumber); // [2, 4, 6] 看起来很简便吧
this
在箭头函数中的使用在工做中常常会遇到 this
在一个对象方法中嵌套函数做用域的问题。express
var age = 2; var kitty = { age: 1, grow: function() { setTimeout(function() { console.log(++this.age); }, 100) } }; kitty.grow(); // 3
其实这是由于,在对象方法的嵌套函数中,this
会指向 global
对象,这被看作是 JavaScript
在设计上的一个重大缺陷,通常都会采用一些 hack
来解决它。编程
let kitty = { age: 1, grow: function() { const self = this; setTimeout(function() { console.log(++self.age); }, 100); } } // 或者 let kitty = { age: 1, grow: function() { setTimeout(function() { console.log(this.age); }.bind(this), 100) } }
如今有了箭头函数,能够很轻松地解决这个问题。数组
let kitty = { age: 1, grow: function() { setTimeout(() => { console.log(this.age); }, 100) } }
可是箭头函数并非万能的,任何事物都具备两面性,语言的新特性经常被误解、滥用,好比箭头函数的使用就存在不少误区。 你们能够移步☞ES6箭头函数使用注意点。app
ES6
出现之前,面对默认参数都会让人感到很痛苦,不得不采用各类 hack
手段,好比说:values = values || []
。如今一切都变得轻松不少。编程语言
function desc(name = 'liangyin', age = 18) { return name + '已经' + age + '岁了' } desc(); // liangyin已经18岁了
Rest
参数当一个函数的最后一个参数有...
这样的前缀,它就会变成一个参数的数组。模块化
function test(...args) { console.log(args); } test(1, 2, 3); // [1,2,3] function test2(name, ...args) { console.log(args); } test2('liangyin', 2, 3); // [2,3]
它和 arguments
参数有以下区别:
Rest
参数只是没有指定变量名称的参数数组,而 arguments
是全部参数的集合。arguments
对象并非一个真正的数组,而 Rest
参数是一个真正的数组,可使用各类数组方法,好比 sort
,map
等。刚刚讲到了 Rest
操做符来实现函数参数的数组,其实这个操做符的能力不只如此。它被称为展开操做符,容许一个表达式在某处展开,在存在多个参数(用于函数调用),多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方就会出现这种状况。
以前在 JavaScript
中,想让函数把一个数组依次做为参数进行调用,通常会采起如下措施:
function test(x, y, z) {}; var args = [1, 2, 3]; test.apply(null, args);
有了 ES6
的展开运算符,能够简化这个过程:
function test(x, y, z) {}; let args = [1, 2, 3]; test(...args);
在以前的版本中,若是想建立含有某些元素的新数组,经常会用到 splice
、concat
、push
等方法:
var arr1 = [1, 2, 3]; var arr2 = [4, 5, 6]; var arr3 = arr1.concat(arr2); console.log(arr3); // [1,2,3,4,5,6]
使用展开运算符之后就简便了不少:
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2]; console.log(arr3); // [1,2,3,4,5,6]
ES7
)数组的展开运算符简单易用,那么对象有没有这个特性?
let uzi = { name: 'uzi', age: 50 }; uzi = { ...uzi, sex: 'male' }; console.log(uzi); // {name: "uzi", age: 50, sex: "male"}
这是 ES7
的提案之一,它可让你以更简洁的形式将一个对象可枚举属性复制到另一个对象上去。
在 ES6
以前的时代,字符串拼接老是一件使人很很很不爽的一件事,可是在ES6
的时代,这个痛点终于被治愈了!!!
// 以前的作法 var name = 'uzi'; var a = 'My name is ' + uzi + '!'; // 多行字符串 var longStory = 'This is a long story,' + 'this is a long story,' + 'this is a long story.' // 有了 ES6 以后咱们能够这么作 let name = 'uzi'; let a = `My name is ${name} !`; let longStory = `This is a long story, this is a long story, this is a long story.`
解构语法能够快速从数组或者对象中提取变量,能够用一个表达式读取整个结构。
let number = ['one', 'two', 'three']; let [one, two, three] = number; console.log(`${one},${two},${three}`); // one,two,three
let uzi = { name: 'uzi', age: 20 }; let { name, age } = uzi; console.log(`${name},${age}`); // uzi,20
解构赋值能够看作一个语法糖,它受 Python
语言的启发,提升效率之神器。
众所周知,在 JavaScript
的世界里是没有传统类的概念,它使用的是原型链的方式来完成继承,可是声明方式老是怪怪的(很大一部分人不遵照规则用小写变量声明一个类,这就致使类和普通方法难以区分)。
在 ES6
中提供了 class
这个语法糖,让开发者模仿其余语言类的声明方式,看起来更加明确清晰。须要注意的是, class
并无带来新的结构,只是原来原型链方式的一种语法糖。
class Animal { // 构造函数 constructor(name, age) { this.name = name; this.age = age; } shout() { return `My name is ${this.name}, age is ${this.age}`; } // 静态方法 static foo() { return 'this is static method'; } } const cow = new Animal('uzi', 2); cow.shout(); // "My name is uzi, age is 2" Animal.foo(); // "this is static method" class Dog extends Animal { constructor(name, age = 2, color = 'black') { // 在构造函数中直接调用 super 方法 super(name, age); this.color = color; } shout() { // 非构造函数中不能直接使用 super 方法 // 可是能够采用 super. + 方法名调用父类方法 return super.shout() + `, color is ${this.color}`; } } const uzisDog = new Dog('uzi'); uzisDog.shout(); // "My name is uzi, age is 2, color is black"
Object.assign
方法用来将源对象的全部可枚举属性复制到目标对象.
let target = { a: 1 }; // 后边的属性值,覆盖前面的属性值 Object.assign(target, { b: 2, c: 3 }, { a: 4 }); console.log(target); // {a: 4, b: 2, c: 3}
class add { constructor(obj) { Object.assign(this, obj); } } let p = new add({ x: 1, y: 2 }); console.log(p); // add {x: 1, y: 2}
Object.assign(add.prototype, { getX() { return this.x; }, setX(x) { this.x = x; } }); let p = new add(1, 2); console.log(p.getX()); // 1
function cloneObj(origin) { return Object.assign({}, origin); }
Set
、Map
和Array.from
Set
Set
里面的成员的值都是惟一的,没有重复的值,Set加入值时不会发生类型转换,因此5和"5"是两个不一样的值。
// 数组去重 function dedupe(array) { return Array.from(new Set(array)); } console.log(dedupe([1, 2, 2, 3])); // 1, 2, 3
Map
Map
相似于对象,也是键值对的集合,可是"键"的范围不限于字符串,各类类型的值(包括对象)均可以当作键.
let m = new Map(); let o = { p: 'Hello World' }; m.set(o, 'content'); m.get(o); // content m.has(o); // true m.delete(o); // true m.has(o); // false m.set(o, 'my content').set(true, 7).set('foo', 8); console.log(m); // Map(3) {{…} => "my content", true => 7, "foo" => 8} // Map/数组/对象 三者之间的相互转换 console.log([...m]); // (3) [Array(2), Array(2), Array(2)]
Array.from
Map
Map
对象的键值对转换成一个一维数组。
const map1 = new Map(); map1.set('k1', 1); map1.set('k2', 2); map1.set('k3', 3); console.log(Array.from(map1)) // [Array(2), Array(2), Array(2)]
Set
const set1 = new Set(); set1.add(1).add(2).add(3); console.log(Array.from(set1)); // [1, 2, 3]
能够把ascii
的字符串拆解成一个数据,也能够准确的将unicode
字符串拆解成数组。
console.log(Array.from('hello world')); console.log(Array.from('\u767d\u8272\u7684\u6d77')); // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"] // ["白", "色", "的", "海"]
一个类数组对象必需要有length
,他们的元素属性名必须是数值或者能够转换成数值的字符。
注意:属性名表明了数组的索引号,若是没有这个索引号,转出来的数组中对应的元素就为空。
console.log(Array.from({ 0: '0', 1: '1', 3: '3', length:4 })); // ["0", "1", undefined, "3"]
若是对象不带length
属性,那么转出来就是空数组。
console.log(Array.from({ 0: 0, 1: 1 })); // []
对象的属性名不能转换成索引号时,转出来也是空数组。
console.log(Array.from({ a: '1', b: '2', length: 2 })); // [undefined, undefined]
Array.from
能够接受三个参数Array.from(arrayLike[, mapFn[, thisArg]])
arrayLike
:被转换的的对象。
mapFn
:map函数。
thisArg
:map函数中this指向的对象。
JavaScript
模块化是一个很古老的话题,它的发展从侧面反映了前端项目愈来愈复杂、愈来愈工程化。在 ES6
以前,JavaScript
没有对模块作出任何定义,知道 ES6
的出现,模块这个概念才真正有了语言特性的支持,如今来看看它是如何被定义的。
// hello.js 文件 // 定义一个命名为 hello 的函数 function hello() { console.log('Hello ES6'); } // 使用 export 导出模块 export {hello}; // main.js // 使用 import 加载这个模块 import { hello } from './hello'; hello(); // Hello ES6
上面的代码就完成了模块的一个最简单的例子,使用 import
和 export
关键字完成模块的导入和导出。固然也能够完成一个模块的多个导出:
// hello.js export const PI = 3.14; export function hello() { console.log('Hello ES6'); } export let person = { name: 'uzi' }; // main.js // 使用对象解构赋值加载这3个变量 import { PI, hello, person } from './hello'; // 也能够将这个模块所有导出 import * as util from './hello'; console.log(util.PI); // 3.14
还可使用 default
关键字来实现模块的默认导出:
// hello.js export default function() { console.log('Hello ES6'); } // main.js import hello from './hello'; hello(); // Hello ES6