JavaScript青少年简明教程:函数及其相关知识(上)

JavaScript青少年简明教程:函数及其相关知识(上)

在JavaScript中,函数是一段可以重复使用的代码块,它执行特定的任务并可能返回结果。

内置函数(Built-in Functions)

内置函数是编程语言中预先定义的函数,也可以称为标准库函数(Standard Library Functions)。这些函数由编程语言的引擎或标准库提供,开发者可以直接使用它们来完成常见的任务,而无需自己实现相应的功能。例如,JavaScript的Math对象就包含了一系列数学相关的内置函数,如Math.abs()、Math.sin()等。

本节重点介绍自定义函数。

自定义函数

先看常见的命名函数

命名函数使用function关键字来定义,后面跟着函数名和一对圆括号内的参数列表,以及大括号内的函数体。格式:

function functionName(parameter1, parameter2, ...) {

  // 函数体

  // 在这里编写要执行的代码

}

其中,functionName 是函数的名称;parameter1, parameter2, ... 是函数的参数——形参。函数体内编写了要执行的代码。

命名函数的调用:

functionName(argument1, argument2, ...);

其中,functionName 是函数的名称;argument1,argument2, ... 是实参。

例如

// 定义函数
function greet(name) {return "Hello, " + name;
}let message = greet("Alice"); // 调用函数
console.log(message);

JavaScript的函数是定义和组织代码的基本构建块之一。一个函数是一组语句的集合,可以在代码中多次调用。函数能够接受输入(称为参数)并返回输出(称为返回值)。

函数的调用

要执行函数中的代码,你需要调用(或执行)该函数。这可以通过在函数名后添加括号(即使该函数不接受任何参数)来完成。

基本函数语法

functionName(argument1, argument2, ..., argumentN)

functionName: 是你要调用的函数的名称。

(): 圆括号是函数调用的标志。

argument1, argument2, ..., argumentN: 是传递给函数的参数(如果有的话)

例如:

// 定义函数
function add(a, b) {return a + b;
}// 调用时传递数字  
add(1, 2); // sum 是 3  

顺便指出下面情况

 也可以传递字符串,尽管这不是期望的用法 

add('1', '2'); // result 是 '12',因为字符串连接了

说明:在 JavaScript 中,+ 运算符既可以用于数字相加,也可以用于字符串拼接。如果 a 或 b 中有一个是字符串,+ 会进行字符串拼接。

嵌套调用

add(2, add(3, 4));  // 输出: 9

嵌套函数调用并不是 JavaScript 独有的特性。许多其他编程语言(如Python、C++、Java等)也支持这种方式。

形参(parameter)和实参(argument)是在函数定义和函数调用时使用的术语。

形参是在定义函数时,括号内部列出的变量名称。它们是函数内部使用的占位符,用于接收将来调用该函数时传入的值。

实参是在调用函数时,括号内部传递给函数形参的具体值。

实参会依次映射到形参上。

函数的参数传递

在JavaScript中,函数参数都是按值传递的。也就是说,传递给函数的是参数值的拷贝。但是对于不同数据类型,这个“值”的内容是有区别的,对于对象参数,这个“值”是对象的引用。

参数传递方面,在 JavaScript中,参数传递方式是按值传递——传递的是副本。具体说来:1)当将一个原始(基本)类型(primitive type)数据如数字、字符串、布尔值等作为参数传递给函数时,实际上是将该值的一个副本传递给函数——将实参值复制给形参,实参和形参相互独立互不干扰。函数内部对该副本的修改不会影响到原始的值。2)当将一个引用数据类型(对象类型)(如对象、数组等)作为参数传递给函数时,传递的是该对象的引用(地址)的副本——将实参引用的地址值复制给形参,形参和实参指向同一个对象的地址,改变形参所指向的对象的属性将影响实参所指向的对象。需要注意,在引用类型的参数传递中,并不会改变形参的值(即引用的地址),而是通过形参改变它所指向的对象的属性。

☆当传递原始数据类型时,实际上是将原始值的副本传递给了函数或其他变量。这意味着函数或其他变量操作的是原始值的副本,而不是原始值本身。

这种传递方式使得函数内部对原始值的修改不会影响到函数外部的变量,因为函数内部操作的是原始值的副本,而不是原始值本身。

例如:

function addTen(num) { // num 是一个局部变量num += 10;return num;
}let count = 20;
let result = addTen(count);
console.log(count);   // 20,没有变化
console.log(result);   // 30

解析如下:

此例展示了在 JavaScript 中传递变量的值的方式。

例中定义了一个函数 addTen,它接受一个参数 num。

在此主要关注变量情况:

