avatar

目录
es6的学习

已更新目前所学内容

let

注:

  1. es6没有对es5有任何修改,全部新增

    1. 在es5中定义变量:var function

    2. 允许先使用再定义

      javascript
      1
      2
      console.log(a)  //变量的提升
      var a = 10 //全局变量会成为window对象属性

let的特点

  1. 使用let定义的变量只在块级中有效

    javascript
    1
    2
    3
    4
    {
    let a = 10 //只在代码块内有效
    console.log(a)
    }
  2. 不存在变量的提升(先定义,在使用)

  3. let定义的变量不允许重复定义

变量的作用域

es5中,变量有两种:全局变量,局部变量

es6 全局变量,局部变量,块级变量

javascript
1
2
3
4
5
6
7
8
9
for(var i=0;i<5;i++){
console.log(5)
}
console.log(i)
//打印出5个5
for (let j = 0; j < 5; j++) {
console.log(j)
//分别打印1 2 3 4 5
}

闭包问题

javascript
1
2
3
4
5
6
7
8
9
10
11
12
var lis  = document.querySelectorAll('li')
// 点击每个li时,弹出自身的位置
// for(var i =0;i<lis.length;i++){
// lis[i].onclick = function(){
// alert(i) //全是5 闭包现象
// }
// }
for(let i =0;i<lis.length;i++){
lis[i].onclick = function(){
alert(i) //全是5 闭包现象
}
}
Code
1
2
注意:变量是let声明的,当前的i只在本轮循环有效,所以每一次循环的其实都是一个新的变量。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
另外,for循环还有一个特别之处,就是设置循环变量的哪部分是一个父作用域,而循环体内部是一个单独的子作用域

暂时性死区

javascript
1
2
3
4
5
6
7
8
9
if(true){
console.log(typeof a) //undefined
var a = 1
}

if(true){
console.log(typeof a)
let a = 1
}

let访问不到a

!!!变量一定要定义再使用

循环中的块级绑定

javascript
1
2
3
4
5
for(var i= 0;i<10;i++){
process[item[i]]
}
//i在此处仍然可被访问
console.log(i)
javascript
1
2
3
4
5
6
for(let j=0;j<10;j++){
progress[item[j]];
}
//此处的i不可访问,会抛出错误

//item请自定义,这里只是举例

let定义变量好处(总结)

  1. 不存在变量提升
  2. let定义的变量不会成为window对象的属性
  3. let不允许重复定义变量

let例子

闭包解决:

javascript
1
2
3
4
5
6
7
8
9
10
11
//希望使用for循环生成一个数组,数组中的元素都是函数,
// 每个函数的功能均为:输出for循环执行时的位置
var arr = []
for (var i = 0; i < 5; i++) {
arr.push((function (j) {
return function () {
console.log(j)
}
})(i))
}
console.log(arr[4]())

let定义:

javascript
1
2
3
4
5
6
7
arr0 = []
for(let j=0;j<5;j++){
arr0.push(function(){
console.log(j)
})
}
console.log(arr0[4]()) //4

const

基本用法

  1. 声明一个只读的常量(不能被修改的量)
  2. 其他用法与let一致

注意:为了与一般的变量进行区分,常量的名称一般为大写 const定义的常量必须在定义的时候初始化(赋值)

javascript
1
2
3
4
const arr = [100,200,300]
//arr=['hello','world'] //对数组重新赋值 error 不允许重复赋值
arr[0]='hello' //可以的,数组的内容是可以改变的
console.log(arr)
javascript
1
2
3
4
5
6
7
8
9
//希望引用类型也变为常量
//使用Object。freeze()方法结合const,可以完成一个引用类型常量的定义
const arr = [1,2,3,4]
//在ES6中Object.freeze()冻结 冰冻
const arr0 = Object.freeze([2,3,4,5]) //该数组已被冻结
console.log(arr0)
//arr0 = [100,200] //对数组进行赋值 error 常量不能被修改
arr0[0]='hello world'//不报错 不生效
console.log(arr0)

上图针对于对象也适用

总结

