JS中函数基础知识之查漏补缺(写给小白的学习笔记)

函数

函数是ECMAScript中 最有意思的部分之一, 主要是因为函数实际上是对象.-- 每个函数 都是Function类型的实例,Function也有属性和方法.

因为函数是对象,所以函数名就是指向函数对象的指针.

常用的定义函数的语法:

①函数声明 ②函数表达式 ③箭头函数

function sum (num1,num2) {
  return num1 + num2;
}
let sum = function( num1 ,sum2){
  return num1 + num2;
}
let sum = ( num1 , num2 ) => {
  return num1 + num2;
};

1. 箭头函数

ES6新增了 使用胖箭头 => 定义函数表达式的能力

任何可以使用函数表达式的地方, 都可以使用箭头函数.

箭头函数 简洁的语法 非常适合嵌入函数的场景

let ins = [1,2,3];
console.log( ins.map( function(i){ return i+1;} ) )//[2,3,4]
console.log( ins.map( (i) => { return i+1;} ) )//[2,3,4]

箭头函数的简写 : ① 如果只有一个参数, 那么参数部分的小括号可以省略 ②如果箭头后面只有一行代码,那么可以省略大括号{} 注意省略大括号 会隐式返回这行代码的值

箭头函数嵌入函数 以及它的简写形式: 

console.log( ins.map( (i) => { return i+1;} ) )//[2,3,4]
console.log( ins.map( i =>  i+1; ) )//[2,3,4]

箭头函数虽然语法简洁,但是很多场合不适用.

① 箭头函数不能使用 arguments对象 、super 和 new.target

② 箭头函数不能用作 构造函数

③ 此外 箭头函数也没有 prototype属性.

2. 函数名

因为函数名  就是指向函数的指针 存储的是该函数对象的引用地址.  所以他们和其他包含对象指针的变量 具有相同的行为. 这意味着一个函数可以有多个名称.如下所示

function sum(num1, num2) {
  return num1 + num2;
}

console.log(sum(10, 10));  //20
let anotherSum = sum; //注意函数名后面没有小括号 只会访问函数指针 不会执行函数.
console.log(anotherSum(10, 10));  //20

sum = null;  //sum 不再指向 函数的存储地址 但是函数对象还在
console.log(anotherSum(10, 10));  //20   //anotherSum 指向函数的存储地址 可以正常使用

3. 理解参数

ECMASript函数的参数 跟大多数其他语言不同, ECMASript函数既不关心 传入的参数的个数 也不关心这些参数的数据类型. 定义函数时 要接受两个参数,并不意味着调用函数时就传两个参数. 调用时 传 0个参数 1 个参数 3个参数 解释器都不会报错. 这是因为ECMAScript函数的参数在内部表现为一个数组. 函数被调用时 总会接收一个数组,但函数并不关心这个数组中包含什么.

事实上, 使用function关键字定义函数时(非箭头函数), 可以在函数内部访问arguments对象从中取得传进来的每个参数值.

arguments对象 是一个类数组对象,(但不是array的实例) ,因此可以使用中括号[] 访问其中的元素.(第一个参数是arguments[0] 第二个参数是arguments[1] ...) 要确定传进来多少个参数,可以访问arguments.length 属性.

function sayhi(name, message){
  console.log("hi "+name+","+message);
}
sayhi('Nico','新的一年 升职加薪 !!')
// 把函数重写为 不声明参数 函数还可以正常调用
function sayhello(){
  console.log("hello "+arguments[0]+","+arguments[1]);
}
sayhello('WanZi','新的一年 好运爆棚 !!')

arguments对象 可以跟命名参数一起使用. 如上方在函数 sayhi 中使用 arguments[0]和 name,并且 arguments对象的值 会和对应的命名参数值自动同步. 即修改 arguments[0]的值 name的值 也会随之改变, 反之亦然.

箭头函数中的参数

如果函数是使用 箭头语法定义的,那么传给函数的参数 将不能使用arguments关键字访问,只能通过定义的命名参数访问.

function foo () {
  console.log( arguments[0]);
}
foo(5); // 5

let bar = () => {
  console.log( arguments[0] );
}
bar(5); //ReferenceError: arguments is not defined

