封面画师:Nengoro(ネんごろぅ)     封面ID:80965079

1. 什么是JavaScript

JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的高级编程语言。虽然它是作为开发Web页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。

那么Java与JavaScript有关系吗?就像老婆与老婆饼的关系,所以…

JavaScript是一门世界上最流行的脚本语言,所以你还不学吗?一个合格的后端程序人员,必须要会JavaScript,所以你还不学吗?

参考链接:JavaScript的起源故事

ECMAScript,可以理解为JavaScript的一种标准。最新版本已经到ES6版本,但是大部分浏览器还只停留在支持ES5的代码上,这就导致了开发环境与线上环境版本不一致。

2. 快速入门

2.1 引入JavaScript

  1. 内部标签
1
2
3
<script>
...
</script>
  1. 外部引入

myjs.js:

1
//...

test.html:

1
<script src="myjs.js"></script>

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title></title>
<!-- 外部引入 -->
<!-- 注意:script标签必须成对出现 -->
<script src="js/myjs.js"></script>
<!-- 可以不用显式定义type,默认就是javascript -->
<script type="text/javascript"></script>
</head>
<body>
</body>
</html>

myjs.js:

1
alert("Hello World!");

2.2 基本语法入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- JavaScript严格区分大小写 -->
<script>
/* JavaScript中,语句后可以不写分号,但是打包时没有分号会合成一行
因此依旧建议写上分号!
*/
// 1. 定义变量:变量类型 变量名 = 变量值;
var score = 1;
//alert(score);
// 2. 条件控制
if(2 > 1){
console.log(score); //打印到控制台
alert("true");
}
</script>

浏览器F12调试常用选项:

F12开发者工具

2.3 数据类型

数值、文本、图形、音频、视频…

变量

使用var进行定义变量。

变量命名与Java语法类似。比如:

1
2
3
4
var _sa = 1;
var $a = "2";
var 我爱 = "中国";
...

number

JS中不区分小数和整数,统一使用number定义。

1
2
3
4
5
6
123 //整数
123.1 //浮点数
1.123e3 //科学计数法
-99 //负数
NaN // not a number
Infinity //表示无限大

字符串

比如:“abc” ‘abc’ …

布尔值

true 、 false

逻辑运算

1
2
3
&& //与
|| // 或
! //非

比较运算符

1
2
3
= //赋值
== //等于(类型不一样,值一样,也会判断为true)
=== //绝对等于(类型一样,值一样,结果才是true)

建议 要求 在JS中进行比较时使用 ==

注意:

  • NaN === NaN,结果为false。NaN与所有数都不相等,包括自己。😱
  • 只能通过isNaN(NaN)来判断这个数是否是NaN。😎

浮点数的问题:

1
console.log((1/3) === (1-2/3));	//结果打印为false

尽量避免使用浮点数进行运算,存在精度问题!😵

我们可以用绝对值来进行浮点数的比较:👍

1
console.log(Math.abs(1/3-(1-2/3))<0.000000001);

null 与undefined

  • null表示空

  • undefined表示未定义。

数组

Java的数组必须是一系列相同类型的对象,但是在JS中不需要这样:

1
2
3
// 为了程序的可读性,尽量使用[]表示数组
var arr = [1,2,3,4,5,6,'hello',null,true];
new Array(1,12,3,4,5,'hello');

使用数组时,如果数组下标越界,将会显示undefined

对象

对象使用大括号{ },数组使用中括号[ ]

JS中的对象每个属性之间使用,隔开,最后一个不需要添加。

1
2
3
4
5
var person = {
name: "mofan",
age: 18,
tags: ['Java','Web','JS','...']
}

取对象的值:

1
2
3
4
person.name
> "mofan"
person.age
> 18

2.4 严格检查模式

在JS中,使用 i = 1定义一个名为 i 的变量时,这个 i 是一个全局变量,如果导入的多个JS文件中都使用了 i ,那么这时候会产生无法预料的后果💥,因此我们需要一种方法来检测这种定义方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 
使用"use strict";的前提编译器支持ES6语法
"use strict";严格检查模式,预防JS的随意性导致产生一些问题
"use strict"; 必须卸载JS的第一行。
局部变量建议使用 let 定义。
-->
<script type="text/javascript">
"use strict";
// 全局变量
i = 1;
// ES6 中我们一般使用let定义局部变量
let a = 2;
</script>

要想在编译器内直接看到错误提示,我们需要对编译器设置支持JS的ES6规范。

使用"use strict";时,这条语句必须放在JS的第一行

3. 数据类型详解

3.1 字符串

  1. 正常的字符串我们使用单引号' '或者双引号" "进行包裹。
  2. 注意转义字符 \ 的使用;
1
2
3
4
5
\'		
\n
\t
\u4e2d //Unicode字符编码 \u####
\x41 //Ascll编码