首先,定义了一个变量 count,并赋值为 20。

接下来,调用 addTen 函数,并将 count 作为参数传递进去。这里需要注意的是,虽然 count 的值是 20,但是在函数中传递的是 count 的值的副本,而不是 count 本身。

在函数内部,num 的值被增加了 10,变成了 30。然后,函数返回了这个新的值,并将其赋给了变量 result。

最后,通过 console.log 打印了 count 和 result 的值。由于在函数中传递的是 count 的值的副本,所以 count 的值没有发生变化,仍然是 20。而 result 的值是函数返回的新值,即 30。图解如下:

☆当传递引用数据类型(reference type)数据如对象、数组时,实际上是将引用的地址传递给了函数。这意味着函数可以通过引用来访问和修改原始对象。引用是指向存储在内存中的对象的地址,所以传递引用时,传递的是指向对象的指针,而不是对象本身的实际值。

这种传递方式使得我们可以在函数内部修改原始对象,并且这些修改会在函数外部可见。因为函数和外部变量都指向同一个对象,所以对对象的修改会影响到所有引用该对象的变量。

例如:

function changeName(obj) {obj.name = 'Bob';
}let person = { name: 'Alice' };
console.log(person.name); // 输出 'Alice'changeName(person);
console.log(person.name); // 输出 'Bob'

解析如下:

此例展示了在 JavaScript 中传递对象的引用的方式。

例中定义了一个函数 changeName,它接受一个参数 obj。

在此主要关注变量情况:

首先,定义了一个对象 person,并赋值为 { name: 'Alice' }。这个对象有一个 name 属性,其初始值为 ‘Alice’。

接下来,通过 console.log 打印了 person.name 的值,即 ‘Alice’。

然后,调用 changeName 函数,并将 person 对象作为参数传递进去。这里需要注意的是,虽然 person 是一个对象,但是在函数中传递的是 person 对象的引用,而不是对象本身的副本。

在函数内部,通过修改 obj 的 name 属性,将其值改为 ‘Bob’。由于 obj 是 person 对象的引用,所以这个修改也会影响到 person 对象本身。

最后,通过 console.log 打印了 person.name 的值,即 ‘Bob’。由于在函数中修改了 person 对象的 name 属性,所以 person.name 的值变成了 ‘Bob’。图解如下:

总结之,传递引用时,传递的是引用的地址,而不是实际的值。传递原始数据类型时,传递的是实际的值的副本。这种差异导致了在函数内部对引用数据类型的修改会影响到函数外部的变量,而对原始数据类型的修改不会影响到函数外部的变量。

为加深理解,下面再给出两个示例说明。

例1、对于原始类型(primitive type)如数字、布尔值、字符串等,传递的就是这个值本身。在函数体内对形参的任何修改,都不会影响实参变量的值。例如:

function changeNum(x) {x = 100; // 修改形参x的值为100
}let num = 10;
changeNum(num);
console.log(num); // 输出10

在这个例子中,num变量的值10作为实参传递给changeNum函数。但在函数内部将形参x重新赋值为100,并不会影响到外部实参num的值。所以最后打印出来的num值仍然是10。

例2、对于引用类型(reference type)如对象、数组等,传递的值是对象的引用。如果在函数体内通过形参引用修改了对象的属性,实参对象的内容也会被修改,因为它们引用的是同一个对象。但如果在函数内部给形参变量重新赋值另一个对象,实参对象本身是不受影响的。如:


function changeArr(arr) {arr.push(4); // 修改数组内容arr = [1, 2]; // 重新赋值数组
}let numbers = [1, 2, 3];
changeArr(numbers);
console.log(numbers); // 输出 [1, 2, 3, 4]

在这个例子中,将数组 [1, 2, 3] 作为实参传递给 changeArr 函数。在函数内部,通过形参 arr 调用 push 方法,向数组追加了一个新元素 4。由于形参和实参引用的是同一个数组,所以实参 numbers 的内容也发生了改变。接下来,将一个新的数组 [1, 2] 赋值给了形参 arr。但是,这个赋值操作只在函数内部生效,实参 numbers 指向的数组没有改变。所以最后输出的 numbers 是 [1, 2, 3, 4]。

理解清楚JavaScript的参数传递方式,可以帮助我们更好地掌控代码的执行过程,减少隐蔽的bug。

学习过其它语言的朋友注意,与Java、C#语言不同,JavaScript的参数传递不检查实参数量和形参数量是否匹配。在JavaScript中,如果你调用的函数提供的参数(实参)少于声明的参数(形参),那么未被传值的形参将被赋予undefined。如果提供的实参多于形参,那么多余的实参将不会被函数内部直接使用,但它们仍然可以通过arguments对象(在非箭头函数中)或剩余参数(rest parameters,使用...语法)来访问。

