专题 JavaScript 函数基础

你将知道:

  • 函数声明和表达式
  • 函数声明和表达式之间的区别
  • 什么是匿名函数
  • 什么是 IIFE
  • 命名函数表达式
  • this 关键字


函数是调用该函数时执行的代码块 。

函数声明和表达式

让我们回顾一下它的语法:

functionfunctionName(param1, param2, ..., paramN) {// Function's body
}

那么什么是函数声明?

函数声明是指作为独立语句而不是表达式存在的函数定义。

// Standalone statement,
// hence a function declaration.
function sum(a, b) {return a + b;
}// Another function declaration.
function outerFn() {// A function declaration within outerFn().function innerFn() {console.log('Working with functions.')}}

这里 sum()outerFn()innerFn() 都是函数声明,因为它们是独立的语句:

在 JavaScript 中,还有另一种创建函数的方式,尽管其语法与函数声明相同,但工作方式略有不同。我们称之为函数表达式

函数声明与函数表达式最重要的区别在于,后者可以定义一个没有名称的函数,这样的函数通常被称为匿名函数 。无论如何,假设函数被分配给一个变量,那么函数表达式在语法上看起来是这样的:

var identifierName = function [functionName](param1, param2, ..., paramN) {// Function's body
}

请注意,使用函数表达式时,可以选择在 function 关键字后包含函数名称(即 functionName ),如上面的语法所示( functionName 周围有 [ ] )。

让我们创建相同的函数 sum() ,这次使用函数表达式:

var sum = function(a, b) {return a + b;
}

这里发生的事情是,一个没有名称的函数被赋值给了变量 sum 。由于 sum 指向内存中的一个函数对象,所以我们可以使用表达式 sum()

下面我们将一个函数表达式作为参数传递给另一个函数:

function execute(fn) {fn();
}execute(function() { console.log('Hello World!'); })

这是 JavaScript 应用程序中常见的另一种编码实践。将一个函数作为参数传递给另一个函数,我们称之为回调函数 ,或者简称为回调

function getLogger() {return function(val) {console.log('We are learning ' + val + '.');}
}var langIntro = getLogger();langIntro('JavaScript');

回调在 JavaScript 的事件 API 以及大量其他 API 中扮演着至关重要的角色。

在第 7 行,当调用 getLogger() 时,执行会转到第 1 行中它的定义处,并退出并返回另一个函数的返回值。这个 “另一个” 函数接受一个参数 val ,并创建一个包含该参数和其他文本的日志。

由于 getLogger() 返回一个函数,因此 langIntro 中保存的值是对函数的引用——具体来说,是对第 2 行定义的匿名函数的引用。这意味着我们可以使用表达式 langIntro () 来调用此函数。这正是我们在最后一行所做的。

从其他函数返回的函数构成了 JavaScript 中的另一个重要概念,称为闭包 ,尽管这个概念不仅存在于从其他函数返回的函数中;它也存在于其他地方。

从核心执行层面来看,函数声明和函数表达式之间没有任何区别。调用时,两者的执行方式相同。区别仅在于脚本编译时解析它们的方式。

函数声明和表达式之间的区别

函数声明和函数表达式之间主要有两个区别。

  • 其中一个被 提升,而另一个则没有。
  • 一个可以创建匿名函数,而另一个则不能。

1. Hoisting 提升

提升是指在执行任何其他代码之前处理各自范围内的所有变量声明的行为。

变量提升是以下代码有效并且不会引发错误的原因:

// We think that x doesn't exist at this point, likewise
// the following statement would throw an error.
// However, that's not the case.
console.log(x);var x = 10;
console.log(x);

内部实现是这样的:引擎首先在代码中搜索所有变量声明语句并在运行任何其他代码之前执行它们。完成后,它会从头开始解析代码。

这意味着在实际执行上述第 4 行之前,变量 x 已被有效声明,因此可以访问。由于 var xx 初始化为 undefined ,因此在第 6 行赋值语句之前访问 x 会返回 undefined

对于函数声明,也会发生类似的情况。我们称之为函数提升 (function hoisting)

整个声明连同函数主体都被置于其范围的最顶部。这意味着下面的代码将完全正常工作:

console.log(sum(10, 20));function sum(a, b) {return a + b
}

在第 3 行中,函数 sum() 在实际定义之前就被访问了。然而,由于函数提升,这次调用并没有标记任何错误。

块语句内的函数声明

我们知道,所有函数声明都会被提升到其作用域的顶部。然而,这个想法有一个小例外,事实上,不同浏览器之间存在不兼容性。

也就是说,如果函数声明出现在块语句内,那不是另一个函数的主体(也是一个块语句),则函数声明在大多数浏览器中不会正常提升 - 只有其名称可以通过值 undefined 访问。

这种行为有一个很好的理由,从下面的代码中可以看出:

var userIsNew = false;if (userIsNew) {function greet() {console.log('Hello!');}
}

函数 greet() 被放在 if 语句块内。该语句块执行的条件是 userIsNewtrue 。然而,事实显然并非如此。因此,理想情况下,这段代码的作者希望该函数不会出现在全局作用域中,因为它的出现条件尚未满足。同样,在这种情况下,大多数浏览器不会提升函数声明,但函数名称在相应的范围内可用作 undefined 标识符。

这里要认识到的最重要的一点是,函数提升仅限于函数声明 — — 而不是函数表达式 。

引擎在第一次遍历一段代码时仅查找函数声明,而不是表达式,并将它们提升。

console.log(sum(10, 20));var sum = function(a, b) {return a + b
}//Uncaught TypeError: sum is not a function

我们有一个变量声明语句 var sum ,同样,它在运行任何其他代码之前会被提升。接下来,从脚本开头的第 1 行开始执行。此时, sum 等于 undefined ,因此无法调用;同样, sum() 会抛出错误。

可能会认为可以通过在表达式中为函数添加名称来缓解此问题。然而,这也行不通:

console.log(sum(10, 20));var sumRef = function sum(a, b) {return a + b
}
//Uncaught TypeError: sum is not a function

引擎只会在给定代码片段中搜索函数声明, 并最终将其提升。函数表达式,无论是命名的还是匿名的,都无关紧要。它们最终还是表达式,同样会在提升阶段被忽略。

2. 匿名函数

没有名称的函数称为匿名函数

当 JavaScript 遇到函数声明时,它期望也看到函数的名称。否则会导致语法错误。

只有在函数表达式中才可以省略函数名称,如下所示:

var sum = function(a, b) {return a + b;
}

匿名函数只能使用函数表达式来表示,是 JavaScript 中的一个重要特性。几乎所有涉及事件和回调的程序都会用到它们.我们将广泛使用匿名函数。

IIFE

在 JavaScript 中,有一种特殊情况,即函数表达式在定义后立即被调用。我们称之为 IIFE ,即立即调用函数表达式

IIFE 是一个在定义后立即调用的函数表达式。

(function() {console.log('I am in an IIFE.')
})();

IIFE 用非常简洁的语法解决了这个问题。它们将代码封装在本地环境中,不会干扰全局作用域。

最棒的是,这个过程非常快捷——我们无需命名这些函数,也无需担心如何将它们赋值给任何标识符。只需创建一个匿名(或命名)函数,用一对括号将其封装起来,将代码放入其中,然后立即调用即可。 瞧!

每个库的代码都封装在 IIFE 中,并且彼此之间完全互不干扰。 简直太棒了。

this 关键字

JavaScript 中有很多令人困惑的概念,其中之一就是 this 的概念。本节我们将深入介绍并解释 this 的核心。

首先, this 是 JavaScript 中的保留关键字,这意味着它不能用作变量的名称。

回到 this 背后的想法,它是为了提供一种引用调用函数的对象的方式。这就是它被创建的初衷。事实上,编程语言 Java 无疑是 JavaScript 的影响源泉,它也包含 this 功能,本质上服务于非常相似的用途。

当在函数内部使用时, this 指的是调用该函数的对象 。

例如,考虑下面的对象 o 。它有一个属性 x 和一个方法 f()

var o = {x: 10,f: function() { console.log(this.x); }
};var x = 20;
o.f();

一旦执行此代码,我们就会在控制台中记录值 10

怎么样? 我们来看看吧。

在第 7 行中,调用表达式 of() 在对象 o 上调用存储在 of 中的函数,同样,它的 this 也指向 o 。这仅仅意味着函数对象内部的表达式 this.x 转换为 o.x ,等于值 10 ,因此控制台中打印的是 10

这是因为 this 会自动指向调用对象——我们不需要自己将其配置为所需的对象。相反,如果我们使用 ox (而不是 this.x ),当 o 的名称将来发生变化或被分配给其他标识符时,我们必须重写这个表达式。理想情况下,只要有可能,就使用 this 关键字引用方法定义中的容器对象 - 而不是使用对象本身的名称。

当直接调用函数对象(而不是作为对象的一部分)时,如果脚本在非严格模式下运行,则其 this 值将解析为全局 window 对象。

// The global context.
console.log(this);
Window {window: Window, self: Window, document: document, name: "", location: Location, …}

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

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

相关文章

数据结构——优先队列(priority_queue)的巧妙运用

优先队列是一种相对高级的数据结构,它的底层原理是二叉堆。然而本篇不会执着于深挖其背后的原理,更主要的是理一下它在题目中的一些实用方法,帮助你更快的上手使用。 优先队列(priority_queue) 优先队列的特别之处就在于它可以自动进行排序&…

Java:继承和多态(必会知识点整理)

主要内容继承多态向上转型向下转型方法重写方法重载super关键字动态绑定封装访问控制构造方法规则一、继承 1. 概念: 一句话说就是:“共性抽取,代码复用”子类会将父类中的成员变量或者成员方法继承到子类中子类继承父类之后,必须…

基于esp32系列的开源无线dap-link项目使用介绍

基于esp32系列的开源无线dap-link项目使用介绍🔖有关esp32/8266相关项目:需要自己搭建编译环境: https://github.com/windowsair/wireless-esp8266-dap/tree/master🌿支持esp32/c3/s3,支持在线固件烧录,支持AP配网&…

深入了解linux系统—— 进程信号的产生

前言 进程在收到信号之后,可以立即处理,也可以在合适的时间再处理(1-31号普通信号可以不被立即处理) 信号不是被立即处理,信号就要被保存下来,让进程在合适的时间再去处理。 相关概念 在了解进程是如何保存…

【Bluedroid】蓝牙协议栈enable流程深度解析

本文详细剖析 Bluedroid 蓝牙功能启用的核心流程,从enable()函数触发开始,深入解析蓝牙协议栈的异步启动机制、核心协议模块初始化、硬件控制器绑定及状态同步全流程。重点阐述接口就绪性检查、异步线程管理、配置文件回调机制等关键环节,揭示…

各种开发语言主要语法对比

各类主流编程语言的语法有着显著差异,这些差异源于语言设计哲学(简洁性 vs 显式性)、应用领域(系统级、Web、数据科学)、运行方式(编译 vs 解释)以及支持的范式(面向对象、函数式、过…

小鹏汽车6月交付车辆34,611辆,同比增长224%

小鹏汽车-W(09868)发布公告,2025年6月,小鹏汽车共交付智能电动汽车34,611辆,同比增长224%,这标志着小鹏汽车已连续第八个月交付量超过了30,000辆。2025年第二季度,小鹏汽车共交付103,181 辆智能电动车,创下…

深入理解观察者模式:构建松耦合的交互系统

在软件开发中,我们经常遇到这样的场景:一个对象的状态变化需要通知其他多个对象,并且这些对象需要根据变化做出相应的反应。比如,用户界面中的数据变化需要实时反映到多个图表上,或者电商系统中的库存变化需要通知订单…

React强大且灵活hooks库——ahooks入门实践之常用场景hook

什么是 ahooks? ahooks 是一个 React Hooks 库,提供了大量实用的自定义 hooks,帮助开发者更高效地构建 React 应用。其中场景类 hooks 是 ahooks 的一个重要分类,专门针对特定业务场景提供解决方案。 安装 ahooks npm install …

Qt常用控件之QWidget(一)

Qt常用控件之QWidget(一)1.QWidget2.enabled属性2.geometry🌟🌟hello,各位读者大大们你们好呀🌟🌟 🚀🚀系列专栏:【Qt的学习】 📝📝本…

AIOT开发选型:行空板 K10 与 M10 适用场景与选型深度解析

前言 随着人工智能和物联网技术的飞速发展,越来越多的开发者、学生和爱好者投身于创意项目的构建。 在众多的开发板中,行空板 K10 和 M10 以其独特的优势脱颖而出。 本文旨在为读者提供一份详尽的行空板 K10 和 M10 对比分析,从适用场景、…

redis汇总笔记

语雀完整版: https://www.yuque.com/g/mingrun/embiys/calwqx/collaborator/join?tokensLcLnqz5Rv8hOKEB&sourcedoc_collaborator# 《Redis笔记》 Redis 一般问题 Redis内存模型(I/O多路模型)多路复用IO如何解释 为什么Redis要使用单线…

STM32用PWM驱动步进电机

硬件介绍:连线:注意这里stp连的是pwm脉冲,dir连的是方向到时候代码pwm波形就是从这里来的,具体接线根据你的代码来注意要点:步进电机和舵机驱动是不一样的,它是根据步长来移动的,所以要开一个中…

力扣25.7.10每日一题——重新安排会议得到最多空余时间 II

Description 今天这道题和昨天类似,只是允许顺序变化。 Solution 把会议区间视作桌子,空余时间视作空位,我们要把一个桌子移到别的空位中。 初步想法是枚举桌子,找一个长度大于等于桌子长度的空位移过去。看上去,找…

IP报文分片与重组原理及实现分析

IP报文分片与重组原理及实现分析 引用: ppp/net/packet/IPFragment.hppp/net/packet/IPFragment.cpp 1. IP分片原理 当IP数据包大小超过MTU(最大传输单元)时,路由器/主机将其分割为多个片段传输,每个片段包含&…

[python]在drf中使用drf_spectacular

安装drf_spectacular 文档 pypi链接:https://pypi.org/project/drf-spectacular/ 文档链接:https://drf-spectacular.readthedocs.io/en/latest/readme.html 安装步骤 在环境中添加 pip install drf-spectacular在setting的INSTALLED_APPS中添加 INSTALLED_APPS [# ALL…

【Datawhale AI 夏令营】 用AI做带货视频评论分析(二)

5.预训练模型跑分 回顾赛题 回顾赛题任务 挑战与难点: 标注数据少 ——> 半监督学习 or 数据增强 聚类分析噪点影响严重 回顾Baseline 问题: TF-IDF无法捕捉以下语义。聚类分析粗糙,未评估聚类质量。 提升方案: 分类任务…

SPSSPRO:数据分析市场SaaS挑战者的战略分析

目录 第一部分:执行摘要 第二部分:平台解构:产品、架构与用户体验 2.1 SaaS范式转移:架构与起源 2.2 功能能力:分析师的工具箱 2.3 “智能分析”的价值主张 第三部分:市场渗透与受众细分 3.1 目标用户…

低版本hive(1.2.1)UDF实现清除历史分区数据

目标&#xff1a;通过UDF实现对表历史数据清除 入参&#xff1a;表名、保留天数N 一、pom文件 <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.…

C++中顶层const与底层const

在C中&#xff0c;const关键字用于定义常量&#xff0c;但它在指针和引用上下文中会产生两种不同的常量性&#xff1a;顶层const&#xff08;top-level const&#xff09;和底层const&#xff08;low-level const&#xff09;。理解它们的区别是避免编译错误和提高代码质量的关…