虽然箭头函数没有arguments对象 但可以在包装函数中 把arguments 对象提供给 箭头函数。也就是外层函数使用function定义 所以有arguments对象 可以传递给该函数内部的箭头函数使用 这个arguments对象

function foo(){
  let bar = () =>{
    console.log( arguments[0]); 
  }
bar();
}
foo(5);//5

注意 ECMAScript 中所有的参数都是按值进行传递的,变量调用时 参数存储的值 复制给 命名参数. 如果把对象作为参数传递,那么传递的值 就是这个对象的引用地址.(因为 该参数存储值 也只是对象的引用地址) (如下面代码与结果展示)

      let name = 'nico';
	  function changeName(str){
		str = str +'happy';
        return str;
	  }
// 函数调用 只是把参数name 存储的值 复制一份给到命名参数 str
	  console.log(changeName(name));
	  console.log(name);
			
	  let obj = {
		a:1,
		b:2,
	  }
	  function change( o ){
		 o.a +=10;
		 o.b += 100;
         return o;
	  }
	 console.log(change(obj));
	 console.log(obj);
			 

命名参数O 和 变量obj 存储的值相同 即指向 同一个对象的引用地址. 所以change函数改变了对象内部的值 打印变量obj 结果也会随之改变.

4. 没有重载

java中一个函数可以有两个定义, 只要签名(接收参数的类型和数量)不同就行. ECMAScript函数不能像java等传统编程这样重载. 因为ECMAScript函数的参数是由包含 零个或多个值的数组表示.没有函数签名,也就没有重载.

如果在ECMAScript中定义了两个同名的函数 后定义的会覆盖先定义的. 函数变量名被重写 保存第二个函数对象的引用地址.

5. 默认参数值

在ES5及以前, 实现默认参数的一种常用方式 就是在函数内部检测某个参数 是否等于 undefined .如果是undefined就 意味着没有传这个参数 那就给它赋一个值.

在函数内部 name = ( typeof name !=='undefined' ) ? name : 'Henry'; 如果参数不是undefined那么 就等于它自己 否则就是默认值 'Henry'.

ES6之后, 支持显式定义默认参数. 只要在函数定义中的 参数后面用 = 就可以为参数赋一个默认值.

funtion( name = 'henry' , age =18 ){ }

注意 : 使用默认参数时, arguments对象的值 只反映 函数调用时传给函数的参数, 不会受参数默认值的影响. 修改命名参数也不会影响arguments对象.它始终以调用函数时传入的值为准.( 不使用默认参数值时 ,arguments对象会受 命名参数值的影响 与之同步改变 )

函数的默认参数 只有在函数被调用时 才会求值 不会在函数定义时求值.

箭头函数 同样可以使用默认参数值, 只是参数的 ( ) 不能被省略了let hi = (name='Henry' ) =>{ ... }

默认参数作用域与暂时性死区

因为在求值默认参数时 可以定义对象,也可以动态调用函数, 如 let hi = ( name=getName() ) =>{ ... } 所以函数参数肯定是在某个作用域中求值的.

给多个参数 定义默认值 实际上跟使用let关键字顺序声明变量一样. 默认参数会按照定义他们的顺序 依次被初始化. 因此 后定义默认值的参数 可以引用先定义的参数. 看下面例子所示

function makeKing ( name = 'Henry', numerals = name ) {
  return `King ${name} ${numerals}`; 
}
console.log( makeKing() ); //King Henry Henry

//可以依照如下实例 想象一下这个过程
function makeKing () {
  let name = 'Henry';
  let numerals = name
  return `King ${name} ${numerals}`; 
}
console.log( makeKing() ); //King Henry Henry

参数初始化顺序 遵循"暂时性死区"规则, 即在参数定义前 ,不可以引用该参数.

function makeKing(name=numerals, numerals='VITT') {
   return `King ${name} ${numerals}`; 
}

如果调用上面的 makeMing函数 但是没有传第一参数 ,那么就会使用参数的默认值 numerals ,但是numerals变量还没有定义 所以这个时候会报错.

参数也存在于自己的作用域中,它们不能引用函数体的作用域:

function makeKing(name = 'Henry', numerals = defaultNumeral) {
    let defaultNumeral = 'VIII';
    return `King ${name} ${numerals}`;
}