当实参数量少于形参数量时,未被传值的形参将被自动赋值为undefined,例如:

function multiply(a, b) {console.log(a, b); // 输出: 2, undefinedreturn a * b;
}console.log(multiply(2)); // 输出: NaN

当实参数量多于形参数量时,多余的实参虽然不能直接在函数内部通过形参名称访问,但仍然可以通过arguments对象或剩余参数来获取:

function sum(a, b) {let result = a + b;for (let i = 2; i < arguments.length; i++) {result += arguments[i];}return result;
}console.log(sum(1, 2, 3, 4)); // 输出: 10

在这个例子中,sum函数只有两个形参a和b,但我们传递了四个实参。通过arguments对象,我们可以在函数内部访问所有实参。

使用剩余参数可以更方便地处理多余的实参:

function sum(a, b, ...numbers) {let result = a + b;for (let value of numbers) {console.log(value);}return result;
}console.log(sum(1, 2, 10, 20));

这里,这里 a 接收 1,b 接收 2,而 ...numbers 收集 10 和 20。

for...of 循环打印了 10 和 20

函数返回 result,即 3

输出:

10
20
3

变量作用域(scope

JavaScript 中的作用域(scope)指的是变量可访问范围。主要有以下几种作用域:

☆全局作用域

全局作用域是最外层的作用域,在任何地方都可以访问全局作用域中的变量。如:

var globalVar = "I'm global";function testScope() {console.log(globalVar);  // 可以访问全局变量
}testScope();  // 输出: I'm global
console.log(globalVar);  // 输出: I'm global

☆函数作用域

函数作用域是指在函数内部声明的变量,只能在该函数内部访问。如:

function testFunctionScope() {var localVar = "I'm local";console.log(localVar);  // 可以访问
}testFunctionScope();  // 输出: I'm local
// console.log(localVar);  // 错误:localVar is not defined

☆块级作用域 (ES6引入)

使用let和const关键字声明的变量具有块级作用域,只在声明它们的块(由 {} 包围)内有效。如:

if (true) {let blockVar = "I'm in a block";console.log(blockVar);  // 输出: I'm in a block
}
// console.log(blockVar);  // 错误:blockVar is not definedfor (let i = 0; i < 3; i++) {console.log(i);  // 输出: 0, 1, 2
}
// console.log(i);  // 错误:i is not defined

☆词法作用域(Lexical Scope)

词法作用域,也称为静态作用域,是指变量的作用域在代码编写时(词法分析时)就已经确定,而不是在运行时确定。在 JavaScript 中,函数的作用域在函数定义时就已经确定,而不是在函数调用时。

关键点:

内部函数可以访问外部函数的变量。

作用域是基于函数的嵌套关系,而不是函数的调用关系。

这种机制使得闭包成为可能。词法作用域的一个重要应用。

示例:

let globalVar = "I'm global";function outerFunction() {let outerVar = "I'm from outer";function innerFunction() {let innerVar = "I'm inner";console.log(globalVar);  // 可以访问全局变量console.log(outerVar);   // 可以访问外部函数的变量console.log(innerVar);   // 可以访问自己的变量}innerFunction();// console.log(innerVar);  // 错误:innerVar 在这里不可访问
}outerFunction();
// console.log(outerVar);  // 错误:outerVar 在这里不可访问

在这个例子中,innerFunction 可以访问它自己的变量、外部函数 outerFunction 的变量,以及全局变量。这就是词法作用域的体现。

变量提升

“语法基础”一节中提到过变量提升,学过函数理解更容易了。

变量提升是 JavaScript 中的一种机制,它将变量和函数的声明移动到它们所在作用域的顶部。这意味着无论你在哪里声明变量和函数,它们都会被视为在当前作用域的开始处声明。

关键点:

只有声明会被提升,赋值不会。

函数声明会被完整地提升。

变量声明使用 var 关键字时会被提升,但 let 和 const 不会(它们会产生暂时性死区)。

console.log(x);  // 输出:undefined
var x = 5;// 上面的代码等同于:
var x;
console.log(x);
x = 5;// 函数提升
sayHello();  // 输出:"Hello!"function sayHello() {console.log("Hello!");
}// let 和 const 不会被提升
console.log(y);  // 抛出 ReferenceError
let y = 10;

函数表达式(Function Expression)

【function expression - JavaScript | MDN 】

函数表达式也是定义函数的一种方式,但函数是赋值给一个变量的。

