零基础学前端-传统前端开发(第四期-JS基础)
经过前面学过的HTML,CSS的学习,相信大家已经可以使用进行常用的页面开发,接下来我们就要学习JavaScript,鉴于内容过多,需要长期练习
流程为:数据类型>>运算>>语法,语句>>对象>>数组>>函数>>类
软件使用:谷歌浏览器(当然如果你了解的话可以用nodejs试一下,但是为了方便,本系列都是用的浏览器)
JavaScript相比起HTML,CSS是有一定难度的,可以作为一门编程语言而学的,但是学会
JavaScript对于后期我们学习nodejs(包括python爬虫的学习)的时候帮助很大,所以仍然要细心学习
JavaScript在前端开发有什么用呢:
在开发房子中HTML相当于房子水泥钢筋铸的结构骨架,CSS相当于房子的装修风格,而JavaScript相当于智能家居一样,可以让网站变得动态(也就是我们看到的动画效果)
学习JavaScript之前,我们先学数据类型,这个东西有过学习其他开发语言的朋友应该了解
数据类型
一、基本数据类型(Primitive Data Types)
基本数据类型是构成 JavaScript 最基础的单位,存储在栈内存中,直接存储实际的数据值。JavaScript 的基本数据类型包括以下几种:
String(字符串):用来表示文本数据,可以是一个字符或一串字符序列,例如"Hello"、'JavaScript'、"42"等。字符串是不可变的,每次对字符串的操作都会产生新的字符串实例。
Number(数字):表示数字,包括整数和浮点数。例如42、3.14、-7等。JavaScript 中的数字没有区分整型和浮点型,统一用 Number 表示。
Boolean(布尔值) :表示逻辑实体,只有两个值:true 和 false。通常用于条件判断中。(在之前的皮卡丘靶场中的布尔类型的内容中我们短暂的了解过)
Undefined(未定义):表示变量未被赋值,或者尚未声明的变量的默认值。例如:let x;,此时 x 的值就是 undefined。
Null(空值):表示没有值,或者对象为空。null 是一个空对象引用。它和 undefined 常常容易混淆,但它们表示的意义不同。
二、引用数据类型(Reference Data Types)
引用数据类型是复合数据类型,通常存储在堆内存中,变量存储的是对象在内存中的地址(引用)。以下是常见的引用数据类型:
Object(对象):是 JavaScript 中最通用的引用数据类型,以键值对的形式存储数据。例如:let person = { name: 'John', age: 30 }。
Array(数组):是一种特殊的对象,用于存储多个值,这些值可以通过索引来访问。例如:let arr = [1, 2, 3, 'JavaScript']。
Function(函数):在 JavaScript 中,函数其实也是一种特殊的对象,可以被赋值、传递和返回。例如:function add(a, b) { return a + b; } 或者箭头函数:const add = (a, b) => a + b;。
以上是数据类型的知识,虽然看起来很简单,但是在开发语言中,这是非常重要的地基,如果没有记牢的话会影响后面的学习
运算
接下来我们学习运算(类似于你学习数学,你得先知道各类数字比如:整数,有小数点的,有分号的,今天的运算相当于了解加减乘除的符号用法)
直接用谷歌浏览器来运行即可:我们按压F12打开console(如果你汉化过叫"控制台")在里面输入即可
一,算数运算符
作用:执行基本的数学运算
+ 加法
- 减法
* 乘法
/ 除法
% 求余
示例
let a = 5;let b = 3;console.log(a + b); // 8console.log(a - b); // 2console.log(a * b); // 15console.log(a / b); // 1.666...console.log(a % b); // 2
类似以上内容,直接把内容放到控制台运行即可
二,比较运算符
作用:比较运算符用于比较两个值,返回布尔值(true
或 false
)
== 等于
!= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
示例
let a = 5;let b = 3;console.log(a == b); // falseconsole.log(a != b); // tureconsole.log(a > b); // trueconsole.log(a <= b); // false
三,逻辑运算符
作用:逻辑运算符用于组合布尔值,返回布尔结果。
&& 逻辑与(AND)
|| 逻辑或(OR)
! 逻辑非(NOT)
示例
let a = 5;let b = 3;console.log(a > 2 && b < 4); // trueconsole.log(a < 2 || b > 4); // falseconsole.log(!(a == b)); // true
四,赋值运算符
作用:赋值运算符用于给变量赋值
= 简单赋值
+= 加法赋值
-= 减法赋值
*= 乘法赋值
/= 除法赋值
%= 取模赋值
示例
let c = 10;c += 5; // c = 15c -= 3; // c = 12c *= 2; // c = 24c /= 4; // c = 6c %= 5; // c = 1console.log(c); // 1
由此我们还可以知道JavaScript读取时显示的的是最后一个结果,是因为每次运算的结果会覆盖之前的值,或者你只在最后调用了输出函数
语法语句
接下来是语法,语句
传统的语法定义:音义结合的各结构单位之间的组织规则的汇集。但是在JavaScript学习中,语法意为描述合法代码书写规则的集合(就是拼拼图,必须按照固定搭配才能拼好)
语句:是程序中执行一个操作的基本单元,它是构成程序的基本结构元素之一(就好像写作文,是一句话一句话拼起来的)
语法:
代码块:
代码块是用花括号 {}括起来的代码片段,通常用于表示代码的逻辑结构。
比如在if语句、for循环、while循环和函数中,代码块用来定义执行的具体操作。(在 JavaScript 中,语句通常以分号(;)结尾,表示一个完整的操作结束。)
if (age >= 18) {console.log("成年了");console.log("可以参加选举了");}这里的 {} 中的内容就是代码块,表示当条件满足时,要执行的多个操作。
注释:
注释是用来解释代码片段的,它不会被浏览器执行,但可以帮助我们理解代码的逻辑。(类似于你背英语单词,考试的时候不会看你了不了解,只看你写出来的回答这就是浏览器执行的内容,你背在脑子里的翻译语法就是注释)
单行注释:
使用 //
,它只能注释这一行的内容
// 这是单行注释,解释变量的作用let x = 10; // 初始化变量 x
多行注释:
使用 /*
和 */
包裹内容,可以注释多行代码。
/*这是一个多行注释,可以用来解释一个复杂的功能模块或者代码逻辑*/
语句:
变量声明
var语句:是 JavaScript 中最初用于声明变量的关键字。它具有函数作用域,也就是说在函数内部用 var
声明的变量,只在该函数内部有效。
function test() {var x = 10;}console.log(x); // 会报错,因为 x 只在 test 函数内部有效
let语句:用于声明变量的关键字,它具有块级作用域。块级作用域是指在一对大括号({})内声明的变量,只在该大括号内有效。
{let y = 20;}console.log(y); // 会报错,因为 y 只在大括号内有效
const:const 用于声明常量,也具有块级作用域。一旦声明后,常量的值不能被改变。
const PI = 3.14;console.log(PI); // 输出 3.14PI = 3; // 会报错,不能修改常量的值
条件语句
(类似于如果...我就..这种说法内容)
if
语句:if 语句用于根据条件执行不同的代码。
语法格式
if (条件) {代码块}
var age = 18;if(age >= 18){console.log("你好");}
if...else
语句:当需要在条件为真和为假时分别执行不同的代码时,可以使用 if...else
语句。
语法格式
if (条件) {代码块 1} else {代码块 2}
var score = 60;if(score >= 60){console.log("及格");}else{console.log("不及格");}如果 `score >= 60` 为真,就执行代码块 1 ,输出 “及格”;否则执行代码块 2 ,输出 “不及格”。
if...else if...else
语句:当有多个条件需要判断时,可以使用 if...else if...else
语句。
语法格式
if (条件 1) {代码块 1} else if (条件 2) {代码块 2} else {代码块 3}
var num = 5;if(num > 10){console.log("大于 10");}else if(num > 5){console.log("大于 5");}else{console.log("小于等于 5");}
循环语句
for
循环:for 循环用于重复执行一段代码,直到满足特定条件为止。
语法格式
for (初始化表达式; 条件表达式; 更新表达式) {代码块}* 解释:* 初始化表达式:通常用于初始化循环变量。* 条件表达式:在每次循环开始前进行判断,如果为真,则执行循环体;如果为假,则退出循环。* 更新表达式:在每次循环体执行完毕后更新循环变量。* 例如:for(let i = 0; i < 3; i++){console.log(i); // 输出 0、1、2}这里初始化变量 `i` 为 0 ,条件是 `i < 3` ,每次循环后 `i` 增加 1 。循环会执行 3 次,依次输出 0、1、2。
while
循环:while
循环也是用于重复执行一段代码,
while (条件) {代码块}* 先判断条件是否为真,如果为真,就执行代码块,然后再次判断条件,直到条件为假。* 例如:let count = 0;while(count < 3){console.log(count); // 输出 0、1、2count++;}如果不注意在循环体中更新变量(如这里的 `count++` ),可能会导致死循环,因为条件一直为真。
do...while
循环:do...while
循环和 while
循环类似,但它的区别在于不管条件是否为真,都会先执行一次代码块,然后再判断条件
语法格式
do {代码块} while (条件);
{let num = 1; // 这个 num 只在这个花括号的作用域内有效do {console.log(num); // 输出 num 的值num++; // num 值加 1} while (num <= 5); // 只要 num 小于等于 5,就继续循环}
循环语句区别
条件判断时机
for
循环:在每次循环开始前都会先执行条件表达式的判断。
while
循环:同样在每次循环开始前判断条件,但没有内置的初始化和更新表达式。
初始化和更新
for
循环:将初始化、条件判断和更新操作集中在一个语句中,结构更紧凑,适合有明确的初始化和更新逻辑的场景。
while
循环:没有内置的初始化和更新操作,需要在循环体内部手动处理。
执行条件
do...while
循环:do...while
循环会先执行一次循环体,然后才判断条件。因此,它至少会执行一次循环体,即使条件一开始就不满足。
for
和 while
循环:如果条件一开始就不满足,循环体可能一次都不执行。
适用点
for 循环
适用场景:当你需要在循环中初始化一个变量,并且每次循环后需要更新这个变量时,for
循环是一个很好的选择。它将初始化、条件判断和更新操作集中在一个地方,代码更清晰。
示例:
遍历数组或集合。
执行固定次数的循环。
for (let i = 0; i < 5; i++) {console.log(i); // 输出 0, 1, 2, 3, 4}
while
循环
适用场景:当你需要在循环中动态地控制条件,或者循环的次数不确定时,while
循环更适合。它更灵活,因为你可以根据需要在循环体中动态地改变条件变量。
示例:
读取用户输入,直到用户输入特定的值。
执行一个任务,直到某个条件满足。
let num = 0;while (num < 5) {console.log(num); // 输出 0, 1, 2, 3, 4num++;}
for
循环:适合有明确的初始化和更新逻辑,循环次数相对确定的场景。
while
循环:适合循环次数不确定,或者需要在循环体中动态控制条件的场景。
break
语句
break
语句用于完全终止当前的循环,跳出循环体,继续执行循环之后的代码。
当你需要在满足某个条件时立即退出循环时,可以使用 break
。
常用于避免无限循环或在找到目标后提前退出循环。
const numbers = [1, 3, 5, 12, 8, 15];for (let i = 0; i < numbers.length; i++) {if (numbers[i] > 10) {console.log("找到第一个大于 10 的数字:", numbers[i]);break; // 找到后立即退出循环}}console.log("循环结束");
continue
语句
continue
语句用于跳过当前循环的剩余部分,直接开始下一次循环迭代。
当你需要跳过某些特定条件的迭代,但仍然继续执行后续的迭代时,可以使用 continue
。
常用于过滤掉不符合条件的元素,只处理符合条件的元素。
const numbers = [1, 2, 3, 4, 5, 6];for (let i = 0; i < numbers.length; i++) {if (numbers[i] % 2 !== 0) {continue; // 如果是奇数,跳过当前迭代}console.log("偶数:", numbers[i]);}
break
和 continue
的区别
break
:
完全终止循环,跳出循环体,继续执行循环之后的代码。
用于提前退出循环。
continue
:
跳过当前迭代的剩余部分,直接开始下一次迭代。
用于跳过某些特定条件的迭代,但仍然继续执行后续的迭代。
至此,语法语句内容完成
对象
接下来,来接触一下对象
首先:此对象非彼对象,学校里面学好了说不定有对象
什么是编程中的对象呢:在 JavaScript 中,对象是一个独立的、可以存储多个值的实体,它由一系列的键值对(key-value pairs)组成。键(key)是对象的属性名称,值(value)可以是任何数据类型,包括数字、字符串、布尔值、函数(方法)等。你可以把对象想象成一个装满各种信息的“盒子”。
例如,一个描述人的对象:
let person = {name: "张三",age: 25,isStudent: true,hobbies: ["阅读", "运动"],greet: function() {console.log("你好,我叫" + this.name);}};// 调用 greet 方法person.greet();
在这个对象中:
name
、age
、isStudent
是属性,它们的值分别是 "张三"
、25
、true
。
hobbies
是一个数组类型的属性,它的值是包含两个字符串的数组。
greet
是一个方法(即对象中的函数,在下一篇会详细介绍函数的),它可以输出 "你好,我叫张三"
。
创建对象
方法:对象字面量
最常见的方式是使用对象字面量来创建对象,就像上面示例中的 person
对象。这种方式简单直观,适合直接定义对象的属性和方法。
语法:
let 对象名 = {属性1: 值1,属性2: 值2,// ...};
访问对象的属性和方法
方法1:点语法
console.log(person.name); // 访问属性person.greet(); // 调用方法
方法2:方括号语法
console.log(person["name"]);person["greet"]();
添加、修改和删除属性
添加属性
person.gender = "男"; // 添加属性
修改属性
person.age = 26; // 修改属性
删除属性
delete person.age; // 删除属性
对象的遍历
(遍历在之前的文章中讲过,就是一个盒子里有很多小球然后一个一个拿出来)
可以使用 for...in
循环来遍历对象的属性:
for (let key in person) {console.log(key + ": " + person[key]);}
可能听的云里雾里的,之后会有一篇关于传统前端实战的分析文章
数组
接下来我们来了解数组
什么是数组:数组是一种非常常用的数据结构,用于存储一组有序的值。这些值可以是数字、字符串、对象,甚至是其他数组。数组中的每个值都有一个索引(从 0 开始),可以通过索引来访问和操作数组中的元素。
(类似于我们学过的数学里的集合,但只是表现上类似而已)
创建数组
方法 :数组字面量
这是最常用的方式,直接用方括号 []
包裹一组值。
let fruits = ["苹果", "香蕉", "橙子"];let numbers = [1, 2, 3, 4, 5];let mixed = [1, "hello", true, { name: "张三" }, [1, 2, 3]];
访问数组元素
数组中的每个元素都有一个索引,从 0 开始。可以通过索引来访问和修改数组中的元素。
let fruits = ["苹果", "香蕉", "橙子"];// 访问元素console.log(fruits[0]); // 输出 "苹果"console.log(fruits[1]); // 输出 "香蕉"// 修改元素fruits[1] = "草莓";console.log(fruits); // 输出 ["苹果", "草莓", "橙子"]
数组的常用方法
添加和删除元素
push()
:向数组末尾添加一个或多个元素,并返回新数组的长度。
fruits.push("葡萄");console.log(fruits); // 输出 ["苹果", "草莓", "橙子", "葡萄"]
pop()
:从数组末尾移除一个元素,并返回被移除的元素。
let removedItem = fruits.pop();console.log(removedItem); // 输出 "葡萄"console.log(fruits); // 输出 ["苹果", "草莓", "橙子"]
shift()
:从数组开头移除一个元素,并返回被移除的元素。
let firstItem = fruits.shift();console.log(firstItem); // 输出 "苹果"console.log(fruits); // 输出 ["草莓", "橙子"]
unshift()
:向数组开头添加一个或多个元素,并返回新数组的长度。
fruits.unshift("樱桃");console.log(fruits); // 输出 ["樱桃", "草莓", "橙子"]
查找元素
indexOf()
:查找某个值在数组中的索引,如果找不到返回 -1
。
let index = fruits.indexOf("橙子");console.log(index); // 输出 2
(因为计数的时候第一个数字记为0,也就是说你觉的苹果应该是1但他的序号实际为0)
includes()
:检查数组是否包含某个值,返回布尔值。
console.log(fruits.includes("草莓")); // 输出 trueconsole.log(fruits.includes("西瓜")); // 输出 false
遍历数组
for
循环
for (let i = 0; i < fruits.length; i++) {console.log(fruits[i]);}
数组的拷贝和拼接
slice()
:返回数组的一个浅拷贝,不会修改原数组。
let newFruits = fruits.slice(1, 3); // 从索引 1 到 3(不包括 3)console.log(newFruits); // 输出 ["草莓", "橙子"]
concat():将多个数组拼接成一个新数组,不会修改原数组。
let moreFruits = ["西瓜", "芒果"];let allFruits = fruits.concat(moreFruits);console.log(allFruits); // 输出 ["樱桃", "草莓", "橙子", "西瓜", "芒果"]
数组的排序
sort()
:对数组进行排序,会修改原数组。
let numbers = [3, 1, 4, 1, 5, 9];numbers.sort();console.log(numbers); // 输出 [1, 1, 3, 4, 5, 9]
如果需要按照数值大小排序,需要提供一个比较函数:
numbers.sort(function(a, b) {return a - b; // 升序});console.log(numbers); // 输出 [1, 1, 3, 4, 5, 9]
数组的过滤和映射
filter()
:创建一个新数组,包含通过测试的所有元素,不会修改原数组。
let evenNumbers = numbers.filter(function(num) {return num % 2 === 0;});console.log(evenNumbers); // 输出 [4]
map()
:创建一个新数组,其元素是调用一次提供的函数后的返回值,不会修改原数组。
let squaredNumbers = numbers.map(function(num) {return num * num;});console.log(squaredNumbers); // 输出 [1, 1, 9, 16, 25, 81]
由于没有足够的实际生活应用,所以大家可能会觉得很莫名其妙,但是现在只要了解基础理论就行了,后面会有实践内容的
函数
在数学中函数的概念就像机器人一样,通过特定的指令做出特定的动作,在JavaScript开发中概念也差不多
函数的定义
函数是代码块,用于执行特定行为。在 JavaScript 中,可以通过多种方式定义函数。
函数声明:
以 function 关键字开头,后面跟着函数名、参数列表(括在圆括号中,用逗号分隔)和函数体(用大括号括起来)。
function functionName (parameter1, parameter2, ...) {// 函数体,执行的代码// 可以使用 return 语句返回值}
我们来分析一个例子
function addNumbers (num1, num2) {let sum = num1 + num2;return sum;}这里定义了一个名为 addNumbers 的函数,它接收两个参数 num1 和 num2,将它们相加并将结果存储在变量 sum 中然后通过 return 语句返回这个结果。
函数表达式:
它将函数赋值给一个变量。可以是匿名函数或者有名字的函数
const functionName = function (parameter1, parameter2, ...) {// 函数体};
比如
const multiplyNumbers = function (num1, num2) {return num1 * num2;};
或者
const divideNumbers = function division (num1, num2) {return num1 / num2;};
这样做的好处是函数可以作为一个值来传递,比如在事件处理等场景中非常有用。
函数的调用
调用函数时,使用函数名后跟一对圆括号,括号中包含实际参数(实参)。
let result = addNumbers (5, 3);console.log (result); // 输出 8
这里调用了之前定义的 addNumbers
函数,传入了实际参数 5 和 3,函数执行后返回它们的和 8,然后将这个结果赋值给变量 result
。
函数的参数
默认参数 :在定义函数时,可以给参数指定默认值。如果调用函数时没有提供对应的参数,就会使用默认值。
function greet (name = "Guest") {console.log (`Hello, ${name}!`);}greet (); // 输出 "Hello, Guest!"greet ("Alice"); // 输出 "Hello, Alice!"
参数的类型检查 :在 JavaScript 中,参数的类型是动态的。可以在函数内部使用 typeof
运算符来检查参数类型
function checkType (value) {console.log (`The type of the parameter is: ${typeof value}`);}checkType (123); // 输出 "The type of the parameter is: number"checkType ("Hello"); // 输出 "The type of the parameter is: string"
函数的返回值
使用
return 语句可以将值返回给调用者。如果函数中没有
return 语句,或者
return 后面没有值,返回值是 undefined。
function sayHello () {console.log ("Hello!");}let result = sayHello ();console.log (result); // 输出 undefined
这个函数只是输出了 "Hello!",没有返回任何值,所以 result
的值是 undefined
。
函数的作用域
函数作用域 :在函数内部声明的变量只能在函数内部访问,这是函数作用域。而变量的声明方式会影响其作用域。
function createVariable () {var functionScopedVar = "I'm function - scoped";console.log (functionScopedVar);}createVariable (); // 正常输出// console.log (functionScopedVar); // 会报错,因为变量只在函数内部
高阶函数
函数可以作为参数传递给其他函数。
function execute (func, value) {return func (value);}function double (num) {return num * 2;}let result = execute (double, 5);console.log (result); // 输出 10
这里 execute
是一个高阶函数,它接收一个函数 func
和一个值 value
作为参数,然后调用 func
函数并将 value
传递给它。
函数可以作为返回值从其他函数返回。
function createMultiplier (multiplier) {return function (num) {return num * multiplier;};}let triple = createMultiplier (3);console.log (triple (5)); // 输出 15
闭包
闭包是一个函数及其词法环境的组合。它可以访问其词法作用域中的变量。
function createCounter () {let count = 0;return function () {count++;return count;};}let counter = createCounter ();console.log (counter ()); // 输出 1console.log (counter ()); // 输出 2
这里 createCounter
函数返回了一个闭包函数。闭包函数可以访问 createCounter
函数中的变量 count
,并能够修改它。每次调用闭包函数时,count
的值都会增加并返回新的值。
递归函数
递归函数是指在函数内部调用自身的函数。
function factorial (n) {if (n === 0) {return 1;} else {return n * factorial (n - 1);}}console.log (factorial (5)); // 输出 120
这个 factorial
函数计算了 5 的阶乘。它不断调用自身,每次将参数减 1,直到参数为 0 时返回 1,然后逐层返回计算结果。
立即调用函数表达式(IIFE)
它是一种定义并立即执行的函数模式,用于创建一个封闭的作用域,避免污染全局作用域。
(function () {let privateVariable = "I'm private";console.log (privateVariable);})();// console.log (privateVariable); // 会报错,变量在 IIFE 内部
这个函数定义后立即执行,privateVariable
在 IIFE 内部被声明和使用,外部无法访问它。
类
类的基本定义
使用 class
关键字定义一个类,类名首字母通常大写,以区分于其他变量名。
class MyClass {// 类体}
示例:定义一个简单的 “Person” 类,用于表示一个人。
class Person {// 类体暂时为空}
构造函数
构造函数是一个特殊的方法,用于初始化对象的属性。它使用 constructor
关键字定义。
当使用 new
关键字创建类的实例时,构造函数会被自动调用。
示例:在 “Person” 类中添加构造函数,用于初始化人的姓名和年龄。
class Person {constructor(name, age) {this.name = name; // 将传入的 name 参数赋值给对象的 name 属性this.age = age; // 将传入的 age 参数赋值给对象的 age 属性}}// 创建 Person 类的实例const person1 = new Person('Alice', 25);console.log(person1.name); // 输出:Aliceconsole.log(person1.age); // 输出:25
类的方法
在类中定义的方法是对象的行为,用于实现对象的功能。
方法的定义方式与普通函数类似,但要写在类的内部。
示例:在 “Person” 类中添加一个方法,用于打印个人信息。
class Person {constructor(name, age) {this.name = name;this.age = age;}// 定义一个方法introduce() {console.log(`My name is ${this.name}, I am ${this.age} years old.`);}}const person1 = new Person('Alice', 25);person1.introduce(); // 输出:My name is Alice, I am 25 years old.
类的继承
通过继承,一个类可以继承另一个类的属性和方法,从而实现代码的复用。
使用 extends
关键字实现继承,子类通过 super()
关键字调用父类的构造函数。
示例:创建一个 “Student” 类,它继承自 “Person” 类,用于表示学生。
class Person {constructor(name, age) {this.name = name;this.age = age;}introduce() {console.log(`My name is ${this.name}, I am ${this.age} years old.`);}}// Student 类继承 Person 类class Student extends Person {constructor(name, age, grade) {super(name, age); // 调用父类的构造函数,初始化 name 和 agethis.grade = grade; // 子类独有的属性}// 子类自己的方法study() {console.log(`I am a student in grade ${this.grade}.`);}}const student1 = new Student('Bob', 18, '10th');student1.introduce(); // 继承自父类的方法student1.study(); // 子类自己的方法
静态方法和属性
静态方法和属性属于类本身,而不是类的实例。使用 static
关键字定义。
静态方法通过类名直接调用,静态属性也通过类名访问。
示例:在 “Person” 类中添加一个静态方法和一个静态属性。
class Person {constructor(name, age) {this.name = name;this.age = age;}introduce() {console.log(`My name is ${this.name}, I am ${this.age} years old.`);}// 静态方法static sayHello() {console.log('Hello, everyone!');}// 静态属性static species = 'Homo sapiens';}Person.sayHello(); // 调用静态方法console.log(Person.species); // 访问静态属性
访问修饰符
在 JavaScript 中,类的属性和方法默认都是公开的(public),即可以在类的外部访问。
也可以使用 #
来定义私有属性和方法,它们只能在类的内部访问。
示例:在 “Person” 类中添加一个私有属性和一个私有方法。
class Person {constructor(name, age) {this.name = name;this.age = age;this.#privateData = 'This is private data'; // 私有属性}introduce() {console.log(`My name is ${this.name}, I am ${this.age} years old.`);this.#privateMethod(); // 调用私有方法}// 私有方法#privateMethod() {console.log(this.#privateData);}}const person1 = new Person('Alice', 25);person1.introduce();// 以下代码会报错,因为无法在类外部访问私有属性和方法// console.log(person1.#privateData);// person1.#privateMethod();
接下来我们整合内容,分析一个全面的实例
示例
目标
创建一个简单的图书管理系统,包含以下功能:
定义图书类(Book
),包含书名、作者和页数。
定义图书馆类(Library
),可以添加图书、列出所有图书、借阅图书和归还图书。
使用数组存储图书数据。
使用函数实现图书的借阅和归还逻辑。
使用类的继承扩展功能。
// 数据类型// 基本数据类型:String, Number, Boolean, Undefined, Null// 复合数据类型:Object, Array// 运算// 算术运算符:+、-、*、/、%// 比较运算符:==、===、!=、!==、>、<、>=、<=// 逻辑运算符:&&、||、!// 语法语句// if...else、for、while、switch// 对象// 使用对象表示图书const book1 = {title: 'JavaScript高级程序设计',author: 'Nicholas C. Zakas',pages: 600,isAvailable: true};const book2 = {title: '你不知道的JavaScript',author: 'Kyle Simpson',pages: 300,isAvailable: true};// 数组// 使用数组存储图书const books = [book1, book2];// 函数// 借阅图书的函数function borrowBook(book) {if (book.isAvailable) {book.isAvailable = false;console.log(`您已成功借阅《${book.title}》。`);} else {console.log(`《${book.title}》当前不可借阅。`);}}// 归还图书的函数function returnBook(book) {book.isAvailable = true;console.log(`您已成功归还《${book.title}》。`);}// 类// 定义图书类class Book {constructor(title, author, pages) {this.title = title;this.author = author;this.pages = pages;this.isAvailable = true;}}// 定义图书馆类class Library {constructor() {this.books [];= }// 添加图书addBook(book) {this.books.push(book);console.log(`图书《${book.title}》已添加到图书馆。`);}// 列出所有图书listBooks() {console.log('图书馆中的图书:');this.books.forEach(book => {console.log(`- ${book.title}(作者:${book.author},页数:${book.pages},状态:${.isbookAvailable ? '可借阅' : '已借出'})`);});}// 借阅图书borrowBook(title) {const book = this.books.find(book => book.title === title);if (book) {if (book.isAvailable) {book.isAvailable = false;console.log(`您已成功借阅《${book.title}》。`);} else {console.log(`《${book.title}》当前不可借阅。`);}} else {console.log(`图书馆中没有找到《${title}》。`);}}// 归还图书returnBook(title) {const book = this.books.find(book => book.title === title);if (book) {book.isAvailable = true;console.log(`您已成功归还《${book.title}》。`);} else {console.log(`图书馆中没有找到《${title}》。`);}}}// 类的继承// 定义一个特殊图书馆类,继承自 Libraryclass SpecialLibrary extends Library {constructor() {super();this.specialBooks = [];}// 添加特殊图书addSpecialBook(book) {this.specialBooks.push(book);console.log(`特殊图书《${book.title}》已添加到特殊图书馆。`);}// 列出特殊图书listSpecialBooks() {console.log('特殊图书馆中的特殊图书:');this.specialBooks.forEach(book => {console.log(`- ${book.title}(作者:${book.author},页数:${book.pages},状态:${book.isAvailable ? '可借阅' : '已借出'})`);});}}// 测试代码// 创建图书馆实例const myLibrary = new Library();// 创建图书实例const book3 = new Book('JavaScript权威指南', 'David Flanagan', 1200);const book4 = new Book('深入理解计算机系统', 'Randal E. Bryant', 1000);// 添加图书到图书馆myLibrary.addBook(book3);myLibrary.addBook(book4);// 列出所有图书myLibrary.listBooks();// 借阅图书myLibrary.borrowBook('JavaScript权威指南');myLibrary.listBooks();// 归还图书myLibrary.returnBook('JavaScript权威指南');myLibrary.listBooks();// 创建特殊图书馆实例const specialLibrary = new SpecialLibrary();// 添加特殊图书specialLibrary.addSpecialBook(book1);specialLibrary.addSpecialBook(book2);// 列出特殊图书specialLibrary.listSpecialBooks();
数据类型:使用了基本数据类型(如字符串、数字、布尔值)和复合数据类型(对象、数组)。
运算:在判断图书是否可借阅时使用了比较运算符和逻辑运算符。
语法语句:使用了 if...else
、for
、while
等语句。
对象:通过对象表示图书,包含书名、作者、页数和是否可借阅的状态。
数组:使用数组存储图书数据,方便管理和遍历。
函数:定义了借阅和归还图书的函数,封装了逻辑。
类:定义了 Book
类表示图书,Library
类表示图书馆,SpecialLibrary
类继承自 Library
类,扩展了功能。
至此,JavaScript完成,传统前端开发也完成了