ES6新特总结

ES6新特性

Babel转码器

ECMAScript 6 简介

学习网址:http://es6.ruanyifeng.com/

ECMAScript是javascript标准

ES6就是ECMAScript的第6个版本

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ECMAScript 和 JavaScript 的关系

ECMAScript是JavaScript的规格,JavaScript是ECMAScript的一种实现,在日常场合,这两个词是可以互换的。

JavaScript的创造者Netscape公司,将JavaScript提交给国际标准化组织ECMA,希望这种语言能够成为国际标准,后来ECMA发布标准文件的第一版(ECMA-262),规定了浏览器脚本语言的标准,并将这种语言称为ECMAScript

该标准从一开始就是针对JavaScript语言制定的,之所以不叫JavaScript,有两个原因:一是商标,Java是Sun公司的商标,根据授权协议,只有Netscape公司可以合法地使用JavaScript这个名字,且JavaScript本身也已经被Netscape公司注册为商标;二是想体现这门语言的制定者是ECMA,不是Netscape,有利于保证这门语言的开放性和中立性。

但事实上,JavaScript比ECMA-262的含义多得多,一个完整的JavaScript实现应该由以下三个部分组成:

  1. ECMAScript:核心

  2. DOM:文档对象模型

  3. BOM:浏览器对象模型


    在这里插入图片描述

ES6 与 ECMAScript 2015 的关系

2011 年,ECMAScript 5.1 版发布后,就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本

标准委员会最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的 6 月份,草案就自然变成了新一年的版本

语法提案的批准流程

任何人都可以向标准委员会(又称 TC39 委员会)提案,要求修改语言标准。

一种新的语法从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由 TC39 委员会批准。

· Stage 0 - Strawman(展示阶段)

· Stage 1 - Proposal(征求意见阶段)

· Stage 2 - Draft(草案阶段)

· Stage 3 - Candidate(候选人阶段)

· Stage 4 - Finished(定案阶段)

一个提案只要能进入 Stage 2,就差不多肯定会包括在以后的正式标准里面

Babel转码器

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。这意味着,你可以用 ES6 的方式编写程序,又不用担心现有环境是否支持

1.下面的命令在项目目录中,安装 Babel

npm install --save-dev @babel/core

2.配置配置文件.babelrc

该文件用来设置转码规则和插件,基本格式如下:

{

 "presets": [],

 "plugins": []

}
在这里插入图片描述

3.最新转码规则

npm install --save-dev @babel/preset-env

4.将这些规则加入.babelrc。

 {

  "presets": [

   "@babel/env",

   "@babel/preset-react"

  ],

  "plugins": []

 }
  1. 命令行转码

Babel 提供命令行工具@babel/cli,用于命令行转码。

它的安装命令如下。

$ npm install --save-dev @babel/cli

6.基本用法如下

# 转码结果输出到标准输出

$ npx babel example.js

# 转码结果写入一个文件# --out-file 或 -o 参数指定输出文件

$ npx babel example.js --out-file compiled.js

# 或者$ **npx babel example.js -o compiled.js**

# 整个目录转码# --out-dir 或 -d 参数指定输出目录

$ npx babel src --out-dir lib

# 或者$ npx babel src -d lib

npm: node page

npx:

代码测试转码:

// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {

 return item + 1;});

转码后

"use strict";

input.map(function (item) {

 return item + 1;

});

严格模式:在函数内部选择进行较为严格的全局或局部的错误条件检测,使用严格模式的好处是可以提早知道代码中的存在的错误,

及时捕获一些可能导致编程错误的ECMAScript行为。在开发中使用严格模式能帮助我们早发现错误

let 和const 声明

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

let块级作用域

{
    var num=10;
    let count=10;
}
console.log(num)
console.log(count);//: count is not defined

