ECMAscript模块

在浏览器环境中,JavaScript分为两大部分,一个是ECMAScript,一个是node api:BOM和DOM

在编写代码之前需要构建一下项目,添加一下工具。
首先用到的就是package.json:在node.js中,模块是一个库或者框架,也是一个node.js项目。node.js遵循模块化的架构,当我们创建了一个node.js项目,意味着创建了一个模块,这个模块的描述文件就是package.json
nodemon: 修改代码后自动执行代码

安装nodemon: npm install -g nodemon (-g 代表全局安装,代表所有项目无论有没安装 nodemon,都可以直接在命令行上运行,因此我们不会在项目里的 node_modules 看到 nodemon。)

let与块级作用域

在ES2015之前是没有块级作用域的,写在{ }内的变量在{ }外面也能取到

var会导致变量提升问题,let就不会

模板字符串

${这里可以传入js语句} 支持换行

带标签的模板字符串

const name = 'aaa'
const age = 10
function tage(strings, name, age) {
	//可以在这里进行一些处理
    console.log(strings, name, age) //['name:','age:']  'aaa'  10
}
const result = tags`name:${name}age:${age}`

字符串的扩展方法

const MESSAGE = 'Error: foo is undefined.'
function strFun(message) {
    //判断字符串的开头是否以xx开始
    console.log(message.startsWith('Error')) //true
    //判断字符串是否以xxx结尾
    console.log(message.endsWith('.')) //true
    //判断字符串是否包含xxx
    console.log(message.includes('foo')) //true
}
strFun(MESSAGE);

剩余参数

function func(...args) {
	console.log(args) //[1,2,3]
}
func(1,2,3)

箭头函数和this

普通函数的this指向他的调用者,而箭头函数的this指向调用者的上下文环境,箭头函数本身是没有this指向的。
这里要注意的是,这个函数的执行环境是浏览器环境还是node环境。浏览器环境因为有window对象。

const person = {
    name: 'aaa',
    sayHi: () => {
        console.log(this.name) ;
    },
    sayHiAync: function () {
        setTimeout(function() {
            console.log(this.name) ;
        }, 1000)
    }
}
person.sayHi() //aaa
person.sayHiAync() //undefined

对象字面量增强

const name = 'abc';
let obj = {
    method1() {
        console.log(111)
    },
    [name]: 'myName'
}
console.log(obj.method1()) //111
console.log(obj.abc) //myName

对象扩展方法

Object.assign(): 将多个源对象中的属性复制到一个对象目标中

const assigns = {
    a: 123,
    b: 456
};
const target = {
    a: 456,
    c: 789
}
let result = Object.assign(target, assigns) //参数1是目标对象,参数2是源对象
console.log(result) // {a: 123, b: 456, c: 789}
console.log(result === target) //true 返回的值为目标对象,并没有改变对象的地址

function funs(obj) {
    // obj.name = 'name1'
    // console.log(obj)
    let objs = Object.assign({}, obj) //新的内存地址,不会覆盖obj1
    objs.name = 'name1'
    console.log(objs)
}
const obj1 = {name: 'glob obj'}
funs(obj1)

Proxy

Proxy(代理对象)用于监视对象的操作过程,是以非侵入的方式监听
defineProperty只能监听对象的读写

const person = {
    name: 'name',
    age: 10
}
const personPoxy = new Proxy(person, {
    //监视代理目标属性的访问
    get(target, property) {
        //target为代理目标对象,外部访问的property属性名
        console.log(target, property);
    },
    //监视代理目标设置属性的过程
    set(target, property, value) {
        //target为代理目标对象,外部设置的property属性名, value为设置的属性值
        console.log(target, property, value);
        target[property] = value;
    },
    //监视代理目标删除操作
    deleteProperty(target, property) {
        //target为代理目标对象,property要删除的属性名
        console.log('delet:', property);
        delete target[property]
    }
}) //第一个参数是需要代理的目标对象, 第二个参数是代理的处理对象
console.log(personPoxy.name)
personPoxy.sex = '女';
console.log(person)
delete personPoxy['sex']
console.log(person)

//监视数组
const list = []
const proxyList = new Proxy(list, {
    //用于监视数组的写入操作
    set(target, property, value) {
        //property此时对应的是数组的下标
        console.log('set', property, value)
        target[property] = value
        return true //表示写入成功
    }
})
proxyList.push(100)

除此之外还有更多的操作监听。

Reflect

Reflect封装了对对象的底层操作,不能通过new实例化一个方法,只能调用他封装的方法
Reflect成员方法就是proxy处理对象的默认实现
意义在于提供了一套统一操作对象的api(一共13个)

