• 【JavaScript高级】函数相关知识:函数、纯函数、柯里化、严格模式


    函数

    函数对象的属性

    • name:可以获得函数的名称
    function foo(){
    
    }
    console.log(foo.name);//foo
    '
    运行
    • length:用于返回函数参数的个数,rest参数不算在内
    var bar = (name, age, ...args) => {
    
    }
    console.log(bar.length);//2
    '
    运行

    arguments

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

    function foo(x, y) {
      console.log(arguments) // [arguments] {'0': 10, '1': 20, '2': 30, '3': 40}
    }
    foo(10, 20, 30, 40)
    '
    运行

    array-like表示它不是一个数组类型,而是一个对象类型:

    • 有数组的一些特性,如length,如可以通过index索引来访问
    • 没有数组的一些方法,比如filter、map等

    arguments转Array

    将arguments转成Array后可以使用数组的一些特性。

    方法1:遍历arguments,添加到一个新数组中

    var newArray = []
    
    function foo(x, y) {
        for (arg of arguments) {
            newArray.push(arg);
        }
    }
    
    foo(1, 2, 3, 4, 5)
    
    console.log(newArray);//(5) [1, 2, 3, 4, 5]
    '
    运行

    方法2:调用数组slice函数的call方法,了解即可

    对数组做一个截取,截取的是this。
    这里slice没有参数,所以是从头截取到尾。
    把this绑定到arguments上,就可以截取整个arguments。

    function foo() {
        var args1 = [].slice.call(arguments) 
        console.log(args1);
    }
    foo(1, 2, 3, 4, 5, 6)//(6) [1, 2, 3, 4, 5, 6]
    '
    运行

    方法3:ES6中的两个方法

    • Array.from
    function foo() {
        var args1 = Array.from(arguments)
        console.log(args1);
    }
    foo(1, 2, 3, 4, 5, 6)//(6) [1, 2, 3, 4, 5, 6]
    '
    运行
    • […arguments]
    function foo() {
        var args1 = [...arguments]
        console.log(args1);
    }
    foo(1, 2, 3, 4, 5, 6)//(6) [1, 2, 3, 4, 5, 6]
    '
    运行

    箭头函数不绑定arguments

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

    var bar = () => {
        console.log(arguments);
    }
    bar(1, 2, 3)
    //报错:ncaught ReferenceError: arguments is not defined
    

    箭头函数不绑定arguments,于是就会去上层找:箭头函数——全局——window。发现都没有就报错了。

    剩余参数(rest)

    ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中。
    如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组

    function foo(x, y, ...rest) {
       console.log(x, y);//1 2
       console.log(rest);//[3,4,5]
    }
    
    foo(1, 2, 3, 4, 5)
    '
    运行

    剩余参数与arguments:

    • 剩余参数只包含没有对应形参的实参,而 arguments 包含了传给函数的所有实参
    • arguments对象不是一个真正的数组,而rest参数一个真正的数组,可以进行数组的所有操作
    • 剩余参数必须放到参数的最后一个位置,否则会报错

    纯函数

    定义

    满足以下要求则此函数为纯函数:

    • 函数相同输入必产生相同输出
    • 函数的输出和输入值与外界的其他隐藏信息或状态无关,也和I/O设备所产生的外部输入无关(其实就是上一条的展开)
    • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等

    省流版:

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

    什么是副作用

    在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响

    如:修改了全局变量,修改参数或者改变外部的存储。

    function foo(info) {
      console.log(info.name, info.age, info.height);
      info.flag = "打印结束了";
    }
    
    var obj = {
      name: "kaisa",
      age: 18,
      height: 1.88,
    };
    
    foo(obj);
    console.log(obj);
    '
    运行

    这里调用函数就为obj添加了一个flag属性,这就是副作用。

    纯函数在执行过程中不能产生这样的副作用,因为副作用是bug的温床。

    纯函数的案例

    1

    var names = ["aaa", "bbb", "ccc", "ddd"];
    
    // slice截取数组时不会对原数组进行任何操作, 而是生成一个新的数组
    var newNames1 = names.slice(0, 2);
    console.log(newNames1);
    
    // splice截取数组, 会返回一个新的数组, 也会对原数组进行修改
    var newNames2 = names.splice(0, 2);
    console.log(newNames2);
    console.log(names);
    '
    运行

    slice是纯函数,splice函数不是纯函数。

    2

    function foo(item1, item2) {
      return = item1 + item2
    }
    

    是纯函数.

    3

    var foo = 5;
    
    function add(num) {
      return foo + num;
    }
    
    console.log(add(5)); // 10
    foo = 10;
    console.log(add(5)); // 15
    '
    运行

    不是纯函数.

    4

    function printInfo(info) {
      console.log(info.namem, info.age);
      info.name = "哈哈哈";
    }
    '
    运行

    不是纯函数.

    作用和优势

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

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

    柯里化

    定义

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

    也就是将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)©

    举个例子:

    柯里化前:

    function foo(x, y, z) {
      console.log(x + y + z) // 60
    }
    foo(10, 20, 30)
    '
    运行

    柯里化后:

    // 柯里化函数 普通写法
    function foo1(x) {
      return function foo2(y) {
        return function foo3(z) {
          console.log(x + y + z); // 60
        };
      };
    }
    foo1(10)(20)(30);
    '
    运行
    // 柯里化函数 箭头函数写法
    var foo = (x) => {
      return (y) => {
        return (z) => {
          console.log(x + y + z);
        };
      };
    };
    foo(10)(20)(30);
    
    // 简写箭头函数
    var foo1 = x => y => z => console.log(x + y + z);
    foo1(10)(20)(30);
    '
    运行

    优势

    优势1:函数的职责单一

    • 在函数式编程中,我们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理
    • 那么我们是否就可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果

    如:传入的函数需要分别被进行如下处理

    1. +2
    2. *2
    3. **2
    function foo(x) {
       x = x + 2;
       return function foo1(y) {
           y = y * 2;
           return function foo2(z) {
               z = z ** 2;
               return x + y + z;
           }
       }
    }
    '
    运行

    优势2:函数的参数复用

    function makeAdder(num) {
      return function (count) {
        return num + count;
      };
    }
    
    // 将num参数固定为5, 以后和5相加, 只需要调用add5
    var add5 = makeAdder(5);
    add5(10); // 15
    add5(100); // 105
    
    // 将num参数固定为10, 以后和10相加, 只需要调用add10
    var add10 = makeAdder(10);
    add10(10); // 20
    add10(100); // 110
    '
    运行

    自动柯里化

    实际开发中, 一般引用的第三库中都有自动柯里化的函数。

    组合函数

    定义

    一种对函数的使用技巧、模式。举个例子:我们想对多个数字进行先*2,再平方的操作。

    不组合:

    function fn1(num) {
       return num * 2;
    }
    
    function fn2(num) {
        return num ** 2;
    }
    
    console.log(fn2(fn1(10)));//400
    console.log(fn2(fn1(20)));//1600
    '
    运行

    组合:

    function fn3(num){
        return fn2(fn1(num))
    }
    
    console.log(fn3(10));//400
    console.log(fn3(20));//1600
    

    组合函数的封装

    了解即可。
    在这里插入图片描述

    其他

    with语句

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

    这里如果没有with,js会直接在全局里面找。使用with就可以查找obj中的属性。

    var obj = {
      name: "kaisa",
      age: 18,
      height: 1.88,
    };
    
    //使用with可以查找obj中的属性
    with (obj) {
      console.log(name, age, height);
    }
    '
    运行

    更多可以看看这里:JavaScript中 with的用法

    eval函数

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

    eval是一个特殊的函数,它可以将传入的字符串当做JavaScript代码来运行,如:

    var string = `var name = "kaisa";
                  console.log(name);`;
    eval(string); // kaisa
    '
    运行

    eval会将最后一句执行语句的结果,作为返回值,如:

    var string = `var name = "kaisa";
                  console.log(name);
                  123`;
    var result = eval(string);
    console.log(result) // 123
    '
    运行

    不建议在开发中使用eval:

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

    严格模式

    定义

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

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

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

    开启

    可以在js文件开启,也可以对某一个函数开启。

    严格模式通过在文件或者函数开头使用 use strict 来开启

    文件:

    <script>
      // 给整个script标签开启严格模式
      "use strict"
    </script>
    

    函数:

    function foo() {
      // 在foo函数作用域中开启严格模式
      "use strict";
    }
    '
    运行
    • 没有类似于 “no use strict” 这样的指令可以使程序返回默认模式
    • 现代 JavaScript 支持 “class” 和 “module” ,它们会自动启用 use strict

    限制

    更具体的可以看这里:JavaScript 严格模式(use strict)

    在非严格模式下,一些错误也被认为是正确的,但在严格模式下就会报错。如:

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

    在非严格模式下, 不使用var变量创建变量, 会默认创建到全局变量
    在严格模式下是不允许的,会报错

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

    "use strict"
    var obj = {
    	name: "why"
    }
    
    // 明确设置obj对象中的name属性不可修改
    Object.defineProperty(obj, "name", {
      writable: false
    })
    
    obj.name = "kobe"
    console.log(obj.name); // name
    

    非严格模式下不允许修改, 但是也不会报错
    严格模式下, 明确说明obj中的name不可修改, 如果修改就会报错

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

    非严格模式下, 不允许删除, 也不会报错。(无事发生)

    4.严格模式不允许函数参数有相同的名称

    非严格模式下允许。

    5.严格模式不允许0开头的八进制语法

    非严格模式下, 0开头的数字都会被默认为八进制
    严格模式下, 0开头8进制是不允许写的, 0o是允许的

    6.严格模式下,不允许使用with

    7.严格模式下,eval不能为上层创建变量

    8.严格模式下,this绑定不会默认转成对象

    非严格模式下, 字符串和数字类型会转换成对应的包装类对象
    严格模式下, 不会转换成对象, 且严格模式下独立函数调用不绑定全局对象window, 而是一个undefined

    参考

    coderwhy的课
    JS高级函数的增强使用、纯函数、柯里化、组合函数的详细介绍-及手写柯里化、手写组合函数
    JavaScript函数式编程(纯函数、柯里化以及组合函数)
    JavaScript函数的增强知识(argument、纯函数)
    MDN-with语句
    JavaScript中 with的用法
    JavaScript 严格模式(use strict)

  • 相关阅读:
    品牌连锁店5G/4G无线组网方案
    Linux命令及详细解释、用法
    python项目实战——银行取款机系统(七)
    跟菜鸟教程快速搭建并简单使用Vue
    基于FPGA的图像自适应阈值二值化算法实现,包括tb测试文件和MATLAB辅助验证
    【二叉树魔法:链式结构与递归的纠缠】
    【python爬虫实战】用python爬百度搜索结果!2023.3发布
    【模拟】螺旋矩阵问题
    java项目实现发送邮箱激活用户功能
    Linux之文件打包,压缩,解压
  • 原文地址:https://blog.csdn.net/karshey/article/details/127043166