var arr=[10,20,30,40];
for(var i=0;i

不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错

// var 的情况
console.log(foo); // 输出undefinedvar foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceErrorlet bar = 2;

上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

// 报错
function func() {
 let a = 10;
 var a = 1;
}

// 报错
 function func() {
    let a = 10;
    let a = 1;
 }

为什么需要块级作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量

var uname='jack';
function change(){
   console.log(uname);
   var uname='rose';
   console.log(uname);
}
change(); 

原因在于变量提升,导致内层的uname变量覆盖了外层的uname变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';
for (var i = 0; i < s.length; i++) {
    console.log(s[i]);
}
console.log(i); // 5

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

const 命令

基本用法

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;

PI // 3.1415

PI = 3;

// TypeError: Assignment to constant variable.

上面代码表明改变常量的值会报错。

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const foo;
// SyntaxError: Missing initializer in const declaration

上面代码表示,对于const来说,只声明不赋值,就会报错。

固定方法

const setName = function(){}

常量写法:const声明字符串常量的时候 一般字符串全部大写

const NAME='hello'

什么时候使用const? 什么时候使用let

只要你所声明的值不发生改变,使用常量,其他使用let

const setName=function(){}
const obj={}

后面会慢慢接触到 更多的使用场景

总结:let具有块级作用域 不允许重复声明 不允许变量提升 这个var不具备的

本章作业

Es6与javascript区别

Let var const区别

Es6基本语法

变量的解构赋值

数组的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

以前,为变量赋值,只能直接指定值。

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

ES6 允许写成下面这样。

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

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

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]];
let [ , , third] = ["foo", "bar", "baz"];

third // "baz"let [x, , y] = [1, 2, 3]; 
var [x,y]=[1,2];

console.log(x);

var [x,y,m]=[3,4];

console.log(m);//undefind

var [x,y]=[80,90,100];//100获取不到

var [m,,,n]=[11,22,33,44];

console.log(m)

var [x,y,[m,n]]=[10,20,[30,40]]

console.log(m)

默认值

解构赋值允许指定默认值。

let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

对象的解构赋值

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };

foo // "aaa"

bar // "bbb"

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

let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。

如果解构失败,变量的值等于undefined

let {foo} = {bar: 'baz'};

foo // undefined

上面代码中,等号右边的对象没有foo属性,所以变量foo取不到值,所以等于undefined。

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量

默认值

对象的解构也可以指定默认值。

var {x = 3} = {};
x // 3

var {x, y = 5} = {x: 1};
x // 1
y // 5

字符串的解构赋值

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

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5

函数参数的解构赋值

函数的参数也可以使用解构赋值。

function add([x, y]){

 return x + y;}

add([1, 2]); // 3

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y

function demo([x,y]){
   return x+y;
}
console.log(demo([10,20]));

function demo([x,y=30]){
   return x+y;
}
console.log(demo([10]));

function demo(x,y=20){
   return x+y;
}

console.log(demo(10)) 

解构赋值用途

(1)交换变量的值

let x = 1;let y = 2;
[x, y] = [y, x];

上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰

面试题:

请用一句代码,实现两个数据值互换?

(2)提取 JSON 数据

解构赋值对提取 JSON 对象中的数据,尤其有用。

let jsonData = {
 id: 42,
 status: "OK",
 data: [867, 5309]};
let { id, status, data: number } = jsonData;

 

console.log(id, status, number);

// 42, "OK", [867, 5309]

(3)函数参数的默认值

jQuery.ajax = function (url, {
 async = true,
 beforeSend = function () {},
 cache = true,
 complete = function () {},
 crossDomain = false,
 global = true,
 // ... more config
} = {}) {
 // ... do stuff
}; 

解构赋值:

后面使用react使用方便:

比如想获取 state里面的数据 普通写法:this.data.state

var state={
   flag:true,
   msg:'ok'
}
var {flag,msg}=state;
console.log(flag,msg)

ES6 加强了对 Unicode 的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。

字符串的扩展

字符的 Unicode 表示法

"\u0061"
// "a"

但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。

"\uD842\uDFB7"
// ""\u20BB7"
// " 7"

字符串的遍历器接口

ES6 为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for...of循环遍历。

for (let codePoint of 'foo') {

 console.log(codePoint)}

// "f"// "o"// "o"