​ 顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。ES6之中,顶层对象的属性与全局变量是等价的。

​ 顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一。这样的几个很大的问题,首先是没法在编译时报出变量未声明的错误,只有运行时才能知道(因为全局变量坑能是顶层对象的属性造成的,而属性的创造是动态的,其次,程序员很不容易不知不觉地就创造除了全局变量),最后,顶层对象的属性是可以到处读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合法的。

​ ES6巍峨改变这一观点,一方面规定,为了保证兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性,另一方面规定,let命令,const命令,class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。

数组的解构

结构——–模式匹配

javascript
1
2
3
4
5
//ES6允许写成如下:
let [a,b,c]=[1,2,3]
//上面的代码表示,可以从数组中提取值,按对应位置赋值变量
//本质上这种写法属于模式匹配,只要等号两边的模式相同,左边的变量会被赋予对应的值
//如果结构不成功,变量的值就等于undefined

对数组的解构:

javascript
1
2
let arr = [10,20,30]
let [a,b,c]=arr

完全结构:

javascript
1
2
let [[a,b],c] = [[10,20],30]
let [a1,a2]= [[10,20],30] //完全结构

不完全结构:

javascript
1
2
let [a,b]=[100]
let [c,d,e] = [1,2,34,5,56,6]

等号左右两边模式不一致

javascript
1
2
3
//报错
//let [foo] = 1; 结构不匹配
let [foo] = false

如果等号的右边的不是数组(或者可以严格地说,不是可遍历的结构),那么将会报错。

默认值

javascript
1
2
3
4
5
6
7
8
//默认值,解构支持默认值
//let [a,b=0]=[10,20]
let [a,b=0]=[10]


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

对象和字符串解构

字符串:

javascript
1
2
let arr = ['hello','world','yes','ok']
let [a,b,c,d]= arr

对象:对象的解构要保证:变量名称与属性名称一致

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

完全结构:

javascript
1
2
3
4
5
6
7
8
let obj ={
name:'xiaowu',
age:22,
addr:'大学城'
}
let {a,b} = obj //不报错,但赋值失败
//注意:对象的解构要保证:变量名与属性名称保持一致
let {name,age,addr}= obj

不完全解构:

javascript
1
2
3
4
5
let {color,age} ={
name: 'xiaowu',
age: 22
color: 'skyblue'
}

简化对象的属性名称:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
let data = {
url: 'http://www.vchamps.cn',
myProductName;'整理笔记太难了'
price: '$22',
sale: '2222'
}
let {myProductName} = data
//简化对象的属性名称(对属性名称重新命名)
let {myProductName:mName} = data

console.log(mName)
console.log(myProductName) //已经被重新命名为mName

字符串解构赋值

完全解构

javascript
1
2
3
let str = 'hello'
let [a,b,c,d,e]=str
console.log(a,b,c,d,e)

函数的参数解构

javascript
1
2
3
4
5
//参数的解构
function add([x,y]){
console.log(x+y)
}
add([10,20])
javascript
1
2
3
4
5
6
7
8
9
10
//参数解构  默认值
function fn({name,age}={}){
console.log(name.age)
}
fn({
name: 'jack',
age:20,
addr: '大学城'
})
// fn() error
javascript
1
2
3
4
function add([x=0,y=0] = []) {
console.log(x+y)
}
add()

用途

1.交换变量的值

javascript
1
2
3
4
5
6
7
let a =3 ,b =5
//交换a和b的值,不能使用第三个变量
a = a+b
b = a-b //3
a = a-b
//ES6中
// [b,a] = [a,b]

2.从函数返回多个值

javascript
1
2
3
4
5
6
//应用2 :函数返回多个值
function fn(){
return [3,5,7]
}

let [a,b,c]= fn()

3.函数参数的定义

javascript
1
2
3
4
5
function add([x=0,y=0] = []) {
console.log(x+y)
}
add([10,30])
//add()

4.提取json数据

javascript
1
2
3
4
5
6
7
let jsonData = {
id:42,
status: 'OK',
data: [867,5360]
}
let {id,status,data:number} = jsonData
console.log(id,status,number)

