基础概念

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 开发中推荐的方式,通过 importexport 语法实现模块化。

// 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 中常用的模块化规范,通过 requiremodule.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>

使用构建工具

在现代前端开发中,使用构建工具(如 WebpackParcel)可以帮助进行代码打包、压缩和优化。

使用 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 **&zwnj;= 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

&&:如果第一个操作数为 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");
}

闭包

数组的基本定义

JavaScript 数组是一种用于存储有序数据集合的特殊对象,可包含任意类型的元素(如数字、字符串、对象、布尔值等),并通过从 0 开始的数字索引访问元素‌


核心特性

动态长度:数组长度可动态增减,无需预先声明固定大小‌

元素类型灵活:支持混合存储不同类型的数据

const arr = [1, "text", true, { key: "value" }, [2, 3]]; 

其中包含数字、字符串、布尔值、对象和嵌套数组‌

连续内存结构‌:数组在内存中表现为一段‌连续的内存地址,数组名指向首地址‌

与普通对象的区别与联系

联系:数组本质是由对象派生而来,可调用部分对象方法(如 toString)‌
区别索引顺序:数组元素按严格数字顺序排列,对象属性无序‌。操作方法:数组提供专有方法(如 pushmap)用于操作元素集合,对象无此类内置方法‌

使用场景

数据列表存储:适用于需保持顺序的数据集合(如排行榜、表格行数据)‌
批量操作:通过循环或高阶函数(如 forEachfilter)高效处理多元素‌


注意事项

性能优化:频繁增删元素时,优先使用 push/pop(操作尾部)而非 unshift/shift(操作头部),避免索引重排开销‌

基本概念

数据集合‌:数组是‌相同类型元素‌的集合,所有元素存储于‌连续的内存空间中,元素类型可以是整型、字符型等任意一致的数据类型‌
元素约束:数组中的元素个数不能为0,且必须在定义时明确大小(C99标准前)或通过变长数组动态指定(C99标准后)‌

数组的元素组成

元素标识‌:数组元素是‌变量‌,通过‌数组名 + 下标唯一标识。例如 arr 表示数组 arr 的第一个元素‌
访问规则‌:元素需先通过数组定义分配内存空间后,才能逐个通过下标访问,‌不支持直接引用整个数组
下标范围:数组下标从 0 开始,依次递增至数组长度减1。例如长度为 n 的数组,最大有效下标为 n-1索引作用‌:下标用于定位元素在数组中的‌顺序位置,类似于数学集合中的索引概念,确保数据的有序性和快速访问‌

数组操作

JavaScript 数组使用方括号 [] 表示,元素以逗号分隔,可包含任意类型的数据(如数值、字符串、对象、其他数组等)‌

// 混合类型数组  
const mixedArr = [1, "text", true, { key: "value" }, [4, 5]];  
// 空数组  
const emptyArr = [];  

数组的创建方式

字面量方式 直接使用方括号 [] 定义元素,是最简洁、常用的方式‌

const arr1 = [1, 2, 3];  
const arr2 = ["a", "b", "c"];  

构造函数方式 使用 new Array()Array() 创建,但需注意参数特性

单一数值参数:表示数组长度(生成空槽数组)‌

多个参数或非数值参数:作为数组元素‌

const arr3 = new Array(3);       // [empty × 3](稀疏数组)  
const arr4 = new Array(1, "a");  // [1, "a"]  

其他创建方式

扩展操作符:复制或合并现有数组‌

const arr5 = [...arr1, ...arr2];  // [1, 2, 3, "a", "b", "c"]  

Array.of():明确将参数作为元素,避免构造函数歧义‌

const arr6 = Array.of(5);

Array.from():将可迭代对象(如字符串、Set)转换为数组‌

const arr7 = Array.from("abc");

元素访问与长度获取

索引访问:通过下标(从 0 开始)直接获取元素。

const fruits = ["apple", "banana"]; 
console.log(fruits); 
console.log(fruits.length);

首尾元素:通过 arrarr[arr.length-1] 获取首尾元素

数组长度:使用 length 属性获取元素数量。

遍历数组元素

for 循环:通过索引遍历。

for (let i = 0; i < fruits.length; i++) {  
  console.log(fruits[i]);  
}

forEach 方法:遍历元素并执行回调。

fruits.forEach((item, index) => {  
  console.log(index, item);  
});

高阶函数:如 mapfilterreduce 等。

onst lengths = fruits.map(fruit => fruit.length); // 计算长度 ‌
const longFruits = fruits.filter(fruit => fruit.length > 5); // 筛选结果 ‌

元素查找与筛选

按值查找

indexOf():返回第一个匹配的索引(无匹配返回 -1)。

