前端之JavaScript

JavaScript基础

1、JavaScript是什么

JavaScript(下文我们会用简称JS来代替)是脚本编程语言,JS语言开发的文件是以.js为后缀,通过在html文件中引入该js文件来控制html代码的交互功能以及前台数据处理的业务逻辑(js语言代码也可以直接写在html文件中),采用的ECMAScript语法,属于编程语言。

ECMAScript目前普遍使用的存在ES5与ES6两个版本,我们也会基于这两个版本来介绍JS这么应用的学习

2、JavaScript如何学习

学习方向:从JS代码书写位置、JS基础语法、JS选择器和JS页面操作四部分进行学习

学习目的:完成页面标签与用户的人机交互及前台数据处理的业务逻辑

3、JS代码书写位置

JS属于脚本(可以以代码片段的方式内嵌到其他语言中)编程语言,可以内嵌到html代码中,但是可以根据实际情况书写在不同的位置,放在不同位置有不同的专业叫法,可以分为行间式、内联式和外联式三种。

3-1 行间式

JS代码书写在标签的事件全局属性中,采用的是JS编程语言的语法

1
2
3
<!-- 关键代码 -->
<!-- 给div标签添加点击事件的交互逻辑:弹出文本提示框 -->
<div onclick="alert('点击我完成页面交互')">点我</div>

3-2 内联式

JS代码书写在script标签中,script标签可以出现在页面中的任意位置,建议放在body标签的最后(html代码是自上而下进行解析加载,放在body标签的最下方,会保证页面所有标签都加载完毕,html再去加载js文件,那么js脚步文件就会更好的控制页面标签的人机交互了),采用的是JS编程语言的语法

1
2
3
4
5
6
7
8
9
10
<!-- 关键代码 -->
<!-- 页面被加载打开时,就会触发事件的交互逻辑:弹出文本提示框 -->
<body>
<!-- body标签中的所有子标签位置 -->

<!-- script标签出现在body标签的最下方 -->
<script>
alert('该页面被加载!')
</script>
</body>

3-3 外联式

JS代码书在外部js文件中,在html页面中用script标签引入js文件(建议在body标签最下方引入,理由同上)

  • js文件夹下的my.js
1
alert('外联式js文件弹出框')
  • 根目录下的first.html
1
2
3
4
5
6
7
8
9
10
<!-- 关键代码 -->
<!-- 页面被加载打开时,就会触发事件的交互逻辑:弹出文本提示框 -->
<body>
<!-- body标签中的所有子标签位置 -->

<!-- script标签出现在body标签的最下方 -->
<script src="js/my.js">
/* 不要在此写JS代码,原因是用来引入外部js文件的script标签,标签内部书写的JS代码不在起作用 */
</script>
</body>
  • 根目录下的second.html
1
2
3
4
5
6
7
8
9
10
<!-- 关键代码 -->
<!-- 页面被加载打开时,就会触发事件的交互逻辑:弹出文本提示框 -->
<body>
<!-- body标签中的所有子标签位置 -->

<!-- script标签出现在body标签的最下方 -->
<script src="js/my.js">
/* 不要在此写JS代码,原因是用来引入外部js文件的script标签,标签内部书写的JS代码不在起作用 */
</script>
</body>

4、总结

行间式控制交互最直接,但是交互逻辑多了直接导致页面可读性变差,且交互逻辑相同的标签样式也需要各自设置,复用性差,不建议使用;

内联式可以同时为多个标签提供交互逻辑(课程后面会详细介绍),学习阶段代码量不大的情况下,也不需要分文件处理的,这时候建议使用内联式;

外联式是分文件管理不同的页面存在的相同与不同的数据处理的业务逻辑与人机交互,可以极大提高开发效率,项目开发时一定要采用外联式来处理JS代码。

通过上面的介绍,大家很清楚JS是一门脚本编程语言,那么我们一定先要了解一下这门编程语言的基础语法,才可以慢慢的展开学。

JavaScript基础数据类型

1、变量的定义

JS中定义变量,不同于Python,我们需要像Python定义函数那样,也需要用特定的关键词来定义变量:

ES5语法,我们采用var关键词定义变量,并且没有常量的概念

