重开ES6

一、ES6的开发环境搭建

现在的Chrome浏览器已经支持ES6了,但是有些低版本的浏览器还是不支持ES6的语法,这就需要我们把ES6的语法自动的转变成ES5的语法。

1、建立工程目录:

先建立一个项目的工程目录,并在目录下边建立两个文件夹:src和dist

  • src:书写ES6代码的文件夹,写的js程序都放在这里。
  • dist:利用Babel编译成的ES5代码的文件夹,在HTML页面需要引入的时这里的js文件。

编写index.html:

文件夹建立好后,我们新建一个index.html文件。

<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./dist/index.js"></script>
</head>
<body>
Hello ECMA Script 6
</body>
</html>

需要注意的是在引入js文件时,引入的是dist目录下的文件。

编写index.js

在src目录下,新建index.js文件。这个文件很简单,我们只作一个a变量的声明,并用console.log()打印出来。

let a=1;
console.log(a);

我们用了let声明,这里let是ES6的一种声明方式,接下来我们需要把这个ES6的语法文件自动编程成ES5的语法文件。

初始化项目

在安装Babel之前,需要用npm init先初始化我们的项目。打开终端或者通过cmd打开命令行工具,进入项目目录,输入下边的命令:

npm init -y

-y代表全部默认同意,就不用一次次按回车了。命令执行完成后,会在项目根目录下生产package.json文件。

{
"name": "es6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

可以根据自己的需要进行修改,比如我们修改name的值。

全局安装Babel-cli

在终端中输入以下命令,如果你安装很慢的话,可以使用淘宝镜像的cnpm来进行安装。安装cnpm的方法,大家自己百度吧。

npm install -g babel-cli

虽然已经安装了babel-cli,只是这样还不能成功进行转换,如果你不相信可以输入下边的命令试一下。

babel src/index.js -o dist/index.js

你会发现,在dist目录下确实生产了index.js文件,但是文件并没有变化,还是使用了ES6的语法。因为我们还需要安装转换包才能成功转换,继续往下看吧。

本地安装babel-preset-es2015 和 babel-cli

npm install --save-dev babel-preset-es2015 babel-cli

安装完成后,我们可以看一下我们的package.json文件,已经多了devDependencies选项。

"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-es2015": "^6.24.1"
}

新建.babelrc

在根目录下新建.babelrc文件,并打开录入下面的代码(mac系统需要通过创建完毕普通文件后通过Cli方式在终端里通过mv 原来文件 .新文件方式创建

{
"presets":[
"es2015"
],
"plugins":[]
}

这个文件我们建立完成后,现在可以在终端输入的转换命令了,这次ES6成功转化为ES5的语法。

babel src/index.js -o dist/index.js

简化转化命令:

在学习vue 的时候,可以使用npm run build 直接利用webpack进行打包,在这里也希望利用这种方式完成转换。打开package.json文件,把文件修改成下面的样子。

{
"name": "es6",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "babel src/index.js -o dist/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-es2015": "^6.24.1"
}
}

修改好后,以后我们就可以使用 npm run build 来进行转换了。

二、新的声明方式

以前我们在声明时只有一种方法,就是使用var来进行声明,ES6对声明的进行了扩展,现在可以有三种声明方式了。

字面理解ES6的三种声明方式:

  1. var:它是variable的简写,可以理解成变量的意思。
  2. let:它在英文中是“让”的意思,也可以理解为一种声明的意思。
  3. const:它在英文中也是常量的意思,在ES6也是用来声明常量的,常量你可以简单理解为不变的量。

var声明:

var在ES6里是用来声明全局变量的,我们可以先作一个最简单的实例,用var声明一个变量a,然后用console.log进行输出。

var a="Gavin"
console.log(a)

可以看到结果是Gavin,如果在全局与局部都声明a,可以发现全局声明的结果会被局部声明覆盖

var a="Gavin"
{
var a="Tom"
}
console.log(a)

let局部声明

通过两个简单的例子,我们对var的全局声明有了一定了解。那跟var对应的是let,它是局部变量声明。还是上面的例子,我们试着在区块里用let声明:

var a="Gavin"
{
let a="Tom";
console.log(a);
}
console.log(a);

这个例子中可以看到局部使用let声明不会影响到var原来的声明值

使用let的好处是使用let声明,不会污染到外部的数据

{
for(let a=0;a<4;a++){
console.log('循环体内'+a);
}
}
console.log("循环体外" + a);

对比之下,如果使用var声明,会发现循环体外的值也跟着改变了

const声明常量

在程序开发中,有些变量是希望声明后在业务层就不再发生变化了,简单来说就是从声明开始,这个变量始终不变,就需要用const进行声明。

我们来一段用const声明错误的代码,在错误中学习const的特性也是非常好的。

const a="Gavin";
var a="xiaopi";
console.log(a);

在编译这段代码的过程中,你就会发现已经报错,无法编译了,原因就是我们const声明的变量是不可以改变的。

三、变量的解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构赋值在实际开发中可以大量减少我们的代码量,并且让我们的程序结构更清晰

数组的解构赋值:

简单的数组解构:

以前,为变量赋值,我们只能直接指定值。比如下面的代码:

let a=0;
let b=1;
let c=2;

而现在我们可以用数组解构的方式来进行赋值。

let [a,b,c]=[1,2,3];

上面的代码表示,可以从数组中提取值,按照位置的对象关系对变量赋值。

数组模式和赋值模式统一:

可以简单的理解为等号左边和等号右边的形式要统一,如果不统一解构将失败。

let [a,[b,c],d]=[1,[2,3],4];

如果等号两边形式不一样,很可能获得undefined或者直接报错。

解构的默认值:

解构赋值是允许你使用默认值的,先看一个最简单的默认是的例子。

let [foo = true] =[];
console.log(foo); //控制台打印出true
let [a,b="Gavin"]=['小皮']
console.log(a+b); //控制台显示“小皮Gavin”

  现在我们对默认值有所了解,需要注意的是undefined和null的区别。

let [a,b="gavin"]=["xiaopi",undefined];
console.log(a+b)

undefined相当于什么都没有,b是默认值。

let [a,b="gavin"]=['xiaopi',null];
console.log(a+b)

null相当于有值,但值为null。所以b并没有取默认值,而是解构成了null。

对象的解构赋值

解构不仅可以用于数组,还可以用于对象。

