您的位置:首页 > Web前端 > JavaScript

五六、ES6常用新特性

2018-01-05 17:22 429 查看


1.箭头函数(=>)

ES6中引入了箭头函数符来代替function,也就是说在定义方法的时候可以不再写function,=>的左边即为函数名和参数,右边即为执行操作和返回值。

例如: copy

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的使用就需要相当小心。例如: copy

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指向。例如: copy

says(say){

setTimeout(function(){

console.log(this.type + ' says ' + say)

}.bind(this), 1000)//用says的this对象替代setTimeout执行方法的this对象

}

由于箭头函数内部并没有定义this对象,所以函数内部的this完全是继承的外部的,所以ES6中将不必担心上面的问题。例如: copy

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使用区别的例子: copy

var name = 'zach'

while (true) {

var name = 'obama'

console.log(name) //obama

break

}

console.log(name) //obama

//由于var作用域只有全局和函数内部,所以第二次声明的作用域跟第一次声明一致。

copy

let name = 'zach'

while (true) {

let name = 'obama'

console.log(name) //obama

break

}

console.log(name) //zach

//let的作用域只存在于块级内部。

var由于作用域的问题存在循环变量泄漏为全局变量的问题。如下: copy

var a = [];

for (var i = 0; i < 10; i++) {

a[i] = function () {

console.log(i);

};

}

a[6](); // 10

上面代码就是因为i被泄漏为了全局变量,当循环完成后i已经为10了,所以无论数组下标是几的结果都是10。

传统的解决方案是采用闭包。例如: copy

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的解决方法如下:view
plain copy

var a = [];

for (let i = 0; i < 10; i++) {

a[i] = function () {

console.log(i);

};

}

a[6](); // 6

const只是用来声明常量,当我们试图修改它声明的常量时,浏览器会报错。

其最好的应用场景是,当我们引入第三方插件库的时候可以保证插件声明不重复。

需注意:静态字符串声明统一使用单引号或反引号,不使用双引号,动态字符串使用反引号。例如:view
plain copy

const PI = Math.PI

const monent = require('moment')

const b = `foo${a}bar`;


3.类的支持(class,extends,super)

ES6引入了class关键字让对象的声明定义前后端更加一致。

ES5中,定义一个对象如下: copy

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);

在ES6中则可以使用如下形式: copy

//定义类

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)

需注意几点:

1.子类必须在constructor中调用super后才能使用this对象。

2.如果子类不显示定义一个constructor,那么会存在一个默认的constructor(...args){super(...args)}方法

3.对象.prototype.constructor = 对象本身

4.子类的__proto__属性,表示构造函数的继承,总是指向父类。(即:Line._proto_=Point)

5.子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的(即:Line.prototype._proto_=Point.prototype)


4.字符串模版

ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含${vraible}的变量。模版字符串会保留反引号包裹的所有空格和换行,如果其中含有反引号,需要\`进行转义。如需去掉换行,可以调用trim()。

ES5中定义一个模版示例如下: copy

$('#result').append(

'There are <b>' + basket.count + '</b> ' +

'items in your basket, ' +

'<em>' + basket.onSale +

'</em> are on sale!'

);

ES6则更将简洁方便。 copy

$('#result').append(`

There are <b>${basket.count}</b> items

in your basket, <em>${basket.onSale}</em>

are on sale!

`);


5.参数

a.默认参数

ES6中允许在定义函数的时候指定参数默认值,例如name=“value”,但是这里并不是赋值操作,而是如果传了参数,那么使用参数值,如果没有传参数,那么使用默认值。示例如下:

[javascript] view
plain copy

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

b.不定参数

这是一个好用的语法糖,可以指定参数名称,以...args的形式传入不定个数的参数。在ES5中我们可以使用arguments变量来实现这一目的,arguments代表了一个Arguments对象。

ES5中传入多个参数的实现如下: copy

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

ES6中传入多个参数的实现如下: copy

//将所有参数相加的函数

function add(...x){

return x.reduce((m,n)=>m+n);

}

//传递任意个数的参数

console.log(add(1,2,3));//输出:6

c.拓展参数

这是另一种形式的语法糖,它允许传递数组或者类数组直接做为函数的参数而不用通过apply。 copy

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.模块

ES6中让js支持了模块概念,我们不需要再去引用其他的js框架(如requireJS)来实现前端的模块化。但需遵循如下规则:

a.每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;

b.每一个模块内声明的变量都是局部变量, 不会污染全局作用域;

c.模块内部的变量或者函数可以通过export导出,且export导出的名称必须和定义的名称一一对应;

d.一个模块可以导入别的模块 copy

//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{函数名或变量名};

export{函数名或变量名 as 别名};

export 函数或变量;

export defalut 函数或变量;

export * from js路径; copy

//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.解构

ES6中可以自动解析数组会对象的值。 view
plain copy

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边最好一一对应,但是允许左边少右边多,这种情况将只匹配右边对应的一部分值。

解构实际上就是让等号2边一一对应,省去了赋值的操作。


8.循环遍历

ES6中引入了for...of...遍历方式,这种循环遍历方式和for...in...有些类似,但是for...in...中的变量是对象的属性名或者数组的下标值。而for...of...中的变量是直接的数组具体值。

[javascript] view
plain copy

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属性是一个布尔值,表示遍历是否结束。

模拟一个next()返回值的方法。

[javascript] view
plain copy

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.对象扩展

a.字符串对象新方法 copy

//(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

b.Map、Set、WeakMap、WeakSet copy

// 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

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。view
plain copy

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来创建。

Symbol对象作为属性,不能使用.来访问

Symbol不能和其他数据类型进行计算 copy

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 是一个对象,从它可以获取异步操作的消息。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称
Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 view
plain copy

//创建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"

});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息