lastIndexOf():返回最后一个匹配的索引 ‌

const idx = fruits.indexOf("banana");

条件查找

find():返回第一个符合条件的元素。

findIndex():返回第一个符合条件的索引

const item = fruits.find(fruit => fruit.startsWith("b"));

批量筛选filter() 返回符合条件的新数组

嵌套数组的检索

需在数组中查找子数组,可通过以下方式:

循环检查:使用 for 循环结合 Array.isArray() 判断元素是否为数组。

const arr = [1, [2, 3], 4];  
for (let i = 0; i < arr.length; i++) {  
  if (Array.isArray(arr[i])) {  
    console.log("子数组:", arr[i]); // [2, 3] ‌:ml-citation{ref="4,6" data="citationList"}  
  }  
}  

批量筛选:通过 filter() 提取所有子数组。

const subArrays = arr.filter(item => Array.isArray(item));

数组的扩展操作

ES6 扩展运算符

复制数组 用扩展运算符可快速创建数组的浅拷贝,避免引用传递问题‌

const original = [1, 2, 3];  
const copy = [...original]; // 新数组,与原数组无引用关系  

合并数组

合并多个数组时无需调用 concat,直接通过扩展运算符实现‌

const arr1 = [1, 2], arr2 = [3, 4];  
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]  

函数参数传递

将数组元素展开为函数参数,替代 apply 方法‌


function sum(a, b, c) { return a + b + c; }  
const nums = [1, 2, 3];  
sum(...nums); // 6  

动态添加元素
在现有数组中插入新元素,保持代码简洁性‌

const base = [2, 3];  
const newArr = [1, ...base, 4]; // [1, 2, 3, 4] 

实例方法的扩展

数据操作基础方法

增删元素

push()/pop():尾部操作元素‌

unshift()/shift():头部操作元素‌

splice():任意位置增删或替换元素‌

let arr = [1, 2];  
arr.push(3); // [1, 2, 3]  
arr.splice(1, 1, 4); // [1, 4, 3]  

高阶函数处理数据

遍历与转换

map():映射新数组‌

filter():筛选符合条件的元素‌

reduce():累计计算为单个值‌

const nums = [1, 2, 3];  
const doubled = nums.map(x => x * 2); // [2, 4, 6]  
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 6 

查找与判断

find():返回首个匹配元素‌

some()/every():判断元素是否满足条件‌

const users = [{id: 1}, {id: 2}];  
const user = users.find(u => u.id === 2); // {id: 2}  

高级技巧

解构赋值结合扩展运算符
提取数组首尾元素时,可配合扩展运算符快速实现‌

const [first, ...rest] = [1, 2, 3];  
console.log(first); // 1  
console.log(rest);  // [2, 3]  

Math 函数结合
直接传递数组参数进行数学计算‌

const nums = [5, 2, 8];  
Math.max(...nums); // 8  

数组的增删改查

添加元素

末尾添加push():添加一个或多个元素到数组末尾,返回新数组长度。

const arr = [1, 2];  
arr.push(3); // 返回 3,数组变为 [1, 2, 3]  

支持链式调用:arr.push(4).push(5)(需注意返回值变化)‌


开头添加unshift():添加一个或多个元素到数组开头,返回新数组长度。

arr.unshift(0); // 返回 4,数组变为 [0, 1, 2, 3] 

性能较低,需移动所有元素索引‌

中间插入splice(startIndex, 0, newElement):从指定位置插入元素,不删除原元素。

arr.splice(2, 0, "a", "b"); // 数组变为 [0, 1, "a", "b", 2, 3] 

删除元素

末尾删除pop():删除并返回最后一个元素。

const last = arr.pop(); // last = 3,数组变为 [0, 1, "a", "b", 2] 

开头删除shift():删除并返回第一个元素。

const first = arr.shift(); // first = 0,数组变为 [1, "a", "b", 2] 

指定位置删除

splice(startIndex, deleteCount):删除指定数量元素,返回被删除元素的数组

const deleted = arr.splice(1, 2); // deleted = ["a", "b"],数组变为 [1, 2] 

修改元素

直接索引赋值 通过下标直接修改元素

使用 splice 替换元素 splice(startIndex, deleteCount, newElement):删除并插入新元素。

arr.splice(0, 1, "start"); // 删除第一个元素并插入 "start",数组变为 ["start", "newValue"] ‌ 

查询元素

索引访问通过下标直接访问元素:

const elem = arr[1]; 

条件查询includes():判断是否包含某元素,返回布尔值。

arr.includes("start"); // true 

find():返回第一个满足条件的元素。

const result = arr.find(item => item === "start"); // "start" ‌  

遍历查询forEach()map() 等方法遍历数组处理数据‌