上面代码中 makeKing函数调用时不传第二个参数会报错


6. 参数扩展与收集

ES6新增了 扩展操作符, 使用它可以非常简洁地 操作和组合 集合数据. 扩展操作符 最有用的场景就是函数定义中的参数列表. 在这里它可以充分利用JS的弱类型及参数长度可变的特点.

扩展操作符 即可以用于调用函数时 传参, 也可以用于定义 函数参数.

6.1. 扩展参数

给函数传参时, 有可能不需要传一个数组, 而是分别传入数组的元素.

在ES6中 可以使用扩展操作符,极为简洁地实现这种操作. 对可迭代对象 应用扩展操作符, 并将其作为一个参数传入 可以将可迭代对象拆分 并将迭代返回的每个值单独传入.

 let values = [1,2,3,4];
 function getSum(){
   let sum = 0;
   for(let i=0 ;i<arguments.length; i++){
     sum+= arguments[i];
	}
	return sum;
 }
 console.log( getSum(-1, ...values) )  //9
 console.log( getSum( ...values,5) )  //15
 console.log( getSum( ...values,5,...[2,3,4]) ) //24

对于函数中的 arguments而言,它并不知道 扩展操作符的存在,而是按照调用函数时 传入的参数接收每一个值.

arguments对象 只是消费扩展操作符的一种方式. 在普通函数(函数内部没有使用arguments对象)和箭头函数调用时 也可以将扩展操作符应用于命名参数. 这些函数在声明时也可以同时使用默认参数.

6.2. 收集参数

在函数定义时 可以使用扩展操作符. 那么在该函数调用时就可以 把不同长度的独立参数 组合为一个数组(例如下面例子中的values). 这有点类似arguments对象的构造机制 不过收集参数的结果会得到一个Array实例.

function getSum(...values){
  //初始值总和为0 顺序累加values中的所有值
  return values.reduce((x,y) => x+y, 0);
}
console.log(getSum(1,2,3) ) //6

收集参数的前面 如果还有命名参数,则只会收集其余的参数,如果没有其余的参数就会得到空数组. 因为收集参数的结果可以变,所以只把它作为最后一个参数, 否则就会报错 如下图所示

箭头函数虽然不支持 arguments对象 但是支持收集参数的定义方式,因此也可以实现 与使用arguments一样的逻辑.

使用收集参数 并不影响使用arguments对象 它依然反映调用时 传给函数的参数.

7. 函数声明与函数表达式

JS引擎在加载数据时, 对函数声明和函数表达式是区别对待的.

JS引擎 在任何代码执行之前 会先读取函数声明,并在执行上下文中 生成函数定义.   在执行代码时 JS引擎会先执行一遍扫描 把发现的函数声明提升到 源代码树的顶部. 因此函数声明可以出现在 调用该函数的代码之后. 这个过程叫做 函数声明提升.

函数表达式 必须等到代码执行到它那一行 才会在执行上下文中生成函数定义.

除了函数什么时候 真正有定义这个区别之外 这两种语法是等价的.

8. 函数作为值

因为函数名 在ECMASript中就是变量,所以函数可以用在任何可以使用变量的地方. 不仅可以把函数作为参数传给另一个函数, 还可以在一个函数名中返回另一个函数.要注意的是,如果是访问函数而不是调用函数,那就必须不带括号.

把函数作为参数传给另一个函数:

function callSomeFunction(someFunction, someArgument) {
  return someFunction(someArgument);
}

这个函数接收两个参数。第一个参数应该是一个函数,第二个参数应该是要传给这个函数的值。任

何函数都可以像下面这样作为参数传递:
 
function add10(num) {
  return num + 10;
}
let result1 = callSomeFunction(add10, 10);
console.log(result1); // 20
function getGreeting(name) {
  return "Hello, " + name;
}
let result2 = callSomeFunction(getGreeting, "Nicholas");
console.log(result2); // "Hello, Nicholas"

在一个函数名中返回另一个函数:

假设有一个包含对象的数组,而我们想按照任意对象属性对数组进行排序。为此,可以定义一个 sort()方法需要的比较函数,它接收两个参数,即要比较的值。但这个比较函数还需要想办法确定根据哪个属性来排序。这个问题可以通过 定义一个根据属性名来创建比较函数的函数来解决。比如:

