js函数增强

news2024/11/27 1:39:47

目录

  • 函数的属性
    • arguments
    • 将arguments转换成数组
    • rest
  • 纯函数
  • 柯里化函数
  • 自动实现函数柯里化
  • 组合函数
  • 自动实现组合化
  • with与eval
    • with
    • eval
  • 严格模式
    • 严格模式的限制

函数的属性

函数其实也是一个对象
是对象就会有对应的方法与属性
以下是几个常用属性

  1. name
    name属性主要用于访问给定函数的名字,在匿名函数中这个值为空
  2. length
    length属性中存放的则是函数参数的个数
    注意,如果是rest参数的话则不会计入
  3. prototype
    这个属性指向了函数的原型对象,当函数作为构造函数生成新对象时新对象会继承原型对象中的属性
    关于原型与继承可以看我这篇文章
    (未动笔,未来可寄)

arguments

arguments是一个对应于传递给函数的参数的类数组对象
即你调用函数时传入的参数都存放在arguments
arguments是一个类数组(like-Array)对象
它有着length属性和能通过索引访问元素
但没有数组对应的方法

将arguments转换成数组

arguments转换成数组有以下三种办法

  1. 遍历整个arguments,将其中的元素一个一个push到新数组中
        function foo() {
            var newArguments = [];
            for (var i = 0; i < arguments.length; i++) {
                newArguments.push(arguments[i])
            }
            console.log(newArguments)
        }
        foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
    
    这种方法最简单,同样的代码也很多
  2. 使用ES6提供的新语法
        function foo() {
            var newArguments = Array.from(arguments);
            console.log(newArguments)
        }
        foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
    
    这种方法使用ES6提供的from方法实现
            function foo() {
            var newArguments = [...arguments];
            console.log(newArguments)
        }
        foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
    
    也可以通过展开运算符来实现
  3. 使用slice方法加显式绑定this实现
        function foo() {
            var newArguments = [].slice.apply(arguments);
            console.log(newArguments)
        }
        foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
    
    如果需要兼容ES5以下环境的话可以使用这种方法或第一种方法
    注意,不能直接Array.slice,因为slice实例方法
    可以通过Array.prototype.slice实现

注意,arguments只存在于非箭头函数中,在箭头函数argumentsrest替代

rest

箭头函数中不绑定arguments,在箭头函数中使用arguments会在上层作用域中查找
作为替代,ES6中引入了了rest parameter,可以将不定数量的参数放入数组中
arguments不同的是,rest是一个数组
rest只包含那些没有形参对应的参数
如果一个函数有形参,那么rest必须放到最后面

        function foo(a, b, ...args) {
            console.log(a, b)
            console.log(args)
        }
        foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)

控制台打印结果
结果

纯函数

在函数式编程中有个概念叫纯函数
只要一个函数符合以下条件就是一个纯函数

  1. 函数在有相同的输入时,需要有相同的输出
  2. 函数不能有副作用

副作用即函数除了返回值之外还对函数调用产生了附加影响
修改全局变量修改参数或外部存储
纯函数的最大好处就是你可以安心的编写以及安心的使用
只需要关心业务逻辑,不需要关心传入的值是否依赖外部变量
在用的时候也可以确定输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出
以下是几个简单的纯函数例子

        function sum(num1, num2) {
            return num1 + num2
        }
        var num = sum(1, 2)

下面就不是一个纯函数,因为确定的输入并不会导致确定的输出

        var flag = true
        function sum(num1, num2) {
            if (flag) {
                return num1 + num2
            } else {
                return num1 * num2
            }
        }
        var num = sum(1, 2)
        flag = false
        num = sum(1, 2)

柯里化函数

函数柯里化即将函数一次接收多个参数变成一次接收一个并且返回一个新函数接收剩下的参数
这个过程就称之为柯里化
柯里化的优势有以下几点

  1. 函数职责单一
    函数式编程中,我们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理
    那么我们就可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果
  2. 函数的参数复用
    如下代码所示
    	function addCurried(a) {
    		return function(b) {
    			return add(a, b);
    		}
    	}
    	const add5 = addCurried(5);
    	console.log(add5(3)); // 输出 8
    	console.log(add5(7)); // 输出 12
    
    如果我们想计算多次5+7和5+3的话就可以利用柯里化的这个特点来实现每次只输入一个参数,剩下的参数进行复用