\'表示一个点。

  1. 多行字符串编写。使用反引号将字符串包裹起来。
1
2
3
4
var msg = `你好,
JavaScript!`;
// 反引号位于Tab键上方
// 使用这种方式,可以将换行和制表符显示出来,而不需要使用\n或\t
  1. 模板字符串
1
2
3
4
5
let name = "mofan";
let age = 18;
let message = `你好,${name}`; //这里的字符串是使用反引号包裹起来的
console.log(message);
// 打印结果为:你好,mofan
  1. 字符串的属性与方法
1
2
3
4
5
6
7
8
9
var stu = "stuDEnt";
console.log(stu.length); // 打印结果为7
console.log(stu[0]); // 打印结果为s
console.log(stu); // 打印结果为student
console.log(stu.toUpperCase()); // 字符串转换为大写
console.log(stu.toLowerCase()); // 字符串转换为小写
console.log(stu.indexOf("D")); // 获取字符串中"D"的下标
console.log(stu.substring(1)); // 截取字符串下标是1及其以后的所有字符
console.log(stu.substring(1,3)); // 截取字符串下标是1到3的所有字符,不包含下标3。范围是[1,3)

浏览器打印结果:

JS字符串属性与函数

  1. 字符串不可变(Java也有这样的特性)

我们在浏览器中进行以下测试(在编译器中测试会直接报错):

JS字符串不可变

3.2 数组

Array可以包含任意的数据类型。

1
2
3
4
var num = [1,2,3];	//通过下标取值和赋值
console.log(num);
num[0] //下标取值
num[0] = 0; //下标赋值
  1. 数组长度测试
1
2
3
4
5
6
7
8
9
10
11
var arr = [1,2,3,4,5,6];
console.log(arr);
console.log(arr.length);
arr[0] = 0;
console.log(arr);
arr.length = 10;
console.log(arr.length);
console.log(arr);
console.log(arr[7]);
arr.length = 2;
console.log(arr);

打印结果:

数组长度测试

注意:如果先定义了一个数组,再给这个数组的length属性进行赋值,数组的长度就会发生变化。数值超过原始长度,多出的数据以undefined填充;数值小于原始长度,数据就会丢失。


  1. indexOf() 通过元素获取下标索引
1
2
3
4
5
var arr = [1,2,3,4,5,6];
console.log(arr.indexOf(3)); //获取元素3的下标索引
var arra = [1,2,3,4,5,6,"1","2"];
console.log(arra.indexOf(1)); //打印结果为0
console.log(arra.indexOf("1")); //打印结果为6

字符串中的 “1” 和数字 1 是不同的。


  1. slice() 截取Array的一部分,返回一个新的数组,类似于String中的substring()
1
2
3
4
5
var arra = [1,2,3,4,5,6,"1","2"];
console.log(arra.slice(3));
// 结果为:[4, 5, 6, "1", "2"]
console.log(arra.slice(1,5));
// 结果为: [2, 3, 4, 5]

  1. push()pop()

push : 压入元素到尾部; pop : 弹出尾部的一个元素

1
2
3
4
5
6
7
var arra = [1,2,3,4,5,6,"1","2"];
arra.push("3","4");
console.log(arra);
// 结果为:[1, 2, 3, 4, 5, 6, "1", "2", "3", "4"]
arra.pop();
console.log(arra);
// 结果为:[1, 2, 3, 4, 5, 6, "1", "2", "3"]

  1. unshift() shift()

unshift : 压入元素到头部; shift : 弹出头部的一个元素

1
2
3
4
5
6
7
var arra = [1,2,3,4,5,6,"1","2"];
arra.unshift(0,-1);
console.log(arra);
// 结果为:[0, -1, 1, 2, 3, 4, 5, 6, "1", "2"]
arra.shift();
console.log(arra);
// 结果为:[-1, 1, 2, 3, 4, 5, 6, "1", "2"]

  1. 排序 sort() 默认从小到大排序
1
2
3
4
var arra = [3,2,1,5,9];
arra.sort();
console.log(arra);
// 结果为: [1, 2, 3, 5, 9]

  1. 元素反转 reverse()
1
2
3
4
var arra = [1, 2, 3, 5, 9];
arra.reverse();
console.log(arra);
// 结果为:[9, 5, 3, 2, 1]

  1. 元素拼接 concat()
1
2
3
4
5
var arra = [9, 5, 3, 2, 1];
arra.concat("11","22","33");
// 拼接后为:[9, 5, 3, 2, 1, "11", "22", "33"],但是元数组并没有改变
console.log(arra);
// 结果为:[9, 5, 3, 2, 1]

注意:concat()没有修改原数组,只是返回了一个新的数组!😮


  1. 连接符 join()

