//{
// name: { configurable: true, enumerable: true, value: “布兰”, writable: true },
// age: {configurable: false, enumerable: false, value: 12, writable: false}
//}
配合 Object.create()
可以实现浅克隆:
const shallowClone = (obj) => Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
String.prototype.padStart()
str.padStart(length [, padStr])
会返回一个新字符串,该字符串将从 str
字符串的左侧开始填充某个字符串 padStr
(非必填,如果不是字符串则会转成字符串, 传入 undefined
和不传这个参数效果一致)直到达到指定位数 length
为止:
‘abc’.padStart(5, 2) // ‘22abc’
‘abc’.padStart(5, undefined) // ’ abc’
‘abc’.padStart(5, {}) // ‘[oabc’
‘abc’.padStart(5) // ’ abc’
‘abcde’.padStart(2, ‘f’) // ‘abcde’
String.prototype.padEnd()
规则和 padStart
类似,但是是从字符串右侧开始填充:
‘abc’.padEnd(5, 2) // ‘abc22’
函数参数尾逗号
允许函数在定义和调用的时候时候最后一个参数后加上逗号:
function init(
param1,
param2,
) { }
init(‘a’, ‘b’,)
Async函数
- 使用
async
可以声明一个async
函数,结合await
可以用一种很简介的方法写成基于Promise
的异步行为,而不需要刻意的链式调用。await
表达式会暂停整个async
函数的执行进程并出让其控制权,只有当其等待的基于Promise
的异步操作被兑现或被拒绝之后才会恢复进程。async
函数有如下几种定义形式:
// 函数声明
async function foo() {}
// 函数表达式
let foo = async function() {}
// 箭头函数
let foo = async () => {}
// 对象方法
lef obj = {
async foo() {}
}
// 类方法
class Dog {
async bark() {}
}
async
函数一定会返回一个Promise
对象,所以它可以使用then
添加处理函数。如果一个async
函数的返回值看起来不是Promise
,那么它将会被隐式地包装在一个Promise
中:
async function foo() {
return ‘a’
}
foo().then(res => {
console.log(res) // ‘a’
})
- 内部如果发生错误,或者显示抛出错误,那么
async
函数会返回一个rejected
状态的Promsie
:
async function foo() {
throw new Error(‘error’)
}
foo().catch(err => {
console.log(err) // Error: error
})
- 返回的
Promise
对象必须等到内部所有await
命令Promise
对象执行完才会发生状态改变,除非遇到return
语句或抛出错误;任何一个await
命令返回的Promise
对象变为rejected
状态,整个Async
函数都会中断后续执行:
async function fn() {
let a = await Promise.resolve(‘success’)
console.log(‘a_’ + a)
let b = await Promise.reject(‘fail’)
console.log(‘b_’ + b) // 不会执行
}
fn().then(res => {
console.log(res) // 不会执行
}, err => {
console.log(err)
})
// ‘a_success’
// ‘fail’
- 所以为了保证
async
里的异步操作都能完成,我们需要将他们放到try...catch()
块里或者在await
返回的Promise
后跟一个catch
处理函数:
async function fn() {
try {
let a = await Promise.reject(‘a fail’)
console.log(‘a_’ + a) // 不会执行
} catch (e) {
console.log(e) // ‘a fail’
}
let b = await Promise.reject(‘b fail’)
.catch(e => {
console.log(e) // ‘b fail’
})
console.log(‘b_’ + b) // ‘bundefined’
}
fn().then(res => {
console.log(res) // undefined
}, err => {
console.log(err) // 不会执行
})
- 如果
async
函数里的多个异步操作之间没有依赖关系,建议将他们写到一起减少执行时间:
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()])
// 写法二
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
await
命令只能用在async
函数之中,如果用在普通函数,就会报错。
共享内存和Atomics对象
-
SharedArrayBuffer
-
Atomics
ES2018
Promise.prototype.finally()
Promise.prototype.finally()
用于给 Promise
对象添加 onFinally
函数,这个函数主要是做一些清理的工作,只有状态变化的时候才会执行该 onFinally
函数。
function onFinally() {
console.log(888) // 并不会执行
}
new Promise((resolve, reject) => {
}).finally(onFinally)
finally()
会生成一个 Promise
新实例,finally 一般会原样后传父 Promise,无论父级实例是什么状态:
let p1 = new Promise(() => {})
let p2 = p1.finally(() => {})
setTimeout(console.log, 0, p2) // Promise {}
let p3 = new Promise((resolve, reject) => {
resolve(3)
})
let p4 = p3.finally(() => {})
setTimeout(console.log, 0, p3) // Promise {: 3}
上面说的是一般,但是也有特殊情况,比如 finally
里返回了一个非 fulfilled
的 Promise
或者抛出了异常的时候,则会返回对应状态的新实例:
let p1 = new Promise((resolve, reject) => {
resolve(3)
})
let p2 = p1.finally(() => new Promise(() => {}))
setTimeout(console.log, 0, p2) // Promise {}
let p3 = p1.finally(() => Promise.reject(6))
setTimeout(console.log, 0, p3) // Promise {: 6}
let p4 = p1.finally(() => {
throw new Error(‘error’)
})
setTimeout(console.log, 0, p4) // Promise {: Error: error}
参考:
- 深入理解Promise
异步迭代器
想要了解异步迭代器最好的方式就是和同步迭代器进行对比。我们知道可迭代数据的内部都是有一个 Symbol.iterator
属性,它是一个函数,执行后会返回一个迭代器对象,这个迭代器对象有一个 next()
方法可以对数据进行迭代,next()
执行后会返回一个对象,包含了当前迭代值 value
和 标识是否完成迭代的 done
属性:
let iterator = [1, 2]Symbol.iterator
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: undefinde, done: true }
上面这里的 next()
执行的是同步操作,所以这个是同步迭代器,但是如果 next()
里需要执行异步操作,那就需要异步迭代了,可异步迭代数据的内部有一个 Symbol.asyncIterator
属性,基于此我们来实现一个异步迭代器:
class Emitter {
constructor(iterable) {
this.data = iterable
}
let length = this.data.length,
index = 0;
return {
next:() => {
const done = index >= length
const value = !done ? this.data[index++] : undefined
return new Promise((resolve, reject) => {
resolve({value, done})
})
}
}
}
}
异步迭代器的 next()
会进行异步的操作,通常是返回一个 Promise
,所以需要对应的处理函数去处理结果:
let emitter = new Emitter([1, 2, 3])
let asyncIterator = emitterSymbol.asyncIterator
asyncIterator.next().then(res => {
console.log(res) // { value: 1, done: false }
})
asyncIterator.next().then(res => {
console.log(res) // { value: 2, done: false }
})
asyncIterator.next().then(res => {
console.log(res) // { value: 3, done: false }
})
另外也可以使用 for await...of
来迭代异步可迭代数据:
let asyncIterable = new Emitter([1, 2, 3])
async function asyncCount() {
for await (const x of asyncIterable ) {
console.log(x)
}
}
asyncCount()
// 1 2 3
另外还可以通过异步生成器来创建异步迭代器:
class Emitter {
constructor(iterable) {
this.data = iterable
}
async *Symbol.asyncIterator {
let length = this.data.length,
index = 0;
while (index < length) {
yield this.data[index++]
}
}
}
async function asyncCount() {
let emitter = new Emitter([1, 2, 3])
const asyncIterable = emitterSymbol.asyncIterator
for await (const x of asyncIterable ) {
console.log(x)
}
}
asyncCount()
// 1 2 3
参考:
-
Iteration_protocols
-
for-await…of
s修饰符(dotAll模式)
正则表达式新增了一个 s
修饰符,使得 .
可以匹配任意单个字符:
/foo.bar/.test(‘foo\nbar’) // false
/foo.bar/s.test(‘foo\nbar’) // true
上面这又被称为 dotAll
模式,表示点(dot
)代表一切字符。所以,正则表达式还引入了一个dotAll属性,返回一个布尔值,表示该正则表达式是否处在dotAll模式:
/foo.bar/s.dotAll // true
具名组匹配
正则表达式可以使用捕获组来匹配字符串,但是想要获取某个组的结果只能通过对应的索引来获取:
let re = /(\d{4})-(\d{2})-(\d{2})/
let result = re.exec(‘2015-01-02’)
// result[0] === ‘2015-01-02’
// result[1] === ‘2015’
// result[2] === ‘01’
// result[3] === ‘02’
而现在我们可以通过给捕获组 (?<name>...)
加上名字 name
,通过名字来获取对应组的结果:
let re = /(?\d{4})-(?\d{2})-(?\d{2})/
let result = re.exec(‘2015-01-02’)
// result.groups.year === ‘2015’
// result.groups.month === ‘01’
// result.groups.day === ‘02’
配合解构赋值可以写出非常精简的代码:
let {groups: {year, month, day}} = /(?\d{4})-(?\d{2})-(?\d{2})/.exec(‘2015-01-02’)
console.log(year, month, day) // 2015 01 02
具名组也可以通过传递给 String.prototype.replace
的替换值中进行引用。如果该值为字符串,则可以使用 $<name>
获取到对应组的结果:
let re = /(?\d{4})-(?\d{2})-(?\d{2})/
let result = ‘2015-01-02’.replace(re, ‘ < d a y > / <day>/ <day>//$’)
// result === ‘02/01/2015’
参考:
- proposal-regexp-named-groups
后行断言
后行断言:(?<=y)x
,x
只有在 y
后面才能匹配:
/(?<=$)\d+/.exec(‘I have $100.’) // [‘100’]
后行否定断言:(?<!y)x
,x
只有不在 y
后面才能匹配:
/(?<!$)\d+/.exec(‘I have $100.’) // [‘00’]
Unicode属性转义
允许正则表达式匹配符合 Unicode
某种属性的所有字符,\p{...}
是匹配包含,\P{...}
是匹配不包含的字符,且必须搭配 /u
修饰符才会生效:
/\p{Emoji}+/u.exec(‘???笑死我了???不行了’) // [‘???’]
/\P{Emoji}+/u.exec(‘???笑死我了???不行了’) // [‘笑死我了’]
这里可以查询到更多的 Unicode
的属性 Full_Properties
对象扩展运算符
对象的扩展运算符可以用到解构赋值上,且只能应用到最后一个变量上:
let {x, …y} = {x: 1, a: 2, b: 3}
console.log(y) // {a: 2, b: 3}
对象扩展运算符不能解构原型上的属性:
let obj = { x: 1 }
obj.proto = { y: 2 }
let {…a} = obj
console.log(a.y) // undefined
应用一:可以实现浅拷贝,但是不会拷贝原始属性:
let person = Object.create({ name: ‘布兰’ })
person.age = 12
// 浅拷贝写法一
let { …pClone1 } = person
console.log(pClone1) // { age: 12 }
console.log(pClone1.name) // undefined
// 浅拷贝写法二
let pClone2 = {…person}
console.log(pClone2) // { age: 12 }
console.log(pClone2.name) // undefined
应用二:合并两个对象:
let ab = {…a, …b}
// 等同于
let ab = Object.assign({}, a, b);
应用三:重写对象属性
let aWithOverrides = { …a, x: 1, y: 2 };
应用四:给新对象设置默认值
let aWithDefaults = { x: 1, y: 2, …a };
应用五:利用扩展运算符的解构赋值可以扩展函数参数:
function baseFunction({ a, b }) {}
function wrapperFunction({ x, y, …restConfig }) {
// 使用 x 和 y 参数进行操作
// 其余参数传给原始函数
return baseFunction(restConfig)
}
参考:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
ES6
-
列举常用的ES6特性:
-
箭头函数需要注意哪些地方?
-
let、const、var
-
拓展:var方式定义的变量有什么样的bug?
-
Set数据结构
-
拓展:数组去重的方法
-
箭头函数this的指向。
-
手写ES6 class继承。
微信小程序
-
简单描述一下微信小程序的相关文件类型?
-
你是怎么封装微信小程序的数据请求?
-
有哪些参数传值的方法?
-
你使用过哪些方法,来提高微信小程序的应用速度?
-
小程序和原生App哪个好?
-
简述微信小程序原理?
-
分析微信小程序的优劣势
-
怎么解决小程序的异步请求问题?
其他知识点面试
-
webpack的原理
-
webpack的loader和plugin的区别?
-
怎么使用webpack对项目进行优化?
-
防抖、节流
-
浏览器的缓存机制
-
描述一下二叉树, 并说明二叉树的几种遍历方式?
-
项目类问题
-
笔试编程题:
最后
技术栈比较搭,基本用过的东西都是一模一样的。快手终面喜欢问智力题,校招也是终面问智力题,大家要准备一下一些经典智力题。如果排列组合、概率论这些基础忘了,建议回去补一下。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
ES6 class继承。
微信小程序
-
简单描述一下微信小程序的相关文件类型?
-
你是怎么封装微信小程序的数据请求?
-
有哪些参数传值的方法?
-
你使用过哪些方法,来提高微信小程序的应用速度?
-
小程序和原生App哪个好?
-
简述微信小程序原理?
-
分析微信小程序的优劣势
-
怎么解决小程序的异步请求问题?
其他知识点面试
-
webpack的原理
-
webpack的loader和plugin的区别?
-
怎么使用webpack对项目进行优化?
-
防抖、节流
-
浏览器的缓存机制
-
描述一下二叉树, 并说明二叉树的几种遍历方式?
-
项目类问题
-
笔试编程题:
最后
技术栈比较搭,基本用过的东西都是一模一样的。快手终面喜欢问智力题,校招也是终面问智力题,大家要准备一下一些经典智力题。如果排列组合、概率论这些基础忘了,建议回去补一下。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算