let {foo,bar}={bar:"gavin",foo:'xiaopi'}
console.log(foo,bar)

注意:对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

圆括号的使用

如果在解构之前就定义了变量,这时候你再解构会出现问题。下面是错误的代码,编译会报错。

let a;
{a} = {a:'gavin'};
console.log(a)

要解决报错,使程序正常,我们这时候只要在解构的语句外边加一个圆括号就可以了。

let a,b;
({a,b="kk"} = {a:'gavin'});
console.log(a+b)

字符串解构

字符串也可以解构,这是因为,此时字符串被转换成了一个类似数组的对象。

let [a,b,c,d] ='xiao';
console.log(a);
console.log(b);
console.log(c);
console.log(d);

四、扩展运算符与rest运算符

对象扩展运算符(…):

当编写一个方法时,我们允许它传入的参数是不确定的。这时候可以使用对象扩展运算符来作参数,看一个简单的列子:

function abc(...arg) {

    console.log(arg[0]);
console.log(arg[1]);
console.log(arg[2]);
console.log(arg[3]);
}
abc(1,2,3);

这时我们看到控制台输出了 1,2,3,undefined,这说明是可以传入多个值,并且就算方法中引用多了也不会报错。

扩展运算符的用处:

我们先用一个例子说明,我们声明两个数组arr1和arr2,然后我们把arr1赋值给arr2,然后我们改变arr2的值,你会发现arr1的值也改变了,因为我们这是对内存堆栈的引用,而不是真正的赋值,这个类似于python的浅拷贝,两个变量指向同样的变量值内存地址空间

let arr1=['aaa','bbb','ccc'];
let arr2=arr1;
console.log(arr2);
arr2.push('dddd');
console.log(arr1);

这是我们不想看到的,可以利用对象扩展运算符简单的解决这个问题,现在我们对代码进行改造。

let arr1=['aaa','bbb','ccc'];
let arr2=[...arr1];
arr2.push('dddd');
console.log(arr2);
console.log(arr1);

现在控制台预览时,你可以看到我们的arr1并没有改变,简单的扩展运算符就解决了这个问题。

rest运算符

如果你已经很好的掌握了对象扩展运算符,那么理解rest运算符并不困难,它们有很多相似之处,甚至很多时候你不用特意去区分。它也用…(三个点)来表示,我们先来看一个例子。

function abc(first,...arg) {
console.log(arg.length);
};
abc(0,1,2,3,4,5,6,7);

这时候控制台打印出了7,说明我们arg里有7个数组元素,这就是rest运算符的最简单用法。

如何循环输出rest运算符

这里我们用for…of循环来进行打印出arg的值,我们这里只是简单使用一下,以后我们会专门讲解for…of循环。

function abc(first,...arg) {
for(let val of arg){
console.log(val);
} };
abc(0,1,2,3,4,5,6,7);

for…of的循环可以避免我们开拓内存空间,增加代码运行效率,所以建议大家在以后的工作中使用for…of循环。有的小伙伴会说了,反正最后要转换成ES5,没有什么差别,但是至少从代码量上我们少打了一些单词,这就是开发效率的提高。

五、字符串模板

在开启新旅程前,先使用live-server来简化我们的调试模式,首先,新建一个目录,然后执行npm init -y 再,创建index.html与index.js,安装live-server(cnpm install live-server -g)如果以前安装了这里就无需安装了,执行live-server让它自动协助编译

字符串模版

先来看一个在ES5下我们的字符串拼接案例:

var x='Gavin';
var str='这是一个字符串模板,让我们增加一个变量进来'+x+'..可以看到它增加进来了'
document.write(str);

ES6新增了字符串模版,可以很好的解决这个问题。字符串模版不再使用‘xxx’这样的单引号,而是换成了xxx这种形式,也叫连接号。

var x='Gavin';
var str=`这是一个字符串模板,让我们增加一个变量进来${x}..可以看到它增加进来了`
document.write(str);

使用${x} 还可以在里面增加标签或者换行

对运算的支持:

let [a,b]=[2,8];
document.write(`${a+b}`);

强大的字符串模版,在实际开发中,我们可以让后台写一个活动页面,然后轻松的输出给用户。

字符串查找

ES6还增加了字符串的查找功能,而且支持中文哦,小伙伴是不是很兴奋。还是拿上边的文字作例子,进行操作。

查找是否存在:

先来看一下ES5的写法,其实这种方法并不实用,给我们的索引位置,我们自己还要确定位置。

var x='Gavin';
var str='这是一个字符串模板,让我们增加一个变量进来---Gavin..可以看到它增加进来了'
document.write(str.indexOf(x));

这种方式需要自己判断是否存在,如果不存在为0,需要自己判断

ES6直接用includes就可以判断,不再返回索引值,这样的结果我们更喜欢,更直接。

var x='Gavin';
var str='这是一个字符串模板,让我们增加一个变量进来---Gavin..可以看到它增加进来了'
document.write(str.includes(x));

这种方式可以直接返回true或false

判断开头是否存在:

let x='Gavin';
let str='Gavin这是一个字符串模板,让我们增加一个变量进来---Gavin..可以看到它增加进来了'
document.write(str.startsWith(x));

判断结尾是否存在:

let x='Gavin';
let str='Gavin这是一个字符串模板,让我们增加一个变量进来---Gavin..可以看到它增加进来了'
document.write(str.endsWith(x));

复制字符串

我们有时候是需要字符串重复的,比如分隔符和特殊符号,这时候复制字符串就派上用场了,语法很简单。

let a='Gavin <br/>|';
document.write(a.repeat(20));

字符串补白功能

在ES7中可以使用,需要增加babel-polyfill来解析

console.log('1'.padStart(5,'0'))  //长度为2,如果长度不够在前面补0   这样显示为01  这个在日期显示十分有用
console.log('1'.padStart(5,'0'))//向后补白
 

标签模板:

let    User1={
'name':'gavin',
'skill':'python'
}; console.log(kkk`i am ${User1.name}: ${User1.skill}`);
function kkk(a,b,c) {
console.log(a+b+c);
return a+b+c
}

raw方法:raw会在\n前面再加一个\

console.log(String.raw`Hi\n you`);
console.log(`Hi\n you`);

六、数字操作

二进制和八进制

二进制和八进制数字的声明并不是ES6的特性,我们只是做一个常识性的回顾,因为很多新人小伙伴会把他们当成字符串或者不知道是什么,所以这算是赠送的知识点