使用特定的连接符将数组内每个元素连接起来形成一个字符串。当然,原数组也没有发生改变。☺️

1
2
3
4
5
var arra = ["A","B","C"];
arra.join("-");
// 拼接后形成: "A-B-C"
console.log(arra);
// 打印结果为: ["A", "B", "C"]

  1. 多维数组
1
2
3
var arra = [[1,2],["3","4"],["a","b"]];
console.log(arra[1][1]);
// 打印结果为:4

3.3 对象

若干个键值对。对象格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var person = {
属性名 : 属性值,
属性名 : 属性值,
...
属性名 : 属性值
}

// 定义了一个person对象,它有四个属性
var person = {
name:"mofan",
age: 18,
gender: "男",
score: 100
}

JS中的对象,{…} 表示一个对象,键值对描述属性及属性值,多个属性之间使用逗号,隔开,最后一个属性不需要加逗号,

JS中所有的键都是字符串,值是任意对象!😳

  1. 对象赋值
1
2
3
4
5
console.log(person.name);
// 打印结果为: mofan
person.name = "默烦";
console.log(person.name);
// 打印结果为: 默烦 , 原对象也会发生改变

  1. 使用一个不存在的对象属性并不会报错! 会显示 undefined
1
2
console.log(person.height);
// 打印结果为: undefined
  1. 动态删减属性
1
2
3
4
delete person.name
// 返回true
console.log(person);
// 打印结果为: {age: 18, gender: "男", score: 100}

  1. 动态添加属性
1
2
3
person.weight = 120;
console.log(person);
// 返回结果为: {age: 18, gender: "男", score: 100, weight: 120}

  1. 判断某个属性是否在这个对象中,继承的属性也算。 使用 in 关键字
1
2
3
4
5
console.log('age' in person);
// 返回结果为: true
// 继承
console.log('toString' in person);
// 返回结果为: true

  1. 判断某个属性是否是这个对象自身拥有的 hasOwnProperty()
1
2
3
4
console.log(person.hasOwnProperty('age'));
// 打印结果为: true
console.log(person.hasOwnProperty('toString'));
// 打印结果为: false

3.4 流程控制

if 判断

1
2
3
4
5
6
7
8
9
10
11
<script type="text/javascript">
"use strict"
var age = 3;
if (age < 3){
alert("小于3");
}else if(age > 3 && age < 5){
alert("大于3,小于5");
}else{
alert("范围未知");
}
</script>

循环

while 循环, 避免死循环

1
2
3
4
5
6
7
8
9
10
var age = 3;
while(age<100){
age = age + 2;
console.log(age);
}
// 区分
do {
age = age + 2;
console.log(age);
}while (age < 100)

for 循环

1
2
3
for (let i = 0; i < 100; i++){
console.log(i);
}

forEach 循环(ES5.1的特性 )

1
2
3
4
var arr = [1,12,34,25,66,775,34];
arr.forEach(function(value){
console.log(value); //遍历数组元素
});

for … in … 循环 (不建议使用这种方式遍历数组)😵

1
2
3
4
5
6
7
8
9
10
11
var arr = [1,12,34,25,66,775,34];
for(var num in arr){ // num 是数组下标
if(arr.hasOwnProperty(num)){
console.log(arr[num]); //遍历数组元素
}
}
arr.name = "345"; //早起的漏洞
for(var x in arr){ // num 是数组下标
console.log(x);
}
// 打印结果为:0 1 2 3 4 5 6 name

3.5 Map和Set

这些是 ES6 的新特性!

Map

1
2
3
4
5
6
7
8
9
10
11
12
// ES6 Map
"use strict"
var map = new Map([['Tom',100],['Jerry',90],['Spike',80]]);
var name = map.get('Tom');
map.set('Butch',60); //新增或修改
console.log(name);
// 打印结果为: 100
console.log(map);
// 打印结果为: Map(4) {"Tom" => 100, "Jerry" => 90, "Spike" => 80, "Butch" => 60}
map.delete('Spike');
console.log(map);
// 打印结果为: Map(3) {"Tom" => 100, "Jerry" => 90, "Butch" => 60}

Set

无需不重复的集合。

1
2
3
4
5
6
7
8
9
10
11
12
//ES6 Set
var set = new Set([3,2,1,1,1,1]);
console.log(set);
// 打印结果为: Set(3) {3, 2, 1}
set.add("2");
console.log(set); //添加
// 打印结果为: Set(4) {3, 2, 1, "2"}
set.delete("2");
console.log(set); //删除
// 打印结果为: Set(3) {3, 2, 1}
console.log(set.has(3)); //是否包含某个元素
// 打印结果为: true

3.6 iterator

ES6的新特性! for … of … 循环

遍历数组:

1
2
3
4
var arra = [1,2,3,4,5];
for(var x of arra){
console.log(x); //遍历数组元素
}

