JavaScript基础

本文主要分享JavaScript基础笔记

JavaScript简介

JavaScript 是一种轻量级的、解释型的或即时编译型的编程语言。它是互联网的核心技术之一,几乎所有的网站都会使用它,并且它也是众多Web开发框架、库和应用程序的基础。以下是对 JavaScript 的详细介绍:

特性

  1. 动态类型:JavaScript 是动态类型的语言,这意味着您不需要提前声明变量的类型,变量的类型会由其值自动决定。

  2. 面向对象:JavaScript 支持面向对象的编程,包括封装、继承和多态等特性。

  3. 函数优先:JavaScript 中的函数是一等公民,它们可以被赋值给变量、作为参数传递或作为其他函数的返回值。

  4. 异步编程:JavaScript 支持异步编程模型,通过回调函数、Promises、async/await 等机制,可以处理非阻塞的 I/O 操作。

  5. 浏览器兼容性:几乎所有的现代浏览器都内置了 JavaScript 引擎,因此 JavaScript 可以直接在浏览器中运行,无需额外的解释器或编译器。

组成

  1. ECMAScript:这是 JavaScript 的核心,定义了语言的基础语法、数据类型、语句、关键字、保留字、运算符、对象等。

  2. DOM(文档对象模型):DOM 是 HTML 和 XML 文档的编程接口,它提供了对文档的结构化表示,并定义了一种方式可以使程序和脚本能够动态地访问和更新文档的内容、结构和样式。

  3. BOM(浏览器对象模型):BOM 提供了独立于任何特定文档的对象,用于浏览器窗口和脚本之间的交互。例如,它可以用于弹出新窗口、导航、打开和关闭浏览器窗口等。

JavaScript的常识

注释

单行注释使用//

多行注释使用/**/

结束符

;代表语句结束,可有可不有,但是有的话建议全局都有

引入

JavaScript在html中使用<script>javascript代码</script>

或者使用src属性添加文件地址<script src="yourfile.js"> </script>

JavaScript变量

JavaScrip的变量命名建议使用小驼峰命名法

变量的声明

JavaScript变量的声明有三种:旧版的var、新版的let、常量const三个关键字来声明变量

下面介绍三种关键字声明变量的区别:

  1. 使用var关键字声明变量
    var是最早的变量声明关键字,它可以在任何作用域(包括全局作用域和函数作用域)中声明变量。使用var声明的变量会进行变量提升(hoisting),这意味着无论它们在代码中的位置如何,都会被视为位于所在作用域的顶部。同时,var声明的变量可以重新赋值。

    var myVariable = 'Hello, World!';  
    console.log(myVariable); // 输出 'Hello, World!'  
    myVariable = 'Goodbye, World!';  
    console.log(myVariable); // 输出 'Goodbye, World!'

    需要注意的是,由于var的变量提升特性,如果在声明之前就访问该变量,其值会是undefined

    此外当你使用 var 时,可以根据需要多次声明相同名称的变量,但是 let 不能。

    var myName = "Chris";
    var myName = "Bob";

    上面的声明是可以滴,但是同样的方法用于let就报错,let只能用如下

    let myName = "Chris";
    myName = "Bob";

    let的这种作坊可以让代码不至于混乱,重新声明

  2. 使用let关键字声明变量
    let是在ES6(ECMAScript 2015)中引入的新的变量声明关键字。它提供了块级作用域(block scope),这意味着let声明的变量只在包含它的块或声明它的函数中有效。let不会像var那样进行变量提升,因此在声明之前访问let变量会抛出ReferenceError。此外,let声明的变量也可以重新赋值。

    let myLetVariable = 'Hello, World!';  
    console.log(myLetVariable); // 输出 'Hello, World!'  
    myLetVariable = 'Goodbye, World!';  
    console.log(myLetVariable); // 输出 'Goodbye, World!'

    if语句、for循环等代码块中使用let声明的变量,只在该代码块内部有效。

  3. 使用const关键字声明常量
    const也是ES6中引入的,用于声明一个只读的常量。一旦一个变量被const声明,它的值就不能再被改变(但如果是对象或数组,其内部状态可以被修改,只是引用不能被重新赋值)。const同样具有块级作用域。

    const myConstant = 'Hello, World!';  
    console.log(myConstant); // 输出 'Hello, World!'  
    // myConstant = 'Goodbye, World!'; // 这行会抛出TypeError

    需要注意的是,使用const声明的变量实际上是变量的引用不可变,但如果变量引用的是一个对象或数组,那么对象或数组的内部状态是可以改变的。

变量的赋值

JavaScript的变量赋值直接用=赋值,变量声明的时候,var和let可以先只声明,不初始化(不立即赋值,此时undefined)也可以声明的时候进行初始化,需要注意的是const声明变量必须立即赋值

下面简单介绍一下JavaScript变量赋值的例子和说明

基本数据类型赋值

对于基本数据类型(如数字、字符串和布尔值),赋值操作会创建一个值的副本并将其存储在变量中。

let number = 42; // 数字赋值  
let string = "Hello, World!"; // 字符串赋值  
let boolean = true; // 布尔值赋值

引用类型赋值

对于引用类型(如对象和数组),赋值操作实际上是复制了对象的引用,而不是对象本身。这意味着两个变量现在指向内存中的同一个对象。

let object = { name: "Alice" }; // 对象赋值  
let array = [1, 2, 3]; // 数组赋值  

// 现在object1和object2指向同一个对象  
let object1 = object;  
let object2 = object;  

// 修改object1的属性也会影响object2和object,因为它们引用的是同一个对象  
object1.name = "Bob";  
console.log(object2.name); // 输出 "Bob"  

// 同理,修改array1也会影响array,因为它们引用的是同一个数组  
let array1 = array;  
array1.push(4);  
console.log(array); // 输出 [1, 2, 3, 4]