函数的扩展

函数参数默认值

基本语法:

在es5中,不能为函数的参数指定默认值

javascript
1
2
3
4
5
6
7
8
9
10
function add(x,y){
if(y == undefined && x ! = undefined){
y=0
}else if(x == undefined){
x = y =0
}
}
add(10,20)
add(10) //x=10,y没有值
add()

在ES6中支持为函数的参数设定默认值

javascript
1
2
3
function add(x=0,y=0){
console.log(x+y)
}

rest参数

es5中:实现求和(没有rest参数)有arguments

javascript
1
2
3
4
5
6
7
8
9
10
11
function getSum() {
var sum = 0
//arguments中保存了所有接受到的参数
console.log(arguments)
//因为arguments不是数组,不能使用foreach
for(var i=0;i<arguments.length;i++){
sum += arguments[i]
}
console.log(sum)
}
getSum()

es6中:rest参数

ES6引入rest参数(形式为.....变量名),用于获取函数多余的参数,这样就不需要使用arguments对象了,rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中
javascript
1
2
3
4
5
6
7
8
9
10
function getSum(...params) {
var sum = 0;
// console.log(params)
params.forEach(function(item){
sum += item
})
console.log(sum)
}

getSum(20,30,40)

!! rest参数注意点

rest参数必须为函数参数的最后一个参数

javascript
1
2
3
4
5
6
7
8
9
//rest参数必须为函数参数的最后一个参数
//function sum(num,....rest){ //错误 error

//}

function sum(num,..rest){
console.log(rest)
console.log(num)
}

函数复习

javascript
1
2
3
4
let fn = function(x){
console.log('java or c?')
return x
}

箭头函数

在ES6中针对含税, =>代表函数

javascript
1
2
3
4
5
6
var fn = function(a){
return a
}

//ES6
let fn = a => a

箭头函数使用规则

  1. 若函数没有参数,则不能省略()

    ​ ①若函数体只有一句代码,则可以省略{},

    ​ 若不是return语句,但是需要注意在语句前添加void;

    ​ 若是return语句,则直接省略return即可

    ②若函数体有多条语句组成,咋不能省略{ }

  2. 有参数函数

    ①有一个参数,可以省略() 其余和以上类似

    ②多个参数,不能省略( )

    ③若函数体只有一句return语句,且返回值为对象,则使用()包裹,若返回值为数组则不需要处理

1.无参函数

javascript
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
//无参函数
//1)函数体只有一条语句
let fn1 = function() {
console.log('hello world')
}
//箭头函数
let fn1 = () => void console.log('hello world') //void标识该代码没有返回值

//2)无参,函数体只有一句代码,return语句
let fn2 = function(){
return 'hello world'
}
let fn2 = () => 'hello world'

//3)无参,函数体有多条语句
let fn3 = function(){
conssole.log('hello')
conssole.log('javascript')
conssole.log('python')
}

let fn3 = () => {
conssole.log('hello')
conssole.log('javascript')
conssole.log('python')
} //不能省略(),函数体有多条语句
  1. 有参函数
javascript
1
2
3
4
5
6
7
8
9
10
//有参函数
let fn4 = function (x){
console.log('hello')
}
let fn4 = x=> void console.log('hello')
let fn4 = x => 'hello'
let fn4 = x => {
console.log('hello')
return 'hello'
}
javascript
1
2
3
4
5
6
7
8
9
let fn5 = function(x,y){
console.log(x)
console.log(x+y)
}

let fn5 = (x,y)=>{
console.log(x)
console.log(x+y)
}

示例:

javascript
1
2
3
4
5
6
7
8
9
10
//函数体,有一句代码,且是return语句
let fn = function(name,age){
return {
name: name,
age: age
}
}

//简化,若函数返回值为对象,有歧义,若返回对象,则使用()包裹起来
let fn = (name,age)=>({name:name,age:age})

特殊情况:

javascript
1
2
3
4
5
6
let fn = function(x){
name:x
}

let fn = x => {name:x} //不会报错,但是返回值为undefined
console.log(fn('小黑'))