ES6语法,我们采用let关键词定义变量,用const关键词定义常量

注:我们不需要像Python那样切换解释器版本来区别到底该书写什么版本语法的代码,在一个JS文件中我们可以同时书写两种语法的JS代码,浏览器都可以自动帮我们解析并运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ES5定义变量:var是关键词、num是变量名、=为赋值符、10为变量值、;为JS语言语句的结束标识符
var num = 10;

// ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
let count = 100;
let [a, b, c] = [1, 2, 3];

// const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.
// const的作用域与let命令相同:只在声明所在的块级作用域内有效。

2、变量的命名规范

1
2
3
4
5
/*
1. 由字母,数字,_,$组成,不能以数字开头(可以包含中文字符)
2. 区分大小写
3. 不能出现关键字及保留字
*/
abstractargumentsbooleanbreakbyte
casecatchcharclass*const
continuedebuggerdefaultdeletedo
doubleelseenum*evalexport*
extends*falsefinalfinallyfloat
forfunctiongotoifimplements
import*ininstanceofintinterface
letlongnativenewnull
packageprivateprotectedpublicreturn
shortstaticsuper*switchsynchronized
thisthrowthrowstransienttrue
trytypeofvarvoidvolatile
whilewithyield

3、基本数据类型

JS语言中基本数据类型分值类型与引用类型两部分。

3-1 值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 数字类型:number
var num = 10;
const PI = 3.14;
console.log(typeof(num), num); // 结果:number 10

// 字符串类型:string
var s1 = "双引号可以表示字符串";
var s2 = '单引号可以表示字符串';
console.log(typeof(s1), s1); // 结果:string 双引号可以表示字符串

// 布尔类型:boolean
var b1 = true;
var b2 = false;
console.log(typeof(b1), b1); // 结果:boolean true

// 未定义类型:undefined
var u1;
var u2 = undefined;
console.log(typeof(u1), u1); // 结果:undefined undefined

字符串常用方法:拼接字符串一般使用“+”

方法说明
.length返回长度
.trim()移除空白
.trimLeft()移除左边的空白
.trimRight()移除右边的空白
.charAt(n)返回第n个字符
.concat(value, …)拼接
.indexOf(substring, start)子序列位置
.substring(from, to)根据索引获取子序列
.slice(start, end)切片
.toLowerCase()小写
.toUpperCase()大写
.split(delimiter, limit)分割

3-2 引用类型

1
2
3
4
5
6
7
8
9
10
11
12
// 函数类型:function
function fn1() {}
var fn2 = function() {};
console.log(typeof(fn1), fn1); // 结果:function ƒ fn1() {}

// 对象类型:object
var obj1 = {}
console.log(typeof(obj1), obj1); // 结果:function {}

// Array对象类型:
var arr1 = [1, 2, 3, 4, 5]
console.log(typeof(arr1), arr1); // 结果:object (5) [1, 2, 3, 4, 5]

4、运算符

4-1 算数运算符

前提:n = 5

运算符描述例子x结果n结果
+加法x=n+275
-减法x=n-235
*乘法x=n*2105
/除法x=n/22.55
%取模(余数)x=n/215
++自增x=++n66
x=n++56
自减x=–n44
x=n–54

4-2 赋值运算符

前提:x=5,y=5

运算符例子等同于运算结果
=x=y5
+=x+=yx=x+y10
-=x-=yx=x-y0
*=x*=yx=x*y25
/=x/=yx=x/y1
%=x%=yx=x%y0

4-3 比较运算符

前提:x=5

运算符描述比较结果
==等于x==“5”true
===绝对等于x===“5”false
!=不等于x!=“5”fales
!==不绝对等于x!==“5”true
>大于x>5false
<小于x<5false
>=大于等于x>=5true
<=小于等于x<=5true

4-4 逻辑运算符

前提:n=5

运算符描述例子结果
&&x=n>10&&++nx=false,n=5(短路)
||x=n<10||n–x=true,n=5(短路)
!x=!nx=false,x=5

4-5 三目运算符

1
2
3
4
5
6
7
// 结果 = 条件表达式 ? 结果1 : 结果2;
// 语法规则:条件表达式成立,将结果1赋值给结果,反正赋值结果2