二进制声明:

二进制的英文单词是Binary,二进制的开始是0(零),然后第二个位置是b(注意这里大小写都可以实现),然后跟上二进制的值就可以了。

let binary=0B101010
console.log(binary);

八进制声明:

八进制的英文单词是Octal,也是以0(零)开始的,然后第二个位置是O(欧),然后跟上八进制的值就可以了。

let octal=0O3424;
document.write(octal);

数字判断和转换

数字验证Number.isFinite( xx )

可以使用Number.isFinite( )来进行数字验证,只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false。

let a=1/4;
console.log(Number.isFinite(a));
console.log(Number.isFinite('Gavin'));
console.log(Number.isFinite(NaN));
console.log(Number.isFinite(undefined));

NaN验证

NaN是特殊的非数字,可以使用Number.isNaN()来进行验证。下边的代码控制台返回了true。

console.log(Number.isNaN(123));
console.log(Number.isNaN(NaN));

判断是否为整数Number.isInteger(xx)

let a=123;
console.log(Number.isInteger(a));

整数转换Number.parseInt(xxx)和浮点型转换Number.parseFloat(xxx)

let a = 19.8302;
console.log(Number.parseInt(a));
console.log(Number.parseFloat(a));

整数取值范围操作

整数的操作是有一个取值范围的,它的取值范围就是2的53次方。我们先用程序来看一下这个数字是什么.

let a=Math.pow(2,53)-1;
console.log(a);

在我们计算时会经常超出这个值,所以我们要进行判断,ES6提供了一个常数,叫做最大安全整数,以后就不需要我们计算了。

最大安全整数

console.log(Number.MAX_SAFE_INTEGER);

最小安全整数

console.log(Number.MIN_SAFE_INTEGER);

安全整数判断isSafeInteger( )

let b=Math.pow(2,53)-1;
console.log(Number.isSafeInteger(b));

数字取整

无论小数部分是否大于5,都只取整数

{
console.log(4.1, Math.trunc(4.1));
console.log(4.1, Math.trunc(4.9));
}

sgin方法

{
console.log(-5,Math.sign(-5));//负数返回-1
console.log(0,Math.sign(0));//零返回0
console.log(5,Math.sign(5));//正数返回+1
console.log('50',Math.sign('50'));//数字型字符串返回字符串转换的数,所以返回1
console.log('-50',Math.sign('-50'));//数字型字符串返回字符串转换的数,返回-1
console.log('foo',Math.sign('foo'));//非数字型字符串返回NaN
}
{
console.log(8,Math.cbrt(8));//求立方根
}

七、ES6新增的数组知识

JSON数组格式转换

JSON的数组格式就是为了前端快速的把JSON转换成数组的一种格式,我们先来看一下JSON的数组格式怎么写。

let json={
'0': 'xiaopi',
'1': '皮皮',
'2': 'Gavin',
length:3
};

这就是一个标准的JSON数组格式,跟普通的JSON对比是在最后多了一个length属性。只要是这种特殊的json格式都可以轻松使用ES6的语法转变成数组。在ES6中绝大部分的Array操作都存在于Array对象里。我们就用Array.from(xxx)来进行转换。我们把上边的JSON代码转换成数组,并打印在控制台。

console.log(Array.from(json));

Array.of()方法:

它负责把一堆文本或者变量转换成数组。在开发中我们经常拿到了一个类似数组的字符串,需要使用eval来进行转换,如果你一个老手程序员都知道eval的效率是很低的,它会拖慢我们的程序。这时候我们就可以使用Array.of方法。我们看下边的代码把一堆数字转换成数组并打印在控制台上:

let arr=Array.of(3,4,5,6);
console.log(arr);

当然它不仅可以转换数字,字符串也是可以转换的,看下边的代码:

let arr2=Array.of('gavin','xiaopi','kk');
console.log(arr2);
{
let arr=Array.of();//定义一个空数组
console.log('arr=',arr);
}

Array.from方法:

Array.from()可以把集合或者伪数组变为真正的数组

{
let p=document.querySelectorAll('p');
let pArr=Array.from(p);
pArr.forEach((item)=>{
console.log(item.textContent)
}) }

在转换数组的同时也可以充当map的功能

console.log(Array.from([1,3,5,6],function (item) {
return item*3;
}))

find( )实例方法:

所谓的实例方法就是并不是以Array对象开始的,而是必须有一个已经存在的数组,然后使用的方法,这就是实例方法(不理解请看下边的代码,再和上边的代码进行比对,你会有所顿悟)。这里的find方法是从数组中查找。在find方法中我们需要传入一个匿名函数,函数需要传入三个参数:

  • value:表示当前查找的值。
  • index:表示当前查找的数组索引。
  • arr:表示当前数组。

在函数中如果找到符合条件的数组元素就进行return,并停止查找。你可以拷贝下边的代码进行测试,就会知道find作用。

let arr=[1,2,3,4,5,6,7,8,9];
console.log(arr.find(function (value,index,arr) {
return value>=5;
}));

如果找不到也不会报错,而是返回undefined

{
console.log([1,2,3,4,5,6].find((item)=>{
return item>4;
}))
}
{
console.log([1,2,3,4,5,6].findIndex((item)=>{
return item>4;
}))
}

includes方法:

通过includes方法可以很容易找到数组中是否包含某个数值,就算是NaN也可以

{
console.log([1,2,NaN].includes(1));
console.log([1,2,NaN].includes(NaN));
}

数组的实例方法: copyWithin()

arry.copyWithin(val,index1,index2)  其中第一个val是从哪个位置开始替换,第二个index1和第三个index2表示从哪个位置要替换前面的val,替换到index2个位置

{
console.log(['aaa','bbbb','cccc',666,'jajfa','akdkak','ajdfa',1010].copyWithin(0,2,6))
}

fill( )实例方法:

fill()也是一个实例方法,它的作用是把数组进行填充,它接收三个参数,第一个参数是填充的变量,第二个是开始填充的位置,第三个是填充到的位置。

let arr=[0,1,2,3,4,5,6,7];
arr.fill('gavin',2,5);//2表示从index未2开始,到5-1结束,表示吧从idnex2到4的元素全部替换为gavin
console.log(arr);
{
console.log('fill-7',[1,'a',undefined].fill(7));//全部被7替换
console.log('fill, pos',[1,3,4,6,2,1].fill(7,1,3));//从第一个位置开始到第三个位置被替换为7,也就是第一个第二个位置被替换为7
}