JavaScript 函数表达式是一种在表达式中定义函数的方式。它允许将函数作为值进行赋值、传递给其他函数或存储在变量中。

函数表达式的语法如下:

letVariableName = function [name](parameters) {

  // 函数体

};

其中,name 是函数的名称,函数名,该名称仅是函数体的局部名称——在外部作用域中不可见。可以省略。当省略函数名的时候,该函数就成为了匿名(anonymous)函数。parameters 是函数的参数列表,而函数体则是具体的代码块。

函数表达式的调用:

VariableName(argument)

其中,argument,实参,是在调用函数时传递给函数形参的具体值。

匿名函数表达式,例如:

const x = function (y) {return y * y;
};console.log( x(2)); //输出 4

具名函数表达式,例如:

let sum = function add(a, b) {return a + b;
};console.log(sum(3,5)); // 输出 8
//console.log(add(3,5)); // 这会抛出错误: add is not defined,因为add在这里不可见

注意,具名函数表达式,这里的函数名在外部作用域中不可见。

这种特性的主要用途递归等:递归函数可以在内部调用自己,而不需要依赖外部的变量名。例如:

const factorial = function calc(n) {if (n <= 1) return 1;return n * calc(n - 1);  // 这里可以使用 calc
};console.log(factorial(5));  // 输出: 120
//console.log(calc(5));    // ReferenceError: calc is not defined

在这个例子中,calc 只在函数内部可见,这允许函数进行递归调用,同时不会污染外部作用域。关于递归函数后面将介绍。

ES6引入了箭头函数,这是匿名函数的一种简洁写法。下一节介绍。

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

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

相关文章

PLC网关:开启工业4.0时代的智能工厂之路

PLC即可编程逻辑控制器&#xff0c;是工业自动化领域的核心设备&#xff0c;广泛应用于各个工业领域。从PLC问世至今&#xff0c;一直表现出强大的生命力和高速增长态势&#xff0c;2020年全球PLC市场的销售量已经达到了百亿RMB级别。 随着行业智能化、数字化推广&#xff0c;…

【Vulnhub系列】Vulnhub_Seattle_003靶场渗透(原创)

【Vulnhub系列靶场】Vulnhub_Seattle_003靶场渗透 原文转载已经过授权 原文链接&#xff1a;Lusen的小窝 - 学无止尽&#xff0c;不进则退 (lusensec.github.io) 一、环境准备 1、从百度网盘下载对应靶机的.ova镜像 2、在VM中选择【打开】该.ova 3、选择存储路径&#xff0…

Nginx系列-12 Nginx使用Lua脚本进行JWT校验

背景 本文介绍Nginx中Lua模块使用方式&#xff0c;并结合案例进行介绍。案例介绍通过lua脚本提取HTTP请求头中的token字段&#xff0c;经过JWT校验并提取id和name信息&#xff0c;设置到http请求头中发向后段服务器。 默认情况下&#xff0c;Nginx自身不携带lua模块&#xff0…

什么是海外云手机?海外云手机有什么用?

在跨境电商的浪潮中&#xff0c;如何高效引流成为了卖家们关注的焦点。近期&#xff0c;越来越多的卖家开始借助海外云手机&#xff0c;通过TikTok平台吸引流量&#xff0c;从而推动商品的海外销售。那么&#xff0c;究竟什么是海外云手机&#xff1f;海外云手机又能为跨境电商…

商家转账到零钱保姆级申请教程

大多数商家在申请微信支付的“商家转账到零钱”过程中都免不了遇到问题&#xff0c;更有不少商家因为屡次驳回严重耽误项目工期。为了帮助商户顺利开通该接口&#xff0c;根据我们上万次成功开通的经验整理这篇攻略以供参考&#xff1a; 一、前期准备 1. 确认主体资格&#xf…

跨境电商独立站术语盘点(一)独立站建站篇

跨境新手总是被一些跨境专业术语弄得头晕脑胀&#xff0c;不懂得查&#xff0c;查了又忘&#xff0c;忘了又得继续查…… 本期【跨境干货】&#xff0c;笔者特地为大家整理汇总了跨境电商独立站常用网站建站方面的专业术语&#xff0c;帮助你了解建站相关知识&#xff01;赶紧收…

安装python插件命令集合

安装python插件pyecharts库 pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple 安装python插件pandas库 pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple PyCharm 中安装步骤&#xff1a;

数据结构之队列详解

1.队列的概念以及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFo(Frist in Frist out)的特性 入队列&#xff1a;进行插入才操作的一端称为队尾 出队列&#xff1a;进行删除操作的一…

1比25万基础电子地图(广西版)

