1.let关键字

  1. 作用:
    • 与var类似, 用于声明一个变量。
  2. 特点:
    • 在块作用域内有效。
    • 不能重复声明。
    • 不会预处理, 不存在变量提升。
  3. 应用:
    • 循环遍历加监听。
    • 使用let取代var是趋势1。
1
2
3
4
5
6
7
8
9
10
11
12
console.log(age);// 报错:age is not defined
let age = 12;
let age = 13;//报错:Identifier 'age' has already been declared
console.log(age);
//let可以解决"循环遍历加监听"问题,此处如果用var定义i,alert全部为2
//加入我页面中有三个<button>
let btns = document.getElementsByTagName('button');
for(let i = 0;i<btns.length;i++){
btns[i].onclick = function () {
alert(i);
}
}

2.const关键字

  1. 作用:
    • 定义一个常量。
  2. 特点:
    • 不能修改。
    • 其它特点同let。
  3. 应用:
    • 保存不用改变的数据。
1
2
3
4
const sex = '男';
console.log(sex);
sex = '女';//不能修改,报错: Assignment to constant variable.
console.log(sex);

3.变量的解构赋值

  1. 理解:
    • 从对象或数组中提取数据, 并赋值给变量(多个)
  2. 对象的解构赋值
    • let {n, a} = {n:’tom’, a:12}
  3. 数组的解构赋值
    • let [a,b] = [1, ‘hello’];
  4. 用途
    • 给多个形参赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1.对象的解构赋值,名称必须与对象属性值一样。
let person = {name : 'kobe', age : 39};
let {name,age} = person;//完全对应每个属性值,需要注意这里的name,age是全局的
console.log(name,age);

let book = {id: 1, price : 39.9};
let {price} = book;//不完全对应,按需索取,只要price,
console.log(price);

function demo({name,age}) {//此时name,age是函数作用域内的值
console.log(name);
console.log(age);
}
demo(person);

//2.数组的解构赋值(不太常用)
let arr = ['abc', 23, true];
let [a, b, c, d] = arr;
let [,,x] = arr//逗号站位,取指定位
console.log(x);//true
console.log(a, b, c, d);//abc 23 true undefined

4.模板字符串

  1. 模板字符串 : 简化字符串的拼接
    • 模板字符串必须用 `` 包含(esc下面的按键)。
    • 变化的部分使用${xxx}定义。
1
2
3
4
5
6
7
8
let obj = {
name : 'anverson',
age : 41
};
//普通拼接
console.log('我叫:' + obj.name + ', 我的年龄是:' + obj.age);//我叫:anverson, 我的年龄是:41
//模板字符串拼接
console.log(`我叫:${obj.name}, 我的年龄是:${obj.age}`);//我叫:anverson, 我的年龄是:41

5.简化对象写法

  1. 简化的对象写法
    • 省略同名的属性值。
    • 省略方法的function。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let x = 3;
let y = 5;
//普通对象的写法
let obj1 = {
x : x,
y : y,
getPoint : function () {
return this.x + this.y
}
};

//简化的写法
let obj2 = {
x,
y,
getPoint(){
return this.x
}
};
console.log(obj1, obj2);// {x: 3, y: 5, getPoint: ƒ} {x: 3, y: 5, getPoint: ƒ}
console.log(obj1.getPoint(), obj2.getPoint());// 8 3

6.箭头函数

  1. 作用: 定义匿名函数。
  2. 基本语法:
    • 没有参数: () => console.log(‘xxxx’)
    • 一个参数: i => i+2
    • 大于一个参数: (i,j) => i+j
    • 函数体不用大括号: 默认返回结果。
    • 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回。
  3. 使用场景: 多用来定义回调函数。
  4. 箭头函数的特点:
    • 代码简洁。
    • 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this。
  5. 扩展理解:箭头函数的this看外层的是否有函数?
    • 如果有,外层函数的this就是内部箭头函数的this。
    • 如果没有,则this是window。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//一般形式定义函数
let fun = function () {
console.log('fun()');
};
fun();

//箭头函数定义:没有形参,并且函数体只有一条语句
let fun1 = () => console.log('fun1()');
fun1();

//一个形参,并且函数体只有一条语句
let fun2 = x => x;
console.log(fun2(5));

