函数属性和arguments

函数对象的属性

◼ 我们知道JavaScript中函数也是一个对象,那么对象中就可以有属性和方法。
属性name:一个函数的名词我们可以通过name来访问;

  1. function foo(){}
  2. console.log(foo.name) //foo

属性length:属性length用于返回函数参数的个数;

  • 注意:rest参数是不参与参数的个数的;

    1. var baz = (name,age,...argz){}
    2. console.log(brz.length)

    认识arguments

    arguments 是一个 对应于 传递给函数的参数 的 类数组(array-like)对象

    1. function foo(x,y,z){
    2. console.log(arguments)
    3. }
    4. foo(10,20,30)

    ◼ array-like意味着它不是一个数组类型,而是一个对象类型:

  • 但是它却拥有数组的一些特性,比如说length,比如可以通过index索引来访问;

  • 但是它却没有数组的一些方法,比如filter、map等;

image.png

arguments转Array

在开发中,我们经常需要将arguments转成Array,以便使用数组的一些特性。

  • 常见的转化方式如下

转化方式一:

  • 遍历arguments,添加到一个新数组中;

转化方式二:较难理解(有点绕),了解即可

  • 调用数组slice函数的call方法;

转化方式三:ES6中的两个方法

  • Array.from
  • […arguments]

image.png

箭头函数不绑定arguments

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

函数的剩余(rest)参数

ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中:

  • 如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组;

image.png
那么剩余参数和arguments有什么区别呢?

  • 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参;
  • arguments对象不是一个真正的数组,而rest参数是一个真正的数组,可以进行数组的所有操作;
  • arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供并且希望以此 来替代arguments的;

剩余参数必须放到最后一个位置,否则会报错。

纯函数的理解和应用

理解JavaScript纯函数

函数式编程中有一个非常重要的概念叫纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念;

  • 在react开发中纯函数是被多次提及的;
  • 比如react中组件就被要求像是一个纯函数(为什么是像,因为还有class组件),redux中有一个reducer的概念,也是要求 必须是一个纯函数;
  • 所以掌握纯函数对于理解很多框架的设计是非常有帮助的;

纯函数的维基百科定义:

  • 在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数:
  • 此函数在相同的输入值时,需产生相同的输出。
  • 函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。

当然上面的定义会过于的晦涩,所以我简单总结一下:

  • 确定的输入,一定会产生确定的输出;
  • 函数在执行过程中,不能产生副作用;

    副作用概念的理解

    那么这里又有一个概念,叫做副作用,什么又是副作用呢?

  • 副作用(side effect)其实本身是医学的一个概念,比如我们经常说吃什么药本来是为了治病,可能会产生一些其他的副作 用;

  • 在计算机科学中,也引用了副作用的概念,表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响, 比如修改了全局变量,修改参数或者改变外部的存储;

纯函数在执行的过程中就是不能产生这样的副作用:

  • 副作用往往是产生bug的 “温床”。

    纯函数的案例

    我们来看一个对数组操作的两个函数:

  • slice:slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组;

  • splice:splice截取数组, 会返回一个新的数组, 也会对原数组进行修改;

slice就是一个纯函数,不会修改数组本身,而splice函数不是一个纯函数;
image.png

纯函数的作用和优势

为什么纯函数在函数式编程中非常重要呢?

  • 因为你可以安心的编写和安心的使用;
  • 你在写的时候保证了函数的纯度,只是单纯实现自己的业务逻辑即可,不需要关心传入的内容是如何获得的或者依赖其他的外部变量是否已经发生了修改;
  • 你在用的时候,你确定你的输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出;

◼ React中就要求我们无论是函数还是class声明一个组件,这个组件都必须像纯函数一样保护它们的props不被修改:
image.png

柯里化的理解和应用

柯里化概念的理解

柯里化也是属于函数式编程里面一个非常重要的概念。

  • 是一种关于函数的高阶技术;
  • 它不仅被用于 JavaScript,还被用于其他编程语言;

