js 函数、闭包及函数对象
js的函数是对象,可以通过程序来操控。比如,可以把函数赋值给变量,然后再传递给其他函数,也可以在函数上设置属性,甚至调用函数的方法。
js函数可以嵌套定义在其他函数里,内嵌函数可以访问定义在函数作用域的任何变量。这意味着js函数是闭包,基于闭包可以实现重要且强大的编程技巧。
1 定义函数
function关键字可以用作函数声明或表达式。ES6定义了“箭头函数”,语法特别简洁,很适合把函数作为参数传给另一个函数。
1.1 函数声明
例如 function fun(x,y) {}
1)函数的名字变成了一个变量,这个变量的值就是函数本身。
2)函数声明语句会被“提升”到包含脚本、函数或代码块的顶部,因此,调用代码可以出现在函数定义代码之前。
1.2 函数表达式
很像函数声明,但函数表达式出现在复杂表达式或语句的上下文中,而且函数名是可选的。例如:
cosnt fun1 = function(x) { return x + 1; };
// 函数表达式可以包含名字,这对递归有用。
const fun2 = function fun(x) { if (x <= 1) return 1; else return fun(x-1); };
// 可以作为其他函数的参数
[1,2,3].sort(function(a,b) { return a-b; });
// 可以定义完立即调用
let resutl = (function(x) { return x * x; }(10)); // result = 100
表达式函数不能在它们定义之前被调用。
1.3 箭头函数
ES6中定义的,一般形式是圆括号中逗号分隔的参数列表,后跟箭头=>,再跟包含在花括号中的函数体,例如:
(x,y) => { return x+y; }
// 函数体中只有一个return语句时,可以省略return关键字、语句末尾的分号以及花括号
(x,y) => x+y
// 只有一个参数时,也可省略包围参数列表的圆括号
x => x * x
// 对于没有参数的箭头函数,则必须把空圆括号写出来
() => 37
箭头函数相比于其他方式定义的函数,有关极其重要的区别:箭头函数从定义自己的环境继承this值。而不想其他方式定义的函数从调用上下文继承this值。而且箭头函数没有prototype属性。
2 调用函数
js函数可以通过5种方式来调用:1)作为函数;2)作为方法;3)作为构造函数;4)通过call()或apply()方法间接调用;5)通过js语言特性隐式调用。
| 函数调用 | 对于非严格模式下的函数调用,this值是全局对象。但在严格模式下,this值是undefined。 |
| 方法调用 | this值是调用它的对象。 |
| 箭头函数 | 总是继承自身定义所在环境的this值。 |
表 函数调用、方法调用及箭头函数的this值
隐式函数调用:对象属性的设置或获取方法、代理对象等。
2.1 构造函数调用
如果函数或方法调用前面加了一个关键字,那它就是构造函数调用。
例如:let o = new Object();
与常规函数的区别在于参数处理、调用上下文(this)和返回值。
构造函数调用会创建一个新的空对象,这个对象继承构造函数的prototype属性指定的对象。新创建的对象会被复制到这个函数的this中。例如:
let obj = new o.m(); 在方法m中,this值是这个被创建的对象而不是o。
构造函数正常情况下不使用return关键字,但是如果构造器函数显式使用啦return语句返回某个对象,那么这个对象就会变成调用表达式的值。如果没有使用return 或者 return 的是一个原始值,则这个返回值会被忽略,仍然以新创建的对象作为调用表达式的值。
3 函数作为值
js 中函数是一个对象。
function fun() {}; //定义一个fun函数,fun是引用函数对象的一个变量名。
let o = {fun: function() {}}; //把函数赋值给对象的属性
3.1 定义函数自己的属性
如果一个函数需要一个“静态”变量,且这个变量的值需要在函数每次调用时都能访问到,则通常把这个变量定义为函数自身的一个属性。例如:
fun.counter = 0;
function fun() { return fun.counter++; }
fun(); // 0
fun(); // 1
4 闭包
js使用词法作用域,这意味着函数执行时使用的是定义函数时生效的变量作用域,而不是调用函数时生效的变量作用域。js函数对象内部不仅要包括函数代码,还要包括对函数定义所在作用域的引用。这种函数对象与作用域(即一组变量绑定)组合起来解析函数变量的机制,被称作闭包。
4.1 闭包的作用
闭包真正值得关注的时候,是定义函数与调用函数的作用域不同的时候。
最常见的情形是一个函数返回了在它内部定义的嵌套函数。
let scope = "global";
function fun() {
let scope = "fun-local";
return function() { return scope }
}。
console.log(fun()()); //fun-local
闭包的本质是:它们会捕获自身定义所在外部函数的局部变量绑定。
let addFun = ( function () {
let count = 0;
return function () { return count++; };
}());
console.log(addFun()); // 0
console.log(addFun()); // 1
在上面代码中,不能通过其他方式直接操作count变量,只能通过addFun这个函数。这样就实现了一个通用的共享私有状态的函数。
4.2 var 和 let 作用域问题
function fun() {
let funArr = [];
for (var i = 0; i < 3; i++) {
funArr[i] = () => i;
}
return funArr
}
let funArr = fun();
console.log(funArr[0]()); // 3
console.log(funArr[1]()) ; // 3
console.log(funArr[2]()); // 3
这跟我们预期结果不同,预期结果是:1、2、3 而不是全都是3。这是因为var作用域提升到了fun()函数顶部,而不是只局限于1次循环,而且与闭包关联的作用域是“活的”,所以在for循环中,修改了i的变量值,最后值变为3,在后续调用中,都共同访问了i这个变量,所以返回的值都是3。
ES6增加了块级作用域变量解决了这个问题,只要把var替换成let或者const。
function fun() {
let funArr = [];
for (let i = 0; i < 3; i++) {
funArr[i] = () => i;
}
return funArr
} l
et funArr = fun();
console.log(funArr[0]()); // 0
console.log(funArr[1]()); // 1
console.log(funArr[2]()); // 2
let 和const是块级作用域的标志,这意味着每一次循环都会定义一个与其他循环不同的独立作用域,而每个作用域中都有自己独立的i绑定。
5 函数属性、方法和构造函数
5.1 call()和apply()方法
运行间接调用一个函数,就像这个函数是某个其他对象的方法一样。第一个参数是要在其上调用这个函数的对象,也就是函数的上下文(this)。
function fun() { console.log(this) }
let obj = { name: ‘obj’}
fun.call(obj);
其中fun.call(obj);的作用相当于:
obj.m = fun;
obj.m();
delete obj.m;
如果箭头函数调用call()及apply()方法,则第一个参数实际上会被忽略。
除了第一个参数,后续所有参数都会传给被调用的函数,apply()方法与call()方法类似,只不过要传给函数的参数需要以数组的形式提供,例如:
function fun(x,y,z) {};
let obj = {};
fun.call(obj,1,2,3); //等价于fun.apply(obj,[1,2,3])
5.2 bind()方法
把函数绑定到对象上,bind()方法第一个参数是要绑定的对象。这个方法会绑定一个新的函数,如果作为函数来调用这个新函数,就会像f是绑定对象的方法一样调用原始函数。
function fun() { console.log(this.x) }
let obj = {x:3};
let newFun = fun.bind(obj)
newFun(); // 3
console.log(obj); // { x: 3 },注意,bind()方法并不会改变该对象的结构,即obj实际上是没有fun这个方法
let obj2 = {x:4, newFun}; obj2.newFun(); // 3, 仍然是像fun是obj上的方法一样,即始终是obj调用了fun
注意,箭头函数调用了bind方法绑定不会起作用。
5.2.1 绑定部分应用
bind()方法也可以执行“部分应用”,即在第一个参数之后传给bind()的参数也会随着this值一起被绑定。
let sum = (x,y) => x + y;
let fun1 = sum.bind(null,3)
console.log(fun1(2,4)); // 5. sum函数中x已被绑定,fun1()形参只有一个y
5.3 Function()构造函数
const f = new Function(“x”,”y”,”return x+y;”); 相当于
const f = function(x,y) { return x+y; };
最后一个参数是函数体文本,可以包含任意js语句,相互以分号分隔。其他参数是新函数的参数名。
Function()构造函数的主要作用是:动态创建和编译函数。
Function()创建的函数不使用词法作用域,而是始终编译为如同顶级函数一样。
let scope = "global";
function fun() {
let scope = "fun-local";
return new Function("console.log(scope)");
}
fun()(); // 报错 scope is not defined
不要在Function构造函数的参数文本中,读取该参数文本以外的变量。
相关文章:
js 函数、闭包及函数对象
js的函数是对象,可以通过程序来操控。比如,可以把函数赋值给变量,然后再传递给其他函数,也可以在函数上设置属性,甚至调用函数的方法。 js函数可以嵌套定义在其他函数里,内嵌函数可以访问定义在函数作用域…...
SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[二]
文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能02-创建项目基础界面需求分析效果图思路分析 代码实现项目前后端分离情况项目前后端分离情况如图 注意事项和细节 SSM–搭建Vue 前端工程–项目基础界面 实现功能02-创建项目基础界面 需求分析 效果图 思路分析 使用V…...
Android 之 AudioManager ( 音频管理器 )
本节引言: 在多媒体的第一节,我们用SoundPool写了个Duang的示例,小猪点击一个按钮后,突然发出"Duang"的 一声,而且当时的声音很大,吓死宝宝了 ,好在不是上班时间,上班时间…...
2023爱分析·超自动化厂商全景报告|爱分析报告
关键发现 当前的超自动化定义主要从技术组合角度阐述超自动化内涵,较难和业务价值建立链接。爱分析对超自动化作如下新定义:超自动化指利用RPA、iPaaS、AI、低代码、BPM、流程挖掘等自动化技术,实现组织端到端流程自动化以及新业务流程快速编…...
【C++进阶知识】04 - 函数默认实参、默认初始化、initializer_list
1. 函数默认实参 默认实参需要注意以下几点: (1)函数默认实参的赋值应从右往左,否则编译报错,因为参数入栈应该从右往左。 void f(int, int, int 1); void f(int, int 2, int); void f(int 3, int, int);&#x…...
C语言笔试训练【第三天】
大家好,我是纪宁。 今天是C语言笔试训练的第三天,大家加油! 第一题 1、已知函数的原型是: int fun(char b[10], int *a) ,设定义: char c[10];int d; ,正确的调用语句是( …...
Android SystemServer中Service的创建和启动方式(基于Android13)
Android SystemServer创建和启动方式(基于Android13) SystemServer 简介 Android System Server是Android框架的核心组件,运行在system_server进程中,拥有system权限。它在Android系统中扮演重要角色,提供服务管理和通信。 system …...
Meta开源AI音频和音乐生成模型
在过去的几年里,我们看到了AI在图像、视频和文本生成方面的巨大进步。然而,音频生成领域的进展却相对滞后。MetaAI这次再为开源贡献重磅产品:AudioCraft,一个支持多个音频生成模型的音频生成开发框架。 AudioCraft开源地址 开源地…...
rust怎么解析json数据?
关注我,学习Rust不迷路!! 在 Rust 中,你可以使用 serde 库来实现结构体与 JSON 之间的互相转换。 serde 是 Rust 社区最常用的序列化和反序列化库,它提供了方便的功能来处理结构体与 JSON 之间的转换。 首先ÿ…...
STM32 NOR_FLASH 学习
NOR FLASH FLASH是常用的,用于存储数据的半导体器件,它具有容量大,可重复擦写、按“扇区/块”擦除、掉电后数据可继续保存的特性。 NOR FLASH的单位是MB,EEPROM的单位是KB。 NM25Q128,是NOR FLASH的一种,…...
【数据结构|二叉树遍历】递归与非递归实现前序遍历、中序遍历、后序遍历
递归与非递归实现二叉树的前序遍历、中序遍历、后序遍历。 二叉树图 定义 前序遍历(Preorder Traversal): 前序遍历的顺序是先访问根节点,然后按照先左后右的顺序访问子节点。对于上面的二叉树,前序遍历的结果是&…...
iPhone 8 Plus透明屏有哪些场景化应用?
iPhone 8 Plus是苹果公司于2017年推出的一款智能手机,它采用了全新的玻璃机身设计,使得手机更加美观和时尚。 而透明屏则是一种新型的屏幕技术,可以使手机屏幕呈现出透明的效果,给人一种科技感十足的视觉体验。 透明屏是通过使用…...
解决 MySQL 删除数据后,ID 自增不连续问题
修复前 除了部分数据,导致后续新增的数据,ID 自增不连续 解决方案 执行下方 SQL 语句即可修复此问题,mbs_order为需要修复的表名 SET i0; UPDATE mbs_order SET id(i:i1); ALTER TABLE mbs_order AUTO_INCREMENT0;...
arcgis--网络分析(理论篇)
1、定义概念 (1)网络:由一系列相互联通的点和线组成,用来描述地理要素(资源)的流动情况。 (2)网络分析:对地理网络(如交通网络、水系网络)&…...
Linux笔记1(系统状态等)
man命令: man name: man section name: man -k regexp: 在 Linux 中,man 命令用于查看命令、函数或配置文件等的手册页,提供了详细的帮助文档。man 是 "manual" 的缩写。man 命令的用法如下: man [选项] [命令名]例如&…...
Set-up ESP-AT Environment on Windows using CMD
Before you start, the following environments need to be installed: Git BashPython environment, suggest Python version: 3.8.7. Please ensure the installation of Python v3.8 version environment, and remember to select the option “add to PATH” during the in…...
SpringBoot中Redis报错:NOAUTH Authentication required
1、问题 org.springframework.dao.InvalidDataAccessApiUsageException: NOAUTH Authentication required.; nested exception is redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required. … 2、解决 如果提供了密码还没解决,那可能是…...
需求飙升120%!芭比产品火爆出圈,意大利人争相购买!
据外媒报道,真人版《芭比》成为今年夏天最火的电影,仅在美国和加拿大,该影片的票房收入就超过3.5亿美元。在意大利《芭比》也备受追捧,目前的票房收入突破1670万欧元,成为2023年观看人数第三多的电影。 除了电影界之外…...
echarts-pie---------3D曲状环形饼图实现!!!
示例(参考此处饼图修改https://www.isqqw.com/viewer?id37497) 话不多说直接上代码 此套代码可以直接再echarts官网中的此处运行 let selectedIndex ; let hoveredIndex ; option getPie3D([{name: 数学,value: 60,itemStyle: {color: #1890FF,},},{…...
合并两个有序链表(leetcode)
题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]思路 每次递归都会比较当前两个节点的值,选择较小的节点作为合并后的链…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