function createComparisonFunction( propertyName ) {
  return function(object1, object2) {
    let value1 = object1[propertyName];
    let value2 = object2[propertyName];

    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  };
}
这个函数的语法乍一看比较复杂,但实际上就是在一个函数中返回另一个函数,注意那个 return
操作符。
内部函数可以访问 propertyName 参数,并通过中括号语法取得要比较的对象的相应属性值。
取得属性值以后,再按照 sort() 方法的需要 返回比较值就行了。这个函数可以像下面这样使用:
let data = [
{name: "Zachary", age: 28},
{name: "Nicholas", age: 29}
];
data.sort(createComparisonFunction("name"));
console.log(data[0].name); // Nichola

arr.sort 方法的使用: sort方法会对数组进行 原位(in-place) 排序,更改元素的顺序。(译注:原位是指在此数组内,而非生成一个新数组。)

要使用我们自己的排序顺序,我们需要提供一个函数作为 arr.sort() 的参数。

if (a > b) return 1; // 表示第一个值比第二个值大

if (a == b) return 0; // 表示两个值相等

if (a < b) return -1; // 表示第一个值比第二个值小

---------文章主要内容来自《JavaScript高级程序设计》-----------------------------------

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

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

相关文章

Skyeye 云 VUE 版本 v3.15.3 发布,涉及 ERP、OA、财务等

Skyeye 云智能制造&#xff0c;采用 Springboot winUI 的低代码平台、移动端采用 UNI-APP。包含 30 多个应用模块、50 多种电子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公告、问卷、报表…

LInux单机安装Redis

1. 安装gee工具包 由于Redis是基于c语言编写的所以安装的时候需要先安装gee以及gcc的依赖,yum云用不了可以看一下这个 linux 替换yum源镜像_更换yum镜像源-CSDN博客 yum install -y gcc tcl 2. 添加redis的压缩包 3. 上传到Linux 上传到 /usr/local/src 目录、这个目录一般用于…

热备份路由HSRP及配置案例

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网路安全入门系列 目录 一&#xff0c;HSRP的相关概念二&#xff0c;…

java开发springoot

阅读理解 命令之间空一行&#xff1a;表示前面的是配置 红色背景&#xff1a;表示待验证蓝色背景&#xff1a;表示常用或推荐绿色背景&#xff1a;注意/推荐 json 转 对象 import com.fasterxml.jackson.databind.ObjectMapper; public DebangResp convertJsonToObject(Stri…

gesp(C++一级)(17)洛谷:B4062:[GESP202412 一级] 温度转换

gesp(C一级)&#xff08;17&#xff09;洛谷&#xff1a;B4062&#xff1a;[GESP202412 一级] 温度转换 题目描述 小杨最近学习了开尔文温度、摄氏温度和华氏温度的转换。令符号 K K K 表开尔文温度&#xff0c;符号 C C C 表摄氏温度&#xff0c;符号 F F F 表华氏温度&am…

windows ping ssh

问题解决1&#xff1a;局域网内&#xff0c;为啥别人ping不到我的IP 问题解决2&#xff1a;ssh连接windows10拒绝连接 第一步&#xff1a;ssh使用的22端口&#xff0c;首先确认windows10的22端口是否开启。 –开启步骤 1.控制面板–>Windws Defender 防火墙–>高级设置…

《Rust权威指南》学习笔记(二)

枚举enum 1.枚举的定义和使用如下图所示&#xff1a; 定义时还可以给枚举的成员指定数据类型&#xff0c;例如&#xff1a;enum IpAddr{V4(u8, u8, u8, u8),V6(String),}。枚举的变体都位于标识符的命名空间下&#xff0c;使用::进行分隔。 2.一个特殊的枚举Option&#xff0…

科研绘图系列:R语言单细胞数据常见的可视化图形

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理图1图2图3图4图5图6系统信息参考介绍 单细胞数据常见的可视化图形 因为本教程是单细胞数据,因此运行本画图脚本需要电脑的内存最少32Gb 加载…

打造三甲医院人工智能矩阵新引擎(二):医学影像大模型篇--“火眼金睛”TransUNet

