Skip to main content

函数方法

this

globalThis.s = 21
const obj = {
s: 42,
m: () => console.log(this.s),
}

obj.m() // 21
  • m 的 this 会指向全局对象. 对象不够成的单独作用域.由于这个原因,对象的属性建议使用传统的写法定义,不要用箭头函数定义。

尾调用

一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

下面都不是尾调用

// 情况一
function f(x) {
let y = g(x)
return y
}

// 情况二
function f(x) {
return g(x) + 1
}

// 情况三
function f(x) {
g(x)
}

尾调用

function f(x) {
if (x > 0) {
return m(x)
}
return n(x)
}

尾调用优化

函数调用会在内存中形成一个调用记录,又称调用帧(call frame),来保存调用位置和内部变量. 如果函数 A 的内部调用函数 B,那么在 A 的调用帧上方,还会形成一个 b 的调用帧.等 b 运行结束,将结果返回给 A,B 的调用帧才会结束 如果 B 内部还有调用函数 C,那么久还有一个 C 的调用帧.... 所有的调用帧,会形成一个调用栈(call stack)

function f() {
let m = 1
let n = 2
return g(m + n)
}
f()

// 等同于
function f() {
return g(3)
}
f()

// 等同于
g(3)

上面代码中,如果函数 g 不是尾调用,函数 f 就需要保存内部变量 m 和 n 的值、g 的调用位置等信息。但由于调用 g 之后,函数 f 就结束了,所以执行到最后一步,完全可以删除 f(x)的调用帧,只保留 g(3)的调用帧 这就叫做尾调用优化(Tail call optimization),即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。

尾递归

函数调用自身,称为递归. 尾调用自身,称为尾递归 递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生栈溢出错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

柯里化(currying):将多参数的函数转换成单一参数的形式,

function currying(fn, n) {
return function (m) {
return fn.call(this, m, n)
}
}
function tailFactorial(n, total) {
if (n === 1) return total
return tailFactorial(n - 1, n * total)
}
const factorial = currying(tailFactorial, 1)
factorial(5) //120

斐波那契

//反向 从后面开始算起
function fibonacci1(n) {
let arr = [1, 2]
if (n <= 1) return arr[n - 1]
for (let i = 2; i < n; i++) {
arr[i] = arr[i - 1] + arr[i - 2]
}
return arr[n - 1]
}

// 空间优化
function fibonacci2(n) {
let [prev1, prev2] = [1, 2]
if (n === 1) return prev1
if (n === 1) return prev2
for (let i = 2; i < n; i++) {
;[prev1, prev2] = [prev2, prev1 + prev2]
}
return prev2
}

//尾递归
function tailFibonacci(n, ac1 = 1, ac2 = 2) {
if (n === 1) return ac1
if (n === 2) return ac2
return tailFibonacci(n - 1, ac2, ac1 + ac2)
}
function curryingFib(fn, ac1, ac2) {
return function (n) {
return fn.call(this, n, ac1, ac2)
}
}
const fibonacci = curryingFib(tailFibonacci, 1, 2)
fibonacci(5)

严格模式

尾递归优化

尾递归优化只在严格模式下生效,

function tco(f) {
var value
var active = false
var accumulated = []

return function accumulator() {
accumulated.push(arguments)
if (!active) {
active = true
while (accumulated.length) {
value = f.apply(this, accumulated.shift())
}
active = false
return value
}
}
}

var sum = tco(function (x, y) {
if (y > 0) {
return sum(x + 1, y - 1)
} else {
return x
}
})

sum(1, 100000)
// 100001