数组的遍历

for…of循环:

这种形式比ES5的for循环要简单而且高效。先来看一个最简单的for…of循环。

let arr=['gavin', 'xiaopi','kkblue'];
for (let item of arr){
console.log(item);
}

for…of数组索引:有时候开发中是需要数组的索引的,那我们可以使用下面的代码输出数组索引。

let arr=['gavin', 'xiaopi','kkblue'];
for(let key of arr.keys()){
console.log(key)
}

可以看到这时的控制台就输出了0,1,2,也就是数组的索引。

同时输出数组的内容和索引:我们用entries()这个实例方法,配合我们的for…of循环就可以同时输出内容和索引了。

let arr=['gavin', 'xiaopi','kkblue'];
for(let[index,val] of arr.entries()){
console.log(`${index}: ${val}`)
}

entries( )实例方法:

entries()实例方式生成的是Iterator形式的数组,那这种形式的好处就是可以让我们在需要时用next()手动跳转到下一个值。我们来看下面的代码:

let arr=['gavin', 'xiaopi','kkblue'];

let list=arr.entries();
console.log(list.next().value);
console.log('-'.repeat(20));
console.log(list.next().value);
console.log('#'.repeat(20));
console.log(list.next().value);
console.log('*'.repeat(20));

九、ES6中的箭头函数与扩展

默认值

在ES6中给我们增加了默认值的操作,我们修改上边的代码,可以看到现在只需要传递一个参数也是可以正常运行的。

function add(a,b=1) {
return a+b;
};
console.log(add(3));

主动抛出错误

在使用Vue的框架中,可以经常看到框架主动抛出一些错误,ES6中我们直接用throw new Error( xxxx ),就可以抛出错误。

function add(a,b=1) {
if(a==0){
throw new Error('this is a error');
}else{
return a+b;
}
}
console.log(add(0));

函数中的严谨模式

我们在ES中就经常使用严谨模式来进行编程,但是必须写在代码最上边,相当于全局使用。在ES6中我们可以写在函数体中,相当于针对函数来使用。

function add(a,b=1) {
'use strict'
if(a==0){
throw new Error('this is a error');
}else{
return a+b;
}
}
console.log(add(1));

上边的代码如果运行的话,你会发现浏览器控制台报错,这是ES6中的一个坑,如果没人指导的话,可能你会陷进去一会。这个错误的原因就是如果你使用了默认值,再使用严谨模式的话,就会有冲突,所以我们要取消默认值的操作,这时候你在运行就正常了。

function add(a,b) {
'use strict'
if(a==0){
throw new Error('this is a error');
}else{
return a+b;
}
}
console.log(add(1));

获得需要传递的参数个数

如果你在使用别人的框架时,不知道别人的函数需要传递几个参数怎么办?ES6为我们提供了得到参数的方法(xxx.length).我们用上边的代码看一下需要传递的参数个数。

function add(a,b) {
'use strict'
if(a==0){
throw new Error('this is a error');
}else{
return a+b;
}
}
console.log(add.length);

这时控制台打印出了2,但是如果我们去掉严谨模式,并给第二个参数加上默认值的话,这时候add.length的值就变成了1, 也就是说它得到的是必须传入的参数。

箭头函数

在学习Vue的时候,我已经大量的使用了箭头函数,因为箭头函数真的很好用,我们来看一个最简单的箭头函数。也就是上边我们写的add函数,进行一个改变,写成箭头函数。

let add=(a,b=1)=> a+b;
console.log(add(1))

{}的使用

在箭头函数中,方法体内如果是两句话,那就需要在方法体外边加上{}括号。例如下边的代码就必须使用{}.

var add=(a,b=1) => {
console.log('gavin');
return a+b;
} console.log(add(1));

箭头函数中不可加new,也就是说箭头函数不能当构造函数进行使用。

对象中存在方法的简介写法

{
let es6_method={
hello(){
console.log('hello')
}
}
es6_method.hello()
}

十、ES6中的函数与数组

对象的函数解构

我们在前后端分离时,后端经常返回来JSON格式的数据,前端的美好愿望是直接把这个JSON格式数据当作参数,传递到函数内部进行处理。ES6就为我们提供了这样的解构赋值。

let json={
a: 'xiaopi',
b: 'gavin',
c: 'pipi'
};
var fun =({a,b,c="web"})=>{
console.log(a,b,c);
};
fun(json);

数组的函数解构

函数能解构JSON,那解构我们的数组就更不在话下了,我们看下边的代码。我们声明一个数组,然后写一个方法,最后用…进行解构赋值。

let arr=['gavin','kkblue','xiaopi'];
var fun=(...arg)=>{
console.log(arg[0],arg[1],arg[2])
} fun(...arr);

in的用法

in是用来判断对象或者数组中是否存在某个值的。我们先来看一下用in如何判断对象里是否有某个值。(主要判断该位置是否为空)

对象判断

let obj={
a:'gavin',
b:'xiaopi'
};
console.log('c' in obj);

数组判断

先来看一下ES5判断的弊端,以前会使用length属性进行判断,为0表示没有数组元素。但是这并不准确,或者说真实开发中有弊端。

let arr=[,,,,,];
console.log(arr.length); //

上边的代码输出了5,但是数组中其实全是空值,这就是一个坑啊。那用ES6的in就可以解决这个问题。

let arr=[,,,,,];
console.log(0 in arr);
let arr1=['gavin','xiaopi','kk'];
console.log(0 in arr1);

注意:这里的0指的是数组下标位置是否为空。

数组的遍历方法

1.forEach

let arr=['gavin','xiaopi','kk'];
arr.forEach((val,index)=>{console.log(index,val)});

注意:val在前面,index在后面,forEach循环的特点是会自动省略为空的数组元素,相当于直接给我们筛空了。

2.filter

let arr=['gavin','xiaopi','kk'];
arr.filter(x=>console.log(x));

3.some

let arr=['gavin','xiaopi','kk'];
arr.some(x=>console.log(x));

4.map

let arr=['gavin','xiaopi','kk'];
console.log(arr.map(x=>'web'));

数组转换字符串

在开发中我们经常会碰到把数组输出成字符串的形式,我们今天学两种方法,你要注意两种方法的区别。

join()方法