我们先来看一下维基百科的解释:

  • 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化加里化
  • 是把接收多个参数的函数,变成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数,而且返回 结果的新函数的技术;
  • 柯里化声称 “如果你固定某些参数,你将得到接受余下参数的一个函数”;

维基百科的结束非常的抽象,我们这里做一个总结:

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

◼ 柯里化是一种函数的转换,将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。

  • 柯里化不会调用函数。它只是对函数进行转换。

    柯里化的代码转换

    image.png
    image.png

    柯里化优势一 - 函数的职责单一

    那么为什么需要有柯里化呢?

  • 在函数式编程中,我们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理;

  • 那么我们是否就可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果;

◼ 比如上面的案例我们进行一个修改:传入的函数需要分别被进行如下处理

  • 第一个参数 + 2
  • 第二个参数 * 2
  • 第三个参数 ** 2

image.png

柯里化优势二 - 函数的参数复用

◼ 另外一个使用柯里化的场景是可以帮助我们可以复用参数逻辑:

  • makeAdder函数要求我们传入一个num(并且如果我们需要的话,可以在这里对num进行一些修改);
  • 在之后使用返回的函数时,我们不需要再继续传入num了;

image.png

柯里化案例练习

◼ 这里我们在演示一个案例,需求是打印一些日志:

  • 日志包括时间、类型、信息;

◼ 普通函数的实现方案如下:
image.png
image.png

柯里化高级 - 自动柯里化函数

◼ 目前我们有将多个普通的函数,转成柯里化函数:
image.png

组合函数的理解和应用

组合函数概念的理解

组合(Compose)函数是在JavaScript开发过程中一种对函数的使用技巧、模式

  • 比如我们现在需要对某一个数据进行函数的调用,执行两个函数fn1和fn2,这两个函数是依次执行的;
  • 那么如果每次我们都需要进行两个函数的调用,操作上就会显得重复;
  • 那么是否可以将这两个函数组合起来,自动依次调用呢?
  • 这个过程就是对函数的组合,我们称之为 组合函数(Compose Function);

image.png
image.png

实现组合函数

◼ 刚才我们实现的compose函数比较简单
◼ 我们需要考虑更加复杂的情况:比如传入了更多的函数,在调用compose函数时,传入了更多的参数:
image.png

with/eval的使用

with语句的使用

with语句 扩展一个语句的作用域链 。
image.png
◼ 不建议使用with语句,因为它可能是混淆错误和兼容性问题的根源。

eval函数的使用

内建函数 eval 允许执行一个代码字符串。

  • eval是一个特殊的函数,它可以将传入的字符串当做JavaScript代码来运行;
  • eval会将最后一句执行语句的结果,作为返回值;

image.png
不建议在开发中使用eval:

  • eval代码的可读性非常的差(代码的可读性是高质量代码的重要原则);
  • eval是一个字符串,那么有可能在执行的过程中被刻意篡改,那么可能会造成被攻击的风险;
  • eval的执行必须经过JavaScript解释器,不能被JavaScript引擎优化;

    严格模式的使用

    认识严格模式

    JavaScript历史的局限性:

  • 长久以来,JavaScript 不断向前发展且并未带来任何兼容性问题;

  • 新的特性被加入,旧的功能也没有改变,这么做有利于兼容旧代码;
  • 但缺点是 JavaScript 创造者的任何错误或不完善的决定也将永远被保留在 JavaScript 语言中;

◼ 在ECMAScript5标准中,JavaScript提出了严格模式的概念(Strict Mode)

  • 严格模式很好理解,是一种具有限制性的JavaScript模式,从而使代码隐式的脱离了 ”懒散(sloppy)模式“;
  • 支持严格模式的浏览器在检测到代码中有严格模式时,会以更加严格的方式对代码进行检测和执行;

◼ 严格模式对正常的JavaScript语义进行了一些限制:

  • 严格模式通过 抛出错误 来消除一些原有的 静默(silent)错误;
  • 严格模式让JS引擎在执行代码时可以进行更多的优化(不需要对一些特殊的语法进行处理);
  • 严格模式禁用了在ECMAScript未来版本中可能会定义的一些语法;

    开启严格模式

    那么如何开启严格模式呢?严格模式支持粒度话的迁移:

  • 可以支持在js文件中开启严格模式;

  • 也支持对某一个函数开启严格模式;

