JS基础
JS基础
作者:IT王小二
JS是什么
JS 是一种轻量级的脚本语言。所谓“脚本语言” (script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。
JS 也是一种嵌入式 (embedded) 语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。JS 本身不提供任何与 I/O (输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JS 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。
简单来说,JS 在前端就是用来操作浏览器的工具。
引入方式
JS 代码可以直接嵌在网页的任何地方,如果想要在网页显示前就载入并运行 JS,通常我们把代码放到 <head>
中。
<html>
<head>
<script>
alert("Hello, world");
</script>
</head>
<body>
...
</body>
</html>
由 <script>...</script>
包含的代码就是 JavaScript 代码,它将直接被浏览器执行。
第二种方法是把 JS 代码放到一个单独的 js
文件,然后在 HTML 中通过 <script src="..."></script>
引入这个文件:
<html>
<head>
<script src="/static/js/abc.js"></script>
</head>
<body>
...
</body>
</html>
这样, /static/js/abc.js
就会被浏览器执行,通常我们使用外部引用。
注释
<script>
// 这是一个行注释
alert("Hello, world");
alert("你好,世界"); // 这是一个行尾注释
/* 这是一个区块注释
这是一个区块注释
这是一个区块注释 */
</script>
变量和常量
变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型;常量指不可变的量,特性就是不可变。
JS 中定义变量主要有三个关键字,var
let
const
,简单用法如下。
// JS 设计之初使用的关键字,但是存在历史遗留问题(可重复定义导致变量覆盖问题;提前解析变量导致变量提升问题),以后都不建议使用,替代品是 let
var a = 1;
// 使用 let 定义的变量,不可重复定义,不会提前解析
let b = 2;
// 使用 const 定义的常量,不可变
const c = 3;
strict 模式
JS 在设计之初甚至并不强制要求用 var
声明变量,这个设计错误带来了严重的后果,如果一个变量没有通过 var
声明就被使用,那么该变量就自动被声明为全局变量。
i = 10; // i 现在是全局变量
所以为了避免这种问题,可以启用 strict
模式,在 JS 代码的第一行加上以下代码。
"use strict";
变量类型
JS 是弱类型语言,对于数据类型的规范比较松散,有以下几种类型。
数据类型 | 说明:(四基两空一对象) |
---|---|
Number | 数值,JS 数值类型不再细分整型、浮点型等。 |
String | 字符串,信息传播的载体,字符串必须包含在单引号、双引号或反引号之中。 |
Boolean | 布尔值,逻辑运算的载体,仅有两个值,true 和 false。 |
Symbol | 符号类型,ES6 引入的一种新的原始数据类型,表示独一无二的值,不常用。 |
null | 空值,表示不存在,当为对象的属性赋值为 null,表示删除该属性,使用 typeof 运算符检测 null 值,返回 Object。 |
undefined | 未定义,当声明变量而没有赋值时会显示该值,可以为变量赋值为 undefined。 |
Object | 对象,是一种无序的数据集合,内容是键值对的形式,键名(key)是字符串,可以包含任意字符(空格),字符串引号可省略。 |
以下先简单介绍除 Symbol 和 Object 之外的几种类型,此外我们可以使用 typeof 变量
来查看变量的类型。
<script>
let num = 1;
let str = "1";
let bool = true;
console.log(typeof num); // number
console.log(typeof str); // string
console.log(typeof bool); // boolean
</script>
数值类型(Number)
即我们数学中学习到的数字,可以是整数、小数、正数、负数。
let num1 = 18;
let num2 = 18.88;
let num3 = -18;
字符串类型(String)
被引号包裹的一段文字信息,单引号、双引号、反引号。
// 定义字符串
let str1 = '我是一段文字';
let str2 = "我是一段文字";
let str3 = `我是一段文字`;
// 字符串中显示双引号
let str4 = '我是一段"文字"';
// 模板字符串,可以引用变量
let name = "itwxe"
let age = 18
let intro = `我是${name},今年${age}岁`
布尔值(Boolean)
表示肯定或否定时在计算机中对应的是布尔类型数据,它有两个固定的值 true
和 false
,表示肯定的数据用 true
,表示否定的数据用 false
。
let isMan = true;
let isWoman = false;
空值(null)和未定义(undefined)
null
表示一个“空”的值,它和 0 以及空字符串 ''
不同,0 是一个数值,''
表示长度为 0 的字符串,而 null
表示“空”。
在其他语言中,也有类似 JS 的 null
的表示,例如 Java 也用 null
,Swift 用 nil
,Python 用 None
表示。但是,在 JS 中,还有一个和 null
类似的 undefined
,它表示 “未定义”。
JS 的设计者希望用 null
表示一个空的值,而 undefined
表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用 null
。undefined
仅仅在判断函数参数是否传递的情况下有用。
console.log(null); // null
let name;
console.log(name); // undefined
运算符
算数运算符
数学运算符,主要包括加、减、乘、除、取余(求模)。
运算符 | 作用 |
---|---|
+ | 求和 |
- | 求差 |
* | 求积 |
/ | 求商 |
% | 取模(取余数),常用于作为某个数字是否被整除 |
console.log(1 + 2); // 3
console.log(1 - 2); // -1
console.log(1 * 2); // 2
console.log(1 / 2); // 0.5
console.log(0 % 2); // 0
console.log(1 % 2); // 特别注意这里和 Java 不一样,结果是 1
console.log(2 % 2); // 0
console.log(3 % 2); // 1
console.log(1 + 2 * 3); // 组合起来也遵循数学的规则 7
console.log("itwxe" + 2); // itwxe2
console.log("itwxe" - 2); // 计算失败返回 NaN
console.log("itwxe" * 2); // 计算失败返回 NaN
console.log("itwxe" / 2); // 计算失败返回 NaN
赋值运算符
赋值运算符:对变量进行赋值的运算符号,已经写了很多 =
号了,以下是一些简写方式。
运算符 | 作用 |
---|---|
+= | 加法赋值,eg:i = i + 1 可以简写为 i += 1 |
-= | 减法赋值,eg:i = i - 1 可以简写为 i -= 1 |
*= | 乘法赋值,eg:i = i * 1 可以简写为 i *= 1 |
/= | 除法赋值,eg:i = i / 1 可以简写为 i /= 1 |
%= | 取余赋值,eg:i = i % 1 可以简写为 i %= 1 |
let num = 100;
num += 100; // 100
num -= 50; // 150
num *= 2; // 300
num /= 3; // 100
num %= 50; // 0
自增/自减运算符
以变量 num
为例说明。
符号 | 说明 |
---|---|
num++ | 使用变量自身的值运算,之后再 +1 |
++num | 使用变量自身的值 +1 之后的值运算 |
num-- | 使用变量自身的值运算,之后 -1 |
--num | 使用变量自身的值 -1 之后的值运算 |
虽然上面说明很简单,但是得结合下面例子,细品~
let num = 100;
console.log(num++); // 100
console.log(num); // 101
console.log(num--); // 101
console.log(num); // 100
console.log(++num); // 101
console.log(num); // 101
console.log(--num); // 100
console.log(num); // 100
比较运算符
使用场景:比较两个数据大小、是否相等,根据比较结果返回一个布尔值(true / false)
运算符 | 作用 |
---|---|
> | 左边是否大于右边 |
< | 左边是否小于右边 |
>= | 左边是否大于或等于右边 |
<= | 左边是否小于或等于右边 |
=== | 左右两边是否类型和值都相等(重点) |
== | 左右两边值是否相等 |
!= | 左右值是否不相等 |
!== | 左右两边是否不全等 |
// 根据比较结果返回 布尔值 true / false
console.log(3 > 6) // false
console.log(3 < 6) // true
console.log(3 >= 6) // false
console.log(3 <= 3) // true
// 提倡使用三等,即比较值和类型
console.log(3 == 3) // true
console.log(3 == '3') // true
console.log(3 === 3) // true
console.log(3 === '3') // false
console.log(3 != 3) // false
console.log(3 !== 3) // false
console.log(3 !== '3') // true
逻辑运算符
符号 | 名称 | 说明 |
---|---|---|
&& | 逻辑与,俗称并且 | 所有条件为 true 则为 true;有一个条件为 false 则为 false |
|| | 逻辑或,俗称或者 | 有一个条件为 true 则为 true,并且碰到条件为 true 时后面的条件就不会判断了 |
! | 逻辑非,俗称取反 | 给条件取反,true 变为 false ;false 变为 true |
console.log(3 < 5 && 2 < 1); // false
console.log(3 < 5 || 2 < 1); // true
console.log(!(3 < 5 || 2 < 1)); // false
运算符优先级
优先级 | 顺序 |
---|---|
1 | () |
2 | ++ -- ! |
3 | 先 * / % 后 + - |
4 | > >= < <= |
5 | == != === !== |
6 | 先 && 后 ` |
7 | = |
显示模式
转换为数字型
有时候需要进行计算的数据不是数字类型,则需要转换后计算。
函数 | 说明 |
---|---|
Number(数据) | 转换成功返回一个数字类型;转换失败则返回 NaN (例如数据里面包含非数字) |
parseInt(数据) | 只保留整数,如果数字开头的字符串,只保留整数数字 比如 12px 返回 12;转换失败则返回 NaN (例如数据以非数字开头) |
parseFloat(数据) | 可以保留小数,如果数字开头的字符串,可以保留小数 比如 12.5px 返回 12.5;转换失败则返回 NaN (例如数据以非数字开头) |
console.log(Number("10.01")); // 10.01
console.log(Number("10.01xyz")); // NaN
console.log(parseInt("10.01")); // 10
console.log(parseInt("10.01xyz")); // 10
console.log(parseInt("xyz10.01")); // NaN
console.log(parseFloat("10.01")); // 10
console.log(parseFloat("10.01xyz")); // 10.01
console.log(parseFloat("xyz10.01")); // NaN
转换为字符串
函数 | 说明 |
---|---|
String(数据) | 转为字符串 |
数据.toString(进制,不填默认10进制) | 转为字符串,可以进行进制转换 |
let num = 10;
let numConvertStr1 = String(num);
let numConvertStr2 = num.toString();
console.log(numConvertStr1); // "10"
console.log(numConvertStr2); // "10"
console.log(typeof numConvertStr1); // string
console.log(typeof numConvertStr2); // string
let numConvertStr3 = num.toString(8); // 转换为8进制,结果:"12"
console.log(numConvertStr3); // "12"
console.log(typeof numConvertStr3); // string
布尔型转换
使用 Boolean(数据)
可以转换为布尔类型,其他数据类型中的 0 "" null undefined NaN
为 false,其他的都为 true。
console.log(Boolean(0)); // false
console.log(Boolean(1)); // true
console.log(Boolean("")); // false
console.log(Boolean("itwxe")); // true
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。
// 算术运算符 - * / 比较运算符 > ==
console.log(9 - "3"); // 6
console.log("300" * "2"); // 600
console.log(3 > "6"); // false
console.log(3 == "3"); // true
// 加正号使用的时候,也会把字符串转换为 数字型
console.log("123"); // "123"
console.log(+"123"); // 123
console.log(typeof +"123"); // number
// 隐式转换为字符串型的运算符,加号拼接字符串,两侧只要有一个是字符串,结果就是字符串
console.log("28" + 18); // "2818"
console.log(typeof ("28" + 18)); // string
// 隐式转换为布尔型的运算符 !逻辑非
console.log(!true); // false
console.log(!0); // true
console.log(!""); // true
console.log(!null); // true
console.log(!undefined); // true
console.log(!NaN); // true
console.log(!false); // true
console.log(!"itwxe"); // false
条件判断
if 分支判断
条件判断语句定义:
// 单分支
if (条件表达式){
// 满足条件要执行的语句
}
// 双分支
if (条件表达式){
// 满足条件要执行的语句
} else {
// 不满足条件要执行的语句
}
// 多分支,else if 可以多个
if (条件表达式1){
// 满足条件表达式1要执行的语句
} else if (条件表达式2) {
// 满足条件表达式2要执行的语句
} else {
// 不满足条件要执行的语句
}
没啥好解释的,上例子。
let score = 70;
// 控制台打印结果为及格
if (score >= 90) {
console.log("优秀");
} else if (score >= 75) {
console.log("良好");
} else if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}
三元表达式
一些简单的双分支,可以使用三元表达式(三元运算符),写起来比 if else 双分支更简洁。
// 语法
// 条件 ? 条件为真时的结果 : 条件为假时的结果;
console.log(5 > 3 ? "真的" : "假的"); // 真的
switch 语句
switch 语句在 if 多分支等值匹配比较多的时候书写起来更加简洁。
switch (表达式) {
case 值1:
代码1
break;
case 值2:
代码2
break;
default:
代码n
}
例如电脑配件的价格示例,示例控制台打印结果为 2000。
let computerParts = "CPU";
switch (computerParts) {
case "主板":
console.log(1000);
break;
case "CPU":
console.log(2000);
break;
case "内存条":
console.log(800);
break;
default:
console.log("没有匹配到");
}
循环
所谓循环,就是满足判断条件则一遍一遍循环的执行一段代码块。
while 循环
语法:
while (判断条件) {
// 满足条件执行的循环体
}
循环打印10次,示例:
let i = 1;
while (i <= 10) {
console.log(`当前循环第${i}次`);
i++;
}
for 循环
语法:
for (初始值; 循环条件; 变量计数) {
// 满足条件执行的循环体
}
循环打印10次,示例:
for (let i = 0; i < 10; i++) {
console.log(`当前循环第${i + 1}次`);
}
死循环和终止循环
死循环是当判断条件始终不满足的时候就会无限循环,而通常死循环是不可取的,所以需要终止循环。
死循环可以使用 while (true)
和 for(;;;)
来实现,当然也有其他方式;而终止循环需要使用到 break
关键字。
let num = 100;
while (true) {
if (num < 50) {
console.log("程序终止")
break;
} else {
num -= 50;
console.log("程序没有终止,num - 50,num = " + num)
}
}
// 程序总共执行了三次循环,执行结果为
程序没有终止,num - 50,num = 50
程序没有终止,num - 50,num = 0
程序终止
break
用于终止循环,而 continue
用于跳过某次循环。
let num = 3;
for (let i = 1; i < 6; i++) {
if (num === i) {
console.log("跳过第" + i + "次循环");
continue;
}
console.log("这是第" + i + "次循环");
}
// 控制台打印结果,可以看到 i=3 的时候没有打印 这是第3次循环
这是第1次循环
这是第2次循环
跳过第3次循环
这是第4次循环
这是第5次循环
循环的嵌套
条件判断可以嵌套,循环也可以嵌套,外层循环循环一次,里层循环循环全部。
<script>
"use strict";
// document.write 是往页面写入信息
document.write("<h2>三角形</h2>")
for (let i = 0; i < 6; i++) {
for (let j = 0; j <= i; j++) {
document.write("☆");
}
document.write("<br>");
}
document.write("<hr>")
document.write("<h2>九九乘法表</h2>")
for (let i = 1; i <= 9; i++) {
let rowStr = "";
for (let j = 1; j <= i; j++) {
rowStr += `${j} * ${i} = ${i * j} `;
}
document.write(rowStr + "<br>");
}
</script>
数组
数组也可以理解为一种数据类型,数组可以存储多个数据。
数组的创建
第一种方法是直接表示:
[1, 2, 3.14, "Hello", null, true];
上述数组包含 6 个元素。数组用 []
表示,元素之间用 ,
分隔。
另一种创建数组的方法是通过 Array()
函数实现:
new Array(1, 2, 3); // 创建了数组[1, 2, 3]
然而,出于代码的可读性考虑,强烈建议直接使用 []
。
访问元素
数组的元素可以通过索引来访问。请注意,索引的起始值为 0
:
let arr = [1, 2, 3.14, "Hello", null, true];
arr[0]; // 返回索引为0的元素,即1
arr[5]; // 返回索引为5的元素,即true
arr[6]; // 索引超出了范围,返回undefined
JavaScript 的 Array 可以包含任意数据类型,并通过索引来访问每个元素。
数组的长度
要取得 Array 的长度,直接访问 length
属性:
const arr = [1, 2, 3.14, "Hello", null, true];
arr.length; // 6
请注意,直接给 Array 的 length
赋一个新的值会导致 Array 大小的变化:
const arr = [1, 2, 3];
arr.length; // 3
arr.length = 6;
arr; // arr 变为 [1, 2, 3, undefined, undefined, undefined]
arr.length = 2;
arr; // arr 变为 [1, 2]
Array 可以通过索引把对应的元素修改为新的值,因此,对 Array 的索引进行赋值会直接修改这个 Array:
const arr = ["A", "B", "C"];
arr[1] = 99;
arr; // arr 现在变为 ['A', 99, 'C']
PS:如果通过索引赋值时,索引超过了范围,同样会引起 Array 大小的变化:
const arr = [1, 2, 3];
arr[5] = "x";
arr; // arr 变为 [1, 2, 3, undefined, undefined, 'x']
大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript 的 Array 却不会有任何错误。在编写代码时,不建议直接修改 Array 的大小,访问索引时要确保索引不会越界。
操作数组
indexOf
与 String 类似,Arra。也可以通过 indexOf()
来搜索一个指定的元素第一次出现的位置:
const arr = [10, 20, "30", "xyz"];
arr.indexOf(10); // 元素 10 的索引为 0
arr.indexOf(20); // 元素 20 的索引为 1
arr.indexOf(30); // 元素 30 没有找到,返回 -1
arr.indexOf("30"); // 元素 '30' 的索引为 2
PS:数字 30
和字符串 '30'
是不同的元素。
slice
slice()
就是对应 String 的 substring()
版本,它截取 Array 的部分元素,然后返回一个新的 Array:
const arr = ["A", "B", "C", "D", "E", "F", "G"];
arr.slice(0, 3); // 从索引 0 开始,到索引 3 结束,但不包括索引 3: ['A', 'B', 'C']
arr.slice(3); // 从索引 3 开始到结束: ['D', 'E', 'F', 'G']
注意到 slice()
的起止参数包括开始索引,不包括结束索引。
如果不给 slice()
传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以很容易地复制一个 Array:
const arr = ["A", "B", "C", "D", "E", "F", "G"];
const aCopy = arr.slice();
aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
aCopy === arr; // false
push 和 pop
push()
向 Array 的末尾添加若干元素, pop()
则 Array 的最后一个元素删除掉:
const arr = [1, 2];
arr.push("A", "B"); // 返回 Array 新的长度: 4
arr; // [1, 2, 'A', 'B']
arr.pop(); // pop() 返回 'B'
arr; // [1, 2, 'A']
arr.pop();
arr.pop();
arr.pop(); // 连续 pop 3次
arr; // []
arr.pop(); // 空数组继续 pop 不会报错,而是返回 undefined
arr; // []
unshift 和 shift
如果要往 Array 的头部添加若干元素,使用 unshift()
方法, shift()
方法则把 Array 的第一个元素删掉:
const arr = [1, 2];
arr.unshift("A", "B"); // 返回 Array 新的长度: 4
arr; // ['A', 'B', 1, 2]
arr.shift(); // 'A'
arr; // ['B', 1, 2]
arr.shift();
arr.shift();
arr.shift(); // 连续 shift 3次
arr; // []
arr.shift(); // 空数组继续 shift 不会报错,而是返回 undefined
arr; // []
sort
sort()
可以对当前 Array 进行排序,它会直接修改当前 Array 的元素位置,直接调用时,按照默认顺序排序:
const arr = ["B", "C", "A"];
arr.sort();
arr; // ['A', 'B', 'C']
数组页可以按照自己指定的顺序排序,将在后面的函数中讲到。
reverse
reverse()
把整个 Array 的元素给掉个个,也就是反转:
const arr = ["one", "two", "three"];
arr.reverse();
arr; // ['three', 'two', 'one']
splice
splice()
方法是修改 Array 的 “万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
const arr = ["Microsoft", "Apple", "Yahoo", "AOL", "Excite", "Oracle"];
// 从索引 2 开始删 3 个元素,然后再添加 2 个元素:
arr.splice(2, 3, "Google", "Facebook"); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, "Google", "Facebook"); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
concat
concat()
方法把当前的 Array 和另一个 Array 连接起来,并返回一个新的 Array:
const arr = ["A", "B", "C"];
const added = arr.concat([1, 2, 3]);
added; // ['A', 'B', 'C', 1, 2, 3]
arr; // ['A', 'B', 'C']
PS:需要留意的是: concat()
方法并没有修改当前 Array,而是返回了一个新的 Array。
实际上, concat()
方法可以接收任意个元素和 Array,并且自动把 Array 拆开,然后全部添加到新的 Array 里:
const arr = ["A", "B", "C"];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
join
join()
方法是一个非常实用的方法,它把当前 Array 的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:
const arr = ["A", "B", "C", 1, 2, 3];
arr.join("-"); // 'A-B-C-1-2-3'
PS:若 Array 的元素不是字符串,将自动转换为字符串后再连接。
多维数组
如果数组的某个元素又是一个 Array,则可以形成多维数组,例如:
const arr = [[1, 2, 3], [400, 500, 600], "-"];
上述 Array 包含 3 个元素,其中头两个元素本身也是 Array。