当前位置: 首页 > news >正文

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

文章目录

    • 函数
      • 函数对象的属性
      • arguments
      • arguments转Array
      • 箭头函数不绑定arguments
      • 剩余参数(rest)
    • 纯函数
      • 定义
      • 纯函数的案例
      • 作用和优势
    • 柯里化
      • 定义
      • 优势
      • 自动柯里化
    • 组合函数
      • 定义
      • 组合函数的封装
    • 其他
      • with语句
      • eval函数
    • 严格模式
      • 定义
      • 开启
      • 限制
    • 参考

函数

函数对象的属性

  • 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)

相关文章:

  • Android多渠道之自定义apk输出
  • Day03 Css的学习深入 background-X属性
  • aardio + Python 可视化快速开发桌面程序,一键生成独立 EXE
  • 分享两款智慧物业系统源码,前后端分离,前端VUE,Uni-app框架
  • 新手看过来----讨厌的运算符
  • Matlab中importdata函数的使用
  • 4)自适应滤波(一)
  • Web前端期末大作业-重庆旅游景区网页设计(HTML+CSS+JS)
  • MySQL:复合查询和内外连接
  • 高亮蓝紫光油溶性ZnSe/ZnS量子点,PL波长390nm-440nm
  • SpringMVC概述及入门案例
  • 这篇文章告诉你三个好用的配音软件
  • 看漫画学Python 原来真的有用
  • Spring Cloud Gateway获取认证用户信息
  • 软件项目的自动化测试
  • JavaScript-如何实现克隆(clone)函数
  • 【前端学习】-粗谈选择器
  • 2017-09-12 前端日报
  • Android开源项目规范总结
  • SpingCloudBus整合RabbitMQ
  • vue-cli3搭建项目
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 理清楚Vue的结构
  • 排序算法学习笔记
  • 强力优化Rancher k8s中国区的使用体验
  • 一份游戏开发学习路线
  • 正则表达式
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • #Lua:Lua调用C++生成的DLL库
  • #pragma pack(1)
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (1)SpringCloud 整合Python
  • (10)ATF MMU转换表
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (LeetCode 49)Anagrams
  • (TOJ2804)Even? Odd?
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • *Django中的Ajax 纯js的书写样式1
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .Net Redis的秒杀Dome和异步执行
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • .NET上SQLite的连接
  • .NET业务框架的构建
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法