以下是一个简单的柯里化函数

        function sum(x, y, z) {
            return x + y + z
        }
        function sum2(x) {
            return function sum3(y) {
                return function sum4(y) {
                    return x + y + z
                }
            }
        }
        console.log(sum(1, 2, 3))
        console.log(sum2(1)(2)(3))

在这其中,sum为改造之前的函数,sum2为改造后的柯里化函数

自动实现函数柯里化

封装一个函数来帮我们自动实现柯里化操作

        function sum(x, y, z) {
            return x + y + z
        }
        function logInfo(error, msg) {
            console.log(`${error}:${msg}`)
        }
        function hyCurrying(fn) {
            function curryFn(...args) {
                if (args.length >= fn.length) {
                    return fn(...args)
                } else {
                    return function (...rest) {
                        return curryFn(...args.concat(rest))
                    }
                }
            }
            return curryFn
        }
        var sumCurry = hyCurrying(sum)
        var infoCurry = hyCurrying(logInfo)
        console.log(sumCurry(1)(2)(3))
        console.log(sum(1, 2, 3))
        infoCurry("ERROR")("not defined")
        logInfo("ERROR", "not defined")

这里使用hyCurrying构造一个柯里化函数curryFn
curryFn中主要做两件事

  1. 当传入的参数大于等于原本函数所需的参数时,就执行原函数,并将原函数可能得返回值返回
  2. 当传入的参数小于原本函数所需的参数时就需要再次调用函数(即以这样的形式func()()),定义一个新函数并将这个新函数返回给sumCurry调用,将再次传进去的参数rest合并到原本的args参数中然后调用curryFn函数,并将curryFn的返回值返回

最后来看看结果

结果
有些时候,我们可能在使用函数的时候给他显式绑定this,这个时候以上的代码就需要进行更改

        function hyCurrying(fn) {
            function curryFn(...args) {
                if (args.length >= fn.length) {
                    return fn.apply(this,args)
                } else {
                    return function (...rest) {
                        return curryFn.apply(this,args.concat(rest))
                    }
                }
            }
            return curryFn
        }

组合函数

有时候我们需要对某一个数据进行函数调用,会执行两个函数
每调用次函数,就要执行两个函数,操作上就显得重复了
将两个函数组合起来,自动调用,这个过程就是组合函数
以下是一个组合函数的例子

        function double(num) {
            return num * 2
        }
        function square(num) {
            return num ** 2
        }
        function compose(fn1, fn2) {
            return function (x) {
                return fn2(fn1(x))
            }
        }
        var calcFn = compose(double, square)
        console.log(calcFn(20))

自动实现组合化

上面列举的代码只能实现两个函数的组合,有时候我们需要传入更多的函数与参数时就需要对以上代码进行优化

        function double(num) {
            return num * 2
        }
        function square(num) {
            return num ** 2
        }
        function compose(...fns) {
            for (var fn of fns) {
                if (typeof fn !== "function") {
                    throw new TypeError(`${fn} Expected a function`)
                }
            }
            if (fns.length == 0) return
            return function (...args) {
                var result = fns[0].apply(this, args)
                for (var i = 1; i < fns.length; i++) {
                    result = fns[i].call(this, result)
                }
                return result
            }
        }
        var calcFn = compose(double, square)
        console.log(calcFn(20))

在函数compose中我们先对传入的参数进行判断,如果传入的不是函数,或者传入的参数为空就直接抛出异常结束执行
在之后我们返回一个新函数
新函数首先会调用fns中的第一个函数来得到result以此作为以后函数传入的参数
每次函数调用完毕后都将更新result里的值,直到fns里的函数运行完毕返回result的值

with与eval

无论是with还是eval都会造成兼容和性能问题,真实开发中一般并不常用仅做了解

with

with语句主要用于延长语句的作用域链
它会在包裹的语句上额外添加一层作用域链
with会造成许多兼容性问题

        var obj = {
            message: "hello world"
        }
        with (obj) {
            console.log(message)
        }

打印结果

结果

eval

eval可以将传入的字符串当做JavaScript代码来运行
eval会将最后一句执行语句的结果,作为返回值
eval代码必须加上分号
eval代码的可读性十分差
eval代码因为是字符串容易被刻意篡改,造成安全问题
eval代码无法被浏览器优化

        var str = 'var message = "hello world"; console.log(message)'
        eval(str)