函数赋值

在JavaScript中,函数也是对象,因此它们也可以被赋值给变量。

function greet() {  
  console.log("Hello!");  
}  

let greetFunction = greet; // 函数赋值  
greetFunction(); // 输出 "Hello!"

重新赋值

变量在JavaScript中是可以重新赋值的,这意味着你可以随时改变一个变量所引用的值。

let variable = "initial value";  
console.log(variable); // 输出 "initial value"  

variable = "new value"; // 重新赋值  
console.log(variable); // 输出 "new value"

解构赋值

ES6引入了解构赋值,它允许你从数组或对象中提取数据,然后将数据赋值给单独的变量。

// 数组解构赋值  
let [first, second] = [1, 2];  
console.log(first); // 输出 1  
console.log(second); // 输出 2  

// 对象解构赋值  
let { name, age } = { name: "Alice", age: 30 };  
console.log(name); // 输出 "Alice"  
console.log(age); // 输出 30

默认值赋值

在解构赋值时,你还可以为变量指定默认值,以防提取的值是undefined

let [missingValue = 'default'] = [];  
console.log(missingValue); // 输出 'default'

变量的类型

JavaScript是一个动态类型的语言,所以声明变量不需要指明他的变量类型,它很贴心的自动推导滴

JavaScript的基本数据类型:

  1. Number:用于表示数字,包括整数和浮点数。例如:423.14159

  2. String:用于表示文本或字符序列。字符串必须用引号(单引号或双引号)括起来。例如:"Hello, World!"

  3. Boolean:是逻辑数据类型,只有两个值:true 或 false

  4. Null:表示一个空值或“无”的值。它只有一个值,即 null

  5. Undefined:当一个变量被声明了,但没有赋值时,它的值就是 undefined

  6. Object:这是 JavaScript 中最复杂的数据类型。它用于存储多个值作为属性,这些属性可以是数据或函数。JavaScript 中的数组、日期、正则表达式等也是对象类型。

    let dog = { name: "Spot", breed: "Dalmatian" };
  7. Symbol(ES6 引入):是一种新的数据类型,表示唯一的、不可变的原始值,通常用作对象的属性键。

    let obj = {};  
    
    let key1 = Symbol('key1');  
    let key2 = Symbol('key2');  
    
    obj[key1] = 'Hello';  
    obj[key2] = 'World';  
    
    console.log(obj[key1]); // 输出:Hello  
    console.log(obj[key2]); // 输出:World  
    
    // 使用 for...in 循环不会枚举 Symbol 属性  
    for (let prop in obj) {  
      console.log(prop); // 这里不会输出任何内容,因为 for...in 不枚举 Symbol 属性  
    }  
    
    // 使用 Object.getOwnPropertySymbols 可以获取所有的 Symbol 属性  
    let symbols = Object.getOwnPropertySymbols(obj);  
    for (let sym of symbols) {  
      console.log(sym, obj[sym]); // 输出:Symbol(key1) Hello 和 Symbol(key2) World  
    }

此外,JavaScript 还支持两种复合类型:

  • Array:用于表示有序的元素集合。例如:[1, 2, 3, 4, 5]

    let myNameArray = ["Chris", "Bob", "Jim"];
    let myNumberArray = [10, 15, 40];
  • Function:在 JavaScript 中,函数也是对象,但它们有一些特殊的属性,使它们可以被调用。(详细之后再说)

JavaScript里面,一切皆可对象,一切都可以储存在变量里面!!!

JavaScript的运算符

算术运算符

  • 加法 (+): 用于将两个数字相加,或者将数字与字符串相加(此时数字会被转换为字符串)。

  • 减法 (-): 用于从第一个数中减去第二个数。

  • 乘法 (*): 用于将两个数相乘。

  • 除法 (/): 用于将第一个数除以第二个数。

  • 取模 (%): 返回两数相除的余数。

  • 递增 (++): 增加变量的值(前缀或后缀)。

  • 递减 (--): 减少变量的值(前缀或后缀)。

比较运算符

  • 等于 (==): 检查两个值是否相等(进行类型转换后)。

  • 严格等于 (===): 检查两个值是否严格相等(不进行类型转换)。

  • 不等于 (!=): 检查两个值是否不相等(进行类型转换后)。

  • 严格不等于 (!==): 检查两个值是否严格不相等(不进行类型转换)。

  • 大于 (>): 检查左边的值是否大于右边的值。

  • 小于 (<): 检查左边的值是否小于右边的值。

  • 大于等于 (>=): 检查左边的值是否大于或等于右边的值。

  • 小于等于 (<=): 检查左边的值是否小于或等于右边的值。

逻辑运算符

  • 逻辑与 (&&): 如果两个操作数都为真,则条件为真。

  • 逻辑或 (||): 如果两个操作数中至少有一个为真,则条件为真。

  • 逻辑非 (!): 反转操作数的逻辑状态。

位运算符

  • 按位与 (&): 对每一个比特执行 AND 操作。

  • 按位或 (|): 对每一个比特执行 OR 操作。

  • 按位异或 (^): 对每一个比特执行 XOR 操作。

  • 按位非 (~): 反转操作数的比特。

  • 左移 (<<): 将数字的二进制表示向左移动指定的位数。

  • 有符号右移 (>>): 将数字的二进制表示向右移动指定的位数,保持符号位。

  • 无符号右移 (>>>): 将数字的二进制表示向右移动指定的位数,忽略符号位。