遍历Map:

1
2
3
4
var map = new Map([['Tom',100],['Jerry',90],['Spike',80]]);
for (let x of map){
console.log(x); //遍历Map
}

遍历Set:

1
2
3
4
var set = new Set([3,2,1,1,1,1]);
for (let x of set){
console.log(x); // 遍历Set
}

4. 函数

4.1 定义函数

定义方式一

绝对值函数:

1
2
3
4
5
6
7
function abs(x){
if (x >= 0){
return x;
}else{
return -x;
}
}

一旦执行到 return 代表函数结束,就返回结果!

如果没有执行 return ,函数执行完也会返回结果,返回结果是undefined。

定义方式二

1
2
3
4
5
6
7
var abs = function(x){
if (x >= 0){
return x;
}else{
return -x;
}
}

function(x){…} 是一个匿名函数,但是可以把结果赋值给abs,通过abs就可以调用函数。

方式一和方式二的效果是等价的!

调用函数

1
2
abs(10);
abs(-10);

调用时的参数问题:

JS中调用函数时可以传入任意个参数,也可以不传入参数。

假设不存在参数,应该怎么规避呢?手动抛出异常👇:

1
2
3
4
5
6
7
8
9
10
11
var abs = function(x){
// 手动抛出异常
if(typeof x !== 'number'){
throw 'Not a Number';
}
if (x >= 0){
return x;
}else{
return -x;
}
}

arguments

arguments 是JS的一个内置属性(JS免费赠送的关键字)。

arguments代表传递入函数的所有参数,是一个数组

1
2
3
4
5
6
7
8
9
10
11
var abs = function(x){
console.log("x=>"+x);
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
if (x >= 0){
return x;
}else{
return -x;
}
}

问题:arguments包含了所有的参数。但我们有时候会使用定义以外的参数来进行附加操作,就需要排除定义的参数,使用arguments却十分麻烦。

如:现有一个函数 function(a,b,c,d,e),但我在使用时只想传入参数c、d,不使用a、b,即:function(c,d)。那么我需要进行参数位置的判断,如果使用arguments,我将会从a开始遍历判断,这显得十分臃肿麻烦,那么有其他的方式获得定义以外的参数吗(直接从c开始遍历判断)?rest解决了这个问题。

rest

ES6新特性! 获取除了已经定义的参数的所有参数。

ES6之前的方式:

1
2
3
4
5
if(arguments.length > 2){
for (var i = 2; i < arguments.length; i++){
...
}
}

ES6引入新特性,可以获取定义参数之外的所有参数:

1
2
3
4
5
function abc (a, b, ...rest){
console.log("a=>"+a);
console.log("b=>"+b);
console.log(rest);
}

rest参数只能写在最后的参数位置,必须用…标识。

4.2 变量的作用域

在JS中,var 定义的变量是有作用域的。

👇假设在函数体内使用 var 定义了一个变量 x ,那么在函数体外是不可以使用 x 的。

1
2
3
4
5
function ab(){
var x = 1;
x = x + 1;
}
x = x + 2; // Uncaught ReferenceError: x is not defined

👇如果在两个函数中使用 var 定义了相同的变量名,只要在函数内部,就不会产生冲突。

1
2
3
4
5
6
7
8
function ab(){
var x = 1;
x = x + 1;
}
function abc(){
var x = "A";
x = x + 1;
}

👇内部函数可以访问外部函数成员,但外部函数不能访问内部函数。

1
2
3
4
5
6
7
8
9
function ab(){
var x = 1;
// 内部函数可以访问外部函数成员
// 但外部函数不能访问内部函数
function ab2(){
var y = x + 1; // 2
}
z = y + 1; // Uncaught ReferenceError: y is not defined
}

👇内部函数变量和外部函数变量重名:

1
2
3
4
5
6
7
8
9
10
11
function ab(){
var x = 1;
function ab2(){
var x = 'A';
console.log('inner:'+x); // outer:1
}
console.log('outer:'+x); // inner:A
ab2();
}

ab()

JS中函数查找变量是从自身函数开始的,由“内”向“外”查找,假设外部存在这个同名的函数变量,则内部函数会屏蔽外部函数的变量。

提升变量的作用域

1
2
3
4
5
6
function ab(){
var x = "x+" + y;
console.log(x);
var y = "y";
}
ab();

打印结果为:x+undefined

说明:JS执行程序时,自动提升了 y 的声明,但是不会提升变量 y 的赋值。

1
2
3
4
5
6
7
// ab()等价于ab2()
function ab2 (){
var y;
var x = "x" + y;
console.log(x);
y = "y";
}

这个是JS建立之初就存在的特性。

❗️养成规范:所有变量都定义在函数的头部,不要随意放置,便于代码维护。如:

