Vue教程03:JS基础
本节课我们将开始学习javascript
!当然,这也许会花费3-5讲的篇幅才能全部讲明白,但是这些工作是十分必要的。没有学会js就学框架非常容易把自己干懵,然后照样啥都不会写,成为一个出色的cv工程师:-(
和之前一样,本文力求速成;然而对于0基础的朋友来说,看一篇语焉不详的博客来打开代码世界的大门是非常不友好的😅。因此,如果您之前学习过C++这种严谨的编译型语言,那么这篇文章将很快地带您入门js
。
本文目标
通过本文的学习,我们将:
- 了解
javascript
的基础语法,包括其内置的数据结构; - 熟悉基础的
DOM
操作; - 完成一个小游戏以熟悉上述内容。
(怎么样是不是很激动)
课前准备:
1.希望您在此之前学习过至少一门编程语言的基础语法;
2.推荐边看边做!在讲解示例时可以先暂停思考;就算只是跟着敲一遍,您也会有所收获的。🤗
JS简介与发展情况
什么是JS ?
JavaScript(简称 JS)是一种高效、灵活的脚本语言,最初专为网页开发设计,用于在浏览器中动态更新页面内容。与 HTML 负责页面结构、CSS 负责样式不同,JavaScript 负责页面的交互和动态效果。它可以控制页面中的文本、图像、样式等元素,并响应用户操作,从而使网页变得更加生动和互动。
如果HTML是骨架,CSS是血肉,那JS就是网页负责思维的大脑;
如果HTML是名词,CSS是形容词,那JS就是让它们行动起来的动词短语。
简而言之,JS,负责处理网页的服务与逻辑。
JS能被用在哪儿?
虽然 JavaScript 最初只在浏览器环境中运行,但随着其生态的发展,它的应用领域逐渐扩大,包括但不限于以下几个方面:
- 网页开发
- 主要用于前端开发,负责实现页面的交互效果和动态数据展示。
- 可通过与 HTML、CSS 的结合,使网页呈现丰富的用户体验。
- 服务器(后)端开发
- 使用 Node.js 等运行环境,JavaScript 也可以在服务器端执行,构建后端 API 服务和服务器应用程序。
- Node.js 提供高效的 I/O 性能,适合实时性强的应用,例如聊天室、实时数据处理。
- 移动端应用
- 通过 React Native、Weex、UniApp 等框架,可以使用 JavaScript 开发原生移动应用程序。
- 这种跨平台开发的方式大幅提高了开发效率,一份代码可运行于多端。
- 桌面应用
- 使用 Electron 等工具,可以将 JavaScript 应用程序打包成桌面应用程序(如 VSCode 就是使用 Electron 开发的)。
JS发展历程
本部分主要是为了阐明ES6的重要性,赶时间可以跳过。
参考链接:https://javascript.ruanyifeng.com/introduction/history.html
JavaScript 因为互联网而生,紧随着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。
1990年底,欧洲核能研究组织(CERN)科学家Tim Berners-Lee,在全世界最大的电脑网络——互联网的基础上,发明了万维网(World Wide Web),从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便。
1992年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。
1994年10月,NCSA的一个主要程序员Marc Andreessen联合风险投资家Jim Clark,成立了Mosaic通信公司(Mosaic Communications),不久后改名为Netscape。这家公司的方向,就是在Mosaic的基础上,开发面向普通用户的新一代的浏览器Netscape Navigator。
1994年12月,Navigator发布了1.0版本,市场份额一举超过90%。
Netscape 公司很快发现,Navigator浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,网速很慢而且上网费很贵,有些操作不宜在服务器端完成。比如,如果用户忘记填写“用户名”,就点了“发送”按钮,到服务器再发现这一点就有点太晚了,最好能在用户发出数据之前,就告诉用户“请填写用户名”。这就需要在网页中嵌入小程序,让浏览器检查每一栏是否都填写了。也就是说,目前急需一门为网页加入各种即时交互效果的语言出现。
于是,在1995年,Netscape找来了函数式编程领域大神Brendan Eich,而这位老哥仅在短短 10 天里创造出了 JavaScript。
那时的 JavaScript 只是个网页上的”小工具”——它能处理简单的交互,比如按钮点击、表单提交等,但就是这样的一种”小脚本语言”迅速火遍了整个互联网!
为了让 JavaScript 在各种浏览器上都能流畅地运行,1997 年,JavaScript 被送上了“国际舞台”,由 ECMA 组织将其标准化,于是诞生了第一个 ECMAScript 标准版本。这为 JavaScript 的跨平台发展铺平了道路。
JavaScript 的真正”进化”始于 ECMAScript 3,这一版本引入了很多新的特性,使得它可以处理更复杂的逻辑。到了 2009 年的 ECMAScript 5(ES5),JavaScript 彻底迈入成熟期,增加了严格模式和 JSON 支持等关键特性。可以说,ES5 让 JavaScript 摆脱了”小脚本”的形象,正式成为一门可用于大型项目的编程语言。
然而,JavaScript 真正腾飞的转折点要数 2015 年的 ECMAScript 6(ES6)。这次更新不仅带来了让人惊叹的新语法(如箭头函数、模板字符串、let
/const
声明等),还为模块化开发、面向对象编程提供了新思路。ES6 的新特性极大提高了开发效率,使得 JavaScript 不再是页面上的辅助工具,而成为构建现代应用的核心语言。JavaScript 在这一年一跃成为业界的“明星语言”。
在 ES6 之后,JavaScript 开发社区也逐渐形成了每年发布一次新版本的习惯。比如 ES7、ES8 等陆续推出的版本都对 JavaScript 进行了小而有效的改进,新增的 async/await
、Object.entries
等功能,让开发更加简洁和现代化。
Javascript 与 Java 到底是什么关系?有人说他们就像老婆和老婆饼,其实也不尽然。
调用方式
- 内联方式:将 JavaScript 代码直接写在 HTML 文件中
<script>
标签内。
- 外部文件方式:通过
<script src="file.js"></script>
引入外部 JavaScript 文件。
请将以下代码复制到index.html
,并在同一目录下创建一个js文件:
1 |
|
打开Live Server,使得电脑调整到如下状态即可开始编程:
使用严格模式
严格模式是在 ES5 引入的,它通过 'use strict';
语句启用。
启用严格模式之前,Js经常会悄无声息地出现运行错误或bug(没有开玩笑),而这种错误对于开发者来说这是一个调试噩梦。严格模式帮助避免了一些难以追踪的错误,并且使代码更加安全和高效。
举个例子,在装mod之前,Js甚至允许一个没有被声明的变量直接输出:
而使用严格模式之后,Js总算变得聪明了一点:
所以,无论如何,请在Js文件的开头加上'use strict';
。
变量与运算符
let
与 const
在 JavaScript 中,let
和 const
是用来声明变量的关键字。它们的出现(尤其是在 ES6 之后)帮助解决了代码中常见的变量提升和作用域混乱问题。
let
用于声明可以重新赋值的变量。const
用于声明常量,表示变量在初始化后不能被重新赋值。
1 | // 使用 let 声明变量 |
注意:虽然
const
声明的变量不能被重新赋值,但如果该变量存储的是一个对象或数组,则对象的内容是可以改变的。另外,虽然也可以使用var命名变量,但是一般我们不这么做。
变量类型
JavaScript 支持多种变量类型,可以分为以下几类:
- 基本类型
number
:表示数字类型,包括整数和浮点数。string
:表示字符串类型。boolean
:布尔类型,只有true
和false
两个值。undefined
:未定义类型,表示变量未赋值。null
:空类型,表示没有对象。symbol
:唯一值,用于创建唯一标识符。
- 引用类型
object
:用于存储多个值的集合或复杂的数据结构。
1 | // 基本类型示例 |
可以在浏览器的控制台查看结果:
运算符
算术运算符
+
:加法 -
:减法 *
:乘法
/
:除法 %
:取余 **
:幂运算(指数)
1 | let a = 10; |
赋值运算符
=
:赋值
+=
、-=
、*=
、/=
、%=
:复合赋值运算符
*比较运算符
>
、<
、>=
、<=
:大小比较
==
和 ===
:判断相等 !=
和 !==
:判断不等
请注意:
==
(宽松相等)仅比较值,在比较前会进行类型转换。如果不同类型的数据可以转换成相同的值,它会返回 true
;
===
(严格相等):会比较值和类型,只有当值和类型都相同的情况下,结果才为 true
。
由于双等号会导致一些意料以外的结果,所以实际编程中,请使用三等号。
1 | console.log(5 === "5"); // false,因为类型不同 |
逻辑运算符
&&
:与(AND) ||
:或(OR) !
:非(NOT)
类型转换
在 JavaScript 中,数据类型转换通常发生在以下几种情况:
*自动转换
- 当涉及不同类型的操作时,JavaScript 会自动将类型转换成适合的类型。例如,字符串和数字相加会将数字转换为字符串。
1
2console.log("5" + 3); // 输出 "53" (字符串拼接)
console.log("5" - 3); // 输出 2 ("5" 自动转换为数字 5)手动转换
- 可以通过
Number()
、String()
等函数将数据转换成指定类型。
1
2
3
4
5
6
7let str = "123";
let num = Number(str); // 将字符串转换为数字
console.log(num); // 123
let value = 42;
let text = String(value); // 将数字转换为字符串
console.log(text); // "42"- 可以通过
输入与输出
输入:
- 使用
prompt()
获取用户输入。
输出:
- 使用
alert()
弹出提示框。 - 使用
console.log()
输出调试信息。 - 使用
document.write()
动态插入 HTML 内容。
1 | let name = prompt("请输入你的名字:"); |
格式化输出
模板字符串允许你在字符串中嵌入表达式,用反引号 ``` 包裹,支持多行字符串和变量插值。它让格式化输出更加简洁和清晰。
1 | let name = "Bob"; |
${}
是模板字符串的占位符,可以插入任何有效的 JavaScript 表达式。- 模板字符串还支持换行,不需要使用
\n
来插入换行符。
1 | let message = `Hello, ${name}! |
最常用的就是上面说到的。当然,实际上还有很多别的用法,这里用截图的方式呈现:
条件与循环语句
由于此处与C++语言几乎相同,(事实上这部分我感觉所有语言都差不多)因此本处不再赘述。
数据类型
函数
函数声明
声明的函数可以在定义前被调用:
1 | console.log(greet("Alice")); // 输出: Hello, Alice! |
函数表达式
函数表达式将函数赋值给变量。与函数声明不同,函数表达式不会被提升,必须在定义后才能调用。
语法:
1 | const 函数变量 = function (参数1, 参数2, ...) { |
示例:
1 | const greet = function(name) { |
*箭头函数
箭头函数是 ES6 引入的一种更简洁的函数写法,尤其适合定义简单的匿名函数。它使用 =>
语法,**并且不会创建自己的 this
,而是从父作用域继承 this
**,因此箭头函数通常用于简化回调函数和匿名函数的写法。(this关键字会在后面的对象部分讲到)
语法:
1 | const 函数变量 = (参数1, 参数2, ...) => { |
当函数只有一个参数时,括号可以省略。
当函数只有一条
return
语句时,大括号和return
关键字可以省略。
示例:
简单箭头函数:
1
2const greet = name => "Hello, " + name + "!";
console.log(greet("Charlie")); // 输出: Hello, Charlie!多参数和多语句的箭头函数:
1
2
3
4
5const add = (a, b) => {
const sum = a + b;
return sum;
};
console.log(add(5, 3)); // 输出: 8
补充:作用域与作用域链
在 JS中,作用域(Scope)决定了代码中变量的可见性和生命周期。作用域分为全局作用域、函数作用域和局部作用域,而 作用域链(Scope Chain)则是 JS 查找变量的一种机制,通过层层查找链条来确定变量的值。
实际上这部分内容也和C++中差不多,然而在javascript中似乎能够更加容易理解,因此补充在这儿。
作用域的类型
全局作用域:
- 在代码的任何位置都可以访问的作用域。
- 任何不在函数或代码块内定义的变量,都会自动成为全局变量。
函数作用域:
- 函数内部定义的变量只能在该函数内部访问,不会影响或污染全局作用域。
- 使用
var
声明的变量有函数作用域,let
和const
则具有块级作用域(见下方代码块作用域)。
然而,也正是因为var的不严谨才使其逐渐被废弃。
块级作用域:
- 块级作用域是由
{}
包裹的代码块,使用let
和const
声明的变量在代码块外不可访问。 - 在
if
、for
等结构内声明的变量会被限制在代码块内。
作用域链
简单来说,一个作用域可以访问他的父级作用域中的变量,但是不能访问兄弟或者儿子作用域中的变量。
数组
与 C++ 等语言中的数组不同,JavaScript 数组的元素可以是不同的数据类型。这意味着我们可以在一个数组中包含数值、字符串、其他数组、对象,甚至函数等。我们可以通过“下标”(索引)来访问数组中的元素。
示例:
1 | let b = [ |
数组的常用属性和方法
1. length
属性
length
属性表示数组的长度,即数组中元素的数量。- 注意:
length
是属性,不是方法,调用时不需要加括号。
示例:
1 | let arr = [1, 2, 3, 4, 5]; |
2. push()
方法
push()
方法用于向数组末尾添加一个或多个元素。- 它会直接修改原数组,并返回新的数组长度。
示例:
1 | let arr = [1, 2, 3]; |
3. pop()
方法
pop()
方法用于删除数组的最后一个元素。- 它会直接修改原数组,并返回被删除的元素。
示例:
1 | let arr = [1, 2, 3]; |
对象
对象由键(key)和值(value)组成的键值对构成,与 C++ 中的 map
数据结构类似。每个键值对中的“键”是属性的名称(或方法的名称),而“值”则是属性的值(或方法的实现)。
对象能够包含不同类型的数据,包括变量、数组、其他对象,甚至函数。这种灵活性使得对象非常适合表示复杂的数据结构。
对象的基本语法
我们可以使用花括号 {}
创建一个对象,并在其中定义键值对。
示例:
1 | let person = { |
*对象属性与方法的访问方式
在 JavaScript 中,可以通过两种方式访问对象的属性和方法:
- 点(
.
)表示法:- 适合大多数场景,更加直观简洁。
- 格式:
对象名.属性名
或对象名.方法名()
。
- 方括号(
[]
)表示法:- 如果属性名称是动态的(例如存储在变量中),或者属性名称包含特殊字符,使用方括号更合适。
- 格式:
对象名["属性名"]
或对象名["方法名"]()
。 - 如果对象的属性名或者方法名也是变量,请使用方括号表示法!!!!!!!!!!!!!!!!!
示例:
1 | // 使用点表示法访问属性和方法 |
*this
关键字
在对象的方法中,this
关键字表示当前对象,即调用该方法的对象实例。它允许方法访问对象的其他属性或方法。
示例:
1 | let car = { |
在 refuel
方法中,this.fuel
表示当前 car
对象的 fuel
属性。
动态添加和删除对象的属性
JavaScript 对象的灵活性不仅体现在多种类型的键值对上,还体现在动态的属性管理上。我们可以随时为对象添加或删除属性。
添加新属性:
1 | person.job = "Developer"; |
删除属性:
1 | delete person.age; |
综合应用
假设我们需要一个表示书籍信息的对象,可以使用对象来存储书籍的标题、作者、出版年份以及查看详细信息的功能。
1 | let book = { |
在上例中,我们定义了 book
对象,其中包含书籍的 title
、author
、year
属性以及一个 getSummary
方法,该方法返回书籍的摘要信息。
补充:this
在箭头函数中的特性
简单来说就是,**箭头函数没有自己的 this
**,它将继承离自己最近的(父级)作用域的属性。
一个示例搞明白这一点:
1 | let name = "zhj" |
到目前为止,我们仍然在用原生的Js编写代码,只不过是利用Chrome浏览器显示我们的编写结果。接下来,我们将学习如何像其他任何网站一样,真正用Js操作网页上的元素。
DOM 操作
DOM 树与 Web APIs 简介
DOM(文档对象模型) ,基本上是html文档的结构化表示。
DOM 通过将网页的各个部分(如标签、属性、文本内容等)映射为对象,允许开发者在 JavaScript 中以对象的方式进行操作。
DOM 树:
在浏览器中加载 HTML 文档后,浏览器会将整个 HTML 文档解析为一个 DOM 树。DOM 树是由多个节点构成的,每个节点代表 HTML 文档中的一个元素、文本或属性。树形结构从 document
节点开始,通过父子关系(父节点、子节点)构成树形层次结构。
Web APIs:
事实上,DOM并不是Js的一部分(并不在Js的内置语法中),而是在不同浏览器提供的Web APIs中。Web APIs是浏览器提供的一组接口,允许 JavaScript 和浏览器之间进行交互,控制网页的内容、样式、事件等,你可以简单理解为一个外部的链接库。而我们要做的就是去调用这些接口中的DOM操作。
为了更好地教学,接下来我将用内联样式呈现代码,注意分辨html语言和Js。
操纵字符或值
获取/修改文本内容:
使用 textContent
或 innerText
属性可以获取元素的文本内容。
1 | <p class="para">Hello World</p> |
获取/修改数值:
利用value属性完成操作。
1 | <input type="number" class="guess" /> |
当然,你也可以不通过类来操纵元素,通过id也是可以的,比如document.querySelector('#myId')
.
还可以同时操作一个类的所有标签,比如使用querySelectorAll()
方法。
还有其他如getElementById()
的操纵方法,详见:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/querySelectorAll
操纵CSS样式
CSS 样式通过 style
属性控制。你可以通过 style
属性直接修改元素的样式,或通过修改 classList
操作元素的类名来控制样式。
*修改内联样式
1 | <p id="demo">This is a paragraph.</p> |
在这个例子中,当点击按钮时,<p>
元素的字体颜色和大小都会改变。
另外值得指出的是,当style样式的名字超过1个单词,如background color时,此时我们并不能像学习CSS那样在中间加上-。Js只支持识别驼峰命名法,即需要把从第二个单词开始的首字母全部大写。
因此,想要操纵背景颜色的样式就得这么写:
1 | document.getElementById("demo").style.backgroundColor = "blue"; |
修改类名来控制样式
通过 classList
你可以方便地操作元素的类名,从而实现样式的变化。
classList
API中实现的方法:
add("className")
:向元素添加一个或多个类。remove("className")
:从元素移除一个或多个类。toggle("className")
:在元素上切换指定的类,如果类存在则移除,如果不存在则添加。contains("className")
:检查元素是否包含指定的类,返回true
或false
。
1 | <p id="demo" class="normal-text">This is a paragraph.</p> |
在这个例子中,当点击按钮时,<p>
元素的类名会在 normal-text
和 new-text
之间切换,从而改变元素的样式。
操纵事件
孩子们,最阴的来了🤣
想要操纵事件,那么就必须了解事件侦听器函数addEventListener()
. 在调用时,它需要至少两个参数:
1 | element.addEventListener(event, function); |
event
:指定要监听的事件类型(如"click"
、"mouseover"
等)。function
:指定事件触发时执行的函数。
比如说,我们想做一个点击按钮就会触发输出的小功能:
1 | <button id="btn">Click Me</button> |
键盘事件
键盘事件用于监听用户的键盘操作。最常见的键盘事件包括 keydown
(按下键时触发)和 keyup
(松开键时触发)。
常见的键盘事件类型:
keydown
:在键盘按下某个键时触发。keyup
:在键盘松开某个键时触发。keypress
:按下并保持按键时触发。
1 | <input type="text" id="inputField" placeholder="Type something"> |
在这个例子中,当用户按下键盘的某个键时,输入框下方的 <p>
元素将显示用户按下的键。
其他事件
感兴趣的同学可以一一尝试:
表单事件
focus
:聚焦某个元素blur
:取消聚焦某个元素change
:某个元素的内容发生了改变
窗口事件
需要作用到
window
元素上
resize
:当窗口大小发生变化scroll
:滚动指定的元素load
:当元素被加载完成
小游戏:猜数字
现在,通过一个小游戏来复习刚才的知识吧!
Step1:项目初始化
请新建一个文件夹,并将已经撰写好的html与css文件放入其中:
1 |
|
1 | @import url('https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap'); |
并在相同目录下新建script.js
, 即可得到这样的初始化界面:
可能会用到的Js代码:
1 | ; |
Step2:梳理需求
结合前文中gif的演示,我们把需求分为以下几点:
- 在表单中输入数字,点击check按钮后,Js能够获取到数字(此处需要过滤空字符、字母和字符串);
- 拿到数字以后能够实现逻辑判断:
·当猜的数字低于正确数字,输出”Too low”相关语句;
·当猜的数字高于正确数字,输出”Too high”相关语句;
·当猜的数字等于正确数字,输出”Correct”相关语句,中间盒子显示正确的数字,盒子宽度变大,背景变成绿色;
- 点击”Again”按钮,重新生成随机数。将当前分数调整为20。若上一局的分数比”HighScore”更高,则替换之。
- 额外需求:当玩家猜满20次依然没有猜到时,输出”You lose the game!!”相关内容。
Step3:Coding!
根据项目需求和实现逻辑完成该小游戏。事实上,要用到的所有知识点都已经在本文中阐明!希望大家能体验到前端如同搭积木一般的爽感。另外,做完任意一个逻辑或者需求之后不要忘记测试环节(如利用console调试等),这点也很重要。
细究下来,其实只是几个if-else语句罢了,没有看上去那么难。不过在此处,我想再讲讲可能值得优化的地方:
1.低于数字、高于数字=>三元运算符
这段代码有大量的重复之处,只有高和低输出的语句不同。因此,我们可以把他们进行合并:
1 | if (guess !== secretNumber) { |
非常的优雅,一下子就清爽很多((
2.封装函数
观察到代码里有大量的querySelector的DOM操作方法,又臭又长,不能忍!遂决定对封装一个选择元素的函数:
1 | const displayMessage = function (messages) { |
于是我们就可以进一步简化代码:
1 | // document.querySelector('.message').textContent = '⛔️ No number!'; |
最终实现
1 | ; |