2020年伊始,我们经历了艰难和恐慌;
转眼间,2020年已接近尾声,继续努力,莫负时光!
回顾过往来不及细细思量,
我们曾为梦想拼搏,也曾为挫折痛哭;
时间匆匆再不说岁月漫长,
酸甜苦辣,悲喜交加,
经历的、错过的,让人对现在的生活倍感珍惜。
历数2020年的前11个月,
有收获有遗憾,有喜悦有不足,
最后的一个月,用努力对抗焦虑,
让2020充实收尾!
let & const
在之前JS只有两种作用域,全局作用域和函数作用,是没有块级作用域的
通过let声明的变量,只能在其所在的作用域中访问。
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = function() {
console.log(i) //这里的i其实打印的都是全局作用域的i
}
}
elements[0].onclick() // 3 循环结束后,i已经累加到了3
这也是闭包的典型案例
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = (function(i) {
console.log(i) //这里的i其实打印的都是全局作用域的i
})(i)
}
elements[0].onclick() // 3 循环结束后,i已经累加到了3
其实闭包也是借助函数作用域摆脱全局作用域产生的影响
现在有了let后就简单了
var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function() {
console.log(i) //这里的i其实打印的都是全局作用域的i
}
}
elements[0].onclick()
让我们在看一下例子
for (let i = 0; i < 3; i++) {
let i = 'foo';
console.log(i)
}
正常输出3次foo, 这两个i是互相不影响的
代码进行拆解
let i = 0;
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
另外const/let不存在变量提升,所以在代码块里必须先声明然后才可以使用,这叫暂时性死区;
let name = 'test'
if (true) {
name = '测试'
let name
}
console.log(name)
const/let 不允许在同一个作用域内,重复声明;
const 声明时必须初始化,且后期不能被修改,但如果初始化的是一个对象,那么不能修改的是该对象的内存地址;
const person = {
name: '测试'
}
person.name = 'test'
console.log(person.name) // 'test' 只是修改了内存空间的数据,没有改变内存地址
person = '' // TypeError 这改变了内存的指向
const/let 在全局作用域中声明的常量/变量不会挂到顶层对象(浏览器中是 window )的属性中;
var name = '测试'
let age = 12
console.log(window.name) // '测试'
console.log(window.age) // undefined
最佳实践:不用var ,主用const,配合let,这种会使你的代码的质量会有明显的提高。
解构
数组解构
// 解构不成功的变量值为 undefined
const [a, b, c] = [1, 2]
console.log(a, b, c) // 1, 2, undefined
// 获取余下的变量
const arr = [100, 200, 300]
const [foo, ...reset] = arr
console.log(reset)
// 赋值
const [foo, bar, baz = 123, more = 'test' ] = arr;
console.log(more)
对象解构
const obj = { name: '123', age: 12 }
const { name } = obj
console.log(name)
但是如果有个重名的name
const name = 'memo'
const { name: objName = 'jack' } = obj //这样就不会重名了
console.log(objName)
场景:
如果代码中出现了大量的console.log
const { log } = console
log('123')
这样我们代码的整体的体积会减少很多
函数参数解构:其实就是运用上面的对象解构和数组解构规则;
function move({x = 0, y = 0} = {}) {
console.log([x, y])
return [x, y];
}
move({x: 1, y: 2}) // [3, 8]
move({x: 3}) // [3, 0]
move({}) // [0, 0]
move() // [0, 0]
模板字符串
模板字符串使用两个反引号标识(``),可以用来定义多行字符串,或者使用它在字符串中插入变量:
let name = 'hero'
let tips = `Hello ${name} ---- ${ 1 + 2 },
welcome to my world.`
console.log( tips )
标签模板
const str = console.log`hello world`
const name = 'element'
const gender = true
function myFunc(strings, name, gender) {
const sex = gender ? 'man' : 'woman'
return strings[0] + name + string[1] + gender + strings[2]
}
const result = myFunc`hey, ${ name } is a ${ gender }.`
场景:这种模板字符串可以实现多语言(如:中英文切换)
参数默认值
之前的方式
function foo(enable) {
enable = enable === undefined ? true : ''
console.log(enable)
}
foo(false)
有了参数默认值
function foo(enable = true) {
console.log(enable)
}
foo(false)
多个参数
带参数默认值的要放到最后
function foo(bar, enable = true) {
console.log(enable)
}
foo(false)
剩余参数
之前的方式我们利用伪数组,arguments
function foo() {
console.log(arguments) // Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
foo(1,2,3,4)
现在
function foo(...args) {
console.log(args)
}
foo(1,2,3,4)
展开数组
const arr = ['foo', 'bar', 'baz']
console.log.apply(console, arr)
现在简化了操作
console.log(...arr)
箭头函数
箭头函数语法比函数表达式更简洁,并且没有自己的 this、arguments,不能用作构造函数和用作生成器。
const arr = [1, 2, 3, 4, 5, 6]
arr.filter(i => i % 2)
const person = {
name: 'tom',
sayHi: () => {
console.log(`hi, my name is ${this.name}`) //undefined
},
sayHiName: () => {
setTimeout(() => {
console.log(this.name)
}, 1000)
}
}
person.sayHiName()
对象字面量增强
const bar = 345
const obj = {
//bar: bar,
bar,
methond() {
console.log(this)
},
[Math.random()]: 123, // 计算属性名 Math.random()的执行结果将作为属性名
[bar]: '23ew'
}
obj[Math.random()] = 123
对象扩展方法
Object.assign()
const source = {
a: 123,
b: 345
}
const source1 = {
a: 789,
b: 789
}
const target = {
a: 678,
c: 908
}
const result = Object.assign(target, source, source1)
在看一个例子
function func(obj) {
obj.name = 'tom'
console.log(obj)
}
const obj = { name: 'lili' }
func(obj)
console.log(obj)
这样外面的obj也改变了
改进:
function func(obj) {
const objNew = Object.assign({}, obj)
objNew.name = 'tom'
console.log(objNew)
}
const obj = { name: 'lili' }
func(obj)
console.log(obj)
Object.is() // 判断两个值是否相等 这个用的少,一般还是要用严格意义的相等===
Object.is(+0, -0)
Object.is(NaN, NaN)
proxy 代理对象
object.defineProperty
const person = {
name: 'li',
age: 25
}
cosnt personProxy = new Proxy(person, {
get(target, property) {
return property in target ? target[property] : 'defalut'
},
set(target, property, value) {
if(property === 'age') {
if(!Number.isInteger(value)) {
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
}
})
personProxy.gender = true
console.log(personProxy.name)
proxy优势 defineProperty只能监听到对象属性的读写
proxy优势能监视到更多对象操作例如:delete操作,
const person = {
name: 'li',
age: 25
}
cosnt personProxy = new Proxy(person, {
deleteProperty(target, property) {
delete target[property]
}
})
proxy是以非侵入方式监管了对象的读写
Reflect (静态类)
Reflect内部封装了一系列对对象的底层操作
const person = {
name: 'li',
age: 25
}
console.log('name' in person)
console.log(delete obj['age'])
console.log(Object.keys())
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
Set数据结构
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)
console.log(s)
console.log(s.size)
console.log(s.has(100)) // 是否包含某个值
console.log(s.delete(3)) // 删除某个值
s.clear() //全部请求
场景:
数组去重
const arr = [1,2,3,4,1,3]
const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
console.log(result)
Map 数据结构 (严格意义上的键值对集合)
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{ a: 1 }] = 'value'
console.log(Object.keys(obj)) // ['123', 'true', '[object, objecct]']
const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90) // 存数据
console.log(m)
m.get(tom)
m.has()
m.delete()
m.clear()
m.forEach((vlaue, key) => {
console.log(value, key)
})
Symbol
shared.js
const cache = {}
a.js
cache['foo'] = Math.random()
b.js
cache['foo'] = '124'
解决这种问题
约定一下
b.js
cache['b_foo'] = '123'
如果有人不遵守这种约定怎么办?
Symbol() 创建的每一个都是独一无二的
console.log(Symbol() === Symbol()) // false
场景: 模拟对象的私有成员
const name = Symbol()
const person = {
[name]: '234',
say() {
console.log(this[name])
}
}
person[Symbol] // 不能通过这种方式调用,每个Symbol() 都是不一样的
person.say()
补充
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
console.log(Symbol.for(true) === Symbol.for('true')) // true
Promise (一种更优的异步编程)
const promise = new Promise(function (resolve, reject) {
resolve(100)
reject(new Error('promise rejected'))
})
promise.then(function (value) {
console.log('resolve', value)
}, function(error) {
console.log('rejected', error)
})
基本应用:
function ajax(url) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttoRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function() {
if(this.status === 200) {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
let promise = ajax('www.www.com')
let pronise2 = promise.then(
function onFullfilled(value) {
console.log('onFullfilled', value)
},
function onRejected(error) {
console.log('onRejected', error)
}
)
解决回调地狱
ajax('api')
.then(function (value)) {
console.log(111)
return ajax('/api/a')
})
.then(function (value)) {
console.log(222)
return ajax('/api/b')
})
.then(function (value)) {
console.log(333)
return ajax('/api/c')
})
.catch(function (error) {
console.log(error)
})
并行执行
const urls = [
'https://api.github.com',
'https://api.github.com/users',
'https://api.github.com/uasdfs/asdfad'
]
const promises = urls.map(item => { axios(item).catch(e => ({})) })
const promise = Promise.allSettled(urls.map(item => axios(item)))
promise.then(res => console.log(res))
Async/Await
async function main () {
try {
const users = await ajax('/api')
console.log(users)
} catch (e) {
console.log(e)
}
}
main()
迭代器(Iterator)
const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
其实这就是for of的实现原理
实现可迭代接口
const obj = {
[Symbol.iterator]: function() {
return {
next: function() {
return{
value: 'test',
done: true
}
}
}
}
}
for(const item of obj) {
console.log('循环体') // 不会被执行,写死了next的返回方法,第一次done就是true
}
改进一下:
const obj = {
store: ['foo' , 'bar', 'baz'],
[Symbol.iterator]: function() {
let index = 0
const self = this
return {
next: function() {
const reult = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for(const item of obj) {
console.log('循环体', item)
}
迭代器设计模式 场景: 你我谢童开发一个任务清单应用
// 我的代码 ==================
设计一个存放所有任务的对象
const todos = {
life: ['eat', 'sleep', 'play doung'],
learn: ['chinese', 'math', 'english']
}
// you的代码
呈现对象到界面上
for(const item of todos.life) {
console.log(item)
}
等等
如果我的对象结构变了怎么办呢?
对外提供一个统一遍历接口
const todos = {
life: ['eat', 'sleep', 'play doung'],
learn: ['chinese', 'math', 'english'],
each: function (callback) {
const all = [].concat(this.life, this.learn)
for(const item of all) {
callback(item)
}
}
}
todos.each(function(item) {
console.log(item)
})
用迭代器的方式
const todos = {
life: ['eat', 'sleep', 'play doung'],
learn: ['chinese', 'math', 'english'],
[Symbol.iterator]: function() {
const all = [...this.life, ...this.learn]
let index = 0
return {
next: function() {
value: all[index],
done: index++ >= all.length
}
}
}
}
for(const item of todos) {
console.log(item)
}
生成器(Generator)
避免异步编程中回调嵌套过深,提供更好的异步编程解决方案
function *foo() {
console.log('test')
return 200
}
const result = foo()
console.log(result)
console.log(result.next())
function *foo() {
console.log('111')
yield 100
console.log('222')
yield 200
}
场景
自增id
function *createIdMaker() {
let id = 1
while(true) {
yield id++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
Array.includes
const arr = ['foo', 1, NaN, fale]
console.log(arr.indexof(NaN)) // -1 不能查找NaN
console.log(arr.includes) // true
指数运算符
console.log(Math.pow(2, 10))
console.log(2 ** 10)
Object.values
const obj = {
foo: 'value1',
bar: 'value2'
}
console.log(Object.values()) // ['value1', 'value2' ]
Object.entries
console.log(Object.entries()) // [['foo', 'value1'], ['bar', 'value2']]
for(const [key, value]) of Object.entries(obj) {
console.log(key, value)
}
Object.getOwnPropertyDescriptors
const p1 = {
firstName: 'li',
lastName: 'xiang',
get fullName(){
return this.firstName + ' ' + this.lastName
}
}
console.log(p1.fullName)
const p2 = Object.assign({}, p1)// 把fullName当成了一个普通的属性进行复制
p2.firstName = 'wang'
console.log(p2.fullName) // li xiang
const descriptors = Object.getOwnPropertyDescriptors(p1)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'wang'
console.log(p2.fullName) // wang xiang
String.prototype.padStart / String.prototype.padEnd
const obj = {
html: 5,
css: 23,
javascript: 234
}
for(const [name, count] of Object.entries(obj)) {
console.log(`${name.padEnd(16, '-')} | ${count.padStart(3, '0')}`)
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!