基础概念
JavaScript 是一种广泛使用的高级编程语言,主要用于实现网页和 Web 应用程序的交互功能。它是一种解释型、基于原型(prototype-based)、多范式(支持面向对象、命令式和函数式编程风格)的语言。JavaScript 最初由 Netscape 的 Brendan Eich 于 1995 年创建,旨在为网页添加动态行为。
起源与发展
- 起源:
JavaScript
由网景公司(Netscape)的布兰登·艾奇(Brendan Eich)于 1995 年创建,最初是为了在网页中添加动态交互功能。 - 标准化:1997 年,
JavaScript
被提交给欧洲计算机制造商协会(ECMA),并成为国际标准(ECMAScript)。目前,JavaScript 的最新版本是 ECMAScript 2024。 - 发展:随着互联网的发展,JavaScript 的功能不断扩展,从最初的简单网页特效,到如今可以用于开发复杂的前端框架(如 React、Vue、Angular)、服务器端应用(Node.js)、移动应用(React Native)等。
主要用途
- 网页交互:JavaScript 可以操作 HTML 和 CSS,实现动态效果,例如响应用户点击、鼠标悬停、表单验证等。
- 前端框架开发:现代前端开发中,JavaScript 是构建单页面应用(SPA)的核心技术。例如:
- React:由 Facebook 开发,用于构建用户界面。
- Vue.js:轻量级的前端框架,易于上手。
- Angular:由 Google 维护,功能强大,适合大型应用。
- 服务器端开发:通过 Node.js,JavaScript 可以在服务器端运行,用于构建高性能的后端服务。
- 移动应用开发:使用 React Native 或其他框架,JavaScript 可以开发跨平台的移动应用。
- 游戏开发:结合 HTML5 和 Canvas,JavaScript 可以开发简单的网页游戏。
核心特点
解释型语言
:JavaScript 代码在运行时由浏览器或其他环境(如 Node.js)解释执行,不需要编译。基于事件驱动
:JavaScript 可以响应用户操作(如点击按钮、提交表单等),并根据这些事件执行相应的代码。跨平台
:可以在多种操作系统和设备上运行,包括 Windows、macOS、Linux 和移动设备。客户端与服务器端支持
:最初用于浏览器中的客户端脚本编写,现在也通过 Node.js 实现了服务器端开发。非阻塞 I/O 操作
:使用异步编程模型,允许高效的并发处理,避免长时间等待资源(如网络请求或文件读取)而阻塞主线程。丰富的生态系统
:拥有庞大的库和框架(如 React、Vue、Angular 等),以及 npm(Node Package Manager)提供的海量第三方包。
运行环境
- 浏览器:JavaScript 最初的运行环境是浏览器,通过浏览器内置的 JavaScript 引擎(如 Chrome 的 V8、Firefox 的 SpiderMonkey)执行代码。
- Node.js:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,允许 JavaScript 在服务器端运行。它提供了丰富的模块和库,用于构建网络应用、服务器等。
javascript 的组成
- ECMAScript 是 JavaScript 的核心语法,定义了语言的基本特性。
- DOM 是用于操作 HTML 文档的接口,允许 JavaScript 动态地修改文档的内容和样式。
- BOM 是用于操作浏览器窗口和环境的接口,提供了与浏览器交互的功能。
ECMAScript
是 JavaScript 的核心语法部分,是 JavaScript 的标准化实现。它定义了语言的基本语法、数据类型、运算符、控制流、函数、对象等基本特性
DOM
是一个与平台和语言无关的接口,用于操作 HTML 和 XML 文档。DOM 将文档表示为一个由节点和对象组成的树形结构,允许 JavaScript 动态地访问和修改文档的内容、结构和样式。
BOM
是浏览器提供的对象模型,用于操作浏览器窗口和浏览器环境。BOM 提供了一系列的全局对象和方法,用于处理浏览器窗口、位置、历史记录、定时器等。
JS注释
在 JavaScript 中,注释用于解释代码的功能和用途,帮助开发人员更好地理解代码。以下是 JavaScript 中常见的注释类型:
单行注释
单行注释以 //
开头,该行后续的所有内容都会被视为注释,不会被解释器执行。
// 这是一个单行注释
console.log("Hello, World!"); // 这也是一个单行注释
多行注释
多行注释以 /*
开头,以*/
结尾。可以用于注释多行内容。
/*
这是一个多行注释
可以跨越多行
*/
文档注释
文档注释用于生成代码文档,通常以 /**
开头,以 */
结尾。文档注释中可以添加特殊标记,如 @param
、@return
等,用于描述函数的参数、返回值等。
/**
* 这是一个函数的文档注释
* @param {number} a - 第一个参数
* @param {number} b - 第二个参数
* @returns {number} 返回 a 和 b 的和
*/
function add(a, b) {
return a + b;
}
注释的用途
代码解释
:帮助开发人员和其他人更好地理解代码的功能和用途。调试
:在调试代码时,可以使用注释临时禁用某些代码块,以便快速定位问题。代码维护
:便于开发人员在日后的代码维护过程中快速理解代码的逻辑和功能。
注释应清晰、简洁,避免冗长和无关的内容
注释应与代码保持一致,避免出现注释与代码不符的情况,导致误解。 合理使用注释,避免过度注释,以免影响代码的可读性。
js的引入方式
在 JavaScript 中,有多种方式可以引入 JS 文件或代码,具体选择取决于项目需求和开发环境。以下是常见的几种引入方式:
使用 <script>
标签引入外部 JS 文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="path/to/your/script.js"></script>
</body>
</html>
这种方式简单易用,但不适合大型项目,因为代码的可维护性较差
使用 ES6 模块
ES6 模块是现代 JavaScript 开发中推荐的方式,通过 import
和 export
语法实现模块化。
// file: module.js 导出模块:
export const myFunction = () => {
console.log('Hello, World!');
};
// file: main.js 导入模块:
import { myFunction } from './module.js';
myFunction();
// 在 HTML 中引入模块:
<script type="module" src="path/to/your/main.js"></script>
使用 CommonJS 模块
CommonJS
是 Node.js 中常用的模块化规范,通过 require
和 module.exports
实现模块化。
// file: module.js
function myFunction() {
console.log('Hello, World!');
}
module.exports = myFunction;
// file: main.js
const myFunction = require('./module.js');
myFunction();
这种方式主要用于 Node.js 环境
动态加载脚本
在某些情况下,需要在运行时动态加载 JavaScript
文件,可以使用 document.createElement
方法。
const script = document.createElement('script');
script.src = 'path/to/your/script.js';
document.head.appendChild(script);
这种方式适用于按需加载或延迟加载的场景
使用第三方模块加载器
在大型项目中,可以使用模块加载器(如 RequireJS、SystemJS)来实现动态加载和依赖管理。
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
<script>
require(['path/to/your/module'], function(module) {
module.myFunction();
});
</script>
使用构建工具
在现代前端开发中,使用构建工具(如 Webpack
、Parcel
)可以帮助进行代码打包、压缩和优化。
使用 Webpack:
# 安装 Webpack:
npm install --save-dev webpack webpack-cli
// 创建配置文件 webpack.config.js:
const path = require('path');
module.exports = {
entry: './scripts/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
# 运行 Webpack 打包:
npx webpack --config webpack.config.js
这种方式适合大型项目,可以提升性能和开发效率
代码分割和懒加载
在大型项目中,可以通过代码分割和懒加载来优化性能。
import(/* webpackChunkName: "module1" */ './module1')
.then(module => {
module.myFunction();
})
.catch(err => {
console.error('Failed to load module:', err);
});
这种方式可以显著提升页面加载速度
js的输出
- 在开发和调试过程中,
console.log()
是最常用的输出方式。 - 如果需要将内容显示在页面上,可以使用
document.write()
或操作 DOM。 alert()
适用于需要用户确认的场景。- debugger; 和其他 console 方法可以辅助调试。
console.log()
这是最常用的输出方式,用于将内容输出到浏览器的控制台。\
console.log("Hello, World!"); // 输出字符串
console.log(123); // 输出数字
console.log(true); // 输出布尔值
console.log({ name: "Kimi", age: 25 }); // 输出对象
document.write()
在页面加载时,可以使用 document.write() 将内容直接输出到 HTML 页面中。
document.write("Hello, World!"); // 将内容输出到页面
注意: document.write()
只能在页面加载时使用,页面加载完成后使用会导致页面内容被清空。
操作 DOM
可以通过操作 DOM 将内容输出到 HTML 页面的指定元素中。
// 获取页面中的某个元素
const element = document.getElementById("myElement");
element.innerHTML = "Hello, World!"; // 将内容设置为元素的 HTML 内容
<!DOCTYPE html>
<html>
<head>
<title>示例</title>
</head>
<body>
<div id="myElement"></div>
<script>
const element = document.getElementById("myElement");
element.innerHTML = "Hello, World!";
</script>
</body>
</html>
alert()
可以使用 alert()
弹出一个警告框,显示内容。
alert("Hello, World!"); // 弹出警告框显示内容
alert()
是阻塞的,会暂停页面的执行,直到用户点击 “确定”。
- debugger;:在代码中插入调试断点,可以在控制台中查看变量的值和调试代码。
- console.error():用于输出错误信息。
- console.warn():用于输出警告信息。
console.error("这是一个错误信息"); // 在控制台输出红色的错误信息
数据类型
JavaScript 是动态类型语言,变量是由值绝对,数据类型分为基础类型和引用类型。
基本数据类型(Primitive Data Types)
基本数据类型是 JavaScript 中最简单的数据类型,它们是不可变的,即直接存储在变量访问的位置。
String(字符串)
特点:字符串是由字符组成的序列,用单引号 '、双引号 " 或反引号 ` 包裹。 常见操作:字符串长度:"hello".length → 返回 5。 字符串拼接:"hello" + "world" → 返回 "helloworld"。
字符串方法:
.toUpperCase()
: 将字符串转化为大写
.toLowerCase()
:将字符串转化为小写
.includes()
:判断字符串中是否包含某个字符串
.charAt()
:获取到指定字符的索引位置
.substring()
:截取字符串
.split()
:将字符串分割为数组。
模板字符串:使用反引号 ` 和 ${} 插值,例如:
let name = 'John';
let gaessta =`Hello, ${name}!`
Number(数字)
特点:数字不分整数和小数,统一使用number表示 常见操作:四则运算:加 +、减 -、乘 *、除 /。 特殊值:
- Infinity:表示正无穷。
- -Infinity:表示负无穷。
- NaN(Not a Number):表示非数字值。
数学方法:
- Math.abs():绝对值。
- Math.round():四舍五入。
- Math.floor():向下取整。
- Math.ceil():向上取整。
- Math.random():生成 0 到 1 之间的随机数。
数据格式
.toFixed():指定小数点后保留的位数。 .toPrecision():指定数字的精度。
Boolean(布尔值)
特点:布尔值只有两个值:true 和 false。 逻辑运算符:
&&(逻辑与):true && true → true ||(逻辑或):true || false → true !(逻辑非):!true → false。
类型转换:某些值在布尔上下文中会被转换为布尔值:
真值
:true、非空字符串、非零数字、对象等。
假值
:false、null、undefined、0、-0、NaN、空字符串 ""。
Undefined
特点:表示变量已声明,但尚未赋值。
let x;
console.log(x); // undefined
Null
特点:表示一个空值,表示一个空对象。
let x = null;
console.log(x); // null
Symbol(ES6 新增)
特点:用于创建唯一的、不可变的值,常用于对象属性的键。
let mySymbol = Symbol("mySymbol");
console.log(mySymbol); // Symbol(mySymbol)
BigInt(ES2020 新增)
特点:用于表示大于 2^53 - 1 的整数。
let bigNumber = BigInt(1234567890123456789012345678901234567890n);
console.log(bigNumber); // 1234567890123456789012345678901234567890n
复合数据类型(Composite Data Types)
复合数据类型可以包含多个值,并且是可变的。
Object(对象)
特点: 对象是是由键值对组成的集合,用花括号 {} 包裹。 常见操作:
创建对象:
let person = {
id:1,
name:"John",
arry:[1,2,3]
arrbyor[{
out: "数值值",
tabis: "数值操作",
}]
}
访问属性:
点符号:person.name。 方括号:person["name"]。
let names = person.name;
let arry = person.["name"];
console.log(names);
添加/修改属性:
person.job = "developer";
person.name = "Bob";
删除属性:
delete person.id;
遍历对象:
for (let key in person) {
console.log(key, person[key]);
}
Array(数组)
特点:数组是有序的值的集合,可以包含任意类型的数据。 常见操作:
创建数组:
let fruits = ["apple", "banana", "cherry"];
访问元素:
console.log(fruits[1]); // "banana"
添加/修改元素:
fruits.push("orange"); // 添加到数组末尾
fruits[1] = "grape"; // 修改元素
删除元素:
fruits.pop(); // 删除数组末尾的元素
fruits.shift(); // 删除数组开头的元素
数组方法:
.length:获取数组长度。 .slice():截取数组。 .splice():删除、替换、添加元素。 .map(): 对数组中的每个元素执行一个函数,并返回一个新数组。 .filter():根据条件过滤数组元素,并返回一个新数组。 .reduce():对数组中的每个元素执行一个函数,并返回一个值。 .sort():对数组进行排序。
特殊数据类型
Function(函数)
特点:函数是一种特殊的数据类型,可以被赋值给变量,也可以作为参数传递或作为返回值。
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("John"));
类型检测
typeof
运算符:
基本数据类型:
- typeof "hello" → "string"
- typeof 42 → "number"
- typeof true → "boolean"
- typeof undefined → "undefined"
- typeof Symbol() → "symbol"
- typeof BigInt(123) → "bigint"
复合数据类型:
- typeof {} → "object"
- typeof [] → "object"(数组也是对象)
- typeof null → "object"(这是 JavaScript 的一个历史遗留问题)
- typeof function () {} → "function"
instanceof
运算符
用于检测对象是否是某个构造函数的实例:
let arr = [];
console.log(arr instanceof Array); // true
类型转换
隐式类型转换: 在某些操作中,JavaScript 会自动将数据类型转换为适合操作的类型,例如:
"123" + 456; // "123456"(字符串拼接)
"123" - 456; // -333(数字运算)
显式类型转换: 字符串转换:
String(value):将值转换为字符串。 value.toString():对象或数组调用 toString() 方法。
数字转换: Number(value):将值转换为数字。 parseInt(value):将字符串解析为整数。 parseFloat(value):将字符串解析为浮点数。
布尔转换: Boolean(value):将值转换为布尔值。
变量
在 JavaScript 中,变量是用于存储数据的容器,可以存储不同类型的数据,如数字、字符串、布尔值、对象等。以下是关于 JavaScript 中变量的详细知识点:
变量的声明
var(es5)
作用域:函数作用域或全局作用域 在函数内声明:作用域为整个函数 在块(如 if、for)中声明:变量会泄漏到块外部。
if (true) {
var x = 10;
}
console.log(x); // 输出 10
变量提升:声明会被提升到作用域顶部,但赋值不提升。
console.log(x); // 输出 undefined
var x = 10;
重复申明:容许重复申明,但会覆盖之前的值。
var a = 1;
var a = 2;
全局污染: 在全局作用域中声明的变量会污染全局作用域,可能影响其他代码。
var logdata = 'hello';
console.log(widow.logdata);
let(es6)
作用域:块作用域,只能在块中访问,不能在函数外部访问。{}
if (true) {
let x = 10;
}
console.log(x); // ReferenceError: x is not defined
暂时性死区(TDZ):声明前访问会报错。
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 10;
不可重复声明:同一作用域内禁止重复声明。
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
循环中的应用:每次迭代创建新的绑定,解决 var 的闭包问题。
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 0, 1, 2
}
const(es6)
常用申明:申明后不可以重新赋值
const pi = 3.14;
pi = 3.14159; // TypeError: Assignment to constant variable.
必须初始化:申明时必须初始化。
const pi; // SyntaxError: Missing initializer in const declaration
对象和数组的修改:允许修改属性或元素,但不可重新赋值。
const obg = {name: 'John'}
obg.name = 'Jane'
obg = {} // TypeError: Assignment to constant variable.
const arr = [1, 2, 3]
arr.push(4)
arr=[4] // TypeError
变量的赋值
变量的赋值是将数据存储到变量中的过程。可以使用以下方式进行赋值:
直接赋值
使用 = 运算符将值赋给变量。
let a;
a = 100; // 为变量 a 赋值为 100
console.log(a); // 输出: 100
解构赋值
这是一种从数组或对象中提取数据并赋值给变量的方式。
// 数组解构赋值
const [b, c] = [200, 300];
console.log(b, c); // 输出: 200 300
// 对象解构赋值
const { name, age } = { name: 'Alice', age: 25 };
console.log(name, age); // 输出: Alice 25
流程控制
流程控制语句用于控制程序的执行顺序,包括条件语句、循环语句和跳转语句等。
条件语句(Conditional Statements)
if
语句
if
语句用于根据条件执行代码块。
if (condition) {
// condition 是条件
// condition 为 true 时执行代码块
}
// 示例:
let age = 18;
if (age >= 18) {
console.log("成年人");
}
if...else
语句
if...else
语句用于根据条件选择执行不同的代码块。
if (condition) {
// condition 条件为 true 时执行代码块
} else {
// condition 条件为 false 时执行代码块
}
// 示例:
let age = 17;
if (age >= 18) {
console.log("成年人");
}else {
console.log("未成年人");
}
if...else if...else
语句
if...else if...else
语句用于处理多个条件。
if (条件1) {
// 条件1 为 true 时执行的代码
} else if (条件2) {
// 条件1 为 false 且条件2 为 true 时执行的代码
}else {
// 所有条件都不满足时执行的代码
}
// 示例:
let score = 85;
if (score >= 90) {
console.log("优秀");
} else if (score >= 70) {
console.log("良好");
} else {
console.log("不及格");
}
switch
语句
switch
语句用于根据变量的值选择执行不同的代码块。
switch (表达式) {
case 值1:
// 表达式等于值1时候执行的代码
break;
case 值2:
// 表达式等于值2时候执行的代码
break;
default:
// 所有条件都不满足时执行的代码
}
// 示例:
let times = 3;
switch (times) {
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
default:
console.log("其他时间");
}
循环语句(Loop Statements)
for
循环
for
循环用于在指定的条件下重复执行代码块。
for (初始化; 条件; 迭代) {
// 循环体
}
// 示例:
for (let i = 0; i < 5; i++) {
clonsole.log(i);
}
while
循环
while
循环用于在条件为 true
时重复执行代码块。
while (条件) {
// 循环体
}
// 示例:
let i = 4;
while (i < 5) {
console.log(i);
i++
}
do...while
循环
do...while
循环至少执行一次循环体,然后根据条件决定是否继续执行。
do {
// 循环体
} while (条件);
// 示例:
let i = 0;
do {
console.log(i);
i++;
} while (i < 5);
for...of
循环(ES6 新增)
for...of
循环用于遍历可迭代对象(如数组、字符串等)。
for (let value of 可迭代对象) {
// 循环体
}
// 示例:
let fruits = ["apple", "banana", "cherry"];
for (let fruit of fruits) {
console.log(fruit);
}
for...in
循环
for...in
循环用于遍历对象的属性名。
for (let key in 对象) {
// 循环体
}
// 示例:
let person = {name: "John", age: 30};
for (let key in person) {
console.log(key + ": " + person[key]);
}
跳转语句(Jump Statements)
break
语句
break
语句用于终止循环或 switch
语句。
for (let i = 0; i < 5; i++) {
if (i === 3) {
break;
}
console.log(i);
}
continue
语句
continue
语句用于跳过当前循环的剩余部分,继续执行下一次循环。
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue; // 跳过偶数
}
console.log(i);
}
return
语句
return
语句用于从函数中返回值,并终止函数的执行。
function add(a, b) {
return a + b; // 返回结果
}
console.log(add(3, 5)); // 输出 8
运算符
JavaScript 中的运算符是编程的基础,用于执行各种操作,如算术计算、比较、逻辑判断等。以下是 JavaScript 中常见的运算符及其用法,帮助你更好地复习:
算术运算符(Arithmetic Operators)
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 10 / 2 | 5 |
% | 求余 | 10 % 3 | 1 |
++ | 自增(前置或后置) | let x = 5; ++x | 6(前置结果为6,后置结果为原值后自增) |
-- | 自减(前置或后置) | let x = 5; x-- | 5(此行为后置,结果为原值,之后x自减为4) |
==** | 幂运算(ES6 新增) | 2 **== 3 | 8 |
自增和自减运算符的前置和后置区别:
let x = 5;
console.log(++x); // 6
console.log(x++); // 6,但 x 的值变为 7
赋值运算符(Assignment Operators)
运算符 | 描述 | 示例 | 等价于 |
---|---|---|---|
= | 简单赋值 | x = 5 | x = 5 |
+= | 加法赋值 | x += 3 | x = x + 3 |
-= | 减法赋值 | x -= 3 | x = x - 3 |
*= | 乘法赋值 | x *= 3 | x = x * 3 |
/= | 除法赋值 | x /= 3 | x = x / 3 |
%= | 求余赋值 | x %= 3 | x = x % 3 |
==**= | 幂运算赋值(ES6 新增) | x **‌= 3 | x = x **== 3 |
比较运算符(Comparison Operators)
比较运算符用于比较两个值,返回布尔值 true 或 false。
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
== | 等于(值相等) | 5 == "5" | true |
=== | 严格等于(值和类型都相等) | 5 === "5" | false |
!= | 不等于 | 5 != "5" | false |
!== | 严格不等于(值和类型不相等) | 5 !== "5" | true |
> | 大于 | 5 > 3 | true |
< | 小于 | 5 < 3 | false |
>= | 大于等于 | 5 >= 3 | true |
<= | 小于等于 | 5 <= 3 | false |
== 和 === 的区别: == 会进行类型转换,例如 5 == "5" 返回 true。 === 不进行类型转换,类型不同直接返回 false,例如 5 === "5" 返回 false。
逻辑运算符(Logical Operators)
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
&& | 逻辑与 | true && false | false |
| | | 逻辑或 | true | | false | true |
! | 逻辑非 | !true | false |
&&
:如果第一个操作数为 false,返回第一个操作数;否则返回第二个操作数
console.log(true && "hello"); // false
console.log(true && "hello"); // "hello"
||
:如果第一个操作数为 true,返回第一个操作数;否则返回第二个操作数。
console.log(true || "hello"); // true
console.log(false || "hello"); // "hello"
三元运算符(Ternary Operator)
三元运算符是一种简化的 if-else 结构。
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
? : | 条件运算符 | condition ? exprTrue : exprFalse | 根据条件返回值 |
let age = 18;
let message = age >= 18 ? "Adult" : "Minor";
console.log(message); // Adult
函数
函数是 JavaScript 中的一个 fundamental building block,它允许我们封装代码,以便于重复使用和重用。
函数定义方式
函数申明:函数声明(function declaration)是 JavaScript 中的一种函数定义方式,它使用 function
关键字来定义函数。
function add(参数1,参数2,参数3....... 参数n) {
// 函数体
return 返回值;
}
// 实例
function add(a, b) {
return a + b;
}
console.log(add(1, 2)); // 输出:3
函数表达式:函数表达式(function expression)是 JavaScript 中的一种函数定义方式,它使用 var
, let
, const
关键字来定义函数。
let 函数名 = function(参数1,参数2,参数3....... 参数n) { retrun 返回值; }
// 示例:
let add = function(name) { return "Hello, " + name + "!"; }
console.log(greet("Alice")); // 输出 "Hello, Alice!"
箭头函数:箭头函数(arrow function)是 JavaScript 中的一种函数定义方式,它使用 =>
来定义函数。
let 函数名 = (参数1,参数2,参数3....... 参数n) => { retrun 返回值; }
// 示例:
let dious =(x) => x * x;
console.log(dious(5)); // 25
let add = (name) => { "Hello, " + name + "!"; }
console.log(greet("Bob"));
函数参数
默认参数(ES6 新增):函数可以在定义时为参数指定默认值,当调用函数时,如果没有为参数赋值,则使用默认值。
function 函数名(参数1 = 默认值1, 参数2 = 默认值2, 参数3 = 默认值3) {}
// 示例:
function greet(name = 'World') {
return `Hello, ${name}!`;
}
function add(a, b = 1) {
return a + b;
}
console.log(add(5));
剩余参数(ES6 新增):在函数定义时,使用 ... 表示一个参数可以接受一个数组作为参数。
function 函数名(...参数名) {
// 函数体
}
function sum(...numbers) {
return numbers.reduce((acc, cur) => acc + cur, 0);
}
let data = sum(1, 2, 3, 4, 5);
console.log(data); // 输出:15
arguments
对象:在函数内部,arguments
对象是一个类数组对象,包含了函数的参数。
function sum() {
let sumtos = 0;
for (let i = 0; i < arguments.length; i++) {
sumtos += arguments[i];
}
return sumtos;
}
console.log(sum(1, 2, 3, 4, 5)); // 输出:15
函数的调用方式
普通调用: 直接调用函数,如 functionName()
function getData() {
retrurn "Hello World";
}
//直接调用函数
getData();
作为方法调用:在对象上调用函数,如 object.method()
let obj = {
name: "John",
getName: function() {
return "你的名字" + this.name;
}
}
// 作为方法调用
obj.getName();
作为构造函数调用:使用 new 关键字调用函数,如 new FunctionName()
function Person(name) {
this.name = name;
}
let person = new Person("John");
通过call/apply调用: call 和 apply 方法可以改变函数的 this 绑定,并调用函数。
// call 方法接受一个 this 值和一系列参数,并将函数的 this 绑定到指定的对象上,然后立即执行该函数。
function greet(greeting,punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // 输出: Hello, Alice!
// apply 方法接受一个 this 值和一个参数数组,并将函数的 this 绑定到指定的对象上,然后立即执行该函数。
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Bob' };
greet.apply(person, ['Hi', '.']); // 输出: Hi, Bob.
函数的返回值
函数可以通过 return 语句返回值。如果没有显式返回值,默认返回 undefined。
function add(a, b) {
return a + b;
}
console.log(add(3, 5)); // 输出 8
匿名函数与回调函数
匿名函数:没有名称的函数称为匿名函数,通常作为函数表达式或回调函数使用。
setTimeout(function() {
console.log("Hello after 1 second!");
}, 1000);
回调函数:回调函数是一个函数,它被传递给另一个函数作为参数,并在这个函数被调用后执行。
const fruits = ["apple", "banana", "cherry"];
fruits.forEach(function(fruit) {
console.log(fruit);
})
自定义回调函数:你可以创建一个自定义的回调函数,并在需要的时候使用它。
function delayedGreeting(name, callback) {
setTimeout(function() {
console.log(`Hello, ${name}!`);
callback();
}, 2000);
}
function sayGoodbye() {
console.log('Goodbye!');
}
delayedGreeting('Bob', sayGoodbye);
// 输出 (2秒后):
// Hello, Bob!
// Goodbye!
递归函数 递归函数是指在函数内部调用自身的函数。递归函数需要有终止条件,否则会导致栈溢出。
function factorial(n) {
if (n === 0) {
return 1; // 终止条件
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 输出 120 (5! = 5 × 4 × 3 × 2 × 1)
箭头函数与普通函数的区别
this 的绑定 this 在箭头函数中指向外部函数的 this,而普通函数中的 this 指向函数定义时的上下文。
let obj = {
name: 'John',
regularFunction:function() {
console.log(this.name);
},
arrowFunction: () => {
console.log(this.name);
}
}
obj.regularFunction(); // 输出 "Alice"
obj.arrowFunction(); // 输出 undefined
不可作为构造函数 箭头函数不能用作构造函数,即不能使用 new 关键字调用箭头函数。
高阶函数(Higher-Order Functions)
高阶函数是接受函数作为参数或返回函数的函数
高阶函数的特点:
01接受函数作为参数:这种函数可以将其他函数作为参数传递进来,并在内部调用这些函数。 02返回函数作为结果:这种函数可以返回一个新的函数,这个返回的函数可以在外部被调用。
高阶函数的应用场景
数组方法:map、filter、reduce
// map 方法接受一个回调函数作为参数,并返回一个新数组,其中每个元素都是回调函数处理后的结果。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(number) {
return number * 2;
})
console.log(doubled); // [2, 4, 6, 8, 10]
// filter 方法接受一个回调函数作为参数,并返回一个新数组,其中包含所有通过回调函数测试的元素。
const nubers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(function(number) {
return number % 2 === 0;
})
console.log(evenNumbers); // [2, 4]
// reduce 方法接受一个回调函数和一个初始值作为参数,并返回一个累积的结果。
const numbers = [1, 2, 3, 4, 5];
* const sum = numbers.reduce(function(accumulator, currentValue) {
return accumulator + currentValue;
},0);
console.log(sum); // 15
函数的提升
函数声明会被提升到作用域的顶部,而函数表达式和箭头函数不会被提升。
console.log(add(3, 5)); // 输出 8(函数声明被提升)
function add(a, b) {
return a + b;
}
// console.log(multiply(3, 5)); // 报错,函数表达式未被提升
let multiply = function(a, b) {
return a * b;
};
JavaScript 作用域
作用域(Scope)是 JavaScript 中一个非常重要的概念,它决定了变量、函数和语句的可见性和生命周期
基本概念
作用域定义了变量、函数和语句的可访问性和生命周期。简单来说,它决定了哪些地方可以访问到某个变量或函数。
动态作用域 vs 词法作用域 JavaScript 使用词法作用域(lexical scoping),也称为静态作用域。词法作用域意味着在编写代码时(解析阶段),变量的作用域就已经确定了,而非在运行时动态确定。
function outer() {
let a = 10;
function inner() {
console.log(a);
}
return inner;
}
const fn = outer();
fn(); // 输出 10
词法作用域的好处
可预测性:变量的作用域在写代码时即可确定,便于理解和调试。 优化空间:浏览器或引擎可以提前解析和优化代码。
作用域链(Scope Chain)
什么是作用域链? 当访问一个变量时,JavaScript 引擎会按照作用域链逐层查找,直到找到该变量或到达全局作用域。
let globalVar = "I'm global";
function outer() {
let outerVar = "I'm outer";
function inner() {
let innerVar = "I'm inner";
console.log(globalVar, outerVar, innerVar);
}
inner();
}
outer(); // 输出 "I'm global I'm outer I'm inner"
作用域链的查找规则
- 当前作用域中查找变量。
- 如果没找到,向上一层作用域查找。
- 直到全局作用域,如果仍未找到,则抛出错误。
全局作用域(Global Scope)
全局作用域是最大的作用域,所有在函数外部定义的变量和函数都属于全局作用域。
let globalVar = "I'm global";
function globalFunc() {
console.log("I'm a global function");
}
避免滥用全局变量:全局变量可能会导致命名冲突和意外的副作用。 浏览器中全局对象:在浏览器中,全局作用域是 window 对象。
局部作用域(Local Scope)
函数作用域(Function Scope)在 JavaScript 中,函数内部的代码块(例如 {})会创建一个新的作用域。变量声明(var、let、const)的规则如下:
- var:具有函数作用域,会变量提升,但不会在块级作用域中创建新的作用域。
- let:具有块级作用域(block scope),在块级作用域(如 if、for、while 等)中声明的变量不会泄露到外部作用域。
- const:与 let 类似,但声明的变量不可重新赋值。
function example() {
if (true) {
let a = 10;
var b = 20;
}
console.log(a); // 报错:a is not defined
console.log(b); // 输出 20
}
example();
块级作用域(Block Scope)
ES6 引入了 let 和 const,使得块级作用域变得更为重要。
{
let a = 10;
var b = 20;
}
console.log(a); // 报错:a is not defined
console.log(b); // 输出 20
作用域提升(Hoisting)变量声明(var、let、const 和 function)会在代码运行前被提升到其作用域的顶部,但仅有声明会被提升,赋值不会。
console.log(a); // 报错:ReferenceError
let a = 10;
console.log(b); // 输出 undefined
var b = 20;
console.log(c); // 输出 undefined
function c() {
console.log("I'm a function");
}
闭包
闭包(Closure)是 JavaScript 中的一个概念,它允许函数记住它创建时的环境,即使该环境已被销毁。