//形参是一个以上,函数体只有一个语句
let fun3 = (x, y) => x + y;
console.log(fun3(25, 39));//64

//函数体有多条语句,同样用{}定义函数体
let fun4 = (x, y) => {
console.log(x, y);
return x + y;
};
console.log(fun4(34, 48));//82

//箭头函数的this
let btn = document.getElementById('btn');

//非箭头函数
btn.onclick = function () {
console.log(this);//btn
};

//箭头函数,外层还是箭头
let btn2 = document.getElementById('btn2');
let bindHandler = {
bindClick : () => {
console.log(this);//window,外层无函数,是window
btn2.onclick = () => {
//window,此时找外层函数的this,外层函数还是箭头函数,外层的外面是window
console.log(this);
};
}
};
bindHandler.bindClick();

//箭头函数,外层是普通函数
function Person() {
this.obj = {
showThis : () => {
console.log(this);
}
}
}
let fun5 = new Person();
fun5.obj.showThis();//Person

7.三点运算符

  1. rest(可变)参数
    • 用来取代arguments 但比arguments灵活,只能是最后部分形参参数
  2. 扩展运算符
    • let arr1 = [1,3,5];
    • let arr2 = [2,…arr1,6];
    • arr2.push(…arr1);
  3. 备注:三点运算符实际上底层调用了数组的Symbol(Symbol.iterator)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//1.关于函数的arguments
function fun(a,b,c) {
//这里的arguments是一个伪数组,包含着callee
console.log(arguments);// [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
//arguments伪数组,forEach会报错
arguments.forEach(function (item,index) {//报错:arguments.forEach is not a function
console.log(item);
})
}
fun(1,2,3);

//2.三点运算符接收参数
function fun2(...value) {
//此时value是真数组
console.log(value);// [1, 2, 3]
value.forEach(function (item,index) {
console.log(index+':'+item);//正常输出
})
}
fun2(1,2,3);

//3.其他形参占位,接收参数
function fun3(a,...value) {//这里不可以写:(...value,a),三点运算后不允许再有其他形参
console.log(a);//1
//此时value是真数组
console.log(value);// [2, 3]
value.forEach(function (item,index) {
console.log(index+':'+item);//正常输出
})
}
fun3(1,2,3);

//4.扩展运算符
let arr = [2, 3, 4, 5];
let arr2 = [1, ...arr, 6];
console.log(arr2);//[1, 2, 3, 4, 5, 6]

8.形参默认值

1.当不传入参数的时候默认使用形参里的默认值

1
2
3
4
5
6
7
8
function Point(x=0, y=0) {
this.x = x;
this.y = y;
}
let p1 = new Point(25, 36);
console.log(p1);//Point {x: 25, y: 36}
let p2 = new Point();
console.log(p2);//Point {x: 0, y: 0}

