ES6学习(一)

ECMAScript 学习

通过我们说的 ES6 泛指 ES5 之后的下一代标准,涵盖了 ES6, ES7, ES8…

ES5-数组的新方法

forEach

forEach方法对数组的每个元素执行一次提供的函数。功能等同于for循环.

应用场景:为一些相同的元素,绑定事件处理器!

var arr = ['张飞', '关羽', '赵云', '马超']
//第一个参数:item,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//常用的参数就第一个和第二个,第三个参数基本上没啥用
arr.forEach(function(item, index, array) {
  console.log(item, index, array)
})
// 箭头函数写法----箭头函数说明在后面
arr.forEach((item, index, array) => {
  console.log(item, index, array)
})
// 优点 1. 不占用全局变量
// 优点 2: 结合箭头函数使用

map

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。返回的数组的长度和原数组是一样的

var arr = [1, 2, 3, 4, 5] // 1 4 9 16 25
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:一个新数组,每个元素都是回调函数的结果。
var newArray = arr.map(function(element, index, array) {
  return element * element
})
console.log(newArray) //[1,4,9,16,25]

// 箭头函数写法
var newArray = arr.map(element => element * element)

filter

filter用于过滤掉“不合格”的元素
返回一个新数组,如果在回调函数中返回 true,那么就留下来,如果返回 false,就扔掉,因此返回的数组的长度和原数组的长度不一定一致

var arr = [1000, 5000, 20000, 3000, 10000, 800, 1500]
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:一个新数组,存储了所有返回true的元素
var newArray = arr.filter(function(element, index, array) {
  if (element > 5000) {
    return false
  } else {
    return true
  }
})
console.log(newArray) //[1000, 5000, 3000, 800, 1500]

// 箭头函数写法
var newArray = arr.filter(element => element > 5000)

some

some用于遍历数组,如果有至少一个满足条件,就返回 true,否则返回 false。同样的每一个元素都会执行一遍 function,相当于最后返回的结果进行了一个 ||运算,全为 false 才是 false

var arr = [2, 4, 6, 8, 10, 21] // 判断数组是否包含奇数
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:布尔类型的值,只要有一个回调函数返回true,就返回true
var flag = arr.some(function(element, index, array) {
  console.log(element, index, array)
  if (element % 2 == 1) {
    return true
  } else {
    return false
  }
})
console.log(flag) //true

// 箭头函数写法
var flag = arr.some(element => element % 2 == 1)

every

every用于遍历数组,只有当所有的元素返回 true,才返回 true,否则返回 false,每个元素执行完 function 返回的结果做一个 && 运算,全为 true 结果才是 true

var arr = [2, 4, 6, 8, 10, 21] //判断数组是否都是偶数
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:布尔类型的值,只有当所有的元素返回true,才返回true,否则返回false。
var flag = arr.every(function(element, index, array) {
  console.log(element, index, array)
  if (element % 2 == 0) {
    return true
  } else {
    return false
  }
})
console.log(flag) //false

// 箭头函数写法
var flag = arr.some(element => element % 2 == 0)
  • forEach: 作用:只会让每个元素执行一次函数, 没有别的功能 for 循环

  • map: 作用:返回一个新的数组,长度和原数组一样 新数组会保存每次 function 返回的值

  • filter: 作用:得到一个新数组, 保留哪些满足条件(返回 true)

  • some: 作用:得到布尔值 只要有函数返回 true,整体结果就是 true

  • every: 作用:得到布尔值,要所有的函数都返回 true,结果就是 true

ES6 常用点

变量声明

ES6 中提供了两个声明变量的关键字:const 和 let

注意:

在ES5中var function 声明的全局变量都是顶级对象(window)的属性,而ES6中声明的全局变量不属于顶级对象的属性

var age = 18
console.log(this.age)  // 18

let age = 18
console.log(this.age)  // undefined
let 使用

ES6 新增了let命令,用来声明变量。它的用法类似于var

  • let 声明的变量只有在当前作用域有效
  • 不存在变量提升
  • 不允许重复声明
  • 块级作用域

简单的理解就是 { } 一对花括号之间就是一个块级作用域,在这个块级作用域外是无法访问内部用 let 声明的变量

{
  let a = 10
  var b = 1
}

a // ReferenceError: a is not defined.
b // 1

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

let a = 10
let a = 1 //报错 Identifier 'a' has already been declared

for循环的计数器,就很合适使用let命令。

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

console.log(i)
// ReferenceError: i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。

下面的代码如果使用var,最后输出的是10

var a = []
for (var i = 0; i < 10; i++) {
  a[i] = function() {
    console.log(i)
  }
}
a[6]() // 10

上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = []
for (let i = 0; i < 10; i++) {
  a[i] = function() {
    console.log(i)
  }
}
a[6]() // 6

上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6

for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc'
  console.log(i)
}
// abc
// abc
// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123