const obj = {
    name: 'zzz',
    age: 10
}
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))

class类

function Person(name) {
    this.name
}
Person.prototype.say = function() {
    console.log('say')
}

class Person {
    constructor(name) {
        this.name = name
    }

    say() {
        console.log('say'+this.name)
    }

    //静态方法,用于创建Person类型的实例
    static create(name) {
        return new Person(name)
    }
}
// const p = new Person('tom')
// p.create()
const tom = Person.create('tom') //由于静态方法是挂载到类型上面的,所以静态方法的内部的this指向的是当前的类型,而不是一个新的实例对象
tom.say()

静态方法

类型中的方法一般分为实例方法和静态方法
实例方法:需要通过这个类型构造的实例对象去调用
静态方法:直接通过类型本身去调用,实例化对象无法访问,new一个实例化对象不会被继承这个静态方法

参考代码参照上面class模块

继承

class Person {
    constructor(name) {
        this.name = name
    }

    say() {
        console.log('say'+this.name)
    }
}

class Student extends Person {
    constructor(name, number) {
        //super指向父类,调用他等于调用父类的构造函数
        super(name);
        this.number = number;
    }

    hello () {
        //调用父类的方法
        super.say();
        console.log('my number is', this.number)
    }
}
const s = new Student(1414)
s.hello()

Set

Set是一个类型,通过实例可以调用。用于存放不重复的数据
Set最常用的用途是给数组去重

const s = new Set();
//add方法会返回集合对象本身,所以可以链式调用,添加过程中重复的值会被忽略
s.add(1).add(2).add(3).add(1)
s.size//获取数组长度
s.has(100) //false
s.delete(1) //true
s.clear() //清除

const arr = [1,2,1,2,4,5,7,5]
const result1 = new Set(arr)
const result2 = Array.from(new Set(arr))
const result3= [...new Set(arr)]

Map

和对象相似,但对象的键值对的键只能是字符串类型
对象中如果是非字符串类型作为键,会被toString转译。

const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{a: 1}] = 'value'
//被toString转换,object对象作为键的话全部都被转译成了'[object Object]'
console.log(Object.keys(obj)) //[ '123', 'true', '[object Object]' ]

const m = new Map()
const tom = {name: 'tom'}
m.set(tom, 90)
m.get(tom) //90

Symbol

由于大量引入第三方模块,很有可能出现值相同互相覆盖的情况,这个时候引用了Symbol,Symbol表示一个独一无二的值。symbol是一个数据类型。
对象可以用string和symbol作为键。
当对象用作symbol作为键时,则这个属性变成了对象的私有成员,因为是独一无二的,所以无法在外界访问到。

const s = Symbol('abc')//参数是对当前symbol的描述文本,对象可以用string和symbol作为键
console.log(typeof s)// --> symbol
//在全局对象中复用一个相同的symbol值,for方法传入的值必须是字符串,若不是则会自动转成字符串
const s1 = Symbol.for('123')
const s2 = Symbol.for('123')
console.log(s1===s2)//true
const obj = {
	[Symbol.toStringTag]: 'Xobj'
}
console.log(obj.toString()) //[object Xobj]

for…of循环

for循环适合遍历数组
for…in适合遍历键值对
for…of可以遍历任何数据结构,但是object对象不能直接遍历,因为object对象没有迭代器,需要自己手动写一个迭代器才可以进行遍历,能直接遍历的有数组,Set,Map对象

const arr = [100, 200, 300, 400]
for(const item of arr) {
    // console.log(item) //数组中的每个值
    if(item > 200) {
        // break; 可以用break跳出for...of循环
    }
}
const m = new Map()
m.set('a',10)
m.set('b',20)
for(const [key, value] of m) {
    //map对象遍历每个是键值对的数组
    console.log(key, value)
}

可迭代接口Iterator

const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]() //执行set对象自带的iterator方法,成为迭代器
console.log(iterator.next()) //迭代器的next方法{ value: 'foo', done: false }
console.log(iterator.next()) //迭代器的next方法
console.log(iterator.next()) //迭代器的next方法

只有有symbol.ierator方法的数据结构才可以被迭代,进行for…of遍历

实现可迭代接口

即有Symbol.iterator方法。
迭代器的应用:对外统一迭代接口,让外部不需要再去遍历特定的对象

const obj = {
    //可迭代接口iterable
    [Symbol.iterator]: function () {
        return {
            //迭代器iterator
            next: function() {
                //迭代结果iterationResult
                return {
                    value: 'zc',
                    done: true
                }
            }
        }
    }
}