9.Promise对象

  1. 理解:
    • Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
    • 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称’回调地狱’)
    • ES6的Promise是一个构造函数, 用来生成promise实例
  2. 核心思想:
    • 通过异步任务的执行结果去动态的修改promise对象的状态来决定调用成功或者失败的回调函数
  3. 使用promise基本步骤(2步):
    • 创建promise对象,根据具体需求操作resolve和reject
    • 调用promise的then()

  4. promise对象的3个状态
    • pending: 初始化状态
    • fullfilled: 成功状态
    • rejected: 失败状态
  5. 应用:
    • 使用promise实现超时处理
    • 使用promise封装处理ajax请求
  6. 注意:promise对象then方法执行完有一个默认的返回值: promise对象(状态默认为成功, resolve(没有传参))!见下面的代码说明。
  7. 基本形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let promise = new Promise((resolve, reject) => {
//初始化promise状态为 pending
//执行异步操作
if(异步操作成功) {
resolve(value);//修改promise的状态为fullfilled
} else {
reject(errMsg);//修改promise的状态为rejected
}
});
promise
.then((value) => { // 成功的回调
console.log(value, '成功了...');
}, (value) => { // 失败的回调
console.log(value, '失败了...');
})

  • 利用promise对象执行异步任务 发送ajax请求(单纯探讨这种模式,开发中几乎不用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
   // 利用promise对象执行异步任务 发送ajax请求
// 封装函数
function sendXml(url) {
let promise = new Promise((resolve, reject) => {
// 初始化promise状态为pending
// 执行异步任务
let xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', url);
xmlHttp.send();
xmlHttp.onreadystatechange = function () {
if(xmlHttp.readyState === 4){
if(xmlHttp.status === 200){
// console.log(xmlHttp.responseText);
// 异步执行成功
// 修改promise对象的状态为成功状态
resolve(xmlHttp.responseText);
}else {
// 异步任务失败
reject('暂时没有新闻推送');
}

}
}
});
return promise;
}
//调用请求函数
sendXml('http://localhost:3000/news?id=2')
.then((value) => {
// 准备评论内容的地址
console.log('这是第一步success:'+value);
let url = `http://localhost:3000${JSON.parse(value).commentsUrl}`;
// 再次发送请求
return sendXml(url);
}, (error) => {
console.log("这是第一步erro:"+error);
/*
注意!这里没有任何return,但是当第一步失败的时候,下面的then依然会调用,并且
走的是成功的回调,所以说Promise的实例的.then方法有默认的返回,是成功的状态,
返回值是空
*/
})
.then((value) => {
console.log('这是第二步success:'+value);
}, (error) => {
console.log('这是第二步erro:'+error);
})

10.Symbol

  1. 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境。
  2. 概念:
  • ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
  1. 特点:
    • Symbol属性对应的值是唯一的,解决命名冲突问题
    • Symbol值不能与其他数据进行计算,包括同字符串拼串
    • for in, for of遍历时不会遍历symbol属性。
  2. 使用:
    • 调用Symbol函数得到symbol值
  3. 传参标识:
    • let symbol = Symbol(‘one’);
    • let symbol2 = Symbol(‘two’);
    • console.log(symbol); // Symbol(‘one’)
    • console.log(symbol2); // Symbol(‘two’)
  4. 内置Symbol值:
    • 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
    • Symbol.iterator
    • 对象的Symbol.iterator属性,指向该对象的默认遍历器方法。
  5. 备注:所以现在JS已有的数据类型有:
    • 基础数据类型:String、Number、boolean、null、undefined、 Symbol。
    • 引用数据类型:Object、Array、Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取symbol
let symbol = Symbol();
let symbol2 = Symbol(2);

console.log(typeof symbol);
console.log(symbol);

// 用作对象的属性(唯一)
let obj = {username: 'kobe', age: 39};
obj[symbol] = 'hello';
console.log(obj);

for(let i in obj){//不会遍历obj[symbol]
console.log(i);
}
//不可拼接
let str = '我是'+symbol2;//报错 Cannot convert a Symbol value to a string

11.iterator遍历器

  1. 概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
  2. 作用:
    • 为各种数据结构,提供一个统一的、简便的访问接口;
    • 使得数据结构的成员能够按某种次序排列
    • ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
  3. 工作原理:
    • 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
    • 第一次调用next方法,指针自动指向数据结构的第一个成员
    • 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
    • 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
      • value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
      • 当遍历结束的时候返回的value值是undefined,done值为false
  4. 原生具备iterator接口的数据(可用for of遍历)
    • Array
    • arguments
    • set容器
    • map容器
    • String
  5. Array可以用 for of去遍历,Object原型中没有iterator,是不可以用for of 遍历的。
  6. 根据遍历器的工作原理,我们模拟一个遍历器,让他既可以实现原生iterator遍历数组,又可以遍历对象,然后让它替换掉Array函数prototype中默认的遍历器,并且在Object的prototype中添加这个遍历器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 模拟遍历器    作用:遍历数据(数组、对象)
function mockIterator() {
console.log(this); // 打印调用的当前函数,即:for of 变量的目标数组
let that = this;// 这里完全可以直接使用this,但是为了安全,我们缓存这个this
// 定义变量标识指针的位置
let index = 0;
// 判断遍历的目标是对象还是数组
if(that instanceof Array){ // 判断是数组
return { // 遍历器对象(指针对象)
next: function () {
return index < that.length?
{value: that[index++], done: false}:{value: that[index++], done: true}
}
}
}else {
let keys = Object.keys(that);//获取对象的全部属性为数组。
return { // 遍历器对象(指针对象)
next: function () {
return index < keys.length?
{value: that[keys[index++]], done: false}:{value: that[keys[index++]], done: true}
}
}
}

}
//显示原型中添加遍历器。
Array.prototype[Symbol.iterator] = mockIterator;
Object.prototype[Symbol.iterator] = mockIterator;

let obj = {name:'kobe',age: 40};
let arr = [1,2,3,4];
//均可正常遍历数组、对象。
for(let i of arr){
console.log(i);
}

for(let i of obj){
console.log(i);
}

12.Generator函数

  1. 概念:
    • ES6提供的解决异步编程的方案之一
    • Generator函数是一个状态机,内部封装了不同状态的数据,
    • 用来生成遍历器对象
    • 可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
  2. 如何定义:用* 号,例如:function* fun()
  3. 特点:
    • function 与函数名之间有一个星号
    • 内部用yield表达式来定义不同的状态
    • generator函数返回的是指针对象,而不会执行函数内部逻辑
    • 调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
    • 再次调用next方法会从上一次停止时的yield处开始,直到最后
    • yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
1
2
3
4
5
6
7
8
9
10
11
12
function* fun(a,b) {
// 当遇到yield表达式的会暂停
yield console.log('函数体的第一条语句');//此时log已经打印完毕,暂停在此处。
yield console.log('函数再次执行');
yield a+b;
yield console.log('这是函数最后一次打印');
}

let f = fun(1,2);
//f();//直接普通方式调用会报错,f is not a function
//要用f.next()调用。

  • 执行图示如下图:

演示

13. async函数(源自ES2017)

  1. 概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
  2. 本质: Generator的语法糖
  3. 语法:
    1
    2
    3
    4
    async function foo(){
    await 异步操作;
    await 异步操作;
    }
  4. 特点:
    • 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
    • 返回的总是Promise对象,可以用then方法进行下一步操作
    • async取代Generator函数的星号*,await取代Generator的yield
    • 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用,哈哈
    • 在解决异步回调的问题时,与promise搭配使用
  5. 消除“回调地狱”的最好办法是:用async和Promise实例搭配,这种方式真正意义上解决异步回调的问题,这样写的好处是:
    • resolve变向通知await当前的异步任务是否成功
    • 在通知的同时可以将成功额数据交给await语句的返回值
  6. 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//发送请求函数
function sendXml(url) {
let promise = new Promise((resolve, reject) => {
$.ajax({
url: url,
dataType: "json",
type: "GET",
success: function(data) {
resolve(data);//resolve把状态变相给了await,并传参。
},
error: function() {
/*需要注意的是,此时虽然是失败的回调,但是我我们这里依然用了resolve,
因为对于实际开发来说,如果此处用reject会导致我们的代码逻辑不会继续
向下走,我们后期拿到的comment是undefined,不便于后期数据渲染页面,
这里我们无论成功失败都调用resolve,然后在失败的时候,将返回值设置
为false,async中加入判断,来看是否是成功还是失败。
*/
resolve(false)
}
});
})
return promise;
}
//获取新闻及评论
async function getNews() {
let result = await sendXml('http://localhost:30001/news?id=2');
let comment;
/判断第一次请求状态
if(result){
let url = 'http://localhost:3000' + result.commentsUrl;
comment = await sendXml(url);
}else{
comment ='暂无数据';
}
console.log(result);
console.log(comment);

}
getNews();

14.类

  1. ES6中引入了类的概念,让JS更进一步迈向了面向对象。
  2. 核心关键字(同java)
    • 通过class定义类/实现类的继承
    • 在类中通过constructor定义构造方法
    • 通过new来创建类的实例
    • 通过extends来实现类的继承
    • 通过super调用父类的构造方法
    • 重写从父类中继承的一般方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//一个父类
class Person {
//调用类的构造方法
constructor(name, age){
this.name = name;
this.age = age;

}
//定义类的一般方法
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person, person.showName());

//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){// 次数也要带着父类形参
//super的操作类似于之前我们用构造器模拟继承,在子类中调用父类构造。
super(name, age);//调用父类的构造方法,此处要传入父类形参
this.salary = salary;
}
showName(){//在子类自身定义方法
console.log(this.name, this.age, this.salary);
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();