JavaScript系列-函数(function)

文章目录

    • 函数定义
      • 函数的特征
    • 创建函数方式
      • 函数声明
        • 实现函数内部操作对外部可见
      • 函数表达式
        • 匿名表达式
        • 带名称表达式
    • 函数调用方式
    • 函数提升
    • 函数作用域
    • 作用域和函数栈
      • 递归
    • 嵌套函数和闭包
      • 闭包特性-保存变量
    • 使用 arguments 对象
    • 箭头函数
      • 定义
    • 更多内容

函数定义

提示:函数是 JavaScript 中的基本组件之一,通常是用来完成一件具体的任务最小单元

函数的特征

  • 一般有输入和输出、内部的过程计算和数据结构操作过程

创建函数方式

定义函数,JavaScript提供两种方式,一种是函数声明和函数表达式

函数声明

函数声明也称为函数定义,由function关键字和以下部分组成

  • 函数名称
  • 函数参数列表(在名称后面),多个参数使用逗号分开
  • JavaScript操作过程语句,使用大括号括起来,{*/- /*}

下面是函数声明的例子如下:

function square(number) {
  return number * number;
}

函数 square 接收一个名为 number 的参数。这个函数只有一个语句,其表示该函数将函数的参数(即 number)自乘后返回。函数的 return 语句指定了函数的返回值:number * number。

参数本质上是按值传递给函数的——因此,即使函数体的代码为传递给函数的参数赋了新值,这个改变也不会反映到全局或调用该函数的代码中。

在这里扩展一下,JavaScript变量的作用域分为两种,一种是全局的,一种是局部的

  • 全局变量声明,直接使用var 语法,或者在函数外部声明的变量都是全局,这种变量,全局对象都可以访问
  • 局部变量的作用域只能在当前的作用域被调用,如函数的实参,在函数外部就无法访问;
实现函数内部操作对外部可见

如果你将对象作为参数传递,而函数改变了这个对象的属性,这样的改变对函数外部是可见的,如下面的例子所示:

function myFunc(theObject) {
  theObject.make = "Toyota";
}

const mycar = {
  make: "Honda",
  model: "Accord",
  year: 1998,
};

console.log(mycar.make); // "Honda"
myFunc(mycar);
console.log(mycar.make); // "Toyota"

虽然函数内部的参数只能在内部访问,但函数可以操作全局的变量,对其进行操作,达到外部可见

函数表达式

匿名表达式

提示:函数表达式类似于声明一个变量的形式,故而有表达式的意思
这样的函数可以是匿名的;它不必有一个名称。例如,函数 square 也可这样来定义:
下面通过一个例子来了解一下

const square = function (number) {
  return number * number;
};

console.log(square(4)); // 16
带名称表达式

函数表达式也可以提供名称,并且可以用于在函数内部代指其本身,或者在调试器堆栈跟踪中识别该函数:
下面的fac指向函数本身 fac(n-1) 就是调用自身

const factorial = function fac(n) {
  return n < 2 ? 1 : n * fac(n - 1);
};

console.log(factorial(3)); // 6

函数调用方式

提示:定义的函数并不会自动执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。

调用函数才会以给定的参数真正执行这些动作。例如,一旦你定义了函数 square,你可以像这样调用它

square(5);

上述语句使用参数 5 来调用函数。函数执行完它的语句会返回值 25。

函数提升

看下面的例子,square 是在console.log 之后声明的,但能输出正确的数值

console.log(square(5)); // 25

function square(n) {
  return n * n;
}

尽管 square() 函数在声明之前被调用,但此代码的运行并没有任何错误。这是因为 JavaScript 解释器会将整个函数声明提升到当前作用域的顶部,因此上面的代码等价于:

// 所有函数声明实际上都位于作用域的顶部
function square(n) {
  return n * n;
}

console.log(square(5)); // 25

注意:函数提升仅适用于函数声明,而不适用于函数表达式。以下代码无法运行

console.log(square(5)); // ReferenceError: Cannot access 'square' before initialization
const square = function (n) {
  return n * n;
};

函数作用域

在函数内定义变量不能在函数之外的任何地方调用,只能在函数这个局部作用域访问,但函数可以访问内部定义的变量和在函数外部的任何变化的函数

也就是说,定义在全局的中的函数可以访问所有定义在全局的变量,而定义在函数内部的函数可以访问其父函数定义的所有变量和父函数有权访问的任何变量

// 下面的变量定义在全局作用域中
const num1 = 20;
const num2 = 3;
const name = "Chamakh";

// 此函数定义在全局作用域中
function multiply() {
  return num1 * num2;
}

console.log(multiply()); // 60

// 嵌套函数示例
function getScore() {
  const num1 = 2;
  const num2 = 3;

  function add() {
    return `${name} 的得分为 ${num1 + num2}`;
  }

  return add();
}

console.log(getScore()); // "Chamakh 的得分为 5"

作用域和函数栈

递归

一个函数可以指向并调用自身。有三种方法可以达到这个目的:

  • 函数名
  • arguments.callee
  • 作用域内一个指向该函数的变量名

例如,思考如下的函数定义:

const foo = function bar() {
  // 这里编写语句
};

在这个函数体内,以下的语句是等价的:

  • bar()
  • arguments.callee()
  • foo()

上面的调用方法调用自身的函数我们称之为递归函数。在某种意义上说,递归近似于循环。两者都重复执行相同的代码,并且两者都需要一个终止条件(避免无限循环,或者在这种情况下更确切地说是无限递归)。

例如,考虑以下的循环:

let x = 0;
// “x < 10”是循环条件
while (x < 10) {
  // 做些什么
  x++;
}

可以被转化成一个递归函数声明,然后调用该函数:

function loop(x) {
  // “x >= 10”是退出条件(等同于“!(x < 10)”)
  if (x >= 10) {
    return;
  }
  // 做些什么
  loop(x + 1); // 递归调用
}
loop(0);

将递归算法转换为非递归算法是可能的,不过逻辑上通常会更加复杂,而且需要使用栈。

事实上,递归本身就使用了栈:函数栈。类似栈的行为可以在以下示例中看到:

function foo(i) {
  if (i < 0) {
    return;
  }
  console.log(`开始:${i}`);
  foo(i - 1);
  console.log(`结束:${i}`);
}
foo(3);

// 打印:
// 开始:3
// 开始:2
// 开始:1
// 开始:0
// 结束:0
// 结束:1
// 结束:2
// 结束:3

嵌套函数和闭包

你可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。

它自身(嵌套函数)也形成了一个闭包(closure)。闭包是可以拥有独立变量以及绑定了这些变量的环境(“封闭”了表达式)的表达式(通常是函数)。

既然嵌套函数是一个闭包,就意味着一个嵌套函数可以“继承”容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。

可以总结如下:

  • 内部函数只可以在外部函数中访问。
  • 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。

总的来说,闭包就是,函数内部的嵌套函数,拥有自己的内部变量环境和表达式,在这个内部环境,可以范围外部的变量,但外部不能范围其内部环境,只能通过函数调用方式,执行这个嵌套函数

下面的例子展示了嵌套函数:

function addSquares(a, b) {
  function square(x) {
    return x * x;
  }
  return square(a) + square(b);
}

console.log(addSquares(2, 3)); // 13
console.log(addSquares(3, 4)); // 25
console.log(addSquares(4, 5)); // 41

由于内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数:

function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}

const fnInside = outside(3); // 可以这样想:给我一个可以将提供的值加上 3 的函数
console.log(fnInside(5)); // 8
console.log(outside(3)(5)); // 8

闭包特性-保存变量

注意到上例中 inside 被返回时 x 是怎么被保留下来的。一个闭包必须保存它可见作用域中所有参数和变量。因为每一次调用传入的参数都可能不同,每一次对外部函数的调用实际上重新创建了一遍这个闭包。只有当返回的 inside 没有再被引用时,内存才会被释放。

也就是说,outside(3) 执行是,就创建了一个闭包,返回了inside,由于inside内部引用了外部父函数的x,因此 x=3 就被保存下来
然后再执行fnInside(5),执行inside(5) 也就是执行return x+y =8

紧接着再一次执行outside(3)(5),
实际上重新创建了闭包,x的内存便会释放

使用 arguments 对象

提示:arguments对象是函数内部提供访问参数的对象
在JavaScript函数中,实际的参数被保存到arguments对象中,类似一个数组,可以通过索引访问

arguments[i];

其中 i 是参数的序号,从 0 开始。所以第一个传入函数的参数会是 arguments[0]。参数的数量由 arguments.length 表示。

在事先不知道函数有多少个参数个数的情况下,使用arguments 对象是十分有用的,如下面的例子

function myConcat(separator) {
  let result = ""; // 初始化列表
  // 迭代 arguments
  for (let i = 1; i < arguments.length; i++) {
    result += arguments[i] + separator;
  }
  return result;
}
console.log(myConcat("、", "红", "橙", "蓝"));
// "红、橙、蓝、"

console.log(myConcat(";", "大象", "长颈鹿", "狮子", "猎豹"));
// "大象;长颈鹿;狮子;猎豹;"

console.log(myConcat("。", "智者", "罗勒", "牛至", "胡椒", "香菜"));
// "智者。罗勒。牛至。胡椒。香菜。

箭头函数

定义

提示:箭头函数表达式相比普通函数表达式,具有较短的语法并且没有自己的this、arguments对象、super和new.target,箭头函数是匿名的

除此之外,箭头函数还有以下特征

  • 箭头函数不能用作构造函数。使用 new 调用它们会引发 TypeError。它们也无法访问 new.target 关键字。
  • 箭头函数不能在其主体中使用 yield,也不能作为生成器函数创建。

其语法如下:

() => expression

param => expression

(param) => expression

(param1, paramN) => expression

() => {
  statements
}

param => {
  statements
}

(param1, paramN) => {
  statements
}

需要注意的是

  • 方法体可以使用表达式体和通常的块体。表达式体隐藏了返回语句,实际上语句执行的结果就是返回值,块体需要声明return

下面举例子

const func = (x) => x * x;
// 表达式体语法,隐含返回值

const func2 = (x, y) => {
  return x + y;
};
// 块体语法,需要明确返回值

更多内容

更多内容请关注下方微信公众号
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MYSQL】MYSQL 的学习教程(六)之 SQL 语句执行流程

1. 一条 SQL 查询语句是如何被执行的 MySQL 的基本架构示意图如下所示&#xff1a; MYSQL 线程处理请求流程&#xff1a; SQL 接口&#xff1a;MySQL 中处理请求的线程在获取到请求以后获取 SQL 语句去交给 SQL 接口去处理查询解析器&#xff1a;解析器会将 SQL 接口传递过来…

VSCode Emoji 在 Windows10 下的显示问题

VSCode Emoji 在 Windows10 下的显示问题 问题描述 使用系统快捷键 Win ;(分号) 或 Win .(句号) 可以打开系统的 Emoji 面板&#xff0c;用于输入表情符号。 但是在 Windows 10 的 VSCode 中&#xff0c;一部分 Emoji 的显示会出现问题&#xff0c;比如以下这些&#xff1…

跟着LearnOpenGL学习9--光照

文章目录 一、颜色二、创建光照场景 一、颜色 显示世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我们需要使用&#xff08;有限的&#xff09;数值来模拟现实世界中&#xff08;无限的&#xff09;的颜色&#xff0c;所以并不是所有现实世界中的颜色都可以用…

网络游戏管理新规:重氪滚服成历史,SLG、MMO与小游戏逻辑亟待换新

12月22日&#xff0c;国家新闻出版署网站发布《网络游戏管理办法&#xff08;草案征求意见稿&#xff09;》&#xff08;以下简称《办法》&#xff09;&#xff0c;向社会公开征求意见。 午间《办法》一经发出后&#xff0c;在行业内立刻引发震动&#xff0c;诸多从业者表示&a…

将mapper.xml保存为idea的文件模板

将mapper.xml保存为idea的文件模板 在idea的File and Code Templates中将需要使用模板的内容添加为模板文件。 那么接下来请看图&#xff0c;跟着步骤操作吧。 mapper.xml文件内容 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper P…

基于SpringBoot的校园疫情防控管理系统 JAVA简易版

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、系统展示四、核心代码4.1 新增健康情况上报4.2 查询健康咨询4.3 新增离返校申请4.4 查询防疫物资4.5 查询防控宣传数据 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBoot…

数字音频编辑软件audition 2021 mac功能介绍

Audition 2021 mac是一款专业数字音频编辑软件&#xff0c;提供先进的音频混音、编辑和效果处理功能&#xff0c;专为音频和视频专业人员设计。无论是要录制音乐、无线电广播&#xff0c;还是为录像配音&#xff0c;Audition都能帮到您。它可提供先进的音频混合、编辑、控制和效…

大创项目推荐 深度学习 植物识别算法系统

文章目录 0 前言2 相关技术2.1 VGG-Net模型2.2 VGG-Net在植物识别的优势(1) 卷积核&#xff0c;池化核大小固定(2) 特征提取更全面(3) 网络训练误差收敛速度较快 3 VGG-Net的搭建3.1 Tornado简介(1) 优势(2) 关键代码 4 Inception V3 神经网络4.1 网络结构 5 开始训练5.1 数据集…

AutoEncoder个人记录

原理 最常见的降维算法有主成分分析法PCA&#xff0c;通过对协方差矩阵进行特征分解而得到数据的主要成分&#xff0c;但是 PCA 本质上是一种线性变换&#xff0c;提取特征的能力极为有限。 AutoEncoder把长度为d_in输入特征向量变换到长度为d_out的输出向量&#xff0c;借助于…

地震勘探原理---数字滤波处理

一. 地震数字滤波的目标 核心任务&#xff1a;去噪&#xff0c;提高地震资料信噪比 噪声压制: 野外采集中可以通过检波器组合, 震源组合, 地震多次覆盖技术来压制干扰波, 但是由于多种原因, 野外采集的资料仍然残留一定干扰波, 必须采用室内数字处理的方式来进行压制. 根据有效…

MyBatis 通过 SqlSession 实现动态Entity批量插入

需要几个关键点: 1、entity对应的service需要继承BaseService 2、entity对应的serviceImpl需要实现baseMapper方法&#xff0c;需要把当前的mapper返回去 3、entity对应的Mapper需要BaseMapper

Java并发工具类---ForkJoin、countDownlatch、CyclicBarrier、Semaphore

一、Fork Join fork join是JDK7引入的一种并发框架&#xff0c;采用分而治之的思想来处理并发任务 ForkJoin框架底层实现了工作窃取&#xff0c;当一个线程完成任务处于空闲状态时&#xff0c;会窃取其他工作线程的任务来做&#xff0c;这样可以充分利用线程来进行并行计算&a…

官宣!DevExpress Blazor UI组件,支持全新的.NET 8渲染模式

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 .NET 8为Blazor引入了令人兴奋的重…

柯桥外语学习-俄语零基础入门教学之与衣服有关的词汇

本期为大家带来的是与衣物有关的相关词汇&#xff01; 最近全国大范围降温&#xff0c;大家一定要关注天气预告及时增减衣物&#xff0c;小心不要感冒啦~ 一、服装组成部分 领子 воротник 方领 квадрадный воротник 圆领 закругленн…

基于SSM框架的电脑测评系统论文

基于 SSM框架的电脑测评系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为一个一般的用户都开始注重与自己的信息展示平台&#xff0c;实现基于SSM框架的电脑测评系统在技术上已成熟。本文介绍了基于SSM框架的电脑测评系统的开发全过程。通过分析用户对于…

Wavesurfer.js绘制波形图

HTML使用Wavesurfer.js 要使用wavesurfer.js&#xff0c;首先需要在HTML文件中引入Wavesurfer.js库&#xff0c;然后创建一个音频元素并将其添加到页面中。接下来&#xff0c;初始化Wavesurfer实例并配置相关选项。以下是一个简单的示例&#xff1a; 在HTML文件中引入Wavesurf…

瑞幸咖啡用户运营的秘诀是什么?普通用户通过数据分析也能得到答案!

大数据产业创新服务媒体 ——聚焦数据 改变商业 在快速发展的数字经济时代&#xff0c;BI已成为企业决策过程中不可或缺的工具。通过高效地收集、处理和分析海量数据&#xff0c;BI技术赋予企业洞察市场动态、优化运营策略、提升客户体验的能力。与人工智能、大数据和云计算的…

碳排放预测 | 基于ARIMA和GM(1,1)的碳排放预测(Matlab)

目录 预测效果基本介绍模型描述ARIMA模型GM(1,1)模型 程序设计参考资料 预测效果 基本介绍 基于ARIMA和GM(1,1)的碳排放预测&#xff08;Matlab&#xff09; 基于ARIMA&#xff08;自回归移动平均模型&#xff09;和GM(1,1)&#xff08;灰色预测模型&#xff09;的碳排放预测是…

如何自定义右键弹框并实现位置自适应?

一、问题 右键显示弹框&#xff0c;但是靠近浏览器边缘的部分会被隐藏&#xff0c;需要实现弹框位置自适应 二、 问题分析 如果想要最终弹框的宽高不超过屏幕视口&#xff0c;就等于屏幕视口的总宽/高减去弹框打开时的起点坐标&#xff0c;剩下的部分大于等于弹框的宽/高&…

智能优化算法应用:基于猎食者算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于猎食者算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于猎食者算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.猎食者算法4.实验参数设定5.算法结果6.参考文…