被定义为此种函数,返回值无

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
//对某段代码进行命名
tag:for(let i =0;i<3;i++){
for(let j=0;j<4;j++){
console.log('i='+i+"j="+j)
if(i==1){
break//结束当前循环(最近这个循环)
}
//希望使用break语句结束整个循环
if(p==1){
break tag //结束指定的循环
}
}
}

箭头函数和解构

数组解构:

javascript
1
2
3
4
5
6
7
let fn = function([a,b,c]){
console.log(a+b+c)
}
let fn = ([a,b,c])=>{
console.log(a+b+c)
}
fn([10,20,30])
javascript
1
2
3
4
5
6
7
//对象解构
let fn = function({name,age}){
console.log(name,age)
}

let fn = ({name,age})=>void console.log(name,age)
fn({name:'小黑',age:22})

举例:

javascript
1
2
3
4
//求圆的面积
let fn = r => Math.PI*r*r
//求长方形的面积
let fn1 = (w,h) => w*h

箭头函数注意事项

Code
1
2
3
4
5
6
箭头函数使用有几个注意事项
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
(2)不可以当做构造函数,也就是说,不可以使用new命令,否则就会抛出一个错误
(3)不可以使用arguments对象,该对象在函数体内不存在,如果要使用,可以使用rest参数代替
(4)不可以使用yield命令,因此箭头函数不能用作generator函数
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的

(1)this的问题:

​ 在普通函数中,this指向调用者

​ 在肩头函数内,this指向声明时的对象

​ 构造函数不能使用箭头函数

​ 原型中也不能使用箭头函数

在箭头函数内部不能使用arguments

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let obj = {
name:'小黑',
aga:22,
say: function(){
console.log(this)
},
sayHello:function(){
//启动一个定时器
//setTimeout(function(){
//console.log(this) //this指向window
//})
setTimeout(()=>{
conssole.log(this) //this指向当前对象
},1000)
}
}

箭头函数与回调函数

javascript
1
2
3
4
5
6
7
8
const arr = [100,200,300,10]
arr.forEach(function(item){
console,log(item)
})

arr.forEach(item =>{
console.log(item)
})
javascript
1
2
3
4
5
6
//map方法,匹配数组中的内容,做修改之后,在返回一个新的数组
let arr0 = arr.map(function(item){
return item*2
})

let arro = arr.map(item => item*2)

字符串的扩展

字符串的遍历器接口

es5遍历:

javascript
1
2
3
4
5
6
7
8
9
let var = 'hello'
//遍历字符串
for(var i=0;i<str.length;i++){
console.log(i,str[i]) //i代表索引
}

for(var i in str){
console.log(i,str[i]) //i代表索引
}

使用for ….of遍历字符串:

javascript
1
2
3
4
//使用for ...of遍历字符串
for(let i of str){
console.log(i) //i 字符串中的数据
}

字符串中新增的方法

  • includes() 判断是否包含指定字符串 布尔类型
  • startsWith 是否以指定字符串开头 布尔类型
  • endwith 是否以指定字符串结尾 布尔类型
  • repeat 返回一个新字符串,表示该字符串重复n次
  • padStart(),padEnd() es2017引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart用于头部补全,padEnd用于尾部补全 padEnd 在末尾追加内容 —-到指定的长度,padStart 在开头追加内容 —–到指定长度

es5:

javascript
1
2
3
4
5
6
7
8
// indexOf()  第一次出现的位置  没有-1
let str = '你好。非常好,very good'
let src = 'images/xxx1.jpg'
if(str.indexOf('李时珍')== -1) {
alert('没有这个字符串')
}else{
alert('有这个')
}
javascript
1
2
3
4
5
6
7
if(src.indexOf('1.jpg') != -1) {
alert('包含')
}

if(src.includes('1.jpg')){
alert('包含')
}
javascript
1
2
3
4
5
6
7
8
9
10
let str = 'hello world'

//是否以hell开头
if(str.startsWith('hell')){
str += '.你好'
}
// 是否以你好结尾
if(str.endsWith('你好')){
str += '你长得真丑'
}