let arr=['gavin','xiaopi','kk'];
console.log(arr.join(' | '));

join()方法就是在数组元素中间,加了一些间隔,开发中很有用处。

toString()方法

let arr=['gavin','xiaopi','kk'];
console.log(arr.toString());

转换时只是是用逗号隔开了

十一、ES6中对象

对象赋值

ES6允许把声明的变量直接赋值给对象,我们看下面的例子。

let name='gavin';
let skill='python';
var obj={name,skill};
console.log(obj);

对象Key值构建

有时候我们会在后台取出key值,而不是我们前台定义好的,这时候我们如何构建我们的key值那。比如我们在后台取了一个key值,然后可以用[ ] 的形式,进行对象的构建。

let key='skill';
var obj={
[key]: 'web'
};
console.log(obj);

自定义对象方法

对象方法就是把兑现中的属性,用匿名函数的形式编程方法。这个在以前就有应用,我们这里只是简单的复习一下。

var obj={
add:function(a,b){
return a+b;
}
}
console.log(obj.add(1,2));

Object.is(  ) 对象比较

对象的比较方法,以前进行对象值的比较,经常使用===来判断,ES6提供了新的方式进行比较:

let obj1={name: 'gavin'};
let obj2={name: 'gavin'};
console.log(Object.is(obj1.name,obj2.name));

区分=== 和 is方法的区别是什么,看下面的代码输出结果。

console.log(+0 === -0);  //true
console.log(NaN === NaN ); //false
console.log(Object.is(+0,-0)); //false
console.log(Object.is(NaN,NaN)); //true

这太诡异了,我要怎么记忆,那技术胖在这里告诉你一个小妙招,===为同值相等,is()为严格相等。

Object.assign(  )合并对象

操作数组时我们经常使用数组合并,那对象也有合并方法,那就是assgin(  )。看一下啊具体的用法。

let a={name: 'gavin'}
let b={skill: 'web'}
let c={alias: 'xiaopi'}
let d=Object.assign(a,b,c)
console.log(d);
console.log('拷贝',Object.assign({a:'a'},{b:'b'}));//浅拷贝
 

Object.entries()

{
let test={
a:456,
b:789
}
for(let [i,v] of Object.entries(test)){
console.log(`${i} : ${v}`)
}

十二、Symbol在对象中的作用

首先来说,Symbol能提供独一无二的值,不重复 不相等,保证唯一。

声明Symbol

我们先来回顾一下我们的数据类型,在最后在看看Symbol如何声明,并进行一个数据类型的判断。

var a = new String;
var b = new Number;
var c = new Boolean;
var d = new Array;
var e = new Object;
var f= Symbol();
console.log(typeof(d));
{
let a1=Symbol();
let a2=Symbol();
console.log(a1===a2);
}

Symbol声明变量的第二种方法:

{
let a3=Symbol.for('key3');//key3是一个key值,在用Symbol.for声明时会先检查这个key是否注册过
//如果注册过,就返回这个值,如果没注册过会调用Symbol去生成这个独一无二的值
let a4=Symbol.for('key3');
console.log(a3===a4);
}
//在生成a4时会检查,如果发现它的key值以前调用过,会使用a3的值来为a4赋值

Symbol的打印

我们先声明一个Symbol,然后我们在控制台输出一下。

let g=Symbol('gavin')
console.log(g)
console.log(g.toString())

这时候我们仔细看控制台是有区别的,没有toString的是红字,toString的是黑字。

Symbol在对象中的应用

看一下如何用Symbol构建对象的Key,并调用和赋值。

let gavin=Symbol();
let obj={
[gavin]: 'web'
};
console.log(obj[gavin]);
obj[gavin]= 'python';
console.log(obj[gavin]);

Symbol对象元素的保护作用

在对象中有很多值,但是循环输出时,并不希望全部输出,那我们就可以使用Symbol进行保护。

没有进行保护的写法:

let obj={name:'gavin', skill: 'web',gae: 22};
for(let item in obj){
console.log(item)
};

现在我不想别人知道我的年龄,这时候我就可以使用Symbol来进行循环保护。

let obj={name:'gavin', skill: 'web'};
let age=Symbol();
obj[age] = 28;
for(let item in obj){
console.log(item)
};
console.log(obj[age]);

在这里再次强调,如果对象中用Symbol做key值,在for。。in和for 。。of中是拿不到对应的值的

{
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':456,
'c': 789
};
for(let [index,value] of Object.entries(obj)){
console.log(`${index}: ${value}`)
}//这种情况下,Symbol声明的value不会打印
}

如果想单纯的打印出Symbo作为key的value可以采用:

let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':456,
'c': 789
};
Object.getOwnPropertySymbols(obj).forEach((item)=>{
console.log(obj[item]);
});

如果想全部显示出来,可以使用:

{
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':456,
'c': 789
};
Reflect.ownKeys(obj).forEach((item)=>{
console.log('ownkeys',item,obj[item])
})
}

十三、Set和WeakSet数据结构

Set的声明

let setAttr = new Set(['gavin','yaoming','xiao','kkblue']);
console.log(setAttr);

Set的两种定义类型

{
let list=new Set();
list.add(5);
list.add(7);
console.log('sie',list.size);
}
{//set的另外一种定义方式,在定义时传入数组
let arr=[1,2,3,4,5];
let list=new Set(arr);
console.log('size',list.size);
}

Set和Array 的区别是Set不允许内部有重复的值,如果有只显示一个,相当于去重。虽然Set很像数组,但是他不是数组。

{
let arr=[1,2,3,1,2];
let list=new Set(arr);
console.log(list);
}

但是注意,set在去重时,不会转换元素的类型,也就是2与'2'是不相等的,也就是不会去重

{
let arr=[1,2,3,1,'2'];
let list=new Set(arr);
console.log(list);
}

Set值的增删查

追加add:

在使用Array的时候,可以用push进行追加值,那Set稍有不同,它用更语义化的add进行追加。

let setAttr = new Set(['gavin','yaoming','xiao','kkblue']);
setAttr.add('xiaoxiao')
console.log(setAttr);

删除delete:

setAttr.delete('gavin');
console.log(setAttr);
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log('has',list.has('add'));//如果有has就返回true
console.log('delete',list.delete('delete'),list);//delete如果有返回true
}

查找has:

用has进行值的查找,返回的是true或者false。

console.log(setAttr.has('yaoming'));
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log('has',list.has('add'));//如果有has就返回true }