1
2
3
4
5
6
function abc(){
var a = 1,
b = a + 1,
c, d, f;
// 然后对这些变量进行使用
}

全局变量

定义在函数外的变量就是全局变量。

1
2
3
4
5
6
7
// 全局变量
var x = 1;
function f(){
console.log(x);
}
f();
console.log(x);

全局变量 window

1
2
3
4
5
var xx = "xxx";
alert(xx);
alert(window.xx);
console.log(xx);
console.log(window.xx);

alert()这个函数本身也是window的一个变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xx = "xxx";
window.alert(xx);
var old_alert = window.alert;
old_alert(xx); // 可以弹出消息框,显示xx
window.alert = function(){

}

// 发现alert失效了
window.alert(123);

// 恢复
window.alert = old_alert;
window.alert(456);

JS实际中只有一个全局作用域window,假设某一变量(函数也可以视为变量)没有在函数作用域内找到,就会向外查找,直到查找到全局作用域window。如果window中也没有找到,就会报错RefrenceError。

规范

我们所有的全局变量都是绑定在全局作用域window上的,如果不同的js文件使用了相同的全局变量就会产生冲突,那么我们应该怎么避免(减少)冲突呢?👇

1
2
3
4
5
6
7
8
9
// 全局唯一变量 
// 相当于我自己定义一个对象,所有的变量都绑定在这个对象上
var myField = {};

// 定义全局变量
myField.name = "mofan";
myField.add = function (a,b){
return a + b;
}

相当于自己的代码全部放到自己定义的唯一命名空间名字中,用于降低全局命名冲突👌。

局部作用域 let

1
2
3
4
5
6
7
8
function bbb(){
for (var i = 0; i < 100; i++) {
console.log(i);
}
console.log(i + 1); // i 出了这个作用域还可以使用
// 输出到101
}
bbb();

在ES6中,提出 let 关键字,解决局部作用域冲突问题。

1
2
3
4
5
6
7
function bbb(){
for (let i = 0; i < 100; i++) {
console.log(i);
}
console.log(i + 1); // Uncaught ReferenceError: i is not defined
}
bbb();

❗️建议 要求 大家都使用 let 去定义局部作用域的变量。

常量 const

在ES6之前,我们这么定义“常量”:

只要用全部大写字母命名的变量,就是“常量”,建议不要修改这样的值。但是这只是一种约定,这个值依然是可以修改的。

1
2
3
4
var PI = '3.14';
console.log(PI); // 打印出:3.14
PI = '123'; // 只是一种约定,依然可以改变
console.log(PI); // 打印出:123

因此在ES6中,引入关键字 const 用来定义常量。

1
2
3
const PI = '3.14';	// 只读变量 常量
PI = '123'; // Uncaught TypeError: Assignment to constant variable.
console.log(PI);

4.3 方法

定义方法

方法就是把函数放在对象里面,对象内只有两个东西:属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
var mofan = {
name: '默烦',
birth: 2000,
//方法
age: function(){
var now = new Date().getFullYear();
return now - this.birth;
}
}

/***********浏览器内调用***********/
mofan.name // 属性
mofan.age() // 年龄

❓那么上述代码中的this是什么意思呢,或者说this指向的是谁呢?

⭐️ 先给出结论:谁调用thisthis就指向谁。

我们可以对上述代码进行改造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getAge(){
var now = new Date().getFullYear();
return now - this.birth;
}

var mofan = {
name: '默烦',
birth: 2000,
age: getAge
}

/* ********浏览器内调用******* */
mofan.age() // 20
getAge() // NaN

为什么输出结果会像上述那样呢?

对于mofan.age()age()表示的就是getAge()getAge()中的this就表示mofan对象;而对于单个getAge(),其中的this指向的是window,但是在window这个全局变量中是没有birth属性的,因此返回的结果就是NaN。

apply

在Java中,this的指向无法指定的,谁调用它,它就指向谁。

但是!在JS中可以控制this的指向。😳

比如:

1
2
3
// 在上述代码中添加下列语句
console.log(getAge.apply(mofan,[])); // 表示this指向了默烦,getAge参数为空
// 浏览器内输出:20

5. 内部对象

标准对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typeof 123
"number"
typeof '123'
"string"
typeof []
"object"
typeof {}
"object"
typeof true
"boolean"
typeof NaN
"number"
typeof Math.abs
"function"
typeof undefined
"undefined"

5.1 Date

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
var now = new Date();
console.log(now); // 详细时间
console.log(now.getFullYear()); // 年
console.log(now.getMonth()); // 月 0~11
console.log(now.getDate()); // 日
console.log(now.getDay()); // 星期
console.log(now.getHours()); // 小时
console.log(now.getMinutes()); // 分钟
console.log(now.getSeconds()); // 秒
console.log(now.getTime()); // 时间戳
// 时间戳转时间
console.log(new Date(1591002279219));

