第十章,函数
散碎
- caller 可以找到哪个函数环境中调用了此函数,(不是 this(严格模式不可用
function func(){
foo()
}
function foo(){
console.log(foo.caller === func, this === globalThis) // true true
}
func()
- function 声明提升并赋值
foo()
fun() // TypeError: fun is not a function
function foo(){ }
var fun = function(){ }
定义函数
new Function('x', 'y', 'return x + y') //(不推荐)
箭头函数
不能使用 arguments、super、new.target、prototype、new
函数名
- 一般情况
函数引用.name // 函数名标识符
foo.name // 'foo'
- 三种特殊情况
new Function().name // 'anonymous'
func.bind().name // bound func
function foo() {}
let obj = {
get age() { },
set age(newAge) { }
}
let propertyDescriptor = Object.getOwnPropertyDescriptor(obj, 'age');
console.log(propertyDescriptor.get.name); // get age
console.log(propertyDescriptor.set.name); // set age
函数参数
- arguments[0] 就是第一个参数,arguments[1] 就是第二个参数...
- 在非严格模式下,改变 arguements[0] 的值, 会影响第一个具名参数的值,反之亦然。(但并不意味着他们访问同一地址,只是他们会同步;且传入几个参数,那么只有这几个参数会保持同步,后面声明但未传的不会同步)
- 严格模式下他们值初始相同,但后续并不同步
- 函数参数有自己的作用域,他不能使用函数体的内容,但是以下代码没有问题
const n = 3
function foo(x = 1, y = x * 2, z = n) {}
- arguments.callee === 函数本身 (严格模式不可用 callee)
arguments 长度严格取决于 传入的参数,与定义了多少参数无关
尾调用
目前大部分环境都还未实现尾调用的优化;尾调用指的是最后直接返回函数的调用,且外部栈帧真的没必要存在了。
// 是
function dfs(x){
return dfs(x + 1)
}
dfs(1)
// 不是
function dfs(x){
return dfs(x) + 1
}
dfs(1)
闭包
闭包的产生,即内部作用域种用到了外部作用域变量 请仔细阅读下方代码
- 不用外部函数作用域变量
const overall = 'overall'
function container(){
const x = 'x'
function inner(){
const y = 'y'
return function content(){
const z = 'z'
}
}
return inner()
}
console.dir(container())
- 用外部函数作用域变量
const overall = 'overall'
function container(){
const x = 'x'
function inner(){
const y = 'y'
return function content(){
const z = 'z'
const closure = x + y
}
}
return inner()
}
console.dir(container())
[[Scopes]] 列表就是作用域链的体现,具体原理就是,词法环境种引用了外部作用域的词法环境,由此链条依次向上查找,构成作用域链。
第十一章,期约与异步函数
散碎
.resolve() 是幂等的,.reject() 不是
const origin = Promise.resolve('inner')
const p = Promise.resolve(origin)
console.log(p === origin, p) // true Promise<fulfilled>: 'inner'
const p2 = Promise.reject(origin)
console.log(p2 === origin, p2) // false Promise<rejected>: Promise
外部 try catch 无法捕捉错误
try {
Promise.reject(1)
} catch (error) {
console.log(error) // 捕获不到,会报错
}
多个串联的期约处理,可以理解为树接口,执行顺序可以理解为层序遍历
// A
// / \
// B C
// / \ / \
// D E F G
let A = new Promise((resolve, reject) => {
console.log('A');
resolve();
});
let B = A.then(() => console.log('B'));
B.then(() => console.log('D'));
let C = A.then(() => console.log('C'));
C.then(() => console.log('F'));
B.then(() => console.log('E'));
C.then(() => console.log('G'));
// A B C D E F G
有方法会静默处理错误(.all、.race
后续解决或拒绝的期约将不再影响返回的期约的状态。这种行为称为“静默处理”,意味着后续的拒绝不会触发任何错误或被捕获的拒绝处理程序。(catch会触发的话,只能被触发一次
let p = Promise.race([Promise.reject(1), new Promise((_, reject) => reject(2))])
p.catch(reason => setTimeout(console.log, 0, reason)) // 1 (之后不再处理2
串行期约组合合并处理
const add_2 = x => x + 2
const add_4 = x => x + 4
const add_8 = x => x + 8
const fns = [add_2, add_4, add_8]
const compose = fns => {
return x => fns.reduce((p, fn) => p.then(fn), Promise.resolve(x))
}
const flow = compose(fns)
flow(1).then(result => {
console.log(result)
})
期约取消与进度
es6+ 并没有实现取消与进度,委员会认为取消与进度是激进的;如果实现取消与进度,会让期约连锁和合成过度复杂
期约取消
以下借鉴进度的写法实现取消
<button id="start">Start</button>
<button id="cancel">Cancel</button>
<script>
const startBtn = document.querySelector('#start')
const cancelBtn = document.querySelector('#cancel')
class CouldCancelPromise {
constructor(executor) {
// 保留原来的 resolve,reject 并添加 cancel 函数
return new Promise((resolve, reject) => {
executor(resolve, reject, resolve)
})
}
}
startBtn.addEventListener('click', () => {
console.log('wait...')
const cancelPromise = new CouldCancelPromise((resolve, reject, cancel) => {
const id = setTimeout(resolve, 3000, 'done')
cancelBtn.addEventListener('click', () => {
cancel('cancel')
clearInterval(id)
})
})
cancelPromise.then(result => {
console.log(result)
})
})
</script>
也可以采用继承的形式(不推荐继承内置对象);推荐采用Promise.race;AbortController;第三方promise库这三种方式来实现
class CouldCancelPromise extends Promise {
constructor(executor) {
super((resolve, reject) => {
executor(resolve, reject, resolve)
})
}
}
期约进度,就如 fetch 的下载进度一样,大致代码如下
class ProgressPromise extends Promise{
constructor(executor) {
const notifyHandlers = []
super((resolve, reject) => {
return executor(resolve, reject, progress => {
notifyHandlers.forEach(notify => {
notify(progress)
})
})
})
this.notifyHandlers = notifyHandlers
}
notify = function (fun) {
this.notifyHandlers.push(fun)
return this
}
}
const p = new ProgressPromise((resolve, reject, notify) => {
function getData(step) {
if (step < 5) {
notify(`${step * 20} %.........`)
setTimeout(getData, 1000, step + 1)
} else {
resolve('done')
}
}
getData(0)
})
p.notify(progress => {
console.log(progress)
})
p.then(res => {
console.log(res)
})
/*
20 %.........
40 %.........
60 %.........
80 %.........
done
*/
异步函数
async
用于定义异步函数,只在异步函数内才能使用 await
await
await 可以展平 thenable 对象
async function foo() {
const origin = 'xz'
const obj = { }
const thenable = {
then(callback) {
callback('thenable')
}
}
const p = Promise.resolve('resolve')
console.log(await origin, await obj, await thenable, await p)
await Promise.reject('reject')
console.log('end') // 不执行
}
foo().catch(console.log)
// xz {} thenable resolve reject
实现 sleep
function sleep(delay){
return new Promise(resolve => setTimeout(resolve, delay))
}
async function main(){
console.log(1)
await sleep(1000)
console.log(2)
}
main()