删除clear:

setAttr.clear();
console.log('clear',list.clear(),list);

set的循环

for…of…循环:

let setAttr = new Set(['gavin','yaoming','xiao','kkblue']);
for(let item of setAttr){
console.log(item)
}
setAttr.forEach((val)=>console.log(val));

key与value都一样:

{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
for(let item of list.keys()){
console.log('keys',item);
}
}
for(let item of list.values()){
console.log('value',item);
}
for(let item of list.entries()){
console.log('value',item);
}

size属性

size属性可以获得Set值的数量。

let setAttr = new Set(['gavin','yaoming','xiao','kkblue']);
console.log(setAttr.size)

WeakSet的声明

let weakObj=new WeakSet();
let obj={
a:'gavin',
b: 'skill',
c: 'xiaopi',
d: 'gavin'
};
weakObj.add(obj);
console.log(weakObj);

这里需要注意的是,如果你直接在new 的时候就放入值,将报错。

WeakSet里边的值也是不允许重复的,我们来测试一下。

let weakObj=new WeakSet();
let obj={
a:'gavin',
b: 'skill',
c: 'xiaopi',
d: 'gavin'
};
weakObj.add(obj);
let obj1=obj;
weakObj.add(obj1);
console.log(weakObj);

十四、map数据结构

Json和map格式的对比

map的效率和灵活性更好

先来写一个JSON,这里我们用对象进行模拟操作。

let json={
name: 'gavin',
skill: 'web'
};
console.log(json);
{
let map = new Map();
let arr = ['123'];
map.set(arr,456);
console.log('map',map,map.get(arr));
}

第二种定义方式

{
let map = new Map([['a',123],['b',456]]);
console.log('map1',map)
}

但是这种反应的速度要低于数组和map结构。而且Map的灵活性要更好,你可以把它看成一种特殊的键值对,但你的key可以设置成数组,值也可以设置成字符串,让它不规律对应起来。

let json={
name: 'gavin',
skill: 'web'
}; console.log(json.name); let map= new Map();
map.set(json,'kkblue');
console.log(map);

当然也可key字符串,value是对象。我们调换一下位置,依然是符合map的数据结构规范的。

map.set('xiaopi',json);

map的增删查

上边我们已经会为map增加值了,就是用我们的set方法,这里我就不重复讲解了。直接看如何取出我们的值。

取值get

现在取json对应的值。

console.log(map.get(json));

删除delete

删除delete的特定值:

map.delete(json);
{
let map = new Map([['a',123],['b',456]]);
console.log('map1',map);
console.log('size',map.size);
console.log('delete',map.delete('a'),map);
}

size属性

console.log(map.size);

查找是否存在has

console.log(map.has(json));

清除所有元素clear

map.clear()

附加章节-set  map 对象  数组数据结构对比 

1、map与数组对比 

{
// 数据结构横向对比,增删改查
let map = new Map();
let array=[];
//增
map.set('t',1);
array.push({t:1});
console.info('map-array',map,array);
//查
let map_exist = map.has('t');
let array_exist = array.find(item=>item.t);
console.info('map-array',map_exist,array_exist);
//改
map.set('t',2);
array.forEach(item=>item.t?item.t=2:'');
console.info('map-array',map,array);
//删除
map.delete('t');
let index=array.findIndex(item=>item.t);
array.splice(index,1);
console.info('map-array-empty',map,array);
}

2、set与数组对比

{
// set和array对比
let item={t:1};
let set=new Set();
let array=[];
//增
set.add(item);
array.push({t:1});
console.info('set-array:',set,array);
//查
let set_exist=set.has(item);
let array_exist=array.find(item=>item.t);
console.info('set-array:',set_exist,array_exist);
//改 对比set可以使用
// item={t:2};
// set.add(item)
set.forEach(item=>item.t?item.t=2:'');
array.forEach(item=>item.t?item.t=2:'');
console.info('set-array-modify:',set,array);
//删
set.forEach(item=>item.t?set.delete(item):'');
let index=array.findIndex(item=>item.t);
array.splice(index,1);
console.info('set-array-delete:',set,array);
}

3、map set  object对比

{
let item={t:1};
let map = new Map();
let set = new Set();
let obj = {};
//增
map.set('t',1);
set.add(item);
obj['t']=1;
console.log('map-set-array',map,set,obj);
//查
let map_exist = map.has('t');
let set_exist = set.has(item);
let obj_exist = 't' in obj;
console.log('exist:',map_exist,set_exist,obj_exist);
//改
map.set('t',2);
item.t=2;
obj['t']=2;
console.log('map-set-array',map,set,obj);
//删
map.delete('t');
set.delete(item);
delete obj['t'];
console.log('map-set-array',map,set,obj); }

总结:

数据结构要最优使用map,如果对数据要求比较高,需要唯一性,考虑set

十五、用proxy进行预处理

Proxy的应用可以使函数更加强大,业务逻辑更加清楚,而且在编写自己的框架或者通用组件时非常好用。Proxy涉及的内容非常多,那这里我就带你入门并且介绍给你后续的学习方法。

在学习新知识之前,先来回顾一下定义对象的方法。

var obj={
add:function(val){
return val+100;
},
name: 'I\'m OK!!!'
}
console.log(obj.add(100));
console.log(obj.name);

声明了一个obj对象,增加了一个对象方法add和一个对象属性name,然后在控制台进行了打印

声明Proxy

我们用new的方法对Proxy进行声明。可以看一下声明Proxy的基本形式。

new Proxy({},{});

需要注意的是这里是两个花括号,第一个花括号就相当于我们方法的主体,后边的花括号就是Proxy代理处理区域,相当于我们写钩子函数的地方。

现在把上边的obj对象改成我们的Proxy形式。

let pro=new Proxy({
add:function (val) {
return val+100;
},
name: 'I\'m OK!!!'
},{
get:function (target,key,property) {
console.log('begin GET!');
console.log(`${target}: ${key}`);
return target[key];// target 表示的对象名,key表示变量名
}
}); console.log(pro.name);

可以在控制台看到结果,先输出了come in Get。相当于在方法调用前的钩子函数。

get属性

get属性是在你得到某对象属性值时预处理的方法,他接受三个参数

  • target:得到的目标值
  • key:目标的key值,相当于对象的属性
  • property:这个不太常用,用法还在研究中,还请大神指教。

set属性