我们为你分享过四川、云南、江西、贵州、重庆、青海、西藏、新疆、甘肃、黑龙江、吉林、湖北、广东和内蒙古的1比25万基础电子地图&#xff0c;现在再为你分享广西版的电子地图。 如果你需要这些省份的1比25万基础电子地图&#xff0c;请在文末查看该数据的领取方法。 1比25万…

flutter开发实战-go_router使用

flutter开发实战-go_router使用 一、go_router介绍与特性 go_router是一个Flutter的第三方声明式路由插件&#xff0c;使用路由器API提供一个方便的、基于url的API&#xff0c;用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场…

【Spring Boot】Spring 的安全框架:Spring Security

Spring 的安全框架&#xff1a;Spring Security 1.Spring Security 初识1.1 核心概念1.2 认证和授权1.2.1 验证&#xff08;authentication&#xff09;1.2.2 授权&#xff08;authorization&#xff09; 1.3 模块 2.核心类2.1 Securitycontext2.2 SecurityContextHolder2.2.1 …

Python字符串处理技巧:一个小技巧竟然能省下你一半时间!

获取Pyhon及副业知识&#xff0c;关注公众号【软件测试圈】 效率翻倍的秘密&#xff1a;Python字符串操作的5个惊人技巧 在Python编程中&#xff0c;字符串处理在数据分析、Web开发、自动化脚本等多个领域都有广泛应用。Python提供了一系列强大的字符串处理函数&#xff0c;能够…

前端工程化11-webpack常见插件

1、webpack的插件Plugin 刚才我们也讲解了下&#xff0c;我们对webpack路径的一个处理&#xff0c;处理的话包括别名的配置&#xff0c;模块是如何找到并加载的&#xff0c;总的来说到现在webpack这个配置到现在来说还是相当的麻烦的&#xff0c;但是目前来说我们讲的这些东西…

前端工程化-vue项目创建

可以使用html、css、javascpript ,以及使用vue、axios等技术搭建前端页面&#xff0c;但效率低、结构乱。 实际前端开发&#xff1a; 前端工程化开发步骤&#xff1a; 一、环境准备 1.安装NodeJS2. 安装vue-cli 二、创建Vue项目 有两种方式创建&#xff0c;一般采用第二种图…

MMCV1.6.0之Runner/Hook/OptimizerHook(反向传播+参数更新)、Fp16OptimizerHook、自定义优化器与config设置

OptimizerHook 这段代码定义了一个名为 OptimizerHook 的类&#xff0c;它是一个用于优化器的自定义操作钩子。该钩子包含了一些用于梯度裁剪和检测异常参数的操作。这对于在深度学习训练过程中优化模型的性能和调试模型非常有用。 类的定义 OptimizerHook 类继承自 Hook&…

documents4j 将word转pdf文件,本地(Windows)测试没问题,部署到服务器(centos)报错

问题 报错如下&#xff1a; 代码 首先要保证你的Java代码没问题&#xff0c;可以参考下面代码 maven依赖 <!--documents4j--> <dependency><groupId>com.documents4j</groupId><artifactId>documents4j-local</artifactId><versi…

解决mysql事件调度器重启服务后自动失效的问题

前段时间为通过mysql事件生成测试数据&#xff0c;今天发现数据在10:57后停止了CREATE EVENT IF NOT EXISTS insert_random_data ON SCHEDULE EVERY 10 SECOND DO INSERT INTO test (createtime, random_number) VALUES (NOW(), FLOOR(RAND() * 100));检查事件状态&#…

C++String类的手撕实现

目录 构造函数 提前准备工作&#xff1a; 有参构造 析构函数 c_str 无参构造&#xff1a; 无参和有参的结合 operater[]的实现 简易版的迭代器 begin end 原因&#xff1a; reserve 思想步骤 获取_capacity 和 _size 测试 push_back 思想步骤 append insert…

平安养老险广西分公司开展7.8公益健步行活动

近日&#xff0c;平安养老保险股份有限公司&#xff08;以下简称“平安养老险”&#xff09;广西分公司在南宁邕江沿岸开展“7.8”公益健步行活动&#xff0c;在分公司班子的号召下&#xff0c;各部门内外勤员工均踊跃参与。 员工们沿途随手捡拾垃圾&#xff0c;传递积极、绿色…

就在刚刚,中国 IMO 奥数遗憾地失去了第一名的宝座,连续五年的统治地位被美国队所终结。

&#x1f431; 个人主页&#xff1a;TechCodeAI启航&#xff0c;公众号&#xff1a;TechCodeAI &#x1f64b;‍♂️ 作者简介&#xff1a;2020参加工作&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab; 优质专…