padstart(),padend()例子:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
//对数字进行补零
let addZero = num =>{
if(num < 10) {
return '0' + num
}
return num
}


//使用padstart
let addZero1 = num => num.padStart(2,'0')
addZero1('1')

模板字符串:

es5字符串拼接:

javascript
1
2
3
4
5
6
let obj = {
name: '小黑',
age: 20,
addr: '大学城'
}
let str = '姓名:'+obj.name + ',年龄:'+obj.age+',地址为:'+obj.addr

ES6中的字符串模板语法:

​ 模板字符串< template string)是增强版的字符串,用反引号(“)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${ }之中。

javascript
1
2
3
4
let fn = x =>x
let x = 90
let str = `输入变量x,输出结果为:${fn(x)}`
console.log(str)

对象扩展

如何定义对象:

ES5中

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = new Object()
obj.name = '小黑'
obj.age = 22
obj.say = function(){
console.log('你好 小黑!!!')
}

// 字面量的形式
var obj1 = {
name: '小黑',
age: 22,
say:function(){
console.log('你好 小黑!!!')
}
}

注:字面量的形式,一次性只能创建一个对象

ES6中,针对这种方案(属性名与值对应的变量名是一致的,可以省略变量名)

javascript
1
2
3
4
5
6
7
let obj1 = {
name,
age,
say:function(){
console.log('dawdaw')
}
}

使用前提,已经定义过当前属性名的变量值

在对象内部可以简写函数

javascript
1
2
3
4
5
6
7
8
var name = '小黑',age = 22
let obj1 = {
name,
age,
say(){
console.log('dad')
}
}

举例:

javascript
1
2
3
4
5
6
7
8
//实现一个函数完成:接受两个参数,将该参数封装为对象之后返回
let fn = (x,y) => {
return {
x:x,
y:y
}
//简化 return {x,y}
}

class 类

javascript
1
2
3
4
5
6
7
8
9
10
11
//在ES5中批量产生对象
function Person(name,age){
this.name = name,
this.age = age
}
Person.prototype.say = function(){
console.log(`${this.name}在说话00`)
}

let p = new Person('小黑',22)
p.say()

ES6提供了更接近传统语言的写法,引入了Class (类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6的class可以看作只是一一个语法糖,它的绝大部分功能, ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Perso {
// Person为类名
// 添加属性 构造器
// 当创建实例对象时,会自动执行constructor(),为类添加属性
constructor(name,age){
this.name = name,
this.age = age
console.log('执行了')
}//每一个类都必须有一个构造器,如果没有显示声明,则系统会自动添加一个屋参的构造器

//添加方法
say(){
console.log(this.name+"dadad")
}
}
let p = Perso('小黑',22) //实例化

基本语法

constructor
Code
1
2
3
4
5
6
7
8
9
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor 方法,如
果没有显式定义,-个空的constructor方法会被默认添加。
Class Point {}
/等同于
class Point {
constructor() {}
}
constructor方法默认返回实例对象(即this) , 完全可以指定返回另外一个对象。
类必须使用new调用,否则会报错。这是它跟普通构造函数的一-个主要区别,后者不用new也可以执行。
class表达式
Code
1
2
3
4
5
6
7
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me .name
}
};
上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me, Me只在Class的内部代码可用,指代当前类。.
class注意事项

*ES5中,构造函数可以有变量提升

ES6 中,类不存在变量提升,先定义在使用**

class的静态方法
Code
1
2
3
4
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会
被实例继承,而是直接通过类来调用,这就称为“静态方法”。
如果静态方法包含this关键字,这个this指的是类,而不是实例。
父类的静态方法,可以被子类继承。.

静态方法只能对象自身调用,不能实例化调用

例子:

javascript
1
2
3
4
5
6
7
8
class Tool {
static config(){
return {
host: '127.0.0.1',
port: '27017',
}
}
}

set和get访问器

Code
1
2
3
4
5
6
7
8
9
10
11
与ES5一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
Class MyClass {
constructor() {
}
get prop() {
return” getter' ;
}
set prop(value) {
console.log('setter: ' +value);
}}
let inst = new MyClass();