if (true) {
  tmp = 'abc' // ReferenceError
  let tmp
}

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if (true) {
  // TDZ开始
  tmp = 'abc' // ReferenceError
  console.log(tmp) // ReferenceError

  let tmp // TDZ结束
  console.log(tmp) // undefined

  tmp = 123
  console.log(tmp) // 123
}

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

const 使用

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

  • 如果 const 声明了一个对象,仅仅保证地址不变
const obj = {name:'zs'};
obj.age = 18;//正确
obj = {};//报错

如果真的想将对象冻结,应该使用Object.freeze方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
  • 其他用法和 let 一样
1. 只能在当前代码块中使用
2. 不会提升
3. 不能重复
let 与 const 的使用场景
1. 如果声明的变量不需要改变,那么使用const
2. 如果声明的变量需要改变,那么用let
3. 学了constlet之后,尽量别用var

解构赋值

数组解构

其实字符串也是可以解构赋值的,字符串被转换成了一个类似数组的对象,类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

ES6 允许写成下面这样。

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

// ...tail这个写法是ES6中函数优化增加的rest参数,表示剩余的所有参数,后面有说明
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

对于 Set 结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"

解构默认值

let [a = 0, b, c] = [1, 2, 3]
对象解构

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

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

如果变量名与属性名不一致,必须写成下面这样。

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

let obj = { first: 'hello', last: 'world' }
let { first: f, last: l } = obj
f // 'hello'
l // 'world'

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

function add([x, y]) {
  return x + y
}

add([1, 2]) // 3

;[[1, 2], [3, 4]].map(([a, b]) => a + b)
// [ 3, 7 ]
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' }

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' }
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

默认值生效的条件是,对象的属性值严格等于undefined

let { x = 3 } = { x: undefined }
x // 3

let { x = 3 } = { x: null }
x // null

上面代码中,属性x等于null,因为nullundefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。

解构赋值的用途

具体用途可以看阮一峰的《ECMAScript 入门》一书

http://es6.ruanyifeng.com/#README

  • 交换变量的值
  • 从函数返回多个值
  • 函数参数的定义
  • 提取 JSON 数据
  • 函数参数的默认值
  • 遍历 Map 结构
  • 输入模块的指定方法

字符串

模版字符串

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

$('#result').append(
  'There are <b>' +
    basket.count +
    '</b> ' +
    'items in your basket, ' +
    '<em>' +
    basket.onSale +
    '</em> are on sale!'
)

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