结果

结果

严格模式

JavaScript长久以来的发展或多或少的留下了一些错误或者不完善的问题
为了兼容旧代码,这些问题会永远的留在了JavaScript
为了解决这个问题,在ES5中正式提出了严格模式的概念
严格模式是一种具有限制性的JavaScript模式
在这种模式运行下的代码会受到更为严格的检测
严格模式对正常的JavaScript语义进行了一些限制

  1. 严格模式会通过抛出错误来替换以前的静默错误
  2. 严格模式也会让代码得到更好的优化
  3. 严格模式也会禁用ES未来版本中可能定义的一些语法

开启严格模式只需要在代码最上方加一行

"use strict"

严格模式支持在js文件中开启
在文件中使用即代表整个文件开启严格模式
也支持在函数中开启
在函数中使用即代表整个函数开启严格模式
必须将此代码写在最上方才能生效
注意,开启严格模式无法返回默认模式

严格模式的限制

  1. 无法意外的创建全局变量
  2. 严格模式会使引起静默失败的赋值操作抛出异常
  3. 严格模式下不允许删除不可删除的属性
  4. 4.严格模式不允许函数参数有相同的名称
  5. 不允许0开头的八进制语法
  6. 在严格模式下,不允许使用with
  7. 在严格模式下,eval不再为上层引用变量
  8. 严格模式下,this绑定不会默认转成对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/781971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue 富文本图片回显

富文本上传 <el-form-item label"服务费打款银行回单" prop"bankreceipt"> <!-- <ImageUpload--> <!-- :value"form.bankreceiptUrl"--> <!-- :fileType"fileType"--> <…

【解决方案】视频传输方案怎样选择适用的WiFi模块

工作环境中&#xff0c;我们接触的最多的是有线传输&#xff0c;但是这个会因为转换接口、传输距离等原因受到一些限制&#xff0c;而无线传输不会&#xff0c;不需要布线&#xff0c;不限制接口&#xff0c;传输距离也由于有线传输&#xff0c;这也是物联网广泛使用无线通信技…

了解下余弦相似度在文本处理中的应用

大家好啊&#xff0c;我是董董灿&#xff01; 昨天写了一篇介绍词向量的文章&#xff1a;5分钟搞懂什么是词嵌入&#xff0c;里面说到&#xff1a;通过把文本转换为词向量&#xff0c;就可以十分方便的计算两者之间的关系&#xff0c;看看哪两个单词更为相近。 比如有四个单词…

AtCoder Beginner Contest 288 F. Integer Division(递推+前缀和优化dp)

题目 给定一个n(2<n<2e5)位的数字串X&#xff0c; 可以将X划分成若干段&#xff0c;得分为每一段的乘积&#xff08;可以不分割&#xff0c;此时得分为X&#xff09; 求所有种分法的得分之和&#xff0c;答案对998244353取模 思路来源 洛谷题解 [ABC288F] Integer …

【PostgreSQL内核学习(十)—— 查询执行(可优化语句执行)】

可优化语句执行 概述物理代数与处理模型物理操作符的数据结构执行器的运行 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要…

SAP客制化区域菜单和IMG配置清单

1. 自定义区域菜单 事务代码 SE43&#xff0c;操作如下 添加菜单对象 展示效果 输入区域菜单名称并回车&#xff0c;效果如下 2. 自定义IMG配置 事务代码 SIMGH IMG structure 示例-事务代码入口 示例-表格维护入口 示例-自定义代码控制对象 需要创建dummy表并设置表维护 页面设…

平头哥TH5120 BeagleV-Ahead开机系统软件使用体验

等了许久&#xff0c;Beagle 社区官网终于上线了BeagleV-Ahead 的主页 网址 https://beagleboard.org/beaglev-ahead &#xff0c;我们的系统软件评测将会以这个官方主页为出发点 &#xff0c;进行一系列的系统软件功能等操作演示&#xff0c;因官网没有中文页面&#xff0c;我…

【雕爷学编程】Arduino动手做(168)---ATTINY85迷你USB开发板2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

瑞吉外卖开发笔记 二

1、完善登录功能 问题分析 前面我们已经完成了后台系统的员工登录功能开发&#xff0c;但是还存在一个问题:用户如果不登录&#xff0c;直接访问系统首页面&#xff0c;照样可以正常访问。 这种设计并不合理&#xff0c;我们希望看到的效果应该是&#xff0c;只有登录成功后…