setter访问器 :弱对属性进行了设置,则会执行getter访问器

getter访问器:若取出当前属性的值,则会执行setter访问器

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
constructor(id,name,age){
this.id =id,
this.name = name,
this.age = age
}
set age(myAge){
console.log('执行了set')
this._age = myAge
}
get age(){
console.log('执行了 get')
return this._age
}
}

let p = new Person(132123,'小黑',22)
p.age = 80
console.log(p.age)

继承

ES5完成继承:

javascript
1
2
3
4
5
6
7
8
//继承父类的方法
//方法一:遍历父类的原型,将所有方法都放入学生原型
for(var i in Person. prototype){
Student. prototype[i ]=Person. prototype[i];
}

//方法二
Student.prototype = new Person()

ES6中继承:

继承是单向的,父类只有一个,子类可以有很多个

父类:基类

子类:派生类

javascript
1
2
3
4
5
6
//Class可以通过extends关键字实现继承,这比ESS的通过修改原型链实现继承,要清晰和方便很多。
Class Point {}
Class ColorPoint extends Point {}
//子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,
//必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和
//方法。如果不调用super方法,子类就得不到this对象。
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
constructor(name,age){
this.name = name,
this.age = age
}
}

class Student extends Person {
constructor(name,age) {
//this.name = name,
//this.age = age
super(name,age) //调用父类的构造函数
this.no = no
}
}

JS异常处理

Code
1
2
3
4
5
6
7
8
9
10
11
12
			JavaScript try-catch语句(错误处理)
错误处理在处理程序设计中的重要性是毋庸置疑的,任何有影响力的veb应用程序都需要-套完善的错误处理机制。当然,
大多数佼佼者确实做到了这一-点,但通常只有服务器端应用程序才能做到如此。实际上,服务器端团队往往会在错误处理机制
上投入较大精力,通常要考虑按照类型、频率,或者其他重要的标准对错误进行分类。这样一来,开发人员就能够理解用户在
使用简单数据库查询或者报告生成脚本时,应用程序可能会出现的问题。
虽然客户端应用程序的错误处理也同样重要,但真正受到重视,还是最近几年的事。实际上,我们要面对这样-一个不争的
事实:使用web的绝大多数人都不是技术高手,其中甚至很多人根本就不明白浏览器到底是什么,更不用说让他们说喜欢哪一
个了。每个浏览器在发生JavaScript错误时的行为都或多或少有一些差异。 有的会显示小图标,有的则什么动静都没有,浏览
器对JavaScript错误的这些默认行为对最终用户而言,毫无规律可循。最理想的情况下,用户遇到错误搞不清为什么,他们会
再试着重做一次,最糟糕的情况下,用户会恼羞成怒,-去不复返了。良好的错误处理机制可以让用户及时得到提醒,知道到
底发生了什么事,因而不会惊慌失措。为此,作为开发人员,我们必须理解在处理JavaScript错误的时候,都有哪些手段和工
具可以利用。

try…..catch

javascript
1
2
3
4
5
6
7
8
try{
console.log('try 语句')
fn()
console.log('fn()执行了')
} catch (error) {
console.log(error)
}
//很严重的语法错误会直接报错,不会被catch捕捉到

finally子句:

​ 虽然在try-catch语句中是可选的,但finally子句- -经使用,其代码无论如何都会执行。换句话说,try 语句块中的代码全部正常执行,finally 子句会执行;如果因为出错而执行了catch语句块,finally 子句照样还会执行。只要代码中包含finally 子句,则无论try或catch语句块中包含什么样的代码–甚 至returm语句,都不会阻止finally子句的执行。来看下面这个函
数:

javascript
1
2
3
4
5
6
7
8
9
10
try{
let arr = [13,13,123,123,12,31,23]
arr.forEach(item => {
console.log(item + i)
})
} catch (error) {
console.log('发现错误',error)
} finally {
console.log('天下第一')
}

错误类型:

Error EvalError RangeError ReferenceError SyntaxError TypeError URIError