赋值运算符

  • 赋值 (=): 将右边的值赋给左边的变量。

  • 加法赋值 (+=): 将右边的值加到左边的变量上,然后将结果赋给左边的变量。

  • 减法赋值 (-=): 从左边的变量中减去右边的值,然后将结果赋给左边的变量。

  • 乘法赋值 (*=): 将左边的变量与右边的值相乘,然后将结果赋给左边的变量。

  • 除法赋值 (/=): 将左边的变量除以右边的值,然后将结果赋给左边的变量。

  • 取模赋值 (%=): 将左边的变量对右边的值取模,然后将结果赋给左边的变量。

类型运算符

  • 类型检测 (typeof): 返回操作数的数据类型。

  • 实例检测 (instanceof): 检查一个对象是否是一个类的实例。

条件(三元)运算符

  • 条件 (?:): 也称为三元运算符,根据条件表达式的值返回两个可能的结果之一。

指数运算符

  • 指数 (**): 用于计算数字的指数(ES2016 引入)。

其他运算符

  • 删除 (delete): 用于删除对象的属性或数组的元素。

  • void: 用于计算一个表达式并返回 undefined

  • 逗号 (,): 用于分隔变量声明或函数参数。

JavaScript语句

这里介绍一下条件语句和循环语句

1 条件语句

条件语句用于根据条件执行不同的代码块。JavaScript中有两种主要的条件语句:if 语句和 switch 语句。

1.1 if 语句

if 语句用于根据指定的条件执行代码块。其语法如下:

if (condition) {  
    // 当 condition 为 true 时执行的代码块  
} else {  
    // 当 condition 为 false 时执行的代码块(可选)  
}
1.2 switch 语句

switch 语句用于基于不同的情况执行不同的代码块。其语法如下:

switch (expression) {  
    case value1:  
        // 当 expression 等于 value1 时执行的代码块  
        break;  
    case value2:  
        // 当 expression 等于 value2 时执行的代码块  
        break;  
    // 可以有更多 case...  
    default:  
        // 当没有 case 匹配时执行的代码块(可选)  
}

2. JavaScript循环吧!

循环语句用于重复执行一段代码,直到满足某个条件为止。JavaScript中有几种循环语句,包括 forwhile 和 do...while

2.1 for 循环

for 循环用于在指定次数内重复执行代码块。其语法如下:

for (let i = 0; i < length; i++) {  
    // 循环体,将执行 length 次  
}
2.2 while 循环

while 循环会在指定的条件为真时重复执行代码块。其语法如下:

let i = 0;  
while (i < length) {  
    // 循环体  
    i++;  
}
2.3 do...while 循环

do...while 循环至少会执行一次代码块,然后在指定的条件为真时继续执行。其语法如下:

let i = 0;  
do {  
    // 循环体  
    i++;  
} while (i < length);

break退出循环,continue跳过当前循环

JavaScript的函数

函数作用不多讲,直接来干货

1. 函数声明

你可以使用 function 关键字来声明一个函数:

function functionName(parameters) {  
    // 函数体,执行某些操作  
    return value; // 可选,返回某个值  
}

例如:

function greet(name) {  
    console.log('Hello, ' + name + '!');  
}

2. 函数表达式(匿名函数)

函数也可以通过表达式来定义,这种方式常用于创建匿名函数或将其立即作为值传递给其他函数(也称为立即执行函数表达式,IIFE):

var greet = function(name) {  
    console.log('Hello, ' + name + '!');  
};  

// 立即执行函数表达式 (IIFE)  
(function() {  
    console.log('This runs immediately!');  
})();

3. 箭头函数

ES6 引入了箭头函数,它提供了一种更简洁的函数语法:

const greet = (name) => {  
    console.log('Hello, ' + name + '!');  
};  

// 如果函数体只有一条语句,可以省略大括号,并隐式返回结果  
const sum = (a, b) => a + b;

4. 参数

函数可以接受任意数量的参数,也可以不接受任何参数。参数在函数声明时定义,并在函数调用时提供。

function add(a, b) {  
    return a + b;  
}  

console.log(add(5, 3)); // 输出 8

5. 默认参数

ES6 允许你为函数参数设置默认值,这样当调用函数时没有提供某个参数时,就会使用默认值。

function greet(name = 'World') {  
    console.log('Hello, ' + name + '!');  
}  

greet(); // 输出 "Hello, World!"  
greet('Alice'); // 输出 "Hello, Alice!"

6. 剩余参数

使用剩余参数(rest parameters),你可以将不定数量的参数作为数组传递给函数。

function sum(...numbers) {  
    return numbers.reduce((a, b) => a + b, 0);  
}  

console.log(sum(1, 2, 3, 4)); // 输出 10

7. 函数作用域和闭包

JavaScript 中的函数可以创建自己的作用域,这意味着在函数内部声明的变量不会影响到函数外部的变量。此外,函数可以访问其外部作用域中的变量,这称为闭包。闭包允许函数记住并访问其词法作用域,即使函数在其词法作用域之外执行。

function outerFunction(outerVariable) {  
    return function innerFunction(innerVariable) {  
        console.log('outerVariable:', outerVariable);  
        console.log('innerVariable:', innerVariable);  
    }  
}  
  
const myInnerFunction = outerFunction('Hello');  
myInnerFunction('World'); // 输出: outerVariable: Hello, innerVariable: World

8. 作为值的函数

在 JavaScript 中,函数是一等公民,这意味着函数可以作为值被传递,可以作为参数传递给其他函数,也可以从其他函数中返回。这种特性使得 JavaScript 的函数非常灵活和强大。

9. 回调函数和高阶函数

回调函数是一个在特定事件发生时(或者特定函数完成其任务后)被调用的函数。高阶函数是接受函数作为参数或返回函数的函数。这些概念在 JavaScript 中非常常见,特别是在处理异步操作或构建可重用的函数库时。

10. this 关键字

在 JavaScript 函数中,this 关键字引用的是调用该函数的对象。在不同的上下文中,this 的值可能会变化,这是 JavaScript 中一个复杂但重要的概念。

