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