// 案例:
var weather = prompt("天气(晴|雨)"); // 文本输入弹出框
var res = weather == '晴' ? "今天天气挺好" : "请假回家收衣服";
console.log(res); // 今天天气挺好

流程控制

if-else

1
2
3
4
5
6
var a = 10;
if (a > 5){
console.log("yes");
}else {
console.log("no");
}

if-else if-else

1
2
3
4
5
6
7
8
var a = 10;
if (a > 5){
console.log("a > 5");
}else if (a < 5) {
console.log("a < 5");
}else {
console.log("a = 5");
}

switch

1
2
3
4
5
6
7
8
9
10
11
var day = new Date().getDay();
switch (day) {
case 0:
console.log("Sunday");
break;
case 1:
console.log("Monday");
break;
default:
console.log("...")
}

switch中的case子句通常都会加break语句,否则程序会继续执行后续case中的语句。

for

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

while

1
2
3
4
5
var i = 0;
while (i < 10) {
console.log(i);
i++;
}

三元运算

1
2
3
var a = 1;
var b = 2;
var c = a > b ? a : b

函数

函数定义

JavaScript中的函数和Python中的非常类似,只是定义方式有点区别。

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
// 普通函数定义
function f1() {
console.log("Hello world!");
}

// 带参数的函数
function f2(a, b) {
console.log(arguments); // 内置的arguments对象
console.log(arguments.length);
console.log(a, b);
}

// 带返回值的函数
function sum(a, b){
return a + b;
}
sum(1, 2); // 调用函数

// 匿名函数方式
var sum = function(a, b){
return a + b;
}
sum(1, 2);

// 立即执行函数
(function(a, b){
return a + b;
})(1, 2);

补充:

ES6中允许使用“箭头”(=>)定义函数。

1
2
3
4
5
var f = v => v;
// 等同于
var f = function(v){
return v;
}

如果箭头函数不需要参数或需要多个参数,就是用圆括号代表参数部分:

1
2
3
4
5
6
7
8
9
var f = () => 5;
// 等同于
var f = function(){return 5};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2){
return num1 + num2;
}

函数中的arguments参数

1
2
3
4
5
6
function add(a,b){
console.log(a+b);
console.log(arguments.length)
}

add(1,2)

输出:

1
2
3
2

注意:

函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。

函数的全局变量和局部变量

局部变量

在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。

全局变量:

在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。

变量生存周期:

JavaScript变量的生命期从它们被声明的时间开始。

局部变量会在函数运行以后被删除。

全局变量会在页面关闭后被删除。

作用域

首先在函数内部查找变量,找不到则到外层函数查找,逐步找到最外层。

几个例子:

  1. eg
1
2
3
4
5
6
7
8
9
10
11
var city = "BeiJing";
function f() {
var city = "ShangHai";
function inner(){
var city = "ShenZhen";
console.log(city);
}
inner();
}

f(); //输出结果是?
  1. eg
1
2
3
4
5
6
7
8
9
10
var city = "BeiJing";
function Bar() {
console.log(city);
}
function f() {
var city = "ShangHai";
return Bar;
}
var ret = f();
ret(); // 打印结果是?

3.闭包

1
2
3
4
5
6
7
8
9
10
var city = "BeiJing";
function f(){
var city = "ShangHai";
function inner(){
console.log(city);
}
return inner;
}
var ret = f();
ret();

词法分析(尝试理解)

JavaScript中在调用函数的那一瞬间,会先进行词法分析。

词法分析的过程:

当函数调用的前一瞬间,会先形成一个激活对象:Avtive Object(AO),并会分析以下3个方面:

1:函数参数,如果有,则将此参数赋值给AO,且值为undefined。如果没有,则不做任何操作。
2:函数局部变量,如果AO上有同名的值,则不做任何操作。如果没有,则将此变量赋值给AO,并且值为undefined。
3:函数声明,如果AO上有,则会将AO上的对象覆盖。如果没有,则不做任何操作。

函数内部无论是使用参数还是使用局部变量都到AO上找。

看两个例子:

1
2
3
4
5
6
7
var age = 18;
function foo(){
console.log(age);
var age = 22;
console.log(age);
}
foo(); // 问:执行foo()之后的结果是?