一、引言 1.1 研究背景与意义 在现代医疗领域,医学影像作为疾病诊断与治疗的关键依据,发挥着不可替代的作用。从传统的X射线、CT(计算机断层扫描)到MRI(磁共振成像)等先进技术,医学影像能够直观呈现人体内部结构,为医生提供丰富的诊断信息,涵盖疾病识别、病灶定位、…

IP查询于访问控制保护你我安全

IP地址查询 查询方法&#xff1a; 命令行工具&#xff1a; ①在Windows系统中&#xff0c;我们可以使用命令提示符&#xff08;WINR&#xff09;查询IP地址&#xff0c;在弹窗中输入“ipconfig”命令查看本地网络适配器的IP地址等配置信息&#xff1b; ②在Linux系统中&…

2025新春烟花代码(一)HTML5夜景放烟花绽放动画效果

标题预览效果 标题HTML代码 <!DOCTYPE html> <html lang"en"> <script>var _hmt _hmt || [];(function () {var hm document.createElement("script");hm.src "https://hm.baidu.com/hm.js?45f95f1bfde85c7777c3d1157e8c2d34&…

【Rust自学】10.6. 生命周期 Pt.2:生命周期的语法与例子

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 10.6.1. 生命周期标注语法 生命周期的标注并不会改变引用的生命周期长度。如果某个函数它制定了泛型生命周期参数&#xff0c;那么它就可…

【C语言程序设计——选择结构程序设计】求一元二次方程的根(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 sqrt() 函数 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;根据求根公式&#xff0c;计算并输出一元二次方程的两个实根&#xff0c;要求精确道小数点后2位。要求方程系数从键盘输入。如果输入的系数不满足求…

【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1. 带权有向图 2. 图的邻接矩阵 3. 图的邻接表 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;编写一个程序实现图的邻接矩阵和邻接表的存储。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 带权有向图…

java 转义 反斜杠 Unexpected internal error near index 1

代码&#xff1a; String str"a\\c"; //出现异常&#xff0c;Unexpected internal error near index 1 //System.out.println(str.replaceAll("\\", "c"));//以下三种都正确 System.out.println(str.replace(\\, c)); System.out.println(str.r…

QML学习(七) 学习QML时,用好Qt设计器,快速了解各个组件的属性

在初步学习QML时&#xff0c;特别建议看看Qt设计器&#xff0c;先利用Qt Quick设计师的使用&#xff0c;快速的对Qt Quick的各个组件及其常用的属性&#xff0c;有个初步的了解和认识。如果初始学习一上来直接以代码形式开干&#xff0c;很容易一头雾水。而设计器以最直白的所见…

Flutter 鸿蒙化 flutter和鸿蒙next混和渲染

前言导读 这一个节课我们讲一下PlatformView的是使用 我们在实战中有可能出现了在鸿蒙next只加载一部分Flutter的情况 我们今天就讲一下这种情况具体实现要使用到我们的PlatformView 效果图 具体实现: 一、Native侧 使用 DevEco Studio工具打开 platform_view_example\oho…

js逆向实战(1)-- 某☁️音乐下载

下载某云音乐源文件.mp4格式 首先随便点进一首歌&#xff0c;如图所示获取该音乐id&#xff0c;然后点击播放键&#xff0c;打开F12进行查询XHR 由此可知&#xff0c;实际请求网址是 https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token「你的token」url需带…

学习随笔:word2vec在win11 vs2022下编译、测试运行

word2vec 官网word2vec的本质是在自然语言词条数据集与计算机浮点数据集之间建立双射关系。word2vec建立的数据集最厉害的一点是&#xff0c;将自然语言词条数据集内部的推理过程&#xff0c;映射到了计算机浮点数据集内部的数值运算。我个人感觉理解这个数据映射方式是理解AI大…

开源数据集成平台白皮书重磅发布《Apache SeaTunnel 2024用户案例合集》!

2025年新年临近&#xff0c;Apache SeaTunnel 社区用户案例精选&#x1f4d8;也跟大家见面啦&#xff01;在过去的时间里&#xff0c;SeaTunnel 社区持续成长&#xff0c;吸引了众多开发者的关注与支持。 为了致谢一路同行的伙伴&#xff0c;也为了激励更多人加入技术共创&…