严格模式通过在文件或者函数开头使用 use strict 来开启。
image.png
image.png
没有类似于 “no use strict” 这样的指令可以使程序返回默认模式。

  • 现代 JavaScript 支持 “class” 和 “module” ,它们会自动启用 use strict;

    严格模式的限制

    ◼ 这里我们来说几个严格模式下的严格语法限制:

  • JavaScript被设计为新手开发者更容易上手,所以有时候本来错误语法,被认为也是可以正常被解析的;

  • 但是这种方式可能给带来留下来安全隐患;
  • 在严格模式下,这种失误就会被当做错误,以便可以快速的发现和修正;

◼ 1. 无法意外的创建全局变量

  • 使用var声明的变量不会挂载到window对象上

◼ 2. 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常

  • name = "张三" 这种没有声明变量就直接对变量赋值的语句会报错处理

◼ 3. 严格模式下试图删除不可删除的属性

  • 属性描述符配置不可删除之后,在严格模式下尝试删除会报错

◼ 4.严格模式不允许函数参数有相同的名称
◼ 5. 不允许0的八进制语法
◼ 6. 在严格模式下,不允许使用with
◼ 7. 在严格模式下,eval不再为上层引用变量
◼ 8. 严格模式下,this绑定不会默认转成对象

  • this不会默认绑定,默认调用函数,this为空

作业与总结

一. 理解纯函数以及编写自己的纯函数

纯函数:纯函数一般具有以下的特点:

  • 确定的输入一定会有确定的输出(外部环境的任何变化不会影响函数内部的操作产生的结果)
  • 纯函数的执行不会产生副作用。(函数内部的操作也不会对函数外部产生任何影响)

纯函数在react和redux中应用比较多。
编写纯函数:

  1. //一般的数学方法可以写成纯函数,例如相加
  2. function sum(...args) {
  3. var result = args.reduce((perValue, item) => {
  4. return preValue + item
  5. }, 0)
  6. return result
  7. }

二. 理解函数柯里化以及说出柯里化的作用

函数的柯里化:将传入多个参数的函数转变成传入单个参数并且返回一个函数用于接收剩余的参数的函数。每一层函数都接收一个参数并对参数进行处理。

柯里化的作用:

  • 单一职责:每一个函数只用处理传入的单个参数,每个函数的职责单一而且确定
  • 参数复用:可以拿到每一层函数执行的返回值作为一个新的函数,复用已经传入过的参数。

三. 理解组合函数以及组合函数的作用

组合函数:组合函数是将多个函数组合到一起,进行依次调用的函数使用模式。
组合函数的作用:

  • 减少重复代码的编写,提高代码的复用性,便于开发。
  • 可以对任意个函数进行组合,返回新的具有多个被组合函数功能的新函数

四. 说说你对严格模式的理解

严格模式是一种JavaScript的限制模式,因为种种历史原因,JavaScript语言在非严格模式下是比较松散的。在JavaScript不断优化和加入新特性的过程中,为了兼容早期的JavaScript,一些错误和不规范的写法也被保留了下来。这些错误也不会被抛出。在开启了严格模式后,js引擎会以一种更严格的规范执行JavaScript代码,一些不规范的写法和错误也会直接抛出。

开启严格模式的方法:

  • 对文件开启:在文件的开头写上”use strict”
  • 对函数开启:在函数的开头写上”use strict”

严格模式下的语法限制:

  • 不允许意外创建全局变量(不写var、let、const这种声明变量的关键字)
  • 会对静默失败的赋值操作抛出异常
  • 试图删除不可删除的属性
  • 不允许函数参数有相同的名称
  • 不允许只有0开头的八进制语法
  • 不允许使用with
  • 无法获取eval中定义的变量
  • this绑定不会默认转成对象