JVM运行时数据区——字符串常量池位置的调整

在JDK6及之前&#xff0c;使用永久代来实现方法区&#xff0c;字符串常量池(StringTable)是在永久代(方法区)中的&#xff0c;但是方法区的回收效率不高&#xff0c;在Full GC时才会回收。 在JDK7中&#xff0c;将字符串常量池转移到了堆中&#xff0c;分配在年轻代和老年代中。…

ROS——roslaunch传参注意

本文就总结自己最近开发项目中出现的一些roslaunch有关传参的注意事项说明一下&#xff0c;以免下次有犯错 roslaunch 文件传入到.cpp 这里我采用传参比较简单的方式&#xff0c;还有其他方式&#xff0c;不过这种最为简便 nh.param<xxx>("roslaunch中的名字,如A…

基于 H5 的画图工具

完整资料进入【数字空间】查看——baidu搜索"writebug" 第一章 绪论 1.1 研究的背景 计算机图形学是随着计算机及其外围设备而产生和发展起来的。它是近代计算机科学 与雷达电视及图象处理技术的发展汇合而产生的硕果。在造船、航空航天、汽车、电子、 机械、土建工…

Spring 容器

Spring 容器 两个核心接口&#xff1a;BeanFactory 和 ApplicationContext&#xff08;是BeanFactory的子接口&#xff09;&#xff0c;生成Bean实例并管理Bean的工厂 Bean 对象 Spring管理的基本单位&#xff0c;在基于Spring应用中&#xff0c;所有的组件都可以理解为是一…

【前端|CSS系列第4篇】CSS布局之网格布局

前言 最近在做的一个项目前台首页有一个展示词条的功能&#xff0c;每一个词条都以一个固定大小的词条卡片进行展示&#xff0c;要将所有的词条卡片展示出来&#xff0c;大概是下面这种布局 每一行的卡片数目会随着屏幕大小自动变化&#xff0c;并且希望整个卡片区域周围不要…

【CSDN竞赛】练习题(练手题)及解析(含代码+注释):小玉家的电费;饿龙咆哮-逃离城堡;收件邮箱;寻找宝藏山

订阅专栏,学习更多干货知识! 为想要参与CSDN竞赛的朋友提供几道练手题目及解决方案。含有题目描述以及不同编程语言的解决方案。 💿目录 🏮一、题目一:小玉家的电费📂1.1 题目描述📂1.2 示例代码🏮二、题目二:饿龙咆哮-逃离城堡📂2.1 题目描述📂2.2 示例代码…

基于PySceneDetect的视频场景变换侦测与处理

剪映中集成了一个智能镜头分割的功能,其实是基于python的三方库PySceneDetect来实现的,主要用于对视频进行分析,寻找场景切换或剪辑。 不过一个一个处理起来比较麻烦,这里介绍一个python的三方库实现自动化批量处理。 文章目录 PySceneDetect主要功能特征PySceneDetect的安…

股票基金入门知识

1.开盘价和收盘价如何产生 时间9:30-11:30 13:00-15:00 集合竞价时间段&#xff1a;9:15-9:25 以此产生开盘价 最后集中竞价时间段&#xff1a;深市14:57-15:00 &#xff0c;以此产生收盘价。 沪市则采用最后一分钟加权得出收盘价影响股价的因素 市场投资情绪&#xff0c;宏观…

Java实现字典树单词插入、查找以及删除

文章目录 前言题目思路代码实现测试用例结果输出 结语 前言 字典树又称为前缀树或Trie树&#xff0c;是处理字符串常见的数据结构。 字典树是一种树形结构&#xff0c;优点是利用字符串的公共前缀来节约存储空间&#xff0c;比如插入"abc"、“abcd”、 “abd”、“…

JTAG 和 SWD 接口定义

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 JTAG引脚分布接…

js的事件循环机制(详解)

答题思路&#xff1a; ● 首先基本定义&#xff0c;宏任务和微任务 ● 事件循环机制执行顺序 ● async / await 执行顺序 一、什么是事件循环 事件循环机制就是一种同步编程模型&#xff0c;用于异步处理操作。当代码中遇到需要等待一部操作结果的语句时&#xff0c;js引擎不会…