第二题:

1
2
3
4
5
6
7
8
9
10
11
var age = 18;
function foo(){
console.log(age);
var age = 22;
console.log(age);
function age(){
console.log("呵呵");
}
console.log(age);
}
foo(); // 执行后的结果是?
1
2
3
4
5
6
7
8
9
10
11
12
13
词法分析过程:
1、分析参数,有一个参数,形成一个 AO.age=undefine;
2、分析变量声明,有一个 var age, 发现 AO 上面已经有一个 AO.age,因此不做任何处理
3、分析函数声明,有一个 function age(){...} 声明, 则把原有的 age 覆盖成 AO.age=function(){...};

最终,AO上的属性只有一个age,并且值为一个函数声明

执行过程:
注意:执行过程中所有的值都是从AO对象上去寻找

1、执行第一个 console.log(age) 时,此时的 AO.age 是一个函数,所以第一个输出的一个函数
2、这句 var age=22; 是对 AO.age 的属性赋值, 此时AO.age=22 ,所以在第二个输出的是 2
3、同理第三个输出的还是22, 因为中间再没有改变age值的语句了

内置对象和方法

JavaScript中的所有事物都是对象:字符串、数字、数组、日期,等等。在JavaScript中,对象是拥有属性和方法的数据。

我们在学习基本数据类型的时候已经带大家了解了,JavaScript中的Number对象、String对象、Array对象等。

注意var s1 = "abc"和var s2 = new String(“abc”)的区别:typeof s1 --> string而 typeof s2 --> Object

img

自定义对象

JavaScript的对象(Object)本质上是键值对的集合(Hash结构),但是只能用字符串作为键。

1
2
3
var a = {"name": "Alex", "age": 18};
console.log(a.name);
console.log(a["age"]);

遍历对象中的内容:

1
2
3
4
var a = {"name": "Alex", "age": 18};
for (var i in a){
console.log(i, a[i]);
}

事情并没有那么简单…

创建对象:

1
2
3
var person=new Object();  // 创建一个person对象
person.name="Alex"; // person对象的name属性
person.age=18; // person对象的age属性

注意:

ES6中提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。

也就是说,Object结构提供了“字符串–值”的对应,Map结构提供了“值–值”的对应,是一种更完善的Hash结构实现。

1
2
3
4
5
6
7
8
9
var m = new Map();
var o = {p: "Hello World"}

m.set(o, "content"}
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 父类构造函数
var Car = function (loc) {
this.loc = loc;
};

// 父类方法
Car.prototype.move = function () {
this.loc ++;
};

// 子类构造函数
var Van = function (loc) {
Car.call(this, loc);
};

// 继承父类的方法
Van.prototype = Object.create(Car.prototype);
// 修复 constructor
Van.prototype.constructor = Van;
// 扩展方法
Van.prototype.grab = function () {
/* ... */
};

Date对象

创建Date对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//方法1:不指定参数
var d1 = new Date();
console.log(d1.toLocaleString());
//方法2:参数为日期字符串
var d2 = new Date("2004/3/20 11:12");
console.log(d2.toLocaleString());
var d3 = new Date("04/03/20 11:12");
console.log(d3.toLocaleString());
//方法3:参数为毫秒数
var d3 = new Date(5000);
console.log(d3.toLocaleString());
console.log(d3.toUTCString());

//方法4:参数为年月日小时分钟秒毫秒
var d4 = new Date(2004,2,20,11,12,0,300);
console.log(d4.toLocaleString()); //毫秒并不直接显示

Date对象的方法:

1
2
3
4
5
6
7
8
9
10
var d = new Date(); 
//getDate() 获取日
//getDay () 获取星期
//getMonth () 获取月(0-11)
//getFullYear () 获取完整年份
//getHours () 获取小时
//getMinutes () 获取分钟
//getSeconds () 获取秒
//getMilliseconds () 获取毫秒
//getTime () 返回累计毫秒数(从1970/1/1午夜)

练习:

编写代码,将当前日期按“2017-12-27 11:11 星期三”格式输出。

详细Date对象方法:点我

JSON对象

