这篇笔记是对ES6中的数组、函数、对象、类、promise和async/await的学习心得。
// 重复申明 变量提升
var命令会发生”变量提升“现象
let不允许在相同作用域内,重复声明同一个变量; 变量不提升
const 的变量不可以被修改,也不能重复申明
使用const的对象可以修改,因为const内部是依靠指针来判断一个对象是否被修改
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,
所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。
如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
[1,2,-3,-4].findIndex(function(n){
return n<0
})// 2
var a = new Array(5)
console.log(a) // [ <5 empty items> ]
a.fill(0)
console.log(a) //[ 0, 0, 0, 0, 0 ]
// ES5方法实现
// 需要借助apply和map方法
var array = Array.apply(null, new Array(5))
.map(function(){
return 0
})
console.log(array) // [ 0, 0, 0, 0, 0 ]
// ES6方式实现
const array = new Array(2).fill(0)
Array.from([array-like])=>[x,x,x]
用于将一个array-like object转换为数组
什么是array-like?
例子:js中的参数对象arguments就是一个array-like object我们可以通过[]来访问其中的元素,
也可以使用length。但是不能使用array对象的方法
// 新建一个array-like object
var a = {}
var i = 0
while(i<10) {
a[i]=i*i
i++
}
a.length = i
// console.log(a)
/* { ‘0‘: 0,
‘1‘: 1,
‘2‘: 4,
‘3‘: 9,
‘4‘: 16,
‘5‘: 25,
‘6‘: 36,
‘7‘: 49,
‘8‘: 64,
‘9‘: 81,
length: 10 } */
// 在es5中可以使用array.prototype.slice方法来将array-like对象转换成真正的数组,
// 不便的是如果要转换一个现有的object
// 通常还要调用call()方法,否则返回的是一个空数组
// ES5
var al = Array.prototype.slice.call(a); // 需要增加call()方法
al.push(10)
console.log(al) // [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 10 ]
// ES6
var a2= Array.from(a); // form方法不是定义在prototype上的
a2.push(10)
console.log(a2) // [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 10 ]
ES6提供三个新的方法 entries, keys 和values用于遍历数组。
// 区别
// keys是对键名的遍历 values是对键值的遍历 entries是对键值对的遍历
var a = [‘a‘, ‘b‘, ‘c‘]
for(let i of a.keys()) {
console.log(i) // 0 1 2
}
for(let i of a.entries()) {
console.log(i)
}
// [ 0, ‘a‘ ]
// [ 1, ‘b‘ ]
// [ 2, ‘c‘ ]
// 参数的默认值
// 如果在参数重使用了默认值,那么就不能再方法体内再使用let关键字申明同名的变量
function greed(x=‘Hello‘, y=‘Node‘) {
console.log(x, y)
}
greed() // Hello Node
// spread运算符
// 合并数组,在ES5中通常调用concat方法来实现
var arr =[1,2,3]
var arr2=[4,5]
console.log([...arr, ...arr2]) // [ 1, 2, 3, 4, 5 ]
// 将字符串转换为数组
var name=[...‘learing‘]
console.log(name) // [ ‘l‘, ‘e‘, ‘a‘, ‘r‘, ‘i‘, ‘n‘, ‘g‘ ]
// ...也可以作为函数的参数,表示该函数有多个参数,也可以在函数调用时使用
function func(x,y,z){
return x+y+z
}
var args = [1,2,3]
func(...args) // 6
优点
箭头函数 可以修复 this作用域
setTimeout中匿名函数的this的指向,node和浏览器是不同的
// 匿名函数中的this
/* var obj={}
obj.func = function() {
console.log(this)
} */
function foo() {
setTimeout( function(){
console.log(this)
}, 1000);
}
foo()
// 浏览器中是Windows 在node中输出的是一个timeout对象
// 使用箭头函数后,this的指向和foo函数内部相同
// 箭头函数自动绑定this, 实际上匿名函数函数内部没有定义this,仅是引用外面一层的this而已。
// 弄清匿名函数内部的this指向
function foo() {
this.name=‘lear‘
setTimeout(() => {
console.log(this)
console.log(‘name--‘, this.name) // lear
}, 100);
}
// foo()
// 箭头函数本身没有定义this,在箭头函数内部使用this关键字时,
// 它开始在代码定义的位置向上找,直到遇见第一个this,
// 这带来了很大的便利,但是在构造函数中会出现问题
function Person() {
this.name =‘lear‘
}
Person.prototype.greet = ()=> {
console.log(this.name) // undefined
}
var person =new Person()
person.greet()
// 出现undefined是因为 箭头函数绑定了上一层的环境,就是全局环境中的this
// 这段代码在浏览器中运行时this是Windows
// 不建议在构造函数中使用箭头函数
Set 的实现类似于数组,和普通数组的不同在于Set中不能包含重复的数据
/* var set= new Set([1,2,3,4,5]) // 使用够构造函数初始化一个set
set.add(6) //添加
set.delete(5) // 删除
console.log(set.has(6)) // true
console.log(set)
for(var i of set) {
console.log(i)
}
set.clear() // 清除所有元素
console.log(set) // {}
*/
let set = new Set([1,2,3])
for(let i of set.keys()) {
console.log(i) // 1 2 3
}
for(let i of set.values()) {
console.log(i) // 123
}
for(let i of set.entries()) {
console.log(i)
}
// [ 1, 1 ]
// [ 2, 2 ]
// [ 3, 3 ]
Map 表示键值对组成的有序集合
有序表现在map的遍历顺序即为插入顺序
var obj = {‘c‘:3}
var map= new Map([[‘a‘,1], [‘b‘,2],[obj,3]])
console.log(map.size) // 3 map的大小
console.log(map.has(‘a‘)) // true 判断是否存在键值对
console.log(map.get(‘a‘)) // 获取摸个键值对的值
map.set(‘d‘, 4) // 如果键值不存在,则增加新的键值对,否者覆盖原有的值
map.delete(‘d‘) // 删除 返回布尔值
// 遍历map
for(let key of map.keys()) {
console.log(key)
}
// a
// b
// { c: 3 }]
// 浅拷贝
var obj1 = {a: {b:1}}
var obj2 = Object.assign({}, obj1)
obj1.a.b=2
// console.log(obj2.a.b) // 2
// 对象的遍历
var obj={
‘name‘: ‘lear‘,
‘age‘: 10,
‘sex‘: ‘male‘
}
// 1.使用for in 遍历
for(var key in obj) {
// console.log(‘key‘, key, obj[key] )
}
// 2.object.keys() 遍历
console.log(Object.keys(obj)) // [ ‘name‘, ‘age‘, ‘sex‘ ]
// ES5
// 在js中,类的所有实例对象都从同一个原型对象上继承属性
/* function Person (sex, age) {
this.sex =sex
this.age= age
}
Person.prototype.getInfo =function(){
return this.sex+‘,‘+this.age
}
var person =new Person(‘man‘, ‘10‘)
console.log(person.getInfo()) // man,10
*/
// ES6 语法糖
class Person {
constructor(sex, age){
this.sex=sex
this.age=age
}
getInfo(){
return this.sex+‘,‘+this.age
}
}
var person=new Person(‘female‘, ‘20‘)
console.log(person.getInfo()) // female,20
// 属性和构造函数
// class中的属性定义在constructor函数(构造函数)中,构造函数负责类的初始化,
// 包括初始化属性和调用其他类方法等
// 构造函数同样支持默认值参数
// 如果申明一个类的时候没有声明构造函数,那么会默认添加一个空的构造函数。
// 构造函数只有在使用关键字new实例化一个对象的时候才会被调用
class Student{
constructor(name="lear", sex=‘male‘){
this.age=‘lear‘
this.sex=‘male‘
}
}
// 类方法
class Student{
constructor(name=‘lear‘, sex=‘male‘){
this.name=name
this.sex=sex
}
getInfo(){
console.log(‘name :‘, this.name, ‘sex :‘,this.sex)
}
}
var student=new Student()
// console.log(student.getInfo()) // name : lear sex : male
// 类方法也可以作为属性定义在构造函数中,
class Student{
constructor(name=‘lear‘, sex=‘male‘){
this.name=name
this.sex=sex
this.getInfo=()=>{ // 类方法也可以是箭头函数
console.log(‘name :‘, this.name, ‘sex :‘,this.sex)
}
}
}
ES5中,类的实现通过__proto__属性来指向构造函数的prototype对象
es6中,getinfo方法和constructor方法虽然看似是定义在类的内部,但实际上还是定义在prototype上,
侧面可以看出ES6是对class的实现依旧是基于prototype
person.constructor=Person.prototype.constructor // true
对象的__proto__属性指向类的原型
类的本质是构造函数
// 在定义类时如果定义了方法,那么该类的每个实例在初始化时都会有一份该方法的备份
// 有时候我们不希望一些方法被继承,而是希望作为父类的属性来使用,可以直接通过调用类名调用的方法,即静态方法
class Person{
static getName() {
return ‘lear‘
}
}
Person.getName() // lear
var person=new Person()
person.getName() // error
// super 关键字调用父类的静态方法
class Person{
static getName() {
return ‘lear‘
}
}
class Student extends Person{
static getName2() {
return super.getName() + ‘, Hi‘
}
}
console.log(Student.getName2()) // lear, Hi
// ES5中的继承
// 我们有一个父类Person,并且在类的内部和原型链上各定义了一个方法:
function Person(name, age){
this.name=name
this.age=age
this.greed=function(){
console.log(‘Hello, i am‘, this.name)
}
}
Person.prototype.getInfo=function(){
return this.name+‘,‘+this.age
}
// 修改原型链
// 这是最普通的继承方法, 通过将子类的prototype指向父类的实例来实现
function Student(){
}
Student.prototype=new Person()
Student.prototype.name=‘lear‘
Student.prototype.age=10
var stud=new Student()
stud.getInfo() // lear,10
// 缺点:在子类构造函数中无法通过传递参数对父类继承的属性值进行修改,只能通过修改prototype的方式进行修改。
// 构造函数继承
function Person(name, age){
this.name=name
this.age=age
this.greed=function(){
console.log(‘Hello, i am‘, this.name)
}
}
Person.prototype.getInfo=function(){
return this.name+‘,‘+this.age
}
function Student(name,age,sex) {
Person.call(this)
this.name=name
this.age=age
this.sex=sex
}
var stud=new Student(‘lear‘,10,‘male‘)
stud.greed() // Hello, i am lear
stud.getInfo() // error
promise是什么
官网解释 promise 表示一个一步操作的最终结果。
翻译 可以将promise理解为一个状态机,它存在三种不同的状态,并在某一时刻只能有一种状态
一个promise是对一个异步操作的封装,异步操作有等待完成、成功和失败三种可能的结果,对应了promise的三种状态。
promise的状态只能有pending转换位resolved或者pending转换为rejected,一旦状态转化完成就无法再改变。
假设我们用promise封了一个异步操作,那么当它被创建的时候就处于pending状态,当异步操作成功完成时,
我们将状态转换为resolved,如果执行中出现错误,将状态转换为rejected。
var promise=new Promise(function(resolve,reject){
// code
if(){
/*异步操作成功 */
resolve(value)
}else{
reject(error)
}
})
var fs=require(‘fs‘)
function readFile_promise(path){
return new Promise(function(resolve,reject){
fs.readFile(path, ‘utf-8‘,function(err,data){
if(data){
resolve(data)
}else{
reject(err)
}
})
})
}
var result=readFile_promise(‘./1.txt‘)
result.then(function(value){
//success
console.log(‘success‘, value)
},function(error){
//failure
console.log(‘failure‘,error)
})
// 将一个异步函数封装成promise,只要在回调函数中针对不同的返回结果调用resolve或者reject方法。
// resolve函数会在异步操作成功完成时被调用,并将异步操作的返回值作为参数传递到外部。
// reject是在异步操作出现异常时被调用,会将错误信息作为参数传递出去。
then方法总是返回一个新的promise对象,多次调用then方法,默认返回一个一个空的promise对象
使用return来来返回。
var promise=readFile_promise(‘./foo.txt‘)
promise.then(function(value){
//success
console.log(‘success‘, value) // foo
return readFile_promise(‘./bar.txt‘)
},function(error){
//failure
console.log(‘failure‘,error)
}).then(function(value){
console.log(‘then‘, value) // bar
})
var promise=new Promise((resolve, reject)=>{
console.log(‘begin‘)
resolve()
})
setTimeout(()=>{
promise.then(()=>{
console.log(‘end‘)
})
},5000)
// 虽然我们是通过then方法来获取promise的结果,但是promise是当then方法调用之后才执行吗?
// 开始begin 5s后end
// 运行顺序是,当promise从被创建的那一刻起就开始执行了,then方法只是提供了访问promise状态的接口,与promise的执行无关。
// 如果有多个promise需要执行,可以使用promise.all()
// 方法统一声明,改方法可以将多个promise对象包装成一个promise
// 该方法接收一个数组作为参数,数据的元素如果不是promise对象,则回先调用resolve方法转换。
// 如果中间有一个promise状态是reject,那么转换后的promise也会变成reject,并且将错误信息传给catch方法
var promises=[‘foo.txt‘,‘bar.txt‘,‘baz.txt‘]
promises.map(function(path){
// console.log(path)
return readFile_promise(path)
})
Promise.all(promises).then(function(results){
console.log(results) // [ ‘foo.txt‘, ‘bar.txt‘, ‘baz.txt‘ ] 顺序排列的
}).catch(function(err){
//
})
// 例子; 有三个文本文件需要顺序读取
var lists=[‘foo.txt‘,‘bar.txt‘,‘baz.txt‘]
var count=0;
readFile_promise(‘foo.txt‘).then(readCB).then(readCB).then(readCB);
function readCB(data){
console.log(data) // foo bar baz
if(++count>2){
return
}
return readFile_promise(lists[count])
}
await关键字后面往往是一个promise,如果不是就隐式调用promise.resolve来转换成一个promise。
await 等待后面的promise执行完成再进行下一步操作。
var asyncReadFile=async function(){
var result1=await readFile_promise(‘./foo.txt‘)
console.log(result1.toString()) // foo
}
asyncReadFile()
async函数总是会返回一个promise对象,如果return关键字后面不是一个promise,那么默认
调用promise。resolve方法进行转换。
async function asyncFunc(){
return ‘hello Node‘
}
asyncFunc().then(function(data){
console.log(data) // hello Node
})
async function asyncFunc(){
console.log(‘begin‘)
return ‘hello Node‘
}
asyncFunc().then(function(data){
console.log(data) // hello Node
console.log(‘end‘)
})
// begin
// hello
// end
await 操作符的结果是由其后面promise对象的操作结果来决定的,如果后面promise对象变为resolved,
await操作符发返回的值就是resolve的值;如果promise对象的状态变成rejected,那么await也会抛出reject的值。
async function readFile(){
var result=await readFile_promise(‘./foo.txt‘)
console.log(result) // foo
}
readFile()
// 等价于
readFile_promise(‘foo.txt‘).then(function(data){
console.log(data) // foo
})
await会等待后面的promise完成后再采取下一步动作,这意味着当多个await操作时,程序会便成完全的
串行操作。
当异步操作之间不存在依赖关系时,可以使用promise.all来实现并行。
async function readFile(){
const [result1, result2]=await Promise.all([
readFile_promise(‘./foo.txt‘),
readFile_promise(‘./bar.txt‘)
])
console.log(result1, result2) // foo bar
}
readFile()
// 等价于
function readFile(){
return Promise.all([
readFile_promise(‘./foo.txt‘),
readFile_promise(‘./baz.txt‘)
]).then((result)=>{
console.log(result) // [ ‘foo‘, ‘baz‘ ]
})
}
readFile()
原文:https://www.cnblogs.com/zhtian/p/10351895.html