除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

模板字符串(重点)

传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

$('#result').append(
 'There are ' + basket.count + ' ' +
 'items in your basket, ' +
 '' + basket.onSale +
 ' are on sale!');

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

$('#result').append(`
 There are ${basket.count} items
  in your basket, ${basket.onSale}
 are on sale!
`);

字符串的新增方法

实例方法:includes(), startsWith(), endsWith()

实例方法:repeat()

实例方法:padStart(),padEnd()

实例方法:trimStart(),trimEnd()

includes(), startsWith(), endsWith()

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

· includes():返回布尔值,表示是否找到了参数字符串。

· startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

· endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置。

let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

实例方法:repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

参数如果是小数,会被取整。

'na'.repeat(2.9) // "nana"

padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

上面代码中,padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。

如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。

trimStart(),trimEnd()

ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

const s = '  abc  ';
s.trim() // "abc"
s.trimStart() // "abc  "
s.trimEnd() // "  abc"

上面代码中,trimStart()只消除头部的空格,保留尾部的空格。trimEnd()也是类似行为。

数组扩展

​ 扩展运算符

​ 1.合并数组:concat

​ 2.求数组中最大值

​ Array.from() : 将类数组转化为真实的数组

​ arguments、nodelist

数组扩展

var arr = [10,20,30]

var arr1 = [40,50,60];

console.log([...arr,...arr1]);

var arr2 = [23,235,2,3,52,35];

console.log(Math.max.apply(null,arr2));

console.log(Math.max(...arr2))

Array.from() : 将类数组转化为真实的数组

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

function hello(){

  arguments = Array.from(arguments);

  console.log(Array.isArray(arguments));

}

hello(1,2,3,4);

var lists = document.getElementsByTagName("li");

lists = Array.from(lists);

console.log(lists);

// push

// arr.push

// Array.isArray(arr);

Array.of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]Array.of(3) // [3]Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []Array(3) // [, , ,]Array(3, 11, 8) // [3, 11, 8]

上面代码中,Array方法没有参数、一个参数、三个参数时,返回结果都不一样。只有当参数个数不少于 2 个时,Array()才会返回由参数组成的新数组

数组实例的 copyWithin()

数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。

· target(必需):从该位置开始替换数据。如果为负值,表示倒数。

· start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。

· end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

这三个参数都应该是数值,如果不是,会自动转为数值。

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

数组实例的 find() 和 findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined

[1, 4, -5, 10].find((n) => n < 0)

// -5

数组实例的 fill()

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)

// [7, 7, 7]new Array(3).fill(7)

// [7, 7, 7]

上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2)

// ['a', 7, 'c']

上面代码表示,fill方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。

注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

for (let index of ['a', 'b'].keys()) {
 console.log(index);
}
// 0// 1

for (let elem of ['a', 'b'].values()) {
 console.log(elem);
}
// 'a'// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
 console.log(index, elem);
}
// 0 "a"

对象的扩展

属性的简洁表示法

var name = "iwen";
var age = 20;
const ADD = "add";
var obj = {
  name, // name:name
  age,
[ADD]: function(){
    console.log("add");
  },
  fn1:function(){
  },
  fn2(){
    console.log("fn2");
  }
}

属性名表达式

JavaScript 定义对象的属性,有两种方法。

// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;

上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。

ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';
let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123};

下面是另一个例子。

let lastWord = 'last word';

const a = {
  'first word': 'hello',
  [lastWord]: 'world'};

a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"

表达式还可以用于定义方法名。

对象新增方法

Object.is()

ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')
// true

Object.is({}, {})
// false

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Promise

promise是什么?

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

promsie产生的原因

在JavaScript的世界中,所有代码都是单线程执行的。

由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

function callback() {
  console.log('Done');
}

console.log('before setTimeout()');
setTimeout(callback, 1000); **// 1秒钟后调用callback函数**
console.log('after setTimeout()');

观察上述代码执行,在Chrome的控制台输出可以看到:

before setTimeout()

after setTimeout()

(等待1秒后)

Done