转换

1
2
3
4
5
6
7
8
9
10
now = new Date(1591002279219)
// Mon Jun 01 2020 17:04:39 GMT+0800 (中国标准时间)
now.toDateString()
// "Mon Jun 01 2020"
now.toLocaleString()
// "2020/6/1 下午5:04:39"
now.toGMTString()
// "Mon, 01 Jun 2020 09:04:39 GMT"
now.toLocaleDateString()
// "2020/6/1"

5.2 JSON

什么是JSON

早期,所有的数据传输习惯使用XML文件!JSON:

  • (JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
  • 它基于 ECMAScript (欧洲计算机协会制定的 JS 规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

——源自百度百科


在JS中,一切皆为对象,任何JS支持的类型都可以用JSON表示。

格式:

  • 对象使用{ }
  • 数组使用[ ]
  • 所有的键值对,都使用 key: value

JSON 的数据类型有:

  • 字符串
  • 数字
  • 对象(JSON 对象)
  • 数组
  • 布尔
  • null

介绍一下对象类型(其他几种都很好理解),被大括号包裹起来的数据就是一个对象(这个 JSON 对象可能没有字段名,也可能有)。

如:{"name":"mofan","age":20,"gender":"男"}

1
2
3
4
5
6
7
8
9
10
11
12
var person = {
name: 'mofan',
age: 20,
gender: '男'
}
console.log(person);
// 对象转换为JSON字符串
var jsonPerson = JSON.stringify(person);
console.log(jsonPerson);
// JSON字符串转换为对象
var objPerson = JSON.parse('{"name":"mofan","age":20,"gender":"男"}');
console.log(objPerson);

JSON 与 对象比较:

1
2
var obj = {name: mofan, age: 20};
var json = '{"name":"mofan", "age":"20"}';

6. 面向对象编程

6.1 什么是面向对象

JS、Java、C# … 都有面向对象。但JS中与其他的有些区别!

类:对象的抽象,模板;对象:具体的实例。

原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Student = {
name: 'mofan',
age: 20,
run: function(){
console.log(this.name + " run...");
}
}

var xiaoming = {
name: "xiaoming"
}

// xiaoming 的原型设置为Student
xiaoming.__proto__ = Student;
xiaoming.run();

var Bird = {
fly: function(){
console.log(this.name + " fly...");
}
}

xiaoming.__proto__ = Bird;
xiaoming.fly();
1
2
3
4
5
6
7
8
function Student(name){
this.name = name;
}

//给Student新增加一个方法
Student.prototype.hello = function(){
alert("Hello!");
}

Class继承

class关键词是在ES6引入的。

1、定义一个类:属性,方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ES6之后
// 定义一个学生的类
class Student{
constructor(name) {
this.name = name;
}
hello(){
alert("Hello!");
}
}

var xiaoming = new Student("xiaoming");
console.log(xiaoming.name);
var xiaowang = new Student("xiaowang");
console.log(xiaowang.name);

2、继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ES6之后
// 定义一个学生的类
class Student{
constructor(name) {
this.name = name;
}
hello(){
alert("Hello!");
}
}

class ElementaryStudents extends Student{
constructor(name, grade) {
super(name);
this.grade = grade;
}

myGrade(){
alert("我考了100分");
}
}

var xiaoming = new Student("xiaoming");
console.log(xiaoming.name);
var xiaowang = new ElementaryStudents("xiaowang", 8);
xiaowang.myGrade();
console.log(xiaowang.name);
console.log(xiaowang.grade);

原型链

__proto__:

参考链接:javascript——原型与原型链

JS原型与原型链

7. 操作BOM对象(重点)

BOM:浏览器对象模型

JS的诞生就是为了能够让它在浏览器中运行!

window (重要)

window代表 浏览器窗口、全局作用域。

1
2
3
4
5
6
7
8
9
// 在浏览器开发者模式中输入...
window.innerHeight
// 150
window.innerWidth
// 768
window.outerHeight
// 840
window.outerWidth
// 784

navigator

Navigator:封装了浏览器的信息(一个类)。

1
2
3
4
navigator.appName	// 浏览器名称
navigator.appVersion // 浏览器的平台和版本信息
navigator.userAgent // 返回由客户机发送服务器的user-agent 头部的值
navigator.platform // 返回运行浏览器的操作系统平台

大多数时候,我们不会使用navigator对象,因为这些内容可以被人为修改。

❗️不建议使用这些属性来判断和编写代码。

screen

screen:代表屏幕的尺寸

1
2
3
4
screen.width
// 1536
screen.height
// 864

location (重要)

location:代表当前页面的URL信息。

1
2
3
4
5
6
host: "www.baidu.com"
href: "https://www.baidu.com/?tn=02003390_42_hao_pg"
protocol: "https:"
reload: ƒ reload() // 刷新网页
// 设置新的地址
location.assign("https://www.bilibili.com/");

document

document代表当前的页面,HTML、DOM文档树。

1
2
3
4
document.title
// "百度一下,你就知道"
document.title = ‘mofan’
// "mofan"

获取具体的文档树节点:

1
2
3
4
5
6
7
8
9
10
<body>
<dl id="app">
<dt>Java</dt>
<dd>JavaSE</dd>
<dd>JavaEE</dd>
</dl>
</body>
<script type="text/javascript">
var dl = document.getElementById('app');
</script>

获取cookie:

1
document.cookie

正是因为可以获取到cookie,因此有些不法分子可能会劫持你的cookie而上传到他的服务器上,让你的隐私曝光!我们在编写代码时,尽量不要让cookie曝光,服务端可以设置cookie:httpOnly,来保证用户信息隐私。

history

history代表浏览器的历史记录。(不建议使用)

1
2
history.back()	// 后退
history.forward() // 前进

8. 操作DOM对象(重点)

核心

一个网页就是一个DOM树形结构。DOM节点可有的操作:

  • 更新:更新DOM节点
  • 遍历:得到DOM节点
  • 删除:删除一个DOM节点
  • 添加:添加一个DOM节点

要操作一个DOM节点,就必须先获得这个DOM节点。

8.1 获得DOM节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body id="father">
<h1>标题1</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>

<script type="text/javascript">
// 对应CSS选择器
var h1 = document.getElementsByTagName('h1');
var p1 = document.getElementById('p1');
var p2 = document.getElementsByClassName('p2');
var father = document.getElementById('father');

var children = father.children; // 获取父节点下的所有子节点
// father.firstChild
// father.lastChild
</script>
</body>

8.2 更新节点

1
2
3
4
5
6
7
8
9
10
<body>
<div id="id1">

</div>
<script type="text/javascript">
var id1 = document.getElementById('id1');
id1.innerText = "hello!";
id1.innerHTML = '<strong>world</strong>';
</script>
</body>

id1.innerText = "hello!"; 修改文本的值

id1.innerHTML = '<strong>world</strong>'; 解析HTML文本标签

紧接上述代码,我们使用JS操作CSS:

1
2
3
id1.style.color = 'red';
id1.style.fontSize = '30px';
id1.style.padding = '2em';

这样,我们页面上的“world”字样呈现红色,字号为30px,内边距为2em。

8.3 删除节点

步骤:

  1. 获取父节点
  2. 再通过父节点删除自己
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body id="father">
<h1>标题1</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>

<script type="text/javascript">
var p1 = document.getElementById('p1');
var father = p1.parentElement
father.removeChild(p1); // 删除p1节点

/* 错误操作:删除是一个动态的过程
father.removeChild(father.children[0]);
father.removeChild(father.children[1]);
father.removeChild(father.children[2]);
*/
</script>
</body>

注意: 删除多个节点的时候,children是在时刻变化的,因此在删除时需要时刻注意!

8.4 插入节点

我们获得了某个界面,假设这个DOM节点时空的,我们可以通过innerHTML给这个节点增加元素,但是如果这个节点内有其他元素,使用innerHTML增加元素会覆盖原来的元素。这并不是我们想要的!😟

因此我们其他方式来添加(追加)节点!

移动已存在的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<p id="js">JS</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="ME">JavaME</p>
</div>

<script type="text/javascript">
var js = document.getElementById('js'); // 已存在的节点
var list = document.getElementById('list');
list.appendChild(js);
</script>
</body>

效果:

移动已存在的节点

创建一个新的节点,实现插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<p id="js">JS</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="ME">JavaME</p>
</div>

<script type="text/javascript">
var js = document.getElementById('js');
var list = document.getElementById('list');
// list.appendChild(js);
// 通过js创建一个新的节点
var newP = document.createElement('p');
newP.id = 'newP';
newP.innerText = 'mofan';
list.appendChild(newP);
// 创建一个标签节点
var myScript = document.createElement('script');
myScript.setAttribute('type','text/javascript');
</script>
</body>

结果:

插入创建的节点

insertBefore

insertBefore()方法可在已有的子节点前插入一个新的子节点。

提示: 如果你想创建一个新的文本列表项,在 LI 元素后你应该添加元素的文本节点,然后在列表中添加 LI元素。

你也可以使用 insertBefore() 方法来 插入/移除 已存在的元素。

语法:

node.insertBefore(newnode,existingnode)

newnode:要插入的节点对象;existingnode:要添加新的节点前的子节点

1
2
3
4
5
var ee = document.getElementById('ee');
var js = document.getElementById('js');
var list = document.getElementById('list');
// 要包含的节点.insertBefore(newNode,existingnode)
list.insertBefore(js,ee);

结果:

insertBefore

9. 操作表单(表单验证)

表单内可以包含:

  • 文本框 text
  • 下拉框 <select>
  • 单选框 radio
  • 多选框 checkbox
  • 隐藏域 hidden
  • 密码框 password

获得填写的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<form action="#" method="post">
<p><span>用户名:</span><input type="text" id="username"></p>
<!-- 多选框的值就是定义好的value值 -->
<p>
<span>性别:</span>
<input type="radio" name="gender" value="boy" id="boy"/>
<input type="radio" name="gender" value="girl" id="girl" checked />
</p>
</form>

<script type="text/javascript">
// 得到输入框的值
var input_text = document.getElementById('username');
var boy_radio = document.getElementById('boy');
var girl_radio = document.getElementById('girl');
console.log(input_text.value);
// 修改输入框的值
input_text.value = '123';
console.log(input_text.value);
// 对于单选框、多选框等固定的值,value只能取到标签内的value属性值
// 查看是否被选中,选中返回1,未选中返回0
console.log(boy_radio.checked);
boy_radio.checked = true; // 设置被选中
console.log(boy_radio.checked);
</script>
</body>

提交表单

表单优化并使用MD5加密密码!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>提交表单并加密</title>
<script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
</head>
<body>
<!--
表单绑定提交事件
onsubmit = 绑定一个提交检测的函数,返回true或false
将这个结果返回给表单,使用onsubmit接收!
onsubmit="return check();"
-->
<form action="https://www.baidu.com/" method="post" onsubmit="return check();">
<p><span>用户名:</span><input type="text" id="username" name="username"></p>
<!-- 多选框的值就是定义好的value值 -->
<p><span>密码:</span><input type="password" id="input-password"></p>
<input type="hidden" name="password" id="md5-password"/>
<!-- <button type="button" onclick="check()">提交</button> -->
<button type="submit">提交</button>
</form>

<script type="text/javascript">
function check(){
var username = document.getElementById('username');
var pwd = document.getElementById('input-password');
var md5Pwd = document.getElementById('md5-password');
console.log(username.value);
md5Pwd.value = md5(pwd.value);
console.log(md5Pwd.value);
// 校验表单内容,true就是校验成功并提交,false阻止提交
return true;
}
</script>
</body>
</html>

10. jQuery

jQuery就是一个封装了大量的JS函数的库。

先上个中文文档:jQuery API 中文文档 😏

引入jQuery:

  • 去官网下载 jQuery
  • 使用CDN
1
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>

使用公式:$(selector).action()

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<a href="" id="test-jQuery">点我弹窗</a>
<script type="text/javascript">
$('#test-jQuery').click(function(){
alert('hello,jQuery!');
});
</script>
</body>
</html>

10.1 jQuery选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
/* 原生JS选择器,选择器少,而且臃肿 */
// 标签
document.getElementsByTagName();
// id
document.getElementById();
// 类
document.getElementsByClassName();
/* jQuery选择器,选择器多且简洁,CSS中的选择器都可在jQuery中使用 */
// 标签
$('p').click();
// id
$('#id1').click();
// 类
$('.class1').click();
</script>

再放一遍中文参考文档网址:jQuery API 中文文档 😉

10.2 jQuery 事件

事件一般分为:鼠标事件、键盘事件、其他事件

事件的响应函数可以去文档中查看(对!又是它!): jQuery API 中文文档 😝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<style type="text/css">
#divMove{
width: 500px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<!-- 获取鼠标当前坐标 -->
mouse:<span id="mouseMove"></span>
<div id="divMove">
在这移动试试
</div>
<script type="text/javascript">
/* 当网页元素加载完毕之后,响应事件 */
$(function(){
$('#divMove').mousemove(function(e){
$('#mouseMove').text('x:'+e.pageX+'y:'+e.pageY);
})
})
</script>
</body>
</html>

10.3 jQuery 操作DOM

节点文本操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<ul id="test-ui">
<li class="js">JS</li>
<li name="java">Java</li>
</ul>

<script type="text/javascript">
// document.getElementById('')
// 获得值
$('#test-ui li[name=java]').text();
// 重载值
$('#test-ui li[name=java]').text('123456');
// 获得值
$('#test-ui').html();
// 重载值
$('#test-ui').html('<u>987654</u>');
</script>
</body>

css的操作:

1
$('#test-ui li[name=java]').css("color","red");

元素的显示和隐藏: 本质依然是 display: none

1
2
$('#test-ui li[name=java]').show();
$('#test-ui li[name=java]').hide();

其他测试:

1
2
3
$(window).width()
$(window).height()
...

还有ajax,参考Spring MVC

其余内容参考文档,最后一次再放出链接:jQuery API 中文文档 😆