Code
1
2
3
4
5
6
其中, Error是基类型,其他错误类型都继承自该类型,因此,所有错误类型共享了- -组相同的属性(错误对象中的方法全是默
认的对象方法》。Error类型的错误很少见,如果有也是浏览器抛出的;这个基类型的主要目的是供开发人员抛出自定义错误。
EvalError.类型的错误是在使用eal()函数而发生异常时抛出。ECMA-262中对这个错误有如下描述:“表示全局函数eval的
使用方式与其定义不相行。"除此之外,并没有救到底什么情况会引发这种错误给出说明。在实际开发中碰到这种错误的可能性
并不大。
RangeError.类型的错误会在数值超出相应范围时触发。例如,在定义数组时,如果指定了数组不支持的项数(如-20或Number.MAX_ _VALUE) ,就会触发这种错误。
Code
1
2
3
4
5
6
7
当try-catch语句中发生错误时,浏览器会认为错误已经被处理了。因而不会通过前面讨论的机制记录或报告错误。对于那些不要求用户懂技术,也不需要用户理解错误的Web应用程序,这应该说是个理想的结果。不过, try-catch 能够让我们实现自己的错误处理机制。
使用try-catch最适合处理那些我们无法控制的错误,假设你在使用-一个大型的JavaScript库中的函数,该函数可能会有意无
意地抛出- -些错误。由于我们不能修改这个库的源代码,所以大可将对该函数的调用放在try-catch 语句当中。万-有什么错
误发生,也好恰当地处理它们。
在明明白白地知道自己的代码会发生错误时,再使用try-catch语句就不太合适了●例如,如果传递给函数的参数是字符串而
非数值,就会造成函数出错,那么就应该先检查函数的类型,然后再决定如果去做。在这种情况下,不应该使用try-catch语
句。
Code
1
2
3
4
5
6
7
8
9
10
11
5. try-catch语句执行顺序
看下面的例子:
try {
throw "test";
} catch (ex) {
console. log(ex);
} finally {
console. log('finally');
//test
//finally
执行顺序为:首先执行try语句块中的代码,如果抛出异常,接着执行catch语句块中代码,如果没有异常,catch语句块中代码将会被忽略,但不管是否有异常,最后最会执行finally子句.try后面必须接着-一个catch或者fnally ,也就是说JavaScript中的try-catch可以有3中组合形式。即try-catch. try-Finally、 try-catch-Finally 三种形式。

promise

Code
1
2
3
4
5
Promise的含义
Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件 --更合理 和更强大。它由社区最早提出和实现,
ES6将其写进了语言标准,统一了用法,原生提供了Promise对象
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果o从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统- - 的API,各种异步操作都可以用同样的方法进行处理。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外, Promise对象提供统一的接口,使得控制异步操作更加容易。

异步:同时执行,并发

javascript
1
2
3
4
5
6
7
8
9
setTimeout(()=>{
console.log('helo 11')
},30)
setTimeout(()=>{
console.log('helo 22')
},50)
setTimeout(()=>{
console.log('helo 33')
},10)

同步:按顺序执行

javascript
1
2
3
console.log('hello 1')
console.log('hello 2')
console.log('hello 3')

异步队列:注意回调地狱

javascript
1
2
3
4
5
6
7
8
9
10
//解决异步队列问题
setTimeout(()=>{
console.log('11')
setTimeout(()=>{
console.log('22')
setTimeout(()=>{
console.log('33')
},0)
},30)
},50)

promise对象:

​ Promise对象:

​ 1)new Promise()对象的时候需要将一个函数作为参数传递过去

​ 2)作为参数的函数,需要接收两个参数,resolve,reject,这两个参数自身又是函数

​ 3)Promise对象有三个状态

​ pending:进行中

​ fullfilled: 已成功

​ rejected: 已失败

​ 状态的转换有两种途径:

​ pending ———> fullfilled 统一描述为:resolved

​ pending ———> rejected 统一描述为:rejected

常见方法

​ catch()

​ then()

​ finally()

基本语法:

Code
1
2
3
4
5
6
7
8
Promise实例生成以后,可以用then方法分别指定resolved状态rejected状态的回调函数。
promise.then(function(value) {
success
}, function(error) {
failure
});
then方法可以接受两个回调函数作为参数。第一一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个
函数都接受Promise对象传出的值作为参数。

举例:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise = new Promise((resolve,reject)=>{
//发起异步操作
// 成功完毕
resolve(10); //resolved
//失败
//reject('error') //rejected
})

promise.then(data => {
console.log(data)
},err => {
console.log(err)
})
console.log(promise)

promise使用:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const p = new Promise((resolve,reject) => {
setTimeout(()=>{
// console.log('11') //异步操作
// resolve('成功了')
reject(JSON.stringify({
code: 401,
msg: '异常错误'
}))
})
})
p.then(data => {
console.log(data)
},err => {
console.log(err)
})
  • 依次输出:settimeout 11 30毫秒
  • settimeout 22 50毫秒
  • settimeout 33 0毫秒 用promise实现
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let fn1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('11')
resolve()
}, 30)
})
}

let fn2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('22')
resolve()
}, 50)
})
}

let fn3 = () => {
setTimeout(() => {
console.log('33')
}, 0)
}
fn1().then(fn2).then(fn3)

promise发起ajax请求:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const promise = new Promise((resolve,reject)=>{
//发起ajax请求
let xhr = new XMLHttpRequest()
xhr.open('GET','https://douban.uieee.com/v2/movie/coming_soon')
xhr.onreadystatechange = function(){
if(this.status==200 && this.readyState ==4){
resolve(this.responseText)
}
}
xhr.send(null)
})

promise.then(data =>{
console.log(JSON.parse(data))
})

封装ajax,用promise:

javascript
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
//es5版
function ajax(method, url, fn, params) {
let xhr = new XMLHttpRequest()
if (method == 'GET' && params != undefined) {
//有参
url = url + '?' + params
}
xhr.open(method, url, true)
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
fn(this.responseText); //后台信息返回,通过回调函数将结果传递给调用者
}
}
if (method == 'POST' && params != undefined) {
xhr.send(params)
} else {
xhr.send(null)
}
}
//promise版
function ajax(method, url, params) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest()
if (method == 'GET' && params != undefined) {
//有参
url = url + '?' + params
}
xhr.open(method, url, true)
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
resolve(this.responseText); //后台信息返回,通过回调函数将结果传递给调用者
}
}
if (method == 'POST' && params != undefined) {
xhr.send(params)
} else {
xhr.send(null)
}
})
}

模块化

Code
1
2
3
4
5
6
7
8
9
10
概述
历史.上, JavaScript一直没有模块(module)体系,无法将- -个大程序拆分成互相依赖的小文件,再用简单的方法拼装
起来I其他语言都有这项功能,比如Ruby的require、Python的import,甚至就连CSS 都有@import ,但是JavaScript
任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在ES6之前,社区制定了一些模块加载方案,最主要的有CommonlS 和AMD两种。前者用于服务器,后者用于浏览器。
ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以职代CommonJS 和AMD规范,成为浏览器
和服务器通用的模块解决方案。
ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonIS和AMD
模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。
ES6模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

export: 导出

import: 导入

​ import —–from———- 从什么模块,导入

严格模式:

严格模式主要有以下限制:

  • 变量必须声明后再使用
  • 函数的参数不能有同名属性,否则报错
  • 不能使用with语句
  • 不能对只读属性赋值,否则报错
  • 不能使用前缀0表示八进制数,否则报错
  • 不能删除不可删除的属性,否则报错
  • 不能删除变量delete prop,会报错,只能删除delete global[prop]
  • evalarguments不能被重新赋值
  • arguments不会自动反映函数参数的变化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局对象
  • 不能使用fn.callerfn.arguments获取函数调用的堆栈
  • 增加了保留字(比如protectedstaticinterface
文章作者: 止戈-
文章链接: http://vchamps.cn/2020/03/07/es6%E7%9A%84%E5%AD%A6%E4%B9%A0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ever-Wu