set属性是你要改变Proxy属性值时,进行的预先处理。它接收四个参数。

  • target:目标值。
  • key:目标的Key值。
  • value:要改变的值。
  • receiver:改变前的原始值。
let pro=new Proxy({
add:function (val) {
return val+100;
},
name: 'I\'m OK!!!'
},{
get:function (target,key,property) {
console.log('begin GET!');
console.log(`${target}: ${key}`);
return target[key];// target 表示的对象名,key表示变量名
},
set:function (target,key,value,receiver) {
console.log(`setting ${key}=${value}`);
return target[key]= value;
}
}); // console.log(pro.name);
pro.name='这是小皮';
console.log(pro.name);

apply的使用

apply的作用是调用内部的方法,它使用在方法体是一个匿名函数时。看下边的代码。

let target=()=>{
return 'I\'m OK!!!'
}
var handler={
apply(target,ctx,args){
console.log('do apply');
return Reflect.apply(...arguments);
}
} let pro=new Proxy(target,handler);
console.log(pro());

Proxy实例演示:

{
let obj={
time:'2018-01-10',
name: 'net',
_r:123 };
let monitor= new Proxy(obj,{
//拦截对象属性的读取
get(target,key){
return target[key].replace('2018','2019')
},
//拦截对象设置属性
set(target,key,value){
//只允许修改name属性,其他属性都不允许修改
return key==='name'?target[key]=value:target[key];
},
// 拦截对象key in object操作
has(target,key){
// 只暴露name属性,其他都不允许暴露
return key==='name'?target[key]:false;
},
// 拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){
delete target[key];
return true;
}else{
return target[key];
}
},
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target){
return Object.keys(target).filter(item=>item!='time')
}
});
//obj对用户不可见,用户需要操作monitor
console.log('get操作:',monitor.time);
monitor.time = '2020';
console.log('set操作:',monitor.time);
monitor.name = 'kkblue';
console.log('set:',monitor.name); console.log('has', 'name' in monitor);
console.log('has', 'time' in monitor);//只暴露name属性,其他属性都不允许暴露 // delete monitor.time;
// console.log('delete操作:',monitor);
// delete monitor._r;
// console.log('delete操作:',monitor); console.log('ownKeys:',Object.keys(monitor));
}

Reflect与Proxy用法一样,所以Proxy的方法Reflect都可以使用:

{
let obj={
time:'2018-01-10',
name: 'net',
_r:123 }; console.log('Reflect get:',Reflect.get(obj,'time'));//读取obj里的time属性
Reflect.set(obj, 'name','gavin');
console.log(obj); console.log('has:',Reflect.has(obj,'name'));
}

举例说明如何使用这些特性:

{
function validator(target,validator) {
return new Proxy(target,{
_validator:validator,
set(target,key,value,proxy){
if(target.hasOwnProperty(key)){
let va=this._validator[key];
if(!!va(value)){
return Reflect.set(target,key,value,proxy)
}else{
throw Error(`不能设置${key}到${value}`)
}
}else{
throw Error(`${key}不存在`)
}
}
})
} const personValidators={
name(val){
return typeof val==='string'
},
age(val){
return typeof val==='number'&& val >18;
}
} class Person{
constructor(name,age){
this.name = name;
this.age = age;
return validator(this,personValidators)
}
}
const person = new Person('gavin',30);
console.info(person);
person.name = 48; }

十六、promise对象的使用

ES6中的promise的出现给我们很好的解决了回调地狱的问题,在使用ES5的时候,在多层嵌套回调时,写完的代码层次过多,很难进行维护和二次开发,ES6认识到了这点问题,现在promise的使用,完美解决了这个问题。那我们如何理解promise这个单词在ES5中的作用那,你可以想象他是一种承诺,当它成功时执行一些代码,当它失败时执行一些代码。它更符合人类的行为思考习惯,而不在是晦涩难懂的冰冷语言。

promise的基本用法

promise执行多步操作非常好用,那我们就来模仿一个多步操作的过程,那就以吃饭为例吧。要想在家吃顿饭,是要经过三个步骤的。

  1. 洗菜做饭。
  2. 坐下来吃饭。
  3. 收拾桌子洗碗。

这个过程是有一定的顺序的,你必须保证上一步完成,才能顺利进行下一步。我们可以在脑海里先想想这样一个简单的过程在ES5写起来就要有多层的嵌套。那我们现在用promise来实现。

let state=1;
function step1(resolve,reject) {
console.log('1.开始-洗菜做饭');
if(state==1){
resolve('洗菜做饭-完成');
}else{
reject('洗菜做饭-失败');
}
}
function step2(resolve,reject) {
console.log('2.开始-吃饭');
if(state==1){
resolve('吃饭-完成');
}else{
reject('吃饭-失败');
}
}
function step3(resolve,reject) {
console.log('3.开始-收拾桌子');
if(state==1){
resolve('收拾桌子-完成');
}else{
reject('收拾桌子-失败');
}
} new Promise(step1).then(function (val) {
console.log(val);
return new Promise(step2);
}).then(function (val) {
console.log(val);
return new Promise(step3);
}).then(function (val) {
console.log(val);
return val;
})

十七、class的使用

我们在ES5中经常使用方法或者对象去模拟类的使用,虽然可以实现功能,但是代码并不优雅,ES6为我们提供了类的使用。需要注意的是我们在写类的时候和ES5中的对象和构造函数要区分开来,不要学混了

类的声明

先声明一个最简单的coder类,类里只有一个name方法,方法中打印出传递的参数。

class coder{
name(val){
console.log(val);
}
};

类的使用

我们已经声明了一个类,并在类里声明了name方法,现在要实例化类,并使用类中的方法。

let gavin= new coder;
console.log(gavin.name('xiaopi'));

类的多方法声明

class coder{
name(val){
console.log(val);
return val;
}
skill(val){
console.log(this.name('gavin')+':'+'Skill'+':'+val);
} }; let gavin= new coder;
console.log(gavin.skill('python'));

这里需要注意的是两个方法中间不要写逗号了,还有这里的this指类本身,还有要注意return 的用法。

类的传参

在类的参数传递中我们用constructor( )进行传参。传递参数后可以直接使用this.xxx进行调用。