JavaScript的数组

JavaScript 的数组是一种特殊的对象,用于在单个变量中存储多个值,即元素。这些元素可以是任何数据类型,包括数字、字符串、布尔值、对象甚至是其他数组。数组的大小是动态的,可以随着元素的添加或删除而改变。

定义数组的方法

  1. 字面量定义

    let arr1 = [1, 2, 3, "four", true];
  2. 构造函数定义

    let arr2 = new Array(1, 2, 3, "four", true);
  3. 使用 Array.of 方法

    let arr3 = Array.of(1, 2, 3); // 创建具有可变数量参数的新数组实例
  4. 使用 Array.from 方法

    let arr4 = Array.from([1, 2, 3]); 
    // 从类似数组或可迭代的对象创建一个新的数组实例

常用数组操作

  1. 访问元素

    let value = arr1[0]; // 访问数组的第一个元素
  2. 添加元素

  • 使用索引添加

    arr1[arr1.length] = "new element"; // 在数组末尾添加新元素
  • 使用 push 方法

    arr1.push("new element"); 
    // 在数组末尾添加一个或多个元素,并返回新的长度
  • 使用 unshift 方法

    arr1.unshift("new element"); 
    // 在数组开头添加一个或多个元素,并返回新的长度

删除元素

  • 使用 pop 方法

    let removed = arr1.pop(); // 删除并返回数组的最后一个元素
  • 使用 shift 方法

    let removed = arr1.shift(); // 删除并返回数组的第一个元素
  • 使用 splice 方法

    arr1.splice(index, howmany, item1, ....., itemX);
     // 通过删除现有元素和/或添加新元素来更改一个数组的内容

遍历数组

  • 使用 for 循环

    for (let i = 0; i < arr1.length; i++) {  
        console.log(arr1[i]);  
    }
  • 使用 for...of 循环

    for (let element of arr1) {  
        console.log(element);  
    }
  • 使用 forEach 方法

    arr1.forEach(function(element) {  
        console.log(element);  
    });

查找元素

  • 使用 indexOf 方法

    let index = arr1.indexOf("four"); 
    // 查找元素并返回其索引,如果未找到则返回 -1
  • 使用 includes 方法

    let exists = arr1.includes("four"); 
    // 判断数组是否包含某个值,返回布尔值

修改数组

  • 使用 map 方法

    let newArr = arr1.map(function(element) {  
        return element * 2;  
    });  
    // 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
  • 使用 filter 方法

    let filteredArr = arr1.filter(function(element) {  
        return typeof element === 'number';  
    }); // 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素

排序数组

  • 使用 sort 方法

    arr1.sort(); // 对数组的元素进行排序,并返回数组

连接数组

  • 使用 concat 方法

    let combinedArr = arr1.concat(arr2); 
    // 连接两个或多个数组,并返回一个新数组,而不会更改现有数组

转换数组

  • 使用 join 方法

    let str = arr1.join(', '); // 把数组的所有元素放入一个字符串中,元素通过指定的分隔符进行分隔
  • 使用 slice 方法

    let slicedArr = arr1.slice(1, 3); 
    // 返回一个新的数组对象,是一个由开始到结束(不包括结束)选择的、由原数组的浅拷贝构成

反转数组

arr1.reverse(); // 反转数组的元素顺序,就地修改原数组,并返回它
  • 使用 reverse 方法

JavaScript的其余方法

1. 字符串方法

charAt()

返回在指定位置的字符。

let str = "Hello";  
let char = str.charAt(1); // "e"

substring()

返回字符串中两个指定的索引号之间的字符。

let str = "Hello, World!";  
let sub = str.substring(0, 5); // "Hello"

toUpperCase() 和 toLowerCase()

将字符串转换为大写或小写。

let str = "Hello";  
let upper = str.toUpperCase(); // "HELLO"  
let lower = str.toLowerCase(); // "hello"

split()

将字符串分割为字符串数组。

let str = "apple,banana,cherry";  
let arr = str.split(","); // ["apple", "banana", "cherry"]

模板字符串