可见,异步操作会在将来的某个时间点触发一个函数调用。

异步回调的问题:

· 之前处理异步是通过纯粹的回调函数的形式进行处理

· 很容易进入到回调地狱中,剥夺了函数return的能力

· 问题可以解决,但是难以读懂,维护困难

· 稍有不慎就会踏入回调地狱 - 嵌套层次深,不好维护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQw4VkBe-1607915453129)(0210ES6新特性.assets/wps6769bk.jpg)]

回调地狱

一般情况我们一次性调用API就可以完成请求。
有些情况需要多次调用服务器API,就会形成一个链式调用,比如为了完成一个功能,我们需要调用API1、API2、API3,依次按照顺序进行调用,这个时候就会出现回调地狱的问题

promise详解

语法

new Promise(

 function (resolve, reject) {

  // 一段耗时的异步操作

  resolve('成功') // 数据处理完成

  // reject('失败') // 数据处理出错

 }).then(

 (res) => {console.log(res)}, // 成功

 (err) => {console.log(err)} // 失败)

resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

【代码演示】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WlmhrjP3-1607915453133)(0210ES6新特性.assets/wpsfHNB4q.jpg)]

请求网络接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oF0hmVQZ-1607915453136)(0210ES6新特性.assets/wpsaa5kGN.jpg)]

图片懒加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1dlMqaz7-1607915453141)(0210ES6新特性.assets/wpsr7W6C8.jpg)]

promise有三个状态:

1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败

当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。

Promise对象的状态改变,只有两种可能:
1、从pending变为fulfilled
2、从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。

promise案例

回调包装成Promise,他有两个显而易见的好处:
1、可读性好
2、返回 的结果可以加入任何Promise队列

实战示例,回调地狱和promise对比:

传统写法

/***
第一步:找到北京的id

  第二步:根据北京的id -> 找到北京公司的id

  第三步:根据北京公司的id -> 找到北京公司的详情

  目的:模拟链式调用、回调地狱
***/
 // 回调地狱
 // 请求第一个API: 地址在北京的公司的id
 $.ajax({
  url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',
  success (resCity) {
   let findCityId = resCity.filter(item => {
    if (item.id == 'c1') {
     return item
    }
   })[0].id
   $.ajax({
    //  请求第二个API: 根据上一个返回的在北京公司的id “findCityId”,找到北京公司的第一家公司的id
    url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',
    success (resPosition) {
     let findPostionId = resPosition.filter(item => {
      if(item.cityId == findCityId) {
       return item
      }
     })[0].id
     // 请求第三个API: 根据上一个API的id(findPostionId)找到具体公司,然后返回公司详情
     $.ajax({
      url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',
      success (resCom) {
       let comInfo = resCom.filter(item => {
        if (findPostionId == item.id) {
         return item
        }
       })[0]
       console.log(comInfo)
      }
     })
   }
   })
  }
 })

1.4.2 Promise

const cityList = new Promise((resolve, reject) => {
   $.ajax({
    url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',
    success (res) {
     resolve(res)
    }
   })

  })

  // 第二步:找到城市是北京的id

   cityList.then(res => {

​    let findCityId = res.filter(item => {

​     if (item.id == 'c1') {

​      return item

​     }

​    })[0].id

​    

​    findCompanyId().then(res => {

​     // 第三步(2):根据北京的id -> 找到北京公司的id

​     let findPostionId = res.filter(item => {

​       if(item.cityId == findCityId) {

​        return item

​       }

​     })[0].id

 

​     // 第四步(2):传入公司的id
     companyInfo(findPostionId)
    })
   })

  // 第三步(1):根据北京的id -> 找到北京公司的id

  function findCompanyId () {

   let aaa = new Promise((resolve, reject) => {
    $.ajax({
     url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',
     success (res) {
      resolve(res)
     }
    })

   })

   return aaa

  }

 // 第四步:根据上一个API的id(findPostionId)找到具体公司,然后返回公司详情

 function companyInfo (id) {

  let companyList = new Promise((resolve, reject) => {

   $.ajax({
    url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',
    success (res) {
     let comInfo = res.filter(item => {
       if (id == item.id) {
         return item
       }
     })[0]
     console.log(comInfo)
    }

   })

  })

 }

