1.箭头函数(=>)
ES6中引入了箭头函数符来代替function,也就是说在定义方法的时候可以不再写function,=>的左边即为函数名和参数,右边即为执行操作和返回值。
例如:
function(i){return i + 1; } //ES5
(i) => i + 1 //ES6
function(x, y) { //ES5
x++;
y--;
return x + y;
}
(x, y) => {x++; y--; return x+y} //ES6
引入箭头函数不仅使得写法更加简洁清晰,同时也解决了js中this作用域的问题。
在ES5中,函数当中的this仅指代当前函数,如果函数当中还包含函数,那么this的使用就需要相当小心。例如:
class Animal {
constructor(){
this.type = 'animal' //这里的this指代Animal对象
}
says(say){
setTimeout(function(){
console.log(this.type + ' says ' + say)//这里的this紧指代setTimeout的执行函数,而该函数中并没有type属性
}, 1000)
}
}
var animal = new Animal()
animal.says('hi') //undefined says hi
传统的做法有以下2种:
第一种:声明一个变量指代最外层对象的this,然后在内层函数中使用该变量。例如:
says(say){
var self = this;
setTimeout(function(){
console.log(self.type + ' says ' + say)
}, 1000)
}
第二种:使用bind(this),bind方法会新创建一个绑定函数,当调用这个绑定函数的时候会以bind方法的第一个参数修改函数内部的this指向。例如:
says(say){
setTimeout(function(){
console.log(this.type + ' says ' + say)
}.bind(this), 1000)//用says的this对象替代setTimeout执行方法的this对象
}
lass Animal {
constructor(){
this.type = 'animal'
}
says(say){
setTimeout( () => {
console.log(this.type + ' says ' + say)
}, 1000)
}
}
var animal = new Animal()
animal.says('hi') //animal says hi
2.变量声明(let、const)
ES6中新引入了2个变量的声明let和const,他们的作用基本跟var相同,具体区别如下:
var 声明的变量作用在全局或者方法体内。块级变量只能使用全局变量或方法体的变量。
let声明的变量只作用在代码块。可以完全使用let代替var。
const只是用来声明常量的,且一旦声明常量的值就不能改变了。能够更清晰的表明这是常量,不能修改。
先来看一个let和var使用区别的例子:
var name = 'zach'
while (true) {
var name = 'obama'
console.log(name) //obama
break
}
console.log(name) //obama
//由于var作用域只有全局和函数内部,所以第二次声明的作用域跟第一次声明一致。
let name = 'zach'
while (true) {
let name = 'obama'
console.log(name) //obama
break
}
console.log(name) //zach
//let的作用域只存在于块级内部。
var由于作用域的问题存在循环变量泄漏为全局变量的问题。如下:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
上面代码就是因为i被泄漏为了全局变量,当循环完成后i已经为10了,所以无论数组下标是几的结果都是10。
传统的解决方案是采用闭包。例如:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = getA(i);
}
function getA(i){
var showA = function () {
console.log(i);
};
return showA;
}
a[6](); // 6
ES6使用let的解决方法如下:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
const只是用来声明常量,当我们试图修改它声明的常量时,浏览器会报错。
其最好的应用场景是,当我们引入第三方插件库的时候可以保证插件声明不重复。
需注意:静态字符串声明统一使用单引号或反引号,不使用双引号,动态字符串使用反引号。例如:
const PI = Math.PI
const monent = require('moment')
const b = `foo${a}bar`;
3.类的支持(class,extends,super)
ES5中,定义一个对象如下:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
//定义类
class Point {
constructor(x, y) {//默认自带的构造函数,不显示声明则是个空的
this.x = x;
this.y = y;
}
toString() {
return this.x + ', ' + this.y;
}
}
class Line extends Point {
constructor(m,n,l){
super(m,n);//调用父类的构造函数
this.l = l;
}
toLine(){
return '('+this.l +','+ super.toString()+')';
}
}
var p = new Point(2,3);
var l = new Line(2,3,4);
p.toString(); //2,3
l.toString(); //2,3
l.getSum(); //(4,2,3)
需注意几点:
5.子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的(即:Line.prototype._proto_=Point.prototype)
4.字符串模版
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
5.参数
function sayHello(name){
//传统的指定默认参数的方式
var name=name||'dude';
console.log('Hello '+name);
}
//运用ES6的默认参数
function sayHello2(name='dude'){
console.log(`Hello ${name}`);
}
sayHello();//输出:Hello dude
sayHello('Wayou');//输出:Hello Wayou
sayHello2();//输出:Hello dude
sayHello2('Wayou');//输出:Hello Wayou
function add(){
var n = arguments.length;
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum = sum + arguments[i];
}
return sum;
}
console.log(add(1,2,3));//6
//将所有参数相加的函数
function add(...x){
return x.reduce((m,n)=>m+n);
}
//传递任意个数的参数
console.log(add(1,2,3));//输出:6
c.拓展参数
var people=['Wayou','John','Sherlock'];
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`);
}
//但是我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数
sayHello(...people);//输出:Hello Wayou,John,Sherlock
//而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法
sayHello.apply(null,people);//输出:Hello Wayou,John,Sherlock
6.模块
a.每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;
b.每一个模块内声明的变量都是局部变量, 不会污染全局作用域;
c.模块内部的变量或者函数可以通过export导出,且export导出的名称必须和定义的名称一一对应;
d.一个模块可以导入别的模块
//lib.js
//导出常量
export const sqrt = Math.sqrt;
//导出函数
export function square(x) {
return x * x;
}
//导出函数
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//main.js
import { square, diag } from './lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
模块的导出有5种方式:
export * from js路径;
//lib.js文件
let fn0 = function() {
console.log("fn0");
};
let obj0 = {}
export { fn0 as foo, obj0 as bar};
test.js
export let test = ()=> console.log("test"),a = "1";
export defalut b = "2"; //默认导出可以任意设置导入名称
//main.js文件
import {foo, bar} from "./lib";
import * form "./test.js"; //相当于继承了test.js中的所有方法属性,但是除开defalut定义的内容。
import c form ".test.js"; //导入defalut定义的b,因为是默认导入所以名字随便取
foo();
console.log(a);
console.log(c);
7.解构
let [a,b,c] = ["1","2","3"];
let {name,age} = {name:"zhangsan",age:24}
console.log(b);//2
console.log(`${name},${age}`);//zhangsan,24
解构实际上就是让等号2边一一对应,省去了赋值的操作。
8.循环遍历
let objArr = [{
name:'zhangsan',
gender:'男'
},
{
name:'李四',
gender:'女'
}]
for(let obj of objArr){
console.log(obj .name+','+obj.gender);
}
//zhangsan,男
//李四,女
for...of是基于Iterator接口实现的。ES6为字符串实现了该接口,所以字符串可以被遍历。
Iterator的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
9.对象扩展
//(1)at():相比于charAt(),可识别码值
'abc'.at(0) // "a"
//(2)repeat(n):将原字符串重复n次
'ab'.repeat(2); //abab
//(3)padStart(str,n):以str字符串补全头部,字符串最小长度为n
// padEnd(str,n):以str字符串补全尾部,字符串的最小长度为n
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
//(4)includes(str,n):返回布尔值,从第n个位置开始查找是否包含str字符串,n可省略默认为0。
// startsWith(str,n):返回布尔值,从第n个位置开始查找,str字符串是否在源字符串头部,n可省略默认为0。
// endsWith(str,n):返回布尔值,从第你n个位置向前查找,str是否在源字符串末尾,n可省略,默认为源字符串长度。
var s = 'Hello world!';
s.startsWith('Hell') // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });//因为添加到ws的这个临时对象没有其他变量引用它,所以ws不会保存它的值,也就是说这次添加其实没有意思
其他的不再一一列举,请自行学习。
10.Symbol
var s1 = Symbol('foo');
var s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
s1===s2 //false
s3=Symbol.for('foo')
s4=Symbol.for('foo')
s3===s4 //true
Symbol.keyFor(s4)//foo
注意:Symbol对象不需要new来创建。
var mySymbol = Symbol();
// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
var a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
11.Promise
//创建promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if ( /*如果成功 */ ) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
//绑定处理程序
promise.then(function(result) {
//promise成功的话会执行这里
console.log(result); // "Stuff worked!"
}, function(err) {
//promise失败会执行这里
console.log(err); // Error: "It broke"
});