今天在学习JavaScript,我其实需要很多练习才可以巩固一点!需要更多!
const
用于声明常量的规则在 JavaScript 中,const
用于声明常量,其核心规则是:声明时【必须】同时初始化(赋值),且后续无法重新赋值 。
const
声明规则
const
声明变量时,必须直接赋初始值,否则会触发 SyntaxError: Missing initializer in const declaration
(语法错误:const
声明中缺少初始化器 )。const num = 5;
,错误用法 const num;
(缺少初始值)。与 let
/var
的区别
let
/var
声明时可暂不赋值(如 let num;
),后续再赋值;但 const
严格要求 “声明即初始化”。const
赋值后,无法通过重新赋值改变其值(会触发 TypeError
),而 let
/var
支持重新赋值。const num;
未在声明时赋值,违反 const
语法规则,SyntaxError
,不会执行到 console.log
步骤。特点是一旦声明并初始化,就不能再被重新赋值 。
const num = 2;
:声明并初始化常量 num
,值为 2
。num = 6;
:尝试给常量 num
重新赋值,这违反了 const
的规则,JavaScript 会直接抛出 TypeError: Assignment to constant variable
(类型错误:对常量变量赋值 )。console.log(num)
根本没机会运行,自然不会输出 2
。在 JavaScript 中,let
声明的变量是可以重新赋值的,这是 let
变量的基本特性之一
比如 let a = 1; a = 2;
是合法操作。
在 JavaScript 中,回调函数是作为参数传递给另一个函数的函数,(有点像回锅肉?)当外层函数执行到合适的时机(比如异步操作完成、循环遍历数组中的每个元素时),就会调用这个作为参数传入的函数。
map
和 filter
方法为例,在 JavaScript 中,map
用于遍历数组并返回一个新数组,filter
用于根据条件过滤数组中的元素。map
给数组每个元素乘以 2,用 filter
筛选出数组中的偶数等 ,这样就实现了代码的复用。回调函数和抽象函数是不同的概念:
总结来说,回调函数是一种灵活的编程方式,能增强代码的复用性和可维护性,它和抽象函数是不同维度的概念。
map
和 filter
函数在 JavaScript 中,map
和 filter
都是数组的高阶函数,它们经常和回调函数配合使用,让对数组的操作更加简洁高效,以下是详细介绍:
map
方法map()
方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。简单来说,它会遍历原数组,对每个元素执行传入的回调函数,并将回调函数的返回值组成一个新数组返回。let newArray = array.map(callback(element[, index[, array]])[, thisArg]);
callback
:必选参数,为数组中每个元素执行的函数,该函数接收三个参数:
element
:当前正在处理的数组元素。index
(可选):当前正在处理的数组元素的索引。array
(可选):调用 map
方法的数组本身。thisArg
(可选):执行 callback
函数时使用的 this
值。javascript
// 将数组中的每个元素都乘以2
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
// 对包含对象的数组进行操作,提取每个对象的某个属性值
const students = [
{ name: 'Alice', age: 20 },
{ name: 'Bob', age: 22 },
{ name: 'Charlie', age: 21 }
];
const names = students.map(student => student.name);
console.log(names); // ["Alice", "Bob", "Charlie"]
filter
方法filter()
方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它会遍历原数组,根据传入的回调函数返回的布尔值来决定当前元素是否要被包含在新数组中。let newArray = array.filter(callback(element[, index[, array]])[, thisArg]);
callback
:必选参数,为数组中每个元素执行的函数,该函数需要返回一个布尔值,决定当前元素是否被包含在新数组中。函数接收三个参数,和 map
方法的 callback
参数接收的参数一致。thisArg
(可选):执行 callback
函数时使用的 this
值。javascript
// 从数组中筛选出偶数
const numbers = [1, 2, 3, 4];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
// 从包含对象的数组中筛选出年龄大于20的学生
const students = [
{ name: 'Alice', age: 20 },
{ name: 'Bob', age: 22 },
{ name: 'Charlie', age: 21 }
];
const eligibleStudents = students.filter(student => student.age > 20);
console.log(eligibleStudents);
// [ { name: 'Bob', age: 22 }, { name: 'Charlie', age: 21 } ]
map
和 filter
对比map
侧重于对数组中的每个元素进行转换,生成一个新的数组,原数组长度和新数组长度一致;而 filter
侧重于筛选,根据条件过滤掉不符合的元素,新数组长度可能小于原数组。map
的回调函数返回值是新数组的元素;filter
的回调函数返回值是布尔值,用于判断当前元素是否保留在新数组中。回调函数的典型用法
用 updateAnimation()
函数举例,想让它每 10 毫秒执行一次(做动画更新这类操作)。手动写代码管理定时器很麻烦,JavaScript 提供了 setInterval
这类工具函数。
setInterval
与回调的关系
setInterval(updateAnimation, 10)
就是利用回调机制:把 updateAnimation
当作回调函数传给 setInterval
,让 JS 引擎在定时器触发时(每 10 毫秒)自动调用它,帮开发者简化了定时器管理的工作。
“我们编写了 updateAnimation()
函数,想让它每 10 毫秒调用一次。由于手动管理定时器代码很麻烦,JavaScript 提供了 setInterval
工具,通过 setInterval(updateAnimation, 10)
,让定时器触发时(每 10 毫秒)自动调用 updateAnimation
函数,其中 updateAnimation
作为回调函数 。”
// 定义要定时调用的函数
function updateAnimation() {
// 假设这里是动画更新逻辑...
}
// 利用 setInterval 传入回调,实现每 10 毫秒调用一次
setInterval(updateAnimation, 10);
//(注:实际网页中 10 毫秒的间隔非常短,可能会有性能问题,一般动画场景常用 requestAnimationFrame ,不过不影响理解回调 + 定时器的案例逻辑 )
简单说,这是用定时器案例,直观展示回调函数如何让 “特定时机执行代码” 更简单 ,体现回调在 Web 开发里处理异步、定时逻辑的价值 。(如果有后续 PPT ,可能还会延伸讲更多回调应用场景,比如事件监听、异步请求等)
在 JavaScript 以及其他编程语言中,使用回调函数有以下多个重要原因:
在 JavaScript 中,像网络请求(如fetch
获取数据)、读取文件、定时器等操作都是异步的,即不会阻塞代码的执行。当异步操作完成时,我们需要一种方式来执行后续的处理逻辑,回调函数就派上了用场。
setTimeout
模拟异步操作javascript
console.log('开始');
setTimeout(() => {
console.log('异步操作完成');
}, 1000);
console.log('结束');
上述代码中,setTimeout
会在 1 秒后执行回调函数,而主程序会继续向下执行,不会等待这 1 秒,体现了异步特性。
回调函数可以将通用的逻辑抽象出来,通过传入不同的回调函数,实现不同的具体操作。比如数组的map
、filter
、reduce
等方法,都是接收回调函数作为参数,让我们可以对数组进行不同的处理,而无需每次都编写遍历数组的代码。
map
方法对数组元素进行处理javascript
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
这里map
方法复用了数组遍历逻辑,通过传入不同的回调函数,可以实现对数组元素的各种转换操作。
在 Web 开发中,经常需要处理用户的交互事件,如点击按钮、鼠标移动等。可以将处理事件的函数作为回调函数,注册到对应的事件上。当事件发生时,浏览器会自动调用这些回调函数。
html
预览
在一些库(如 jQuery)中,通过回调函数可以实现方法的链式调用,使代码更加简洁和易读。虽然 JavaScript 原生没有广泛采用这种链式调用方式,但回调函数在设计这种可链式调用的 API 时起到了关键作用。
javascript
$(document).ready(() => {
$('#myDiv')
.css('color','red')
.text('修改后的文本');
});
通过回调函数,可以在文档加载完成后,对特定元素进行一系列的操作。
在大型项目中,回调函数有助于将不同的业务逻辑分离。比如在一个复杂的系统中,底层模块可以通过回调函数将处理结果返回给上层模块,上层模块不需要关心底层具体的实现细节,只需要关注回调函数中的处理逻辑,实现了代码的抽象和分层。
在 JavaScript 中,自定义函数是可以作为回调函数的。以下从几个常见的应用场景来说明:
JavaScript 中数组的许多方法,如map
、filter
、reduce
等,都接收回调函数作为参数。我们可以将自定义函数传入这些方法中,以实现特定的功能。
javascript
// 自定义函数作为map方法的回调
function double(num) {
return num * 2;
}
const numbers = [1, 2, 3];
const result = numbers.map(double);
console.log(result); // [2, 4, 6]
// 自定义函数作为filter方法的回调
function isEven(num) {
return num % 2 === 0;
}
const numbersArray = [1, 2, 3, 4];
const filteredResult = numbersArray.filter(isEven);
console.log(filteredResult); // [2, 4]
setTimeout
和setInterval
等定时器函数也支持传入回调函数,我们可以把自定义函数作为回调,在特定的时间点或周期性地执行。
javascript
// 自定义函数作为setTimeout的回调
function sayHello() {
console.log('Hello!');
}
setTimeout(sayHello, 1000); // 1秒后执行sayHello函数
// 自定义函数作为setInterval的回调
function count() {
static count = 0;
console.log(count++);
}
setInterval(count, 2000); // 每隔2秒执行一次count函数
在 DOM 事件处理中,addEventListener
方法用于给元素添加事件监听器,它接收的第二个参数就是事件触发时要执行的回调函数,我们可以使用自定义函数来处理特定的事件。
html
预览
在处理异步操作,比如使用fetch
进行网络请求时,也可以将自定义函数作为回调,在请求成功或失败时执行相应的处理逻辑。
javascript
function handleResponse(response) {
return response.json();
}
function handleError(error) {
console.log('请求出错:', error);
}
fetch('https://api.example.com/data')
.then(handleResponse)
.catch(handleError);
综上,只要自定义函数的参数和返回值符合回调函数所在上下文的要求,就可以作为回调函数使用。
JavaScript(JS)与 Python、Java 相比,在多方面存在差异,呈现出独特的特点:
let num = 1; num = "hello";
,变量num
先是数值类型,后又变为字符串类型。这使得代码编写更加灵活,上手容易,开发效率高,但也可能导致一些潜在的类型错误,比如在进行计算时,没有注意类型转换,就会出现意料之外的结果。int num = 1;
,num
只能存储整数类型的数据。这种严格的类型系统在编译阶段就能发现很多类型相关的错误,提高了代码的稳定性和可维护性,尤其适合大型项目的开发,但在开发效率上,相比动态类型语言会稍低一些,因为需要更多的时间来定义类型。特性 | JavaScript | Python | Java | C 语言 |
---|---|---|---|---|
类型系统 | 动态类型、弱类型 | 动态类型、强类型(隐式类型转换少) | 静态类型、强类型 | 静态类型、弱类型(允许较多隐式转换) |
编译 / 解释 | 解释执行(JIT 编译优化) | 解释执行(部分 JIT 如 PyPy) | 编译(字节码)→ JVM 执行 | 编译为机器码直接执行 |
主要范式 | 多范式(函数式、原型继承 OOP) | 多范式(函数式、类 OOP) | 纯面向对象(一切基于类) | 过程式、结构化(支持简单 OOP) |
内存管理 | 自动垃圾回收(GC) | 自动垃圾回收(引用计数 + GC) | 自动垃圾回收(GC) | 手动管理(malloc/free) |
应用场景 | 前端、Node.js 后端、移动 / 桌面应用 | 数据科学、Web 后端、自动化、AI | 企业级后端、Android 应用、大数据 | 系统软件、嵌入式开发、游戏引擎、驱动 |
性能 | 中等(JIT 优化后接近原生) | 较慢(解释执行) | 较快(JIT 优化后) | 极快(直接操作硬件) |
语法特点 | 灵活(函数式特性丰富) | 简洁(强制缩进、可读性高) | 严谨(大量样板代码) | 底层(指针、位操作) |
异步支持 | 原生 Promise、async/await | asyncio、协程 | CompletableFuture、线程池 | 回调函数、多线程(需手动管理) |
跨平台性 | 浏览器原生支持,Node.js 跨平台 | 解释器跨平台(需环境配置) | 一次编写,到处运行(JVM 依赖) | 需针对不同平台编译 |
独特标识 | 函数是一等公民、原型继承 | 缩进代替大括号、动态类型注解 | 强制面向对象、严格访问控制 | 指针操作、直接内存访问 |
JavaScript
Object.create()
)。map
/filter
)是核心特性。Python
typing
模块添加类型提示,但非强制。Java
Unsafe
类),避免野指针问题。C 语言