1
2
3
4
5
6
var str1 = '{"name": "Alex", "age": 18}';
var obj1 = {"name": "Alex", "age": 18};
// JSON字符串转换成对象
var obj = JSON.parse(str1);
// 对象转换成JSON字符串
var str = JSON.stringify(obj1);

RegExp对象

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//RegExp对象

//创建正则对象方式1
// 参数1 正则表达式(不能有空格)
// 参数2 匹配模式:常用g(全局匹配;找到所有匹配,而不是在第一个匹配后停止)和i(忽略大小写)

// 用户名只能是英文字母、数字和_,并且首字母必须是英文字母。长度最短不能少于6位 最长不能超过12位。

// 创建RegExp对象方式(逗号后面不要加空格)
var reg1 = new RegExp("^[a-zA-Z][a-zA-Z0-9_]{5,11}$");

// 匹配响应的字符串
var s1 = "bc123";

//RegExp对象的test方法,测试一个字符串是否符合对应的正则规则,返回值是true或false。
reg1.test(s1); // true

// 创建方式2
// /填写正则表达式/匹配模式(逗号后面不要加空格)
var reg2 = /^[a-zA-Z][a-zA-Z0-9_]{5,11}$/;

reg2.test(s1); // true


// String对象与正则结合的4个方法
var s2 = "hello world";

s2.match(/o/g); // ["o", "o"] 查找字符串中 符合正则 的内容
s2.search(/h/g); // 0 查找字符串中符合正则表达式的内容位置
s2.split(/o/g); // ["hell", " w", "rld"] 按照正则表达式对字符串进行切割
s2.replace(/o/g, "s"); // "hells wsrld" 对字符串按照正则进行替换

// 关于匹配模式:g和i的简单示例
var s1 = "name:Alex age:18";

s1.replace(/a/, "哈哈哈"); // "n哈哈哈me:Alex age:18"
s1.replace(/a/g, "哈哈哈"); // "n哈哈哈me:Alex 哈哈哈ge:18" 全局匹配
s1.replace(/a/gi, "哈哈哈"); // "n哈哈哈me:哈哈哈lex 哈哈哈ge:18" 不区分大小写


// 注意事项1:
// 如果regExpObject带有全局标志g,test()函数不是从字符串的开头开始查找,而是从属性regExpObject.lastIndex所指定的索引处开始查找。
// 该属性值默认为0,所以第一次仍然是从字符串的开头查找。
// 当找到一个匹配时,test()函数会将regExpObject.lastIndex的值改为字符串中本次匹配内容的最后一个字符的下一个索引位置。
// 当再次执行test()函数时,将会从该索引位置处开始查找,从而找到下一个匹配。
// 因此,当我们使用test()函数执行了一次匹配之后,如果想要重新使用test()函数从头开始查找,则需要手动将regExpObject.lastIndex的值重置为 0。
// 如果test()函数再也找不到可以匹配的文本时,该函数会自动把regExpObject.lastIndex属性重置为 0。

var reg3 = /foo/g;
// 此时 regex.lastIndex=0
reg3.test('foo'); // 返回true
// 此时 regex.lastIndex=3
reg3.test('xxxfoo'); // 还是返回true
// 所以我们在使用test()方法校验一个字符串是否完全匹配时,一定要加上^和$符号。

// 注意事项2(说出来你可能不信系列):
// 当我们不加参数调用RegExpObj.test()方法时, 相当于执行RegExpObj.test("undefined"), 并且/undefined/.test()默认返回true。
var reg4 = /^undefined$/;
reg4.test(); // 返回true
reg4.test(undefined); // 返回true
reg4.test("undefined"); // 返回true

扩展阅读

Math对象

1
2
3
4
5
6
7
8
9
10
11
12
abs(x)      返回数的绝对值。
exp(x) 返回 e 的指数。
floor(x) 对数进行下舍入。
log(x) 返回数的自然对数(底为e)。
max(x,y) 返回 x 和 y 中的最高值。
min(x,y) 返回 x 和 y 中的最低值。
pow(x,y) 返回 x 的 y 次幂。
random() 返回 0 ~ 1 之间的随机数。
round(x) 把数四舍五入为最接近的整数。
sin(x) 返回数的正弦。
sqrt(x) 返回数的平方根。
tan(x) 返回角的正切。