模板字符串是ES6中引入的一种新的字符串表示方法,它使用反引号(```)来定义,并允许在字符串中嵌入表达式。模板字符串为字符串的拼接和格式化提供了更简洁、更直观的方式。

下面是一个模板字符串使用的示例:

let name = 'Achen';  
let age = 25;  
  
// 使用模板字符串拼接和格式化字符串  
let greeting = `Hello, my name is ${name} and I'm ${age} years old.`;  
  
console.log(greeting);  // 输出: Hello, my name is Achen and I'm 25 years old.

2. 类型转换

parseInt() 和 parseFloat()

将字符串转换为整数或浮点数。

let numStr = "123";  
let intNum = parseInt(numStr); // 123  
let floatNum = parseFloat("123.45"); // 123.45

Number()

将给定的值转换为数字,如果转换失败则返回 NaN

let num = Number("123"); // 123  
let notANum = Number("hello"); // NaN

String()

将给定的值转换为字符串。

let strNum = String(123); // "123"  
let strBool = String(true); // "true"

3. 数值方法

Math.round()

四舍五入到最接近的整数。

let num = Math.round(123.456); // 123

Math.floor() 和 Math.ceil()

向下或向上取整。

let floorNum = Math.floor(123.456); // 123  
let ceilNum = Math.ceil(123.456); // 124

Math.random()

返回 0(包括) 到 1(不包括) 之间的一个伪随机数。

let randomNum = Math.random(); // 例如:0.7345678901234567

4. 日期方法

Date()

创建日期对象。

let date = new Date(); // 当前日期和时间

getDate()getMonth()getFullYear()

获取日期的日、月、年。

let date = new Date();  
let day = date.getDate(); // 1-31  
let month = date.getMonth(); // 0-11  
let year = date.getFullYear(); // 例如:2023

setDate()setMonth()setFullYear()

设置日期的日、月、年。

let date = new Date();  
date.setDate(15);  
date.setMonth(5); // 注意月份是从0开始的,所以5表示6月  
date.setFullYear(2024);

5. 对象方法

Object.keys()

返回一个由对象的自身可枚举属性键(不包括原型链中的属性)组成的数组。

let obj = { a: 1, b: 2, c: 3 };  
let keys = Object.keys(obj); // ["a", "b", "c"]

Object.values()

返回一个由对象自身的可枚举属性值组成的数组。

let obj = { a: 1, b: 2, c: 3 };  
let values = Object.values(obj); // [1, 2, 3]

Object.assign()

用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。

let obj1 = { a: 1 };  
let obj2 = { b: 2 };
let mergedObj = Object.assign({}, obj1, obj2); // { a: 1, b: 2 }

6. 函数方法

Function.prototype.call()Function.prototype.apply()

调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数。

function greet(name) {  
 console.log(`Hello, ${name}!`);  
}

const obj = { name: "World" };  
greet.call(obj, "World"); // 使用 call 调用,输出 "Hello, World!"  
greet.apply(obj, ["World"]); // 使用 apply 调用,输出 "Hello, World!"

Function.prototype.bind()

创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,其余的参数将作为新函数的参数供调用时使用。

function list() {  
  return Array.prototype.slice.call(arguments);  
}  
  
const list1 = list(1, 2, 3); // [1, 2, 3]  
  
// 创建一个拥有预设初始参数的新函数  
function listWithThree() {  
  return list.apply(this, arguments.concat([4, 5, 6]));  
}  
  
const list2 = listWithThree.bind(null, 1, 2); // 创建一个新函数,预设初始参数为1和2  
const list3 = list2(3); // 调用新函数,并传入最后一个参数3,输出 [1, 2, 3, 4, 5, 6]

类的使用

在ES6中,JavaScript引入了类的概念,这使得我们可以使用面向对象的方式编写代码。虽然JavaScript的类在语法上看起来与传统的面向对象语言(如Java或C++)的类相似,但它们实际上是基于原型链的语法糖。

下面是一个简单的类定义和使用的示例:

class Person {  
    constructor(name, age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    greet() {  
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);  
    }  
}  
  
// 创建类的实例  
let john = new Person('Achen', 28);  
  
// 调用实例的方法  
john.greet();  // 输出: Hello, my name is Achen and I'm 28 years old.

在这个例子中,我们定义了一个名为Person的类,它有一个构造函数constructor用于初始化实例的属性,以及一个方法greet用于打印问候语。然后,我们使用new关键字创建了Person类的一个实例john,并调用了它的greet方法。

此外JavaScript的类还有其他特性,如实例字段、静态字段、静态方法、静态块、私有成员

实例字段是类的实例所特有的属性。在类的构造函数中或通过类的方法,你可以访问和修改这些字段。

实例方法是定义在类上,由类的实例调用的函数。

静态字段是定义在类上而非实例上的属性,通过类本身来访问。

静态方法是定义在类上而非实例上的函数,通过类本身来调用。

静态块是在类加载时执行的代码块,用于初始化静态字段或执行其他只需在类加载时执行一次的操作。

从ES2020开始,JavaScript引入了私有类的字段和方法。它们以#符号开头,并且只能在类的内部访问。

示例代码

class Person {
    // 静态字段
    static species;
    // 静态方法
    static sayHello() {
        console.log('Hello from the Person class.');
    }

    // 静态块
    static {
        Person.species = 'Homo sapiens';
        console.log('Static block executed.');
    }
    #name;

    constructor(name) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}.`);
    }

    #privateMethod() {
        return 'This is a private method.';
    }
//     调用私有方法
    myPrint(){
        this.#privateMethod();
    }
}

const person = new Person('Achen');
// 调用静态方法
Person.sayHello();//Hello from the Person class.
// 调用实例方法
person.greet(); // 输出: Hello, my name is Achen.
// person.#name; // 语法错误,不能从外部访问私有字段
// person.#privateMethod(); // 语法错误,不能从外部调用私有方法
person.myPrint();  
//his is a private method.// 语法错误,不能从外部调用私有方法

迭代器和生成器

当然可以,让我们分别深入了解JavaScript中的迭代器和生成器。

迭代器 (Iterator)

迭代器是一个对象,它实现了迭代协议(iteration protocol),即它有一个next()方法。每次调用next()方法时,它都会返回一个对象,该对象具有两个属性:valuedonevalue属性是当前迭代的元素的值,done属性是一个布尔值,当没有更多元素可迭代时,它为true

迭代器的主要用途是允许我们遍历数据结构(如数组、Map、Set等)中的元素,而无需了解数据结构的内部表示。

下面是一个简单的迭代器示例:

let iterator = {  
  index: 0,  
  data: [1, 2, 3, 4, 5],  
  next() {  
    if (this.index < this.data.length) {  
      return {  
        value: this.data[this.index++],  
        done: false  
      };  
    } else {  
      return {  
        value: undefined,  
        done: true  
      };  
    }  
  }  
};  
  
console.log(iterator.next()); // { value: 1, done: false }  
console.log(iterator.next()); // { value: 2, done: false }  
// ...  
console.log(iterator.next()); // { value: undefined, done: true }

JavaScript的内置对象,如ArrayMapSet等,都有一个[Symbol.iterator]()方法,它返回一个迭代器,用于遍历该对象的元素。

生成器 (Generator)

生成器是一种特殊的函数,它允许你暂停和恢复函数的执行。生成器函数使用function*语法定义,并且可以使用yield关键字来暂停和恢复执行。

当调用一个生成器函数时,它不会立即执行,而是返回一个生成器对象。这个生成器对象有一个next()方法,与迭代器对象的next()方法类似,但yield关键字允许在函数内部控制执行流程。

下面是一个简单的生成器示例:

function* numberGenerator() {  
  yield 1;  
  yield 2;  
  yield 3;  
}  
  
let generator = numberGenerator();  
  
console.log(generator.next()); // { value: 1, done: false }  
console.log(generator.next()); // { value: 2, done: false }  
console.log(generator.next()); // { value: 3, done: false }  
console.log(generator.next()); // { value: undefined, done: true }

生成器的一个常见用途是创建无限序列或惰性序列,因为你可以根据需要逐个生成值,而不是一次性生成所有值。

此外,生成器还可以与for...of循环一起使用,这使得遍历生成器产生的值变得非常简单。

function* numberGenerator() {  
  let i = 0;  
  while (true) {  
    yield i++;  
  }  
}  
  
for (let num of numberGenerator()) {  
  if (num > 5) break;  
  console.log(num); // 输出 0 到 5  
}

在这个例子中,numberGenerator是一个无限生成器,但通过使用break语句,我们只在for...of循环中遍历了前6个值。

JavaScript的模块

模块的导入导出

ES6引入了原生的模块系统,它使用importexport关键字来管理模块的导入和导出

导入模块

import moduleName from './path/to/module';  
// 或者  
import { namedExport1, namedExport2 } from './path/to/module';  
// 或者  
import * as allExports from './path/to/module';

导出模块

在模块的源文件中,你可以使用export关键字来导出内容:

// 导出默认内容  
export default function() { /* ... */ }  
  
// 导出命名内容  
export const namedExport = /* ... */;  
export function anotherNamedExport() { /* ... */ }

动态导入(现代浏览器和Node.js)

ES6还提供了动态导入的功能,允许你在运行时导入模块。这通常用于代码分割和懒加载。

import('./path/to/module')  
  .then(module => {  
    // 使用module对象  
  })  
  .catch(err => {  
    // 处理导入失败的情况  
  });

DOM操作

JavaScript 的 DOM(文档对象模型)操作涵盖了众多方面,包括获取元素、修改元素内容、修改元素属性、修改元素样式、添加和删除元素等。

1. 获取元素

可以通过多种方式来获取 DOM 元素,比如 getElementByIdgetElementsByClassNamegetElementsByTagNamequerySelector 和 querySelectorAll 等。

// 通过 id 获取元素  
var elementById = document.getElementById('myElementId');  
  
// 通过类名获取元素集合  
var elementsByClassName = document.getElementsByClassName('myClassName');  
  
// 通过标签名获取元素集合  
var elementsByTagName = document.getElementsByTagName('p');  
  
// 通过选择器获取元素,可以是任何有效的 CSS 选择器  
var elementBySelector = document.querySelector('.myClass .mySubElement');  
  
// 通过选择器获取元素集合  
var elementsBySelector = document.querySelectorAll('.myClass .mySubElement');

2. 修改元素内容

可以通过修改元素的 innerHTML 或 textContent 属性来修改元素的内容。

// 修改元素的 HTML 内容  
elementById.innerHTML = '<p>新的内容</p>';  
  
// 修改元素的文本内容  
elementById.textContent = '新的文本内容';

3. 修改元素属性

可以通过 .setAttribute() 方法设置元素属性,通过 .getAttribute() 方法获取元素属性,通过 .removeAttribute() 方法删除元素属性。

// 设置元素属性  
elementById.setAttribute('data-custom', 'value');  
  
// 获取元素属性  
var attributeValue = elementById.getAttribute('data-custom');  
  
// 删除元素属性  
elementById.removeAttribute('data-custom');

4. 修改元素样式

可以通过修改元素的 style 对象来直接修改内联样式,或者通过修改元素的 classList 来添加、删除或切换类名。

// 修改内联样式  
elementById.style.color = 'red';  
elementById.style.fontSize = '16px';  
  
// 添加类名  
elementById.classList.add('newClass');  
  
// 删除类名  
elementById.classList.remove('oldClass');  
  
// 切换类名(如果不存在则添加,如果存在则删除)  
elementById.classList.toggle('toggleClass');

5. 添加和删除元素

可以通过创建新的 DOM 元素,然后使用 appendChildinsertBefore 等方法将其添加到 DOM 树中,或者使用 removeChild 方法从 DOM 树中删除元素。

// 创建新的元素  
var newElement = document.createElement('div');  
newElement.textContent = '新的元素';  
  
// 将新元素添加到父元素的子元素列表末尾  
elementById.appendChild(newElement);  
  
// 在参考元素之前插入新元素  
var referenceElement = elementById.children[0];  
elementById.insertBefore(newElement, referenceElement);  
  
// 从父元素中删除元素  
elementById.removeChild(newElement);

这些只是 DOM 操作的一部分,实际上 JavaScript 提供了非常丰富的 API 来操作 DOM,包括处理事件、修改元素的属性、获取和设置元素的尺寸和位置等。在实际开发中,根据具体需求,可以选择合适的 API 来操作 DOM。

示例

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>DOM 操作示例</title>  
</head>  
<body>  
    <h1 id="myTitle">原始标题</h1>  
    <p class="myParagraph">原始段落。</p>  
    <button onclick="addParagraph()">添加段落</button>  
  
    <script src="script.js"></script>  
</body>  
</html>
//script.js
// 获取元素  
var titleElement = document.getElementById('myTitle');  
var paragraphElements = document.getElementsByClassName('myParagraph');  
  
// 修改元素内容  
titleElement.textContent = '修改后的标题';  
paragraphElements[0].innerHTML = '<b>修改后的段落内容</b>';  
  
// 新增元素  
function addParagraph() {  
    // 创建一个新的段落元素  
    var newParagraph = document.createElement('p');  
    // 设置新段落的内容  
    newParagraph.textContent = '这是新添加的段落。';  
    // 将新段落添加到 body 元素的末尾  
    document.body.appendChild(newParagraph);  
}

更多操作可以参考官方文档

事件处理

JavaScript中的事件处理操作是前端开发的核心之一,它允许我们响应用户在浏览器中的各种行为,如点击按钮、滚动页面、输入文本等。下面将详细介绍一些常用的事件处理操作:

1. 事件监听

事件监听是JavaScript中处理事件的主要方式。通过addEventListener()方法,我们可以为DOM元素添加事件监听器。

var button = document.getElementById('myButton');  
button.addEventListener('click', function() {  
    alert('按钮被点击了!');  
});

在这个例子中,我们为ID为myButton的按钮添加了一个点击事件监听器。当按钮被点击时,会弹出一个警告框。

2. 事件类型

JavaScript支持多种类型的事件,包括但不限于:

  • click: 用户点击元素时触发。

  • dblclick: 用户双击元素时触发。

  • mousedown: 用户按下鼠标按键时触发。

  • mouseup: 用户释放鼠标按键时触发。

  • mousemove: 鼠标指针在元素内部移动时触发。

  • mouseover: 鼠标指针移入元素时触发。

  • mouseout: 鼠标指针移出元素时触发。

  • keydown: 用户按下键盘按键时触发。

  • keyup: 用户释放键盘按键时触发。

  • keypress: 用户按下并释放一个可以产生字符的键时触发。

  • submit: 用户提交表单时触发。

  • scroll: 用户滚动元素时触发。

  • load: 元素(如图像、脚本、框架、样式表等)加载完成时触发。

  • unload: 用户离开页面时触发。

  • focus: 元素获得焦点时触发。

  • blur: 元素失去焦点时触发。

3. 事件对象

事件监听器的回调函数通常接收一个事件对象作为参数,该对象包含了与事件相关的各种信息,如触发事件的元素、按键的编码、鼠标的位置等。

button.addEventListener('click', function(event) {  
    console.log(event.target); // 输出触发事件的元素  
});

在这个例子中,event.target指向触发点击事件的元素(即按钮本身)。

4. 事件委托

事件委托是一种通过利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件的技术。通常将事件监听器添加到父元素上,然后在事件处理程序中检查事件的目标(event.target)来确定实际被操作的元素。

var list = document.getElementById('myList');  
list.addEventListener('click', function(event) {  
    var target = event.target;  
    if (target.tagName.toLowerCase() === 'li') {  
        alert('你点击了一个列表项!');  
    }  
});

在这个例子中,我们为列表(ul)添加了一个点击事件监听器。当用户点击列表中的任何一项(li)时,事件会冒泡到ul,然后我们在事件处理程序中检查事件的目标,以确定是否点击了列表项。

5. 移除事件监听器

使用removeEventListener()方法可以移除之前通过addEventListener()添加的事件监听器。

button.removeEventListener('click', clickHandler);

在这个例子中,假设clickHandler是之前用于添加点击事件监听器的函数。通过传递相同的函数引用和事件类型,我们可以移除该事件监听器。

6. 阻止默认行为和事件冒泡

事件对象提供了方法来阻止元素的默认行为和阻止事件冒泡。

  • event.preventDefault(): 阻止元素的默认行为。例如,阻止表单的提交或链接的跳转。

  • event.stopPropagation(): 阻止事件冒泡。即阻止事件进一步传播到父元素。

7. 自定义事件

除了内置的事件类型,JavaScript还允许创建和触发自定义事件。

var event = new CustomEvent('myCustomEvent', {  
    detail: {  
        message: 'Hello, world!'  
    }  
});  
  
document.dispatchEvent(event);

在这个例子中,我们创建了一个名为myCustomEvent的自定义事件,并附带了一个包含消息的detail对象。然后,我们使用dispatchEvent()方法触发了这个事件。任何监听这个自定义事件的元素都会收到通知。

异步编程

JavaScript 的异步编程是其核心特性之一,它使得 JavaScript 能够非阻塞地执行代码,从而允许在执行长时间运行的任务(如网络请求或 I/O 操作)时,浏览器或 Node.js 环境能够继续处理其他任务。异步编程在 JavaScript 中主要通过以下几种方式实现:回调函数、Promises、async/await 和事件循环。

1. 回调函数(Callbacks)

回调函数是最早的异步编程方式,它通过将函数作为参数传递给其他函数,并在某个特定事件(如异步操作完成)发生时调用这个函数。

const fs = require('fs');  
  
fs.readFile('/path/to/file', 'utf8', function(err, data) {  
  if (err) {  
    console.error("Error reading file:", err);  
    return;  
  }  
  console.log("File contents:", data);  
});

在这个例子中,fs.readFile 是一个异步函数,它接受一个回调函数作为参数。当文件读取完成后,这个回调函数会被调用。

2. Promises

Promises 是对回调函数的改进,它提供了一种更优雅的方式来处理异步操作的结果或错误。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。

const fs = require('fs').promises;  
  
fs.readFile('/path/to/file', 'utf8')  
  .then(data => {  
    console.log("File contents:", data);  
  })  
  .catch(err => {  
    console.error("Error reading file:", err);  
  });

在这个例子中,fs.readFile 返回一个 Promise 对象,我们可以使用 .then() 方法来处理成功的情况,使用 .catch() 方法来处理错误。

3. async/await

async/await 是基于 Promise 的语法糖,它使得异步代码看起来更像同步代码,更易于理解和维护。

const fs = require('fs').promises;  
  
async function readFileAsync() {  
  try {  
    const data = await fs.readFile('/path/to/file', 'utf8');  
    console.log("File contents:", data);  
  } catch (err) {  
    console.error("Error reading file:", err);  
  }  
}  
  
readFileAsync();

在这个例子中,readFileAsync 是一个 async 函数,它使用 await 关键字等待 Promise 的结果。如果在等待过程中 Promise 被 rejected,则会抛出异常,我们可以在 try...catch 块中捕获这个异常。

4. 事件循环(Event Loop)

JavaScript 的事件循环是其异步编程模型的核心。它不断地从任务队列中取出任务并执行,从而实现了非阻塞的 I/O 操作。在事件循环中,任务可以是宏任务(如 script、setTimeout、setInterval、setImmediate、I/O、UI rendering)或微任务(如 Promise.then、process.nextTick、MutationObserver)。每次事件循环都会先执行完所有的微任务,再执行一个宏任务,然后进入下一个循环。

示例

console.log('Start of the script'); // 1. 同步代码,直接执行  
  
setTimeout(function() {  
  console.log('setTimeout callback'); // 5. 宏任务,被放入宏任务队列  
}, 0);  
  
Promise.resolve().then(function() {  
  console.log('Promise.then callback'); // 3. 微任务,被放入微任务队列  
});  
  
process.nextTick(function() {  
  console.log('process.nextTick callback'); // 4. Node.js 特有的微任务,优先级高于 Promise.then  
});  
  
console.log('End of the script'); // 2. 同步代码,直接执行  
  
// 事件循环开始  
// 1. 执行同步代码  
// 2. 当同步代码执行完后,开始检查微任务队列  
//    a. 执行 process.nextTick  
//    b. 执行 Promise.then  
// 3. 微任务队列清空后,开始执行宏任务队列中的任务(如果有的话)  
//    a. 执行 setTimeout 回调

有几个关键的注意事项需要牢记:

  1. 错误处理

异步操作很容易因为各种原因而失败,比如网络问题、文件读取错误等。因此,对于每个异步操作,都需要有相应的错误处理机制。在 Promise 中,可以使用 .catch() 方法来处理错误;在 async/await 中,可以使用 try...catch 块来捕获异常。

  1. 回调地狱(Callback Hell)

当异步操作层层嵌套时,代码会变得难以阅读和维护,这就是所谓的“回调地狱”。Promise 和 async/await 都是用来解决这个问题的工具,它们可以让异步代码更加扁平化,易于理解。

  1. 异步函数的返回值

异步函数(无论是使用回调函数、Promise 还是 async/await)都不会立即返回结果,而是返回一个表示未来结果的占位符(在 Promise 的情况下)或者一个挂起的异步操作(在 async/await 的情况下)。因此,你不能直接对异步函数的返回值进行操作,而是需要等待异步操作完成后再处理结果。

  1. 并发与并行

并发和并行在异步编程中常常被提及。并发是指同时处理多个任务,而并行是指同时执行多个任务。在 JavaScript 中,由于单线程的特性,真正的并行是不可能的,但我们可以通过异步编程实现并发,即同时处理多个异步操作。

  1. 资源管理

异步操作可能涉及到资源的创建和销毁(如数据库连接、文件句柄等)。在使用这些资源时,需要确保在操作完成后正确释放它们,以避免资源泄漏。

请求网络

在JavaScript中,请求接口(即发起网络请求)通常涉及到使用某种HTTP客户端库或浏览器提供的原生API。下面,我将解释两种常见的方法:使用fetch API(现代浏览器提供的原生API)和使用axios库(一个流行的第三方库)。

1. 使用Fetch API

fetch API 提供了一个原生的、基于Promise的方式来执行网络请求。它返回一个Promise对象,这个对象在请求成功时解析为Response对象,或者在请求失败时拒绝。

下面是一个使用fetch API 发起GET请求的示例:

fetch('https://api.example.com/data')  
  .then(response => {  
    // 检查响应状态码  
    if (!response.ok) {  
      throw new Error('Network response was not ok');  
    }  
    // 解析JSON数据  
    return response.json();  
  })  
  .then(data => {  
    console.log(data); // 处理响应数据  
  })  
  .catch(error => {  
    console.error('There has been a problem with your fetch operation:', error);  
  });

在这个例子中,我们首先使用fetch函数发送一个GET请求到指定的URL。然后,我们链式调用.then()来处理响应。第一个.then()检查响应是否成功(通常是通过检查状态码是否在200-299之间)。如果响应成功,我们解析响应体为JSON。第二个.then()处理解析后的数据。如果在任何阶段发生错误,.catch()会捕获错误并处理它。

2. 使用Axios库

axios 是一个基于Promise的HTTP客户端,用于浏览器和node.js。它提供了许多有用的功能,比如拦截请求和响应、转换请求和响应数据、取消请求等。

首先,你需要安装axios(如果你是在Node.js环境中工作的话):

npm install axios

或者,如果你是在浏览器中直接使用,可以通过CDN引入axios

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

下面是一个使用axios发起GET请求的示例:

const axios = require('axios'); // 如果你在Node.js环境中  
  
axios.get('https://api.example.com/data')  
  .then(function (response) {  
    // 处理响应数据  
    console.log(response.data);  
  })  
  .catch(function (error) {  
    // 处理错误  
    console.log(error);  
  });

在这个例子中,我们使用axios.get方法发送GET请求,并处理响应或错误。response.data包含了从服务器返回的数据。

无论是使用fetch还是axios,你都需要考虑处理网络请求的各个方面,包括错误处理、请求超时、请求头设置、请求体发送等。此外,出于安全考虑,当处理跨域请求时,你需要确保服务器支持CORS(跨源资源共享)。

JavaScript基础部分内容就分享到这里,更多内容大家可以参考官方文档,JavaScript内容较多,这只是一个最简单的开始,后续需要大家逐步深入学习!

官方文档地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是阿尘呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值