函数对象的属性

  • 我们知道JavaScript中函数也是一个对象,那么对象中就可以有属性和方法。
  • 属性name:一个函数的名词我们可以通过name来访问;image.png
  • 属性length:属性length用于返回函数参数的个数;
    • 注意:rest参数是不参与参数的个数的; image.png

认识arguments

  • arguments 是一个 对应于 传递给函数的参数 的 类数组(array-like)对象。
  • array-like意味着它不是一个数组类型,而是一个对象类型:
    • 但是它却拥有数组的一些特性,比如说length,比如可以通过index索引来访问;
    • 但是它却没有数组的一些方法,比如filter、map等;
1
2
3
4
5
6
 function foo(m, n) {
// arguments 类似数组对象
console.log(arguments)
}

foo(10, 25, 32, 41)

image.png

arguments 遍历和转换成数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function foo(m, n) {

// 1.默认用法:
// 通过索引获取内容
console.log(arguments[0])
console.log(arguments[1])

// // 遍历
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i])
}
for (var arg of arguments) {
console.log(arg)
}
}

foo(10, 25, 32, 41)

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function foo (m, n) {
// 2.需求获取所有参数中的偶数
// 数组 filter
for (var arg of arguments) {
if (arg % 2 === 0) {
console.log(arg)
}
}
// var evenNums = arguments.filter(item => item % 2 === 0) // 无filter方法,会报错
// console.log(eventNums)

console.log("----------")
// 2.1.将arguments转成数组方式一:
var newArguments = []
for (var arg of arguments) {
newArguments.push(arg)
}
console.log(newArguments)
console.log("----------")

// 2.2.将arguments转成数组方式三: ES6中方式
var newArgs1 = Array.from(arguments)
console.log(newArgs1)
var newArgs2 = [...arguments]
console.log(newArgs2)
console.log("----------")

// 2.3.将arguments转成数组方式二: 调用slice方法 slice方法: 不会修改原数组
var newArgs = [].slice.apply(arguments)
// var newArgs = Array.prototype.slice.apply(arguments)
console.log(newArgs)
}

foo(10, 25, 32, 41)

image.png

箭头函数不绑定arguments

箭头函数是不绑定arguments的,所以我们在箭头函数中使用arguments会去上层作用域查找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1.箭头函数不绑定arguments
// var bar = () => {
// console.log(arguments)
// }

// bar(11, 22, 33) // 报错


// 2.函数的嵌套箭头函数
function foo () {
var bar = () => {
console.log(arguments)
}
bar()
}

foo(111, 222)

image.png

函数的剩余(rest)参数

  • ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中:
    • 如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组;
  • 那么剩余参数和arguments有什么区别呢?
    • 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参;
    • arguments对象不是一个真正的数组,而rest参数是一个真正的数组,可以进行数组的所有操作;
    • arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供并且希望以此 来替代arguments的;
  • 剩余参数必须放到最后一个位置,否则会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 剩余参数: rest parameters
function foo(num1, num2, ...otherNums) {
// otherNums数组
console.log(otherNums)
}

foo(20, 30, 111, 222, 333)


// 默认一个函数只有剩余参数
function bar(...args) {
console.log(args)
}

bar("abc", 123, "cba", 321)

image.png

理解JavaScript纯函数

  • 函数式编程中有一个非常重要的概念叫纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念;
    • 在react开发中纯函数是被多次提及的;
    • 比如react中组件就被要求像是一个纯函数(为什么是像,因为还有class组件),redux中有一个reducer的概念,也是要求 必须是一个纯函数;
    • 所以掌握纯函数对于理解很多框架的设计是非常有帮助的;
  • 纯函数的简单总结:
    • 确定的输入,一定会产生确定的输出;
    • 函数在执行过程中,不能产生副作用;

纯函数的案例 slice

  • 我们来看一个对数组操作的两个函数:
    • slice:slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组;
    • splice:splice截取数组, 会返回一个新的数组, 也会对原数组进行修改;
  • slice就是一个纯函数,不会修改数组本身,而splice函数不是一个纯函数; image.png

柯里化

只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数;这个过程就称之为柯里化;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 普通的函数
function foo1(x, y, z) {
console.log(x + y + z)
}

// foo1(10, 20, 30)
// foo1(20, 33, 55)


// 柯里化函数
function foo2(x) {
return function(y) {
return function(z) {
console.log(x + y + z)
}
}
}

foo2(10)(20)(30)
foo2(20)(33)(55)


// 另外一种写法: 箭头函数的写法
// function foo3(x) {
// return y => {
// return z => {
// console.log(x + y + z)
// }
// }
// }

var foo3 = x => y => z => {
console.log(x + y + z)
}

foo3(10)(20)(30)

组合函数

  • 组合(Compose)函数是在JavaScript开发过程中一种对函数的使用技巧、模式:
    • 比如我们现在需要对某一个数据进行函数的调用,执行两个函数fn1和fn2,这两个函数是依次执行的;
    • 那么如果每次我们都需要进行两个函数的调用,操作上就会显得重复;
    • 那么是否可以将这两个函数组合起来,自动依次调用呢?
    • 这个过程就是对函数的组合,我们称之为 组合函数;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 第一步对数字*2
function double(num) {
return num * 2
}

// 第二步对数字**2
function pow(num) {
return num ** 2
}

// 封装的函数: 你传入多个函数, 我自动的将多个函数组合在一起挨个调用
function composeFn(...fns) {
// 1.边界判断(edge case)
var length = fns.length
if (length <= 0) return
for (var i = 0; i < length; i++) {
var fn = fns[i]
if (typeof fn !== "function") {
throw new Error(`index position ${i} must be function`)
}
}

// 2.返回的新函数
return function(...args) {
var result = fns[0].apply(this, args)
for (var i = 1; i < length; i++) {
var fn = fns[i]
result = fn.apply(this, [result])
}
return result
}
}

var newFn = composeFn(double, pow, console.log)
newFn(100)
newFn(55)
newFn(22)

eval函数