$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`)

字符串模版的优点

  • 允许换行
  • 可以使用插值 ${}
字符串方法
  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

数组

find

find 是 ES6 新增的语法

find()  方法返回数组中满足提供的测试函数的第一个元素的值。否则返回  undefined

// 获取第一个大于10的数
let array1 = [5, 12, 8, 130, 44]

let found = array1.find(function(element) {
  return element > 10
})
console.log(found)
findexIndex

findIndex 是 ES6 新增的语法

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

// 获取第一个大于10的下标
let array1 = [5, 12, 8, 130, 44]

function findFirstLargeNumber(element) {
  return element > 13
}

console.log(array1.findIndex(findFirstLargeNumber))

函数

ES6 标准新增了一种新的函数:Arrow Function(箭头函数)。

基本使用

let fn = function(x, y) {
  console.log(x + y)
}

相当于
//语法: (参数列表) => {函数体}
let fn = (x, y) => {
  console.log(x + y)
}

参数详解

  • 如果没有参数列表,使用()表示参数列表
  • 如果只有一个参数,可以省略()
  • 如果有多个参数,需要使用()把参数列表括起来
// 没有参数
let sum = () => {
  console.log('哈哈')
}
// 有一个参数
let sum = n1 => {
  console.log('哈哈')
}
// 有多个参数
let sum = (n1, n2) => {
  console.log('哈哈')
}
返回值详解
  • 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来
  • 如果函数体只有一行一句,那么可以省略{}和return
var fn = (n1, n2) => n1 + n2;
箭头函数的注意点
  1. 箭头函数内部没有this,因此箭头函数内部的this指向了外部的this
  2. 箭头函数不能作为构造函数,因为箭头函数没有this
// 箭头函数内部没有this
const obj = {
    name: 'zs',
    age: 18,
    // 以后碰到了this的指向问题
    sayHi: function() {
        // 1. let that = this
        let that = this
        setInterval(function() {
            console.log(`大家好,我是${that.name},我今年${that.age}`)
        }, 1000)
    },
    sayHi1: function() {
        // 2. 使用bind(this)
        setInterval(
            function() {
                console.log(`大家好,我是${this.name},我今年${this.age}`)
            }.bind(this),
            1000
        )
    },
    sayHi2: function() {
        setInterval(() => {
            // 3. 直接使用箭头函数
            console.log(`大家好,我是${this.name},我今年${this.age}`)
        }, 1000)
    }
}
rest参数
// rest参数  剩余参数
// arguments: 没有定义参数, 别人用的时候不知道传什么参数
// es6中新增了一个 rest参数, 剩余参数
// 剩余参数必须是最后一个参数
const add = function(num, ...colors) {
    // console.log(arguments)
    console.log(colors) // 2,3,4
    // 如果没有num这个参数,那么输出的就是1,2,3,4
}
add(1, 2, 3, 4)

对象

属性的简写

ES6中,如果对象的属性名 和 属性值的变量相同, 可以省略一个

let page = 1
let pageSize = 5

data:{
	page:page,
    pageSize:pageSize
}
// 简写成
data:{
	page,
    pageSize
}
方法的简写

对象中的方法 可以省略 :function

const o = {
  method: function() {
    return "Hello!";
  }
};

// 简写成
const o = {
  method() {
    return "Hello!";
  }
};
展开运算符( … )
  • 展开一个数组
const arr = [1, 2, 3]
console.log(...arr)

const arr = [1, 2, 3]
const arr2 = [4, 5, 6]
const newArr = [...arr, ...arr2, 5, 6, 7, ...arr]
console.log(newArr)

const arr = [1, 2, 3, 5, 4, 7, 6]
const result = Math.max.call(null, ...arr)
console.log(result)

 const result = Math.max(...arr)
 console.log(result)
  • 展开一个对象
const obj = {
    name: 'zs',
    age: 18,
    gender: '男'
}

const obj1 = {
    money: 100,
    house: '房子'
}

const obj2 = {
    ...obj,
    ...obj1,
    sayHi() {
        console.log('哈哈')
    }
}
console.log(obj2)
Set 对象
 // Set类型和数组非常的像,  set中的数据不允许重复,Set集合的意思,集合中不允许有重复
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
arr = [...new Set(arr)]
console.log(arr)
// 接受一个数组,但是不会有重复的
const s = new Set(arr)
console.log([...s])

伪数组转为数组

let inputs = document.querySelectorAll('ul input')  // DOM对象的伪数组
// 方法一  借调slice方法
let arr = [].slice.call(inputs,0) // 转换成了一个真数组

// 方法二 展开运算符
let arr = [...inputs] // 转换成了真数组

Promise对象

Promise 是异步编程的一种解决方案,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

Promise对象有以下两个特点。

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

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

注1: Promise任然是一个异步操作,

const p = Promise.resolve('Hello');

p.then(function (s) {
    console.log(s)
});
console.log('1111')

// 输出顺序
// 1111
// Hello

注2:Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。
// one
// two
// three

Promise的基本使用

基本语法

/*
	参数是两个函数
	调用resolve函数来将promise状态改成fulfilled,
	调用reject 函数将promise的状态改为rejected。
	如果在executor函数中抛出一个错误,那么该promise 状态为rejected
*/
const promise = new Promise((resolve, reject) => { 
    /* 常常是一些异步的操作 */ 
    if(/*成功*/){
        resolve('success')
    }else{
        reject('error')
    }
})

// 这时候异步操作的结果就会存在promise这实例对象中
/*
	Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用
	(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。	当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,
	当Promise状态为rejected时,调用 then 的 onrejected 方法)
*/
/*
	第一个回调函数是Promise对象的状态变为resolved时调用,
	第二个回调函数是Promise对象的状态变为rejected时调用。
	第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。	
*/
promise.then(function(result){
    // 执行fulfilled状态的
},function(err){
    // 执行rejected状态的
})

调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail

p1p2都是 Promise 的实例,但是p2resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。

注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

采用链式的then

可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

这是一个读写文件的链式then,读取数据后,把读取的数据在写入一个新文件中

const fs = require('fs')
const path = require('path')

const filepath = path.join(__dirname, '1.txt')
const filepath1 = path.join(__dirname, '2.txt')

function writeData(data) {
    const p1 = new Promise((resolve, reject) => {
        fs.writeFile(filepath1, data, err => {
            if (err) return reject(err)
            return resolve('success')
        })
    })
    return p1
}

function readData() {
    const p = new Promise((resolve, reject) => {
        fs.readFile(filepath, 'utf8', (err, data) => {
            if (err) return reject(err)
            return resolve(data)
        })
    })
    return p
}

readData().then(data => writeData(data))
    .then(res => console.log(res))
    .catch(err => console.log(err))

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

// 改写上面的promise
promise.then(function(result){
    // 执行fulfilled状态的
},function(err){
    // 执行rejected状态的
})

// 改写成
promise.then(result => { })// 执行fulfilled状态的
    .catch(err =>{})// 执行rejected状态的

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

Promise.prototype.finally() finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

再次改写

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

Promise.try() 实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误

注: try 不是实例上的方法

Promise.try(readData())
	.then(result => {···})
	.catch(error => {···})
	.finally(() => {···});

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

上面代码中,Promise.all方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

p的状态由p1p2p3决定,分成两种情况。

  • 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

  • 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

  • 32
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值