JavaScript基础
此文章根据b站 JavaScript速成课【油管最火JS教程】整理,并做了一些补充。
JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。
为什么要学JavaScript?
- 运行在浏览器端
- 构建交互式页面,如React,angular,vue.js
- 可以使用Node构建服务端和全栈应用
- 可以使用React Native和NativeScript构建移动端APP,甚至可以用Electron构建PC端APP
Hello World
与CSS类似,JavaScript有两种写法,直接写在html文件中,或写到独立的js文件中。
以下以一个简单的hello world引入,调用 alert()
函数,为window对象中的方法,显示弹窗。alert() 函数在 JavaScript 中并不常用,但它对于代码测试非常方便。
\newline
写在html文件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<header>
<h1>JS Crash Course</h1>
</header>
<script>
alert('Hello World');
</script>
</body>
</html>
效果:
首先弹出弹窗:
点击确定后显示页面内容:
\newline
写在独立js文件中
html文件中设置 script
标签 src
属性为js文件路径:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<header>
<h1>JS Crash Course</h1>
</header>
<script src="helloworld.js"></script>
</body>
</html>
js文件中直接写js内容:
alert('Hello World');
运行结果与写在html文件中一致。
就alert()函数来说,一般不用来调试或输出值,否则会阻塞后面运行的脚本,效率很低,一般用console.log()。
\newline
浏览器中执行 JavaScript
Console 窗口调试 JavaScript 代码。
在上述js文件中编写以下代码:
console.log('Hello World');
console.error('This is an error');
console.warn('This is a warning');
在html文件中引入此js文件,运行html文件,在浏览器页面右键选择“检查”:
查看“控制台”一栏,鼠标经过上方图标显示“1 error,1 warning”,同时控制台输出了程序中的字符串。
可以在控制台输入命令:
可以在控制台输入命令,得到相应的输出内容。
以下案例的演示都会使用到浏览器控制台查看输出。
\newline
变量和常量
声明方式
var
:全局作用域let
:可以重新赋值const
:不可以重新赋值
以下例子可以很清楚地看到let和const的区别:
let:
let age = 30;
age = 40;
console.log(age);
浏览器控制台正常输出40:
const:
浏览器控制台报错 “Uncaught TypeError: Assignment to constant variable.”
,类型错误:常量不能当变量赋值。
\newline
数据类型
原始数据类型
所有 JavaScript 值,除了原始值,都是对象。原始值指的是没有属性或方法的值。原始数据类型指的是拥有原始值的数据。原始数据类型,数据直接分配给内存,而不是资源类型。
JavaScript 定义了 5 种原始数据类型:
string
number
boolean
null
undefined
以下例子测试不同数据类型:
// string
// 单引号,双引号均可;分号可有可无,一般都会有;
const str = 'John';
// const name = "John";
// number
const age = 30;
// js中没有浮点数类型
const rating = 4.5;
// boolean
const flag = true;
// object
const x = null;
// undefined
const y = undefined;
// 未对值进行初始化也相当于undefined
let z;
// 测试数据类型
console.log(typeof str);
console.log(typeof age);
console.log(typeof rating);
console.log(typeof flag);
console.log(typeof x);
console.log(typeof y);
console.log(typeof z);
浏览器控制台输出:
注:js中null是对象,js的执行会先查看值的标记类型,object类型标记为0,null标记为null指针,一旦有返回值,null的标记结果就为0,即false,因此为object类型。
\newline
字符串String
以下代码展示了字符串的属性和用法:
const name = 'John';
const age = 30;
// 输出
// 字符串和数字拼接
console.log('My name is name and I am age');
// 传统写法
console.log('My name is ' + name + ' and I am ' + age);
// 模板字符串,使用反引号(esc下面的键)
console.log(`My name is ${name} and I am ${age}`);
const hello = `My name is ${name} and I am ${age}`;
console.log(hello);
// 属性和方法
const s = 'Hello World!';
// 字符串长度
console.log(s.length);
// 全部转换为大写/小写
console.log(s.toUpperCase());
console.log(s.toLowerCase());
// 提取连续字符串,两个参数:开始索引,结束索引,[start,end-1]
console.log(s.substring(0, 5));
// 多种方法,直接连接在后面
console.log(s.substring(0, 5).toUpperCase);
// 分隔字符串
console.log(s.split('')); // 将字符逐个分割,得到字符数组(包含空格)
// 实际中是在表单中添加很多内容,相当于一个字符串数组,分割完将每个字符串添加到数据库中,可以做搜索之类的,很方便
const ss = 'technology, computers, it, code';
console.log(ss.split(',')); // 按逗号分隔
浏览器控制台输出:
\newline
数组Array
数组是一种特殊的变量,它能够一次存放一个以上的值。
// 使用Array构造函数构造数组
const numbers = new Array(1, 2, 3, 4, 5);
console.log(numbers);
// 在同一个数组中可以保存不同类型值
const fruits = ['apples', 'oranges', 'pears', 10, true];
console.log(fruits);
// js是动态语言,可以向数组中添加值,虽然是const类型也可以
fruits[1] = 'banana'; // 直接把oranges替换掉
fruits[5] = 'grapes';
console.log(fruits);
// 不能对const类型的数据重新赋值,以下两种情况都不可以,会报错
// fruits = [];
// fruits = ['watermelon'];
// 当不知道有多少个数据,但要向末尾添加数据时,使用push
fruits.push('mangos');
console.log(fruits);
// 向开头添加数据
fruits.unshift('strawberries');
console.log(fruits);
// 去掉最后一项
fruits.pop();
console.log(fruits);
// 判断某变量是否为数组
console.log(Array.isArray(fruits));
console.log(Array.isArray('hello'));
// 得到某个值的索引
console.log(fruits.indexOf('oranges'));
console.log(fruits.indexOf('banana'));
结果:
对象
对象就是键值对。在 JavaScript 中,几乎“所有事物”都是对象。
- 布尔是对象(如果用 new 关键词定义)
- 数字是对象(如果用 new 关键词定义)
- 字符串是对象(如果用 new 关键词定义)
- 日期永远都是对象
- 算术永远都是对象
- 正则表达式永远都是对象
- 数组永远都是对象
- 函数永远都是对象
- 对象永远都是对象
// 键值对,可以是字符串,数字,数组,对象
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
hobbies: ['music', 'movies', 'sports'],
address: {
street: '50 main st',
city: 'Boston',
state: 'MA'
}
}
console.log(person);
console.log(person.firstName);
console.log(person.firstName, person.hobbies[1], person.address.state);
// 解构赋值
const { firstName, address: { city } } = person; // 从person对象中取出
console.log(firstName);
console.log(city);
// 直接添加属性
person.email = 'John@gmail.com';
console.log(person);
结果:
\newline
对象数组
const todos = [
{
id: 1,
text: 'Take out trash',
isCompleted: true
},
{
id: 2,
text: 'Meeting with boss',
isCompleted: true
},
{
id: 3,
text: 'Dentist appt',
isCompleted: false
}
];
console.log(todos);
console.log(todos[1].text);
结果:
JSON
JSON是一种数据类型,在全栈开发中年广泛使用。当向服务器发送数据时会用到JSON格式。
JSON数据与JS对象数据主要差别:JS对象的键名可用单引号或双引号包围也可以不用,而JSON只能使用双引号,使用本工具可以快速处理引号问题,将JS对象转为JSON。
使用在线转换数据将js代码转换为JSON代码:
可以直接调用方法实现js转换为json:
const todos = [
{
id: 1,
text: 'Take out trash',
isCompleted: true
},
{
id: 2,
text: 'Meeting with boss',
isCompleted: true
},
{
id: 3,
text: 'Dentist appt',
isCompleted: false
}
];
const todoJSON = JSON.stringify(todos);
console.log(todoJSON);
可以看到代码都转换成了JSON格式:
\newline
循环
JavaScript 支持不同类型的循环:
- for - 多次遍历代码块
- for/in - 遍历对象属性
- while - 当指定条件为 true 时循环一段代码块
- do/while - 当指定条件为 true 时循环一段代码块
通过以下例子感受循环语法:
const todos = [
{
id: 1,
text: 'Take out trash',
isCompleted: true
},
{
id: 2,
text: 'Meeting with boss',
isCompleted: true
},
{
id: 3,
text: 'Dentist appt',
isCompleted: false
}
];
// For
for (let i = 0; i < 5; i++) {
// console.log(i);
console.log(`For Loop Number: ${i}`); // 注意是反引号
}
// While
let i = 0;
while (i < 5) {
console.log(`While Loop Number: ${i}`); // 注意是反引号
i++;
}
// 遍历数组常规方法:for
for (let i = 0; i < todos.length; i++) {
console.log(todos[i].text);
}
// 2
for (let todo of todos) {
console.log(todo.id);
}
// 高阶数组方法:forEach, map, filter
// 参数为函数,回调函数中可以写多个参数,第一个参数为迭代对象
todos.forEach(function (todo) {
console.log(todo.text); // 循环打印每一项
});
// map返回常规数组,而非对象
const todoText = todos.map(function (todo) {
return todo.text;
});
console.log(todoText);
// filter筛选出列表中符合条件的值,返回常规数组,而非对象
const todoCompleted = todos.filter(function (todo) {
return todo.isCompleted === true; // 恒等于,比==更严格
});
console.log(todoCompleted);
// 也可以链接其他方法
const completed = todos.filter(function (todo) {
return todo.isCompleted === true; // 恒等于,比==更严格,要求数据类型一致
}).map(function (todo) {
return todo.text;
})
console.log(completed);
结果:
条件
在 JavaScript 中,我们可使用如下条件语句:
- 使用
if
来规定要执行的代码块,如果指定条件为 true - 使用
else
来规定要执行的代码块,如果相同的条件为 false - 使用
else if
来规定要测试的新条件,如果第一个条件为 false - 使用
switch
来规定多个被执行的备选代码块
if-else if-else
const x = '10';
// true
if (x == 10) {
console.log('x = 10');
}
// false,前者为字符串,后者为数字,数据类型不同
if (x === 10) {
console.log('x is 10');
}
// if-else,逻辑或||,逻辑与&&
const y = 20;
if (x === 10) {
console.log('x is 10');
} else if (x > 10) {
console.log('x is greater than 10');
}
else {
console.log('x is less than 10');
}
结果:
三目运算符,switch
// 三目运算符
const x = 11;
const color = x > 10 ? 'red' : 'blue';
console.log(color);
// switch
switch (color) {
case 'red':
console.log('color is red');
break;
case 'blue':
console.log('color is blue');
break;
default:
console.log('color is NOT red or blue');
break;
}
结果:
\newline
函数
JavaScript 函数通过 function 关键词进行定义,其后是函数名和括号 ()。函数名可包含字母、数字、下划线和美元符号(规则与变量名相同)。
函数中的代码将在其他代码调用该函数时执行:
- 当事件发生时(当用户点击按钮时)
- 当 JavaScript 代码调用时
- 自动的(自调用)
// function写法
function addNums1(num1 = 1, num2 = 1) {
return num1 + num2;
}
console.log(addNums1(5, 5));
// 另一种写法,箭头函数
const addNums2 = (num1 = 1, num2 = 1) => {
console.log(num1 + num2);
}
addNums2(5, 5);
// or
const addNums3 = (num1 = 1, num2 = 1) => num1 + num2;
console.log(addNums3(5, 5));
// or
const addNums4 = (num1 = 1, num2 = 1) => {
return num1 + num2;
}
console.log(addNums4(5, 5));
// 箭头函数写法很简便
const addNum = num1 => num1 + 5;
console.log(addNums3(5, 5));
// forEach箭头函数,like:
// todos.forEach((todo) => console.log(todo));
结果:
\newline
面向对象编程
在 JavaScript 中,被称为 this 的事物是代码的“拥有者”。this 的值,在对象中使用时,就是对象本身。在构造函数中,this 是没有值的,它是新对象的替代物。 当一个新对象被创建时,this 的值会成为这个新对象。this 并不是变量,而是关键字,无法改变 this 的值。
以下例子中,函数 Person() 就是对象构造函数。通过 new 关键词调用构造函数可以创建相同类型的对象。
在构造函数中实现函数有两种方法,第一种为直接在对象的构造函数中写函数,另一种为使用原型实现。
构造函数中实现对象函数
// 可以通过构造函数来构建对象
function Person(firstName, lastName, dob) {
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date(dob); // 将日期由字符串转换为Date类型
// 给Person对象添加函数
this.getBirthYear = function () {
return this.dob.getFullYear(); // 使用this可以访问对象属性
}
this.getFullYear = function () {
return `${this.firstName} ${this.lastName}`;
}
}
// 实例化对象
// 创建Person对象
const person1 = new Person('John', 'Doe', '4-3-1980');
console.log(person1);
console.log(person1.getBirthYear());
console.log(person1.getBirthYear());
const person2 = new Person('Mary', 'Smith', '3-3-1960');
console.log(person2.dob);
console.log(person2.dob.getFullYear());
结果:
\newline
原型实现对象函数
// 可以通过构造函数来构建对象
// 用原型实现构造函数中的函数
function Person(firstName, lastName, dob) {
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date(dob); // 将日期由字符串转换为Date类型
}
// 原型对象就是Person对象的父类
Person.prototype.getBirthYear = function () {
return this.dob.getFullYear();
}
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
// 实例化对象
// 创建Person对象
const person1 = new Person('John', 'Doe', '4-3-1980');
console.log(person1);
console.log(person1.getBirthYear());
console.log(person1.getBirthYear());
const person2 = new Person('Mary', 'Smith', '3-3-1960');
console.log(person2.dob);
console.log(person2.dob.getFullYear());
结果:
\newline
类
类与上一部分面向编程能实现的功能一样,只不过语法略有不同。使用类编写更加漂亮,易写易读。
// class
class Person {
constructor(firstName, lastName, dob) {
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date(dob); // 将日期由字符串转换为Date类型
}
// 给Person对象添加函数
getBirthYear() {
return this.dob.getFullYear(); // 使用this可以访问对象属性
}
getFullYear() {
return `${this.firstName} ${this.lastName}`;
}
}
// 实例化对象
// 创建Person对象
const person1 = new Person('John', 'Doe', '4-3-1980');
console.log(person1);
console.log(person1.getBirthYear());
console.log(person1.getBirthYear());
const person2 = new Person('Mary', 'Smith', '3-3-1960');
console.log(person2.dob);
console.log(person2.dob.getFullYear());
结果:
可以看到控制台输出的内容与面向对象编程的一致,同样可以使用原型实现类中的函数,这里就不给出代码了。
\newline
DOM
DOM是文档对象模型的缩写。
console.log(window); // window对象是浏览器的父对象
window.alert(1);
// 等同于
alert(1); // window为很高级别,可以省略window,一切皆为window对象
浏览器控制台输出:可以看到有很多window下的方法,包括alert(),localStorage(),document()等。
localStorage()为在浏览器中存储的一种方式,也是window对象的一部分。
fetch()是发起HTTP请求的方法。
document()是构成DOM的文档对象模型,可以看到有很多属性、方法。
通过document从文档中选东西。先给出index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>JS For Beginners</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>JS For Beginners</h1>
</header>
<section class="container">
<form id="my-form">
<h1>Add User</h1>
<div class="msg"></div>
<div>
<label for="name">Name:</label>
<input type="text" id="name">
</div>
<div>
<label for="email">Email:</label>
<input type="text" id="email">
</div>
<input class="btn" type="submit" value="Submit">
</form>
<ul id="users"></ul>
<ul class="items">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
</section>
<script src="index.js"></script>
</body>
</html>
style.css:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
line-height: 1.6;
}
ul {
list-style: none;
}
ul li {
padding: 5px;
background: #f4f4f4;
margin: 5px 0;
}
header {
background: #f4f4f4;
padding: 1rem;
text-align: center;
}
.container {
margin: auto;
width: 500px;
overflow: auto;
padding: 3rem 2rem;
}
#my-form {
padding: 2rem;
background: #f4f4f4;
}
#my-form label {
display: block;
}
#my-form input[type='text'] {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
.btn {
display: block;
width: 100%;
padding: 10px 15px;
border: 0;
background: #333;
color: #fff;
border-radius: 5px;
margin: 5px 0;
}
.btn:hover {
background: #444;
}
.bg-dark {
background: #333;
color: #fff;
}
.error {
background: orangered;
color: #fff;
padding: 5px;
margin: 5px;
}
document选择元素
选择单个元素
index.js中选择单个元素,获取html文件中的元素。
使用document的 getElementById()
方法,参数为id;使用 querySelector()
方法,参数为类名/标签名/id(注意格式,类前要加 .
,id前要加 #
),如果有多个相同类名和标签的应用,则只选择第一个。
// Document
// 单个元素
console.log(document.getElementById('my-form')); // 通过id选择单个元素,获取html文件中的form元素
console.log(document.querySelector('.container')); // 查询选择器,可以选择标签,类等大多数,运行很像jQuery
console.log(document.querySelector('h1')); // 如果有多个h1,只会选择第一个
console.log(document.querySelector('#my-form'));
// 除了直接打印,还可以将获取的元素存到变量中
const form = document.getElementById('my-form');
console.log(form);
结果:将鼠标光标放到浏览器控制台的输出代码上,可以看到左边界面选中了对应的元素。
\newline
选择多个元素
index.js中选择多个元素,获取html文件中的元素。
使用document的 querySelectorAll()
方法,参数为类名/标签名/id;使用 getElementsByClassName()
方法,参数为类名(不需要加 .
),选择类名对应的元素;使用 getElementsByTagName()
方法,参数为标签名,选择标签名对应的元素。
// 获取多个元素,得到数组
console.log(document.querySelectorAll('.item')); // html中有多个类名为item的应用,将它们全都选出来
console.log(document.querySelectorAll('h1'));
console.log(document.querySelectorAll('.container')); // 只有单个元素也可以选出来
console.log(document.querySelectorAll('#my-form'));
// 只选择类名,得到HTML集合
console.log(document.getElementsByClassName('item')); // 类名不需要加.
// 只选择标签,得到HTML集合
console.log(document.getElementsByTagName('li'));
结果:可以看到选出了对应的元素,使用 querySelectorAll()
方法得到的为 NodeList[]
数组,使用 getElementsByClassName()
得到的为 HTMLCollection()
,为HTML集合。
\newline
遍历元素
用document的 querySelectorAll()
方法选出元素,使用forEach遍历该标签/类底下所有元素。
const items = document.querySelectorAll('.item');
items.forEach((item) => console.log(item));
结果:可以看到打印出了.item底下的所有item。
\newline
操纵DOM
// 操纵DOM
const ul = document.querySelector('.items');
// ul.remove(); // 删除.items下的所有li标签
// ul.lastElementChild.remove(); // 删除.items下的最后一个li标签
ul.firstElementChild.textContent = 'Hello'; // 把第一个li标签内容改为‘Hello’
ul.children[1].innerText = 'Brad'; // 把第二个li标签的内容改为‘Brad’
ul.lastElementChild.innerHTML = '<h1>Hello</h1>'; // 把最后一个li标签的html语句改为'<h1>Hello</h1>'
// 改变样式
const btn = document.querySelector('.btn');
btn.style.background = 'red';
结果:可以看到li标签内容被修改,‘submit’按钮背景变成了红色。
\newline
事件
创建事件和函数,可以动态处理,如点击时改变颜色,大小等,依次实现在页面中实时操纵东西。
// 写成事件和函数,可以动态处理,可以点击时改变颜色,大小等,可以在页面中实时操纵东西
const btn = document.querySelector('.btn'); // 获取btn元素
// 创建事件监听,指定两个参数:事件和函数(事件发生时运行的函数)
btn.addEventListener('click', (e) => { // 把这里的click事件改为'mouseover'或'mouseout'可以通过鼠标移入/移出submit按钮改变样式
e.preventDefault(); // 使用此方法可以防止单击时控制台输出的内容闪现,原因为submit按钮用来提交表单,此方法可以阻止此默认行为,使表单不再处于提交状态。
console.log('click');
console.log(e); // 打印事件对象
console.log(e.target);
console.log(e.target.className);
// 点击按钮改变样式,实现用户界面交互
// 点击submit按钮后改变my-form的背景颜色
document.querySelector('#my-form').style.background = '#ccc';
// 添加背景颜色样式(bg-dark类已在css文件中写好但在html文件中没有调用,这里设置点击submit按钮后对body应用此样式
document.querySelector('body').classList.add('bg-dark');
// 对某个元素应用html语句的内容
document.querySelector('.items').lastElementChild.innerHTML = '<h1>Hello</h1>';
});
结果:可以看到左边是html页面,点击提交按钮后,表单背景变成了灰色,页面背景变成了黑色;右边是浏览器控制台输出,其中 PointerEvent()
为事件对象。
查看事件对象及其属性:
\newline
简单应用
用上述index.html和style.css文件,以及如下的form.js文件体会如何操纵DOM。
form.js:
// 用户表单脚本,提交的内容只存在于界面上,一刷新就没了,没有被保存下来,除非有后端和数据库等
// 从DOM中获取内容并存到变量中
const myForm = document.querySelector('#my-form');
const nameInput = document.querySelector('#name');
const emailInput = document.querySelector('#email');
const msg = document.querySelector('.msg');
const userList = document.querySelector('#users');
// 监听表单的提交事件:submit(只要作用在form就可以用submit)
myForm.addEventListener('submit', onSubmit);
// 事件发生时的执行函数
function onSubmit(e) {
e.preventDefault(); // 一旦提交,阻止默认行为
// 表单验证:两项都得填写,否则不能提交
if (nameInput.value === '' || emailInput.value === '') {
// alert('Please enter all fields');
msg.classList.add('error'); // 添加error类
msg.innerHTML = 'Please enter all fields'; // 类名为msg的元素提示填写完整
// 让error显示3s后消失(移除)
setTimeout(() => msg.remove(), 3000);
} else {
// 创建新元素插入到DOM中
const li = document.createElement('li');
// 插入带有输入框文本值的文本节点
li.appendChild(document.createTextNode(`${nameInput.value}: ${emailInput.value}`));
// Add HTML
// li.innerHTML = `<strong>${nameInput.value}</strong>e: ${emailInput.value}`;
// 将li添加到ul中
userList.appendChild(li);
// 清空输入框
nameInput.value = '';
emailInput.value = '';
}
}
结果如下,输入name和email后,将结果打印在form下方。