本章作业

Promise是什么 如何使用

Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

Generator语法

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

function* getData() {
  yield http();
  yield getLog();
}

 // Generator函数
 function* generator() {
  yield 'status one'     // yield 表达式是暂停执行的标记  
  return 'hello world'
}

 let iterator = generator()  // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
 iterator.next()       // {value: "status one", done: false},value 表示返回值,done 表示遍历还没有结束
 iterator.next()        // {value: "hello world", done: true},value 表示返回值,done 表示遍历结束

yield 表达式

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的next方法的运行逻辑如下。

(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。

(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

function* getData() {
  yield http();
  yield getLog();
}

function getLog() {
  console.log("我是网络请求");
}

function http() {
  return $.ajax({
    type: "get",
    url: "http://iwenwiki.com/api/blueberrypai/getIndexChating.php",
    success: function (data) {
      console.log(data);
    }
  })
}

var gd = getData();
gd.next();
gd.next();

async

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖

function getLog(result,ms) {
  console.log(result);
  // 页面渲染函数
  $("#root").append(``);
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

function Hello() {
  console.log("我是最后执行");
}

function http() {
  return $.ajax({
    type: "get",
    url: "http://iwenwiki.com/api/blueberrypai/getIndexChating.php",
    success: function (data) {
      console.log("网络请求执行完成");
    }
  })
}
 
async function getData() {
  var result = await http();
  await getLog(result,2000);
  Hello();
}

getData();

本章作业

Generator 函数

模块的导入导出

在es5中,用module.exports和exports导出模块,用require引入模块。

es6新增export和export default导出模块,import导入模块。

名字导出(name export)

名字导出可以在模块中导出多个声明。

export

export后必须跟语句, 何为语句, 如声明, for, if 等都是语句, export 不能导出匿名函数, 也不能导出某个已经声明的变量, 如:

export const bar = function() {}; // 合法 
export bar; // 非法
export 1; // 非法
export function foo () {}; // 合法, 后跟的是声明语句 
export { foo }; // 合法, 后面跟的{}理解为语句, 就像if后面的{}一样 
export { foo as bar }; // 合法export { foo: foo }; // 非法, 后面的{}被解析成对象

默认导出(default export)

一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致,这对于导出匿名函数或类非常有用。

export default

export default在整个模块中只能出现一次, 后只能具体的值, 何为具体的值, 如1, 2, 3, 再比如一个函数声明(非表达式), 或者是一个类声明(与函数声明一个意思), 或者匿名函数, 只要是能用变量接受的都可以, 例子:

export default 1; // 合法 
export default function foo() {}; // 合法, 因为function foo() {} 能被变量接受, 如 var bar = function foo() {}
export default const bar = 1; // 非法, 因为var a = const bar = 1 是不合法的 
export default { foo }; // 合法, {} 被理解为一个对象 
export default { foo: foo }; // 合法, 同上

import

import语法为:

import { x, y } from './test.js';import * as some from './test.js';  // 命名空间导入import './test.js';import { default as test } from './test.js';

导入再导出

export { some } from './test.js';export * from './test.js';

导入后跟需要导入的绑定和模块说明符, 导入绑定的列表并非对象的解构, 二者并无关联, 导入的标识符很像const声明的变量, 不可更改, 也同时存在暂时性死区, 如有理解错误的地方还请不吝指教

Class类

传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。这样的写法相对于其它传统面向对象语言来讲,很有一种独树一帜的感脚!非常容易让人困惑!
如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。构造函数示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gr3DDapc-1607915453145)(0210ES6新特性.assets/wpsxmOwA0.jpg)]

  1. 当使用了构造函数,并且new 构造函数(),后台会隐式执行new Object()创建对象;

  2. 将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象

  3. 执行构造函数的代码。

  4. 返回新对象(后台直接返回);

你可能感兴趣的:(ES6新特总结)