//举例
const obj = {
    store: ['aaa', 'bbb', 'ccc'],
    //可迭代接口iterable
    [Symbol.iterator]: function () {
        let this_ = this
        let index = 0
        return {
            //迭代器iterator
            next: function() {
                //迭代结果iterationResult
                const result = {
                    value: this_.store[index],
                    done: this_.store.length >= index ? false : true
                }
                index ++ 
                return result
            }
        }
    }
}
for(const item of obj) {
    console.log(item)
}   
//对外统一迭代接口,让外部不需要再去遍历特定的对象
const todos = {
    life: ['吃饭', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '外语'],
    works: ['喝茶'],

    [Symbol.iterator]: function() {
        const all = [...this.life, ...this.learn, ...this.works]
        let index = 0
        return {
            next: function () {
                return {
                    value: all[index],
                    done: index++ >= all.length
                }
            }
        }
    }
}
for(const item of todos) {
    console.log(item)
}

Generator生成器

Generator生成器:避免异步编程中,回调嵌套过深,提供更好的异步解决办法

yield后面的值将作为next()的结果返回

function * foo() {
    console.log(1111)
    yield 100;
    console.log(222)
    yield 200;
    console.log(333)
    yield 300;
}
const generator = foo()
console.log(generator.next()) //111 { value: 100, done: false }
console.log(generator.next()) //222 { value: 200, done: false }
console.log(generator.next()) //333 { value: 300, done: false }
console.log(generator.next()) // { value: undefined, done: true }

Generator生成器的应用

1.可用作发号器

function * createIDMaker () {
    let id = 1
    while (true) {
        yield id++
    }
}
const idMaker = createIDMaker()
console.log(idMaker.next().value)

2.使用generator函数生成iterator迭代器

const todos = {
    life: ['吃饭', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '外语'],
    works: ['喝茶'],

    [Symbol.iterator]: function * () {
        const all = [...this.life, ...this.learn, ...this.works]
        for(const item of all) {
            yield item
        }
    }
}
for(const item of todos) {
    console.log(item)
}

ECMAScript2016

数组的indexof()方法查找返回元素的下标,但如果是查找NaN类型,则查找不到。
1.ES6新增了一个includes方法,返回值为布尔类型,可以查找NaN类型。
2.新增指数运算符,如2的十次方写作: 2**10

ECMAScript2017

Object.values(obj) 返回值的数组
Object.entries(obj) 对象键值对组成一个个数组之后,返回全部的键值对数组
Object.getOwnPropertyDescriptors(obj) 获取对象中的所有描述
string.padEnd(),string.padStart()

const obj = {
    foo: 'value1',
    bar: 'value2'
}
console.log(Object.values(obj)) //[ 'value1', 'value2' ]
console.log(Object.entries(obj)) //[ [ 'foo', 'value1' ], [ 'bar', 'value2' ] ]
for( const [key, value] of Object.entries(obj)) {
    console.log(key, value)
}
//这里由于与Map对象的返回值相同,所以可以转化为Map对象
console.log(new Map(Object.entries(obj)))



const p1 = {
    firstName: 'value1',
    lastName: 'value2',
    get fullName () {
        return this.firstName + this.lastName
    }
}
// const p2 = Object.assign({}, p1);
// p2.firstName = 'aaa'
// //set方法无法复制
// console.log(p2)

const description = Object.getOwnPropertyDescriptors(p1)
console.log(description)
const p2 = Object.assign({}, description)
console.log(p2)

const books = {
    html: 5,
    css: 16,
    javascript: 120
}
for(const [key, value] of Object.entries(books)) {
//第一个是总体字符长度,第二个参数是填充内容
    console.log(`${key.padEnd(16, '-')}|${value.toString().padStart(3, '0')}`)
}
//html------------|005
//css-------------|016
//javascript------|120

这一模块遇见的问题:

  1. package.json和package-lock.json问题
    这个其实是Npm5之后,npm install 都会有一个package-lock.json文件,原来package.json文件只能锁定大版本,也就是版本号的第一位,并不能锁定后面的小版本,每次npm install都是拉取的该大版本下的最新的版本。package-lock.json功能,为的是让开发者知道只要你保存了源文件,到一个新的机器上、或者新的下载源,只要按照这个package-lock.json所标示的具体版本下载依赖库包,就能确保所有库包与你上次安装的完全一样。
  2. 闭包
    闭包函数:声明在一个函数中的函数,叫做闭包函数。
    闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。