es6出来好久了一直都没学,东西挺多的,找了一些博客整理一些常用的知识点,按照自己理解敲了一遍代码,想了解全部的那无疑就是阮一峰大大的博客啦。
参考博客:
ECMAScript 6 入门
实例感受-es6的常用语法和优越性
ES6常用知识点概述
JS几种数组遍历方式以及性能分析对比
let与const
- 块级作用域
- 声明不提升
- 不可重复声明
- const声明的为常量,如果是基本类型,值不可更改,如果是引用类型,引用不可改变,但是值可以改变
if(true){
var a=10;
let b=20;
}
console.log(a); // 10
console.log(b); // 报错,没有定义,只能在块级作用域中使用
function fn(){
console.log(a1); // undefined 变量声明提升,有声明无初始值
var a1=10;
console.log(a2); // 报错,没有定义
let a2=20;
}
fn();
var b=10;
let b=12; //报错 a已经被声明
const c1=10;
c1=20; // 报错,不能更改const常量
const c2=[1,2]; // 数组c2的地址不可更改
c2[1]=3; // 可以,更改值可以
console.log(c2); // [1,3]
c2=[1,3]; // 报错,不可重新赋值地址
编程风格
- 考虑到var和let语义相同且let没有全局污染的缺点,应该全面用let替换var
- let与const都可以时,应该使用const,所有的函数表达式都应该使用const,所有的全局参数都应该尽量使用const
字符串
字符串模板
用es5写一些较长的含有变量的字符串时,总是使用+号进行拼接,极其不方便,es6提供了字符串模板的方式填充变量。
字符串模板必须使用在反引号``中
// es5
var a=10;
var b='<div id="'+a+'"></div>'; // 拼接很麻烦,尤其是涉及到单双引号
console.log(b); // <div id="10"></div>
// es6
let c=`<div id="${a}"></div>` // 放到反引号中
console.log(c) // <div id="10"></div>
// 反引号中所有的空格都会保留,可以使用trim()去除头尾的空格
字符串遍历/数组遍历/对象遍历
es6新增了字符串的遍历接口
let str='abcdefg';
for(let s of str){
console.log(s); // a b c d e f g
}
再整理一下数组和对象常用的遍历方法
// 数组遍历
// for循环是最常用的一种
for(let i=0;i<arr.length;i++){
}
// 将数组长度保存下来,是上面一种的改进版本,几种遍历方法中性能最好
for(let i=0,len=arr.length;i<len;i++){
}
// for in,用法上是for循环的变体,但效率是最低的
for(let key in arr){
console.log(arr[key])
}
// for of,es6提供的,同时支持一些类数组的遍历
for(let value of arr){
console.log(value)
}
// forEach(),可以同时操作value和key,但是性能比不上for循环
arr.forEach(function(value,key,arr){
})
// 对象遍历
// for in ,遍历对象自身和继承的可枚举属性
for(let key in obj){
console.log(obj[key])
}
// Object.keys(obj)
// Object.keys返回一个数组,包括对象自身的所有可枚举属性的键名。
Object.keys(obj).forEach(function(key){
console.log(obj[key])
})
// Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性,包括不可枚举属性的键名。
Object.getOwnPropertyNames(obj).forEach(function(key){
console.log(obj[key])
})
解构赋值
es6允许按照一定的模式从数组和对象中提取值,然后对变量进行赋值。
// 一一对应
let [a,b,c] = [1,2,3]
console.log(`${a}====${b}====${c}`) // 1====2====3
let [s1,arr] = [1,[1,2,3]]
console.log(s1) // 1
console.log(arr) // [1,2,3]
// 左少右多
let [a,b]=[1,2,3,4] // a=1,b=2
// 左多右少
let [a,b,c,d]=[1,2] // a=1,b=2,c=undefined,d=undefined
// 可以默认值
let [a,b,c=10]=[1,2] // a=1,b=2,c=10
// 与扩展运算符一起使用
let [a,...arr]=[1,2,3,4]
console.log(a) // 1
console.log(arr) // [2,3,4]
// 对象的解构
let {name, age} = {name:'xx',age: 10}
console.log(name) // xx
console.log(age) // 10
// 对象的解构赋值与顺序没有关系,而是属性名相同
// let {name, age} = {name:'xx',age: 10} 与 let {age,name} = {name:'xx',age: 10}结果相同
// let {age,name} 是let {name:name,age:age}的简写,赋值的是后面的属性变量
let { foo: baz , bar } = { foo: "aaa", bar: "bbb" };
foo // foo is not defined
baz // 'aaa'
bar // 'bbb
编程风格
- 当使用多个数组元素时,优先考虑解构赋值
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
- 当函数参数为对象时,优先考虑解构赋值
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
}
// best
function getFullName({ firstName, lastName }) {
}
函数
箭头函数
// es5
var add=function(x,y){
rerturn x+y;
}
// es6
const add=(x,y)=>x+y
// 没有参数
const f=()=>5
//等同于
var f=function(){
return 6
}
// 只有一个参数
const f=x=>x+5
// 等同于
var f=function(x){
return x+5
}
// 有多个参数和语句
const f=(x,y)=>{x++;y++;return x+y}
** 注意 **
箭头函数内部没有this, 所以不可当作构造函数使用,因为没有this,所以在箭头函数内部使用this的话,会绑定到箭头函数定义时的作用域。
箭头函数的该特性特别适合用于回调函数。
var handler = {
id: '123456',
init: function() {
document.addEventListener('click',
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};
// 这里的this始终指向handler, 而如果是普通函数的话,则会使用调用该函数的对象,即document,就会出现错误。
reset参数
reset参数用于收集函数的多余参数,并且存入一个真正的数组中,这样就可以不必使用arguments这个伪数组参数了。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
// 替换arguments
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
编程风格
- 单行的,简单的,不用复用的函数都尽量使用箭头函数
- 需要绑定this的函数也使用箭头函数
- 复杂的经常复用的函数还是需要使用一般函数
- 使用reset参数代替arguments
class与extends
在es5中使用构造函数和原型的方式来定义类,es6封装了class来更接近java/c++等语言的编写习惯.
// es5
function Person(x,y){
this.x=x;
this.y=y;
}
Person.prototype.say=function(){
return this.x+this.y
}
var p=new Person('x','y')
// es6将其改写为
class Person{
constructor(x,y){
this.x=x;
this.y=y;
}
say(){
return this.x+this.y
}
}
let p=new Person(x,y) // 默认会调用constructor
p.say()
所有在类中声明的方法其实都是定义在protoytpe属性上。
在es5中一般使用组合继承的方式来实现继承,写起来很麻烦,es6采用extends关键字来实现继承,也更贴近java/c++的写法。
class Person{
constructor(x,y){
this.x=x;
this.y=y;
}
say(){
console.log(`${this.x}++++++${this.y}`)
}
}
let p=new Person('xixi',10);
p.say(); // xixi++++++10
class Girl extends Person{
constructor(name,age,sex){
super(name,age) // 调用父类构造函数,必须要写,后面才能使用this
this.sex=sex
}
play(){
super.say() // 调用父类方法
console.log(this.sex)
}
}
let g=new Girl('xi',10,'girl')
g.play() // xi++++++10 girl
编程风格
- 应该使用class与extends来代替es5中的构造函数和原型继承
CommonJS/AMD/ES6 Module
这些都是对js模块化的规范输出,所谓js模块化,我理解的就是一个js文件就是一个模块,里面封装了某些功能,可以在其他地方导入使用,像python里import某个包一样。
可以使用外联script的形式引入别人的js代码,如:
<script src='file1.js'>
<script src='file2.js'>
<script src='file3.js'>
但是这样需要注意引用顺序,如file2.js里面需要file1.js里面的某个函数,必须要放在file1.js后面,同时还存在变量污染的问题。所以就出现了模块的概念,模块里面的变量是私有的,不会影响到别的模块里的变量,同时为了可以方便的使用其他人的模块,而不是需要一个个去输入网址去找版本下载,也出现了npm之类的包管理器,将大家的模块统一管理,使用指令即可下载使用。
CommonJS
是通过node.js实现的,在后端使用的模块化规范。
// add.js
function add(x,y){
return x+y
}
module.exports=add
// index.js
const add=require('./add.js')
导出模块的原理,首先nodejs会先准备一个module对象:
// 准备module对象:
var module = {
id: 'add',
exports: {}
};
var load = function (module) {
// 读取的add.js代码:
function add(x,y){
return x+y
}
module.exports = add;
// add.js代码结束
return module.exports;
};
var exported = load(module);
// 保存module:
save(module, exported);
Node保存了所有导入的module,当我们用require()获取module时,Node找到对应的module,把这个module的exports变量返回,这样,另一个模块就顺利拿到了模块的输出:
const add=require('./add.js')
关于模块中变量不会污染,是因为nodejs在加载模块时,会将代码自动放入一个立即执行函数中形成自己的作用域,这样全局变量就变成了局部变量。
AMD
commonjs虽然可以实现模块化,但是机制是同步的,适合后端,对于浏览器端,应该使用异步机制,这就产生了AMD,Asynchromous Module Definition,异步加载模块,是requirejs关于模块化标准输出。
// myModule.js
define(['依赖的模块路径'], function(依赖模块名称){
 ...
 return {
 };
});
// index.js
require(['./myModule'],function(myModule){
})
es6 module
es6也规范了模块化的概念,比requirejs更加高效,使用的是export和import
// 可以导出多种形式
export var a=10
var m=1
export {m} // 必须加{}
var fn=function(){}
export fn=function(){}
export {fn}
// 引入
import {fn} from './xxx.js'
import * from './xxx.js'