1. 函数的返回值由什么确定?

答:’x1’

当初在JS故意这么设计的,如果a像参数x一样也是在调用的时候确认的话,那和干脆把a也直接传入不就好了,所以a由定义时的环境决定。

同理:下图结果为’x2’

2. 闭包

对象也可以模拟闭包

1
2
3
4
5
6
const self = {
_a: 1, // js可以设置为私有属性
fn() {
return this.a++
}
}

函数可以模拟对象

1
2
3
4
5
6
7
8
9
10
11
// 定义
function fn(age, name) {
return function(key) {
if (key === 'age') return age;
if (key === 'name') return name;
}
}
// 使用
const obj = fn('18', 'zch')
obj('age') // 18
obj('name') // zch

3. this(this是参数,箭头函数中this是环境)

答button是错的!

完美打法,我现在无法确定this的值是什么(this只有在调用的时候才能确定*(因为this是参数)*),如果是用户点击时调用的话,this就是button,如果 var fn = button.onclick; fn() ,此时的this就是window

看到此题时,我们先改写

1
2
3
4
5
6
7
8
9
10
11
let length = 10
function fn() { console.log(this.length) }

let obj = {
lengh: 5,
methods(fn) {
fn.call(undefined)
arguments[0].call(arguments) // 把 arguments[0]() 比作 arguments.0()
}
}
obj.methods.call(obj, fn, 1)

所以第一次打印出的值是 window.length (窗口中iframe的个数)
第二次为 arguments.length(函数实参的个数)

记法

3. 闭包

如果在函数里面能访问外面的变量,那么这个函数+这些变量=闭包

并不是所有语言都有闭包, ruby 就没有, ruby用def声明的函数就不能使用闭包, 使用lambda才可以

4. 递归

栗子:阶乘

调用栈中的图:

栗子:斐波那契数列(0,1,1,2,3,5………)每一项都是前两项的和

我们发现在计算中会爆栈,因为会重复计算,如下图,会重复计算

解决方法:尾递归优化或者记忆化函数

- 尾递归

js基本不考虑,语言没有做优化,依旧会记忆很多东西

- 记忆化函数

1
2
3
4
5
6
7
8
// 比如说用循环的中的数组来记录斐波那契
const fn = (n) => {
let array = [0, 1]
for (let i = 0; i < n-2; i++) {
array[i+2] = array[i+1] + array[i]
}
return array[array.length-1]
}

react 中的记忆栗子

测试题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 测试题
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

答案

5.科里化

1
2
3
const add = (a, b) => a + b

const add = a => b => a+b

分析: const add = a => b => a+b
一、正常理解我们可以拆解为两个部分1. 函数接受一个参数 a。2. 返回一个函数 b => a + b。即:

1
2
>const add = a =>
b => a +b

二、如果是函数式的思想来理解,将会拆解成如下两部分1. 接受参数a, 接受参数b。2.返回a+b

1
2
3
>const add = a =>
b =>
a + b

测试题
1
2
3
4
5
6
7
8
9
10
11
12
// 测试题
const currify = (fn, params = [])=>
请补全

addTwo = (a,b)=>a+b
addThree = (a,b,c)=>a+b+c

newAddTwo = currify(addTwo)
newAddThree = currify(addThree)

console.log(newAddTwo(1)(2)) // 3
console.log(newAddThree(1)(2)(3)) // 6

答案

6.高阶函数

把函数作为参数或者返回值的函数。

推理 bind.call() 是什么意思

1
2
3
4
5
6
7
8
9
10
11
var bind = Function.prototype.bind
const fn = function() {
console.log('this', this)
console.log('arguments', arguments)
}

const newFn = fn.bind({ name: 'zch' }, 1, 2, 3)
// 等价于
const newFn = fn.bind.call(fn, { name: 'zch' }, 1, 2, 3) // 参考:上述第三点this中:obj.methods() 等价于 obj.methods.call(obj)
// 即:
bind.call(fn, { name: 'zch' }, 1, 2, 3) // fn.bind === Function.prototype.bind // true

所以就是第一个参数是fn,所以bind把函数作为参数的函数,验证bind为高级函数。

  • apply
  • call
  • sort
  • map
  • filter
  • reduce

同理

函数组合(pipe)

1
2
3
4
5
6
7
8
9
10
11
12
function dobuleSay(str) {
return str + ', ' + str
}
function capitalize(str) {
return str[0].toUpperCase() + str.substring(1)
}
function exclaim(str) {
return srt + '!'
}

let result = exclaim(capitalize(dobuleSay(str))) // 痛点:这样子的写法太麻烦
// "Hello, hello"

如果使用将来的js语法 |> 改写

1
2
3
4
5
6
7
8
let result = "hello"
|> doubleSay
|> captitalize
|> exclaim

// 如果现在想用,只能使用 Ramda.js 库
// const say = R.compose(doubleSay, captitalize, exclaim)
// let result = say('hello')

可以应用 react 组件之中,比如 from 的表单提交按钮,可以放到外部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function App() {
return (
<div>
<From>
<button>提交</button> // 注意:此时的button无法将From元素提交出去,所以我们将使用高级组件
</From>
</div>
)
}

function From() {
const submit = () => console.log('submit')
return (
<div>
//...一堆的表单组件
{props.children}
</div>
)
}
为了解决外部的 button 能够提交,使用高级组件解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function App() {
return (
<div>
<From>
{ submit => (<button onClick={submit}>提交</button>) } // 这里改成函数的形式,返回button组件
</From>
</div>
)
}

function From() {
const submit = () => console.log('submit')
return (
<div>
//...一堆的表单组件
{ props.children() } // 这里改成调用
</div>
)
}