函数让前端飞Web前端之路

再谈js中的函数

2019-08-21  本文已影响80人  大春春

前言

博主进入前端领域工作到现在也已经有两年的时间了,回看了两年多前刚开始学习js这门语言的时候写的关于函数的文章
,发现又有了新的理解,但在此博客中不对函数的创建和声明提升等概念做更多的理解,只说一些新的理解,概念性可能较强,也会有一些面试题以及专门针对react的优化方式。

js中的函数

在之前,我看过很多种对函数的称呼,除了函数本身外,还有方法、过程等,但是他们之间是有一定区别的。

函数返回值的确定

前面说到在js中,函数永远都有返回值,那么函数的返回值又是由什么去确定的呢?

关于闭包

闭包相关的题目是出现频率巨高的面试题,也是老生常谈的问题了。在这里就说说闭包的定义和特点吧。

关于this

this问题在js中也是老生常谈的问题的了,讨论的最多的就是它的指向问题,在这里分为三种情况进行讨论(严格模式下的this不考虑)。

let length = 10

function fn() {
    console.log(this.length)
}

let obj = {
    length: 5,
    method(fn) {
        fn()
        arguments[0]()
    }
}

obj.method(fn, 1) // 请问输出什么

可能有人会说答案是10和10,然而这时错的,正确的答案是window.length(当前页面的iframe数量)和2。
为什么呢?

  1. 首先我们考虑第一次执行fn时候,也就是method中fn()的时候,很明显fn中的this是指向window的,然而let length = 10因为使用的是let,所以并不会将length变量的值挂到window.length上去,所以fn()时候输出的是window.length的值
  2. arguments[0]()执行的时候,arguments是method的arguments,那么arguments[0]也就是执行fn,但是这么时候需要注意,arguments是一个对象,arguments[0]()相当于arguments[0].call(arguments),又因为obj.method(fn, 1)输入了两个参数,所以这里输出的是2

递归

说到函数就不能不说递归了,递归是指函数自身调用自身,它的使用范围很广,面试题出的也多,比如最常见的求阶乘斐波那契的第n位数:

const j = n => {
    for(let i = n - 1; i >= 1; i--) {
        n = n * i
    }
    console.log(n)
}

j(3)  // 6

斐波那契则可以改写成如下:

const f = n => {
    let arr = [0, 1]
    for(let i = 0; i <= n - 2; i++) {
        arr[i + 2] = arr[i + 1] + arr[i]
    }
    console.log(arr[arr.length - 1])
}

除此之外,还可以将递归改写成尾递归的形式进行优化,但在此不再赘述。

调用栈

在上面讨论递归的时候有提到了调用栈这个东西,那么调用栈究竟是什么呢?

从对react的优化中看记忆化函数

该部分会涉及到react相关的知识,默认读者已具备相应的知识。首先将会介绍在react中的两种减少组件计算的优化方式react.memouseCallback,通过这两个优化再对记忆化函数进行理解。

const memo = (fn) => {
  请补全
}

const x2 = memo((x) => {
    console.log('执行了一次')
    return x * 2
  })
  // 第一次调用 x2(1)
console.log(x2(1)) // 打印出执行了,并且返回2
  // 第二次调用 x2(1)
console.log(x2(1)) // 不打印执行,并且返回上次的结果2
  // 第三次调用 x2(1)
console.log(x2(1)) // 不打印执行,并且返回上次的结果2

要实现记忆化函数实际上并不难,只需要找到一个容器对之前已经计算过的结果进行存储,当输入的参数和之前的参数一样时候,就直接从容器中取出结果,如果不是就执行函数,并对结果进行存储:

const memo = (fn) => {
    const memoed = key => {
        // 如果参数和之前不同则进行结果缓存
        if(!(key in memoed.cache)) {
            memoed.cache[key] = fn(key)
        }
        // 否则直接输出结果
        return memoed.cache[key]
    }
    memoed.cache = {}
    return memoed
}

执行结果:


image.png

函数柯里化(Currying)

函数柯里化也是现在前端面试的常客了,虽然我一直觉得嵌套太多层会导致代码难看

柯里化是编译原理层面实现多参函数的一个技术

const bind =  (context, ...args) => {
    return (...rest) => this.call(context, ...args, ...rest);
}

另外,也可以利用柯里化函数延迟执行的特性对代码进行优化,例子可以直接点这里查看

答案:


image.png
上一篇 下一篇

猜你喜欢

热点阅读