class coder{
name(val){
console.log(val);
return val;
}
skill(val){
console.log(this.name('gavin')+':'+'Skill'+':'+val);
}
constructor(a,b){
this.a = a;
this.b = b;
}
add(){
return parseInt(this.a)+parseInt(this.b);
} }; let gavin= new coder(1,4);
console.log(gavin.skill('python'));
console.log(gavin.add());

我们用constructor来约定了传递参数,然后用作了一个add方法,把参数相加。这和以前我们的传递方法有些不一样,所以需要小伙伴们多注意下。

class的继承

如果你学过java,一定知道类的一大特点就是继承。ES6中也就继承,在这里我们简单的看看继承的用法。

class Htmler extends Coder{

}

let htmlera = new Htmler;
console.log(htmlera.name('xiaopi'));

声明一个htmler的新类并继承Coder类,htmler新类里边为空,这时候我们实例化新类,并调用里边的name方法。结果也是可以调用到的。

{
// 继承传递参数
class Parent{
constructor(name='gavin'){
this.name = name;
}
} class Child extends Parent{
constructor(name,age){
super(name);//如果super()表示继承Parent的所有参数
this.age = age;
}
}
}

setter与getter属性操作

{
// getter setter操作
class Parent{
constructor(name='gavin'){
this.name = name;
}
// get表示getter set表示setter 这里的get 与set不是方法,只是属性
get longName(){
return `mk: ${this.name}`
}
set longName(value){
this.name = value;
}
}
const v = new Parent('gavin');
console.log('getter', v.longName);
v.longName= 'xiaopi';
console.log('setter', v.longName);
}

静态方法与静态属性:

{
//static静态方法
class Parent {
constructor(name = 'gavin') {
this.name = name;
}
static tell(){
console.log('tell');
}
Parent.type = 'static属性就是如此定义,不需要new一个实例化方式即可调用'
}
Parent.tell();//调用静态方法
console.log('静态属性:', Parent.type);
}

十八、模块化操作

在ES5中我们要进行模块华操作需要引入第三方类库,随着前后端分离,前端的业务日渐复杂,ES6为我们增加了模块话操作。模块化操作主要包括两个方面。

  • export :负责进行模块化,也是模块的输出。
  • import : 负责把模块引,也是模块的引入操作。

export的用法:

export可以让我们把变量,函数,对象进行模块话,提供外部调用接口,让外部进行引用。先来看个最简单的例子,把一个变量模块化。我们新建一个temp.js文件,然后在文件中输出一个模块变量。

export var a = 'xiaopi';

然后可以在index.js中以import的形式引入。

import {a} from './temp.js';//其中temp.js后面的.js可以省略

console.log(a);

这就是一个最简单的模块的输出和引入。

如果想将一个模块中函数都引入,可以使用:

import * as lesson from './class/lesson18'; //*代表所有的东西  as 为别名

console.log(lesson.A);

多变量的输出

这里声明了3个变量,需要把这3个变量都进行模块化输出,这时候我们给他们包装成对象就可以了。

var a ='gavin';
var b ='xiaopi';
var c = 'web'; export {a,b,c}

函数的模块化输出

export function add(a,b){
return a+b;
}

as的用法

有些时候我们并不想暴露模块里边的变量名称,而给模块起一个更语义话的名称,这时候我们就可以使用as来操作。

var a ='xiaopi';
var b =‘gavin';
var c = 'web'; export {
x as a,
y as b,
z as c
}

export default的使用

加上default相当是一个默认的入口。在一个文件里export default只能有一个。我们来对比一下export和export   default的区别

1.export

export var a ='xiaopi';
export function add(a,b){
return a+b;
}

对应的导入方式

import {a,add} form './temp';//也可以分开写

2.export defalut

export default var a='gavin';

对应的引入方式

import str from './temp';

最常用的方式:

lesson18.js文件 进行export default:

export let A=123;
export function test() {
console.log('test')
} export class Hello{
test(){
console.log('class');
}
} export default{
A,
test,
Hello
}

index.js导入

import Lesson18 from './class/lesson18';
console.log(lesson18.A);

十九、Iterator和for of循环

Iterator的使用

{
let arr=['hello','world'];
let map=arr[Symbol.iterator]();
// map中有一个实例方法next,通过调用next()来完成实例的数据调用
console.log(map.next());
console.log(map.next());
console.log(map.next());
}

自定义Iterator接口

let obj={
start:[1,3,2],
end:[7,9,9],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
next(){
if(index<len){
return{
value:arr[index++],
done:false
}
}else{
return{
value:arr[index++],
done:true
}
}
}
} } } for(let key of obj) {
console.log(key);
}

总结:

无论如何调用Iterator接口,都必须首先定义:

[Symbol.iterator](){

  return {

    next(){

      return{
        value:
        done:
      }
    }
} }

二十、Generator

就是异步编程的一种解决方案

// 基本用法
let tell = function* () {
yield 'a';
yield 'b';
return 'c'
}; let k = tell(); console.log(k.next());
console.log(k.next());
console.log(k.next());

Generator实现对象的for of输出

{
let obj={};
obj[Symbol.iterator]=function* () {
yield 1;
yield 2;
yield 3;
}
for(let key of obj){
console.log(key);
}
}

实例一:抽奖

{
// 具体实例:抽奖的剩余次数
let draw = function (count) {
// 具体抽奖逻辑
console.info(`剩余${count}次`);
} let residue=function* (count) {
while(count>0){
count--;
yield draw(count);
}
} let start=residue(5);
let btn=document.createElement('button');
btn.id='start';
btn.textContent='抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function () {
start.next(); },false)
}

二十一、Decorator装饰器

装饰器就是用来修改类的行为的函数

首先需要安装

cnpm i babel-plugin-transform-decorators-legacy --save-dev

其次修改babelrc文件

{
"presets": ['es2015'],
"plugins": ["transform-decorators-legacy"]
}

装饰器的两种用法:

{
// 用法一
let readonly=function (target,name,descriptor) {
descriptor.writable=false;
return descriptor
}; class Test{
@readonly
time(){
return '218-01-11'
}
} let test=new Test();
console.log(test.time());
test.time=function () {
console.log('rest time!');
}
console.log(test.time());
} {
// 用法二
let typename=function (target,name,descriptor) {
target.myname='hello'//添加一个类的静态属性
} @typename
class Test{ } console.log(Test.myname);
}

第三方修饰器js库:core-decorators: cnpm install core-decorators

上一篇:Qt自定义信号槽的使用浅析+实例


下一篇:php 中的$argv与$argc