说起来网站都搭好了,现在才开始系统地学一遍Javascript,确实抽象。只不过也无所谓了,毕竟HTML和CSS学得也是依托()。放一个学习链接。对于这个网站也插播一句,开源精神yyds。

简介

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。JavaScript 也是一种嵌入式语言,只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。

JS基础知识

  • HTML 定义了网页的内容
  • CSS 描述了网页的布局
  • JavaScript 网页的行为

对应人的骨架、外貌和动作,写一篇博客来专门学Javascript。

hello world

经典hello world,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<h1>我的第一个 Web 页面</h1>
<p>我的第一个段落。</p>
<script>
alert("hello wrold");
</script>
</body>
</html>

JavaScript 可以通过不同的方式来输出数据:

  • 使用 window.alert() 弹出警告框。
  • 使用 document.write() 方法将内容写到 HTML 文档中。
  • 使用 innerHTML 写入到 HTML 元素。
  • 使用 console.log() 写入到浏览器的控制台。

注释

类似于C++:

  • 单行://
  • 多行:/* ………… */

变量

使用关键字 var 来定义变量,用于存储数据,使用等号来为变量赋值。 eg: var a,length;
JavaScript有以下几种数据类型:

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
  • 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。

注意:JavaScript 对大小写敏感,所以不管是操纵DOM元素还是JS变量命名都需要注意大小写。可以用typeof 操作符来获取变量的类型。JavaScript 拥有动态类型。即相同的一个变量名可以在程序中有不同的类型(取决于对变量的赋值),如下例:

1
2
3
4
var x;               // x 为 undefined
var x = 5; // 现在 x 为数字
var x = "John"; // 现在 x 为字符串
console.log(typeof x);

Boolean 用法与C++一样,这里给一下数组的用法:

1
2
3
4
var cars=new Array();
cars[0]="Saab";
cars[1]="Volvo";
cars[2]="BMW";

或者:

1
var cars=new Array("Saab","Volvo","BMW");

或者:

1
var cars=["Saab","Volvo","BMW"];

这里不需要指定数组的长度和大小,直接赋值即可。JS变量声明如果没有赋初值,则默认为Undefined类型,也可以声明时指定类型:

1
var carname=new String; /* 指定为string */

JS对象

JS对象由花括号分隔。在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义。键值对在 JavaScript 对象通常称为对象属性,属性之间由逗号分隔。对象的方法定义了一个函数,并作为对象的属性存储,对象方法通过添加 () 调用 (作为一个函数)。对象方法的语法格式如下:

1
2
3
4
methodName : function() {
// 代码
}
object.methodName();//调用方式

下面是一个对象的示例:

1
2
3
4
5
6
7
8
9
10
11
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function()
{
return this.firstName + " " + this.lastName;
}
};
console.log(typeof person["fullName"]);// return "funciton"
console.log(typeof person.fullName());// return "string"

对象的寻址方式:

1
2
name=person.lastname;
name=person["lastname"];

简单说,JavaScript对象是属性和方法的容器,是一个键值对集合,类似于Pytho中的字典。对象是可变的,它们是通过引用来传递的。以下实例的person对象不会创建副本:

1
2
3
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
var x = person;
x.age = 10; // x.age 和 person.age 都会改变

字面量

一个字面量是一个常量,eg: 3.14、”helloworld”

  • 数字字面量:可以是整数或者是小数,或者是科学计数(e)。eg:3.14、2002、1023e5
  • 字符串字面量:可以是单引号,也可以是双引号。eg:”Tom”、’Jack’
  • 数组字面量:[100,200,300]
  • 表达式字面量:5*6 4/2+3
  • 对象字面量:{firstName:”John”, lastName:”Doe”, age:50, eyeColor:”blue”}
  • 函数字面量:function myFunction(a, b) { return a * b;}

操作符和运算符同C++,语句也是以 ; 结尾。JavaScript 标识符必须以字母、下划线(_)或美元符($)开始。这一点也与C++类似,只不过多了一个可以以$开始的特性。

函数

函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。语法定义:

1
2
3
function FunctionName(var1, var2,……) {
//FunctionCode
}

同时,函数也可以作为变量进行赋值,函数存储在变量中,不需要函数名称,通过变量名来调用。当然,JS函数也支持递归,支持缺省参数。

1
2
3
var x = function(a,b){return a* b};
var z = x(2,3);
console.log(z);//output 6

ES6新增了箭头函数。用法如下:

1
2
3
4
5
6
7
// ES5
var z = function(x, y) {
return x * y;
}
// ES6
//(参数1, 参数2, …, 参数N) => { 函数声明 }
const z = (x, y) => x * y;

关于函数参数,还有一种特殊用法,主要用于支持可变参数,用arguments[i]来表示传入函数的参数数组,如下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var findmax=function(){
if(arguments.length===0){
return;
}
var res=arguments[0];
if(arguments.length===1){
return res;
}
for(let i=0;i<arguments.length;i++){
if(res<arguments[i]){
res=arguments[i];
}
}
return res;
}
console.log(findmax(1,7,11,2));//output 11

而函数体对于传入参数的改变,如果是普通的值,则函数体不会影响该值;如果是传入的对象,则函数会产生影响,具体说就像C++里的普通传值和引用传值。示例如下:

1
2
3
4
5
6
7
8
9
function test(a,b){
a="changed";
b.age=18;
}
var x="origin";
var y={age:17,name:"liu"};
test(x,y);
console.log(x);//output "origin"
console.log(y);//output {age:18,name:liu}

在JS中, 函数也是对象。JS函数有它的属性和方法。call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

1
2
3
4
5
6
function myFunction(a,b){return a+b;}
var myArray = [3,5];
var myObject1 = myFunction.call(myObject1, 10, 2);
var myObject2 = myFunction.apply(myObject2,myArray);
console.log(myObject1);// output 12
console.log(myObject2);//output 8

关键字(不全)

跟C++大差不差

语句描述
break用于跳出循环。
catch语句块,在 try 语句块执行出错时执行 catch 语句块。
continue 跳过循环中的一个迭代。
do … while执行一个语句块,在条件语句为 true 时继续执行该语句块。
for在条件语句为 true 时,可以将代码块执行指定的次数。
for … in用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。
function定义一个函数
if … else用于基于不同的条件来执行不同的动作。
return退出函数
switch用于基于不同的条件来执行不同的动作。
throw抛出(生成)错误 。
try实现错误处理,与 catch 一同使用。
var声明一个变量。
while当条件语句为 true 时,执行语句块。

运算符

&、|、>>、&&、||、!、<、>之类的都与C++类似。但有两个不同的,还是因为Javascript变量的动态类型导致的。

  • == 数值相等,变量类型可以不相同
  • === 数值类型均相同

!== 和 !===与此类似。如下面的例子

1
2
console.log(5=="5");// return true
console.log(5==="5");// return false

分支控制

if等用法与C++类似,注意switch进行匹配的时候是===的严格匹配。下面是for……in 的用法,跟Python有一点像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var person={fname:"John",lname:"Doe",age:25};
var y=[1,2,3];
var txt="";
for (x in person){
txt=txt + person[x];
}
console.log(txt);
for( x in y){// x=0,1,2……
console.log(y[x]);
}
/* result:
JohnDoe25
1
2
3
*/

标签用法,直接看例子:

1
2
3
4
5
6
7
8
9
10
cars=["BMW","Volvo","Saab","Ford"];
list:{
document.write(cars[0] + "<br>");
document.write(cars[1] + "<br>");
document.write(cars[2] + "<br>");
break list;
document.write(cars[3] + "<br>");
document.write(cars[4] + "<br>");
document.write(cars[5] + "<br>");
}

break即可跳出标签所在的代码块,单独的break则用于跳出循环。

JSON(JavaScript Object Notation)

JSON 是用于存储和传输数据的格式,通常用于服务端向网页传递数据。比如发送端可以把一个JS对象转为JSON,之后传输给接收端,接收端再把JSON转为JS对象。JSON示例如下:

1
2
3
4
5
6
7
8
var person={"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}],
count:3
}
var s = JSON.stringify(person);// js对象 => json
var t = JSON.parse(s); // json => js对象

JSON就是键值对的集合,上面的键即为 employees,值又是一个数组,数组中包含三个JS对象。JSON数据的具体规则:

  • 数据为键值对。
  • 数据由逗号分隔。
  • 大括号保存对象。
  • 方括号保存数组。

一些特殊关键字

const

const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改,跟C++一样的。

1
2
const test=2;
test=3;//error!

但对于声明的常量对象,又可以对每个属性进行赋值,但不能整体重新赋值,例子:

1
2
3
4
5
6
const car = {type:"Fiat", model:"500", color:"white"};
car.color = "red";
car.owner = "Johnson";
console.log(car);
//output { type: 'Fiat', model: '500', color: 'red', owner: 'Johnson' }
car = {type:"Volvo", model:"EX60", color:"red"}; // error!

null

在 JavaScript 中 null 表示 “什么都没有”。null是一个只有一个值的特殊类型。表示一个空对象引用。

1
2
3
4
5
6
7
var num1=1,num2=2;
num1=null;// 值为null,类型为object(历史遗留问题)
num2=undefined;//值和类型均为undefined
console.log(typeof num1);
console.log(typeof num2);
console.log(num1 ==num2);//true
console.log(num1 ===num2);//false

this

JavaScript this 关键字用法:

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。

let vs var

简单说,var关键字支持函数作用域和全局作用域,但没有块作用域这个概念在里边。比如下面的代码:

1
2
3
let i=9;
for(var i=0;i<5;i++){}
console.log(i);// 输出为5

可以看到,在循环外仍然能够访问循环中的初值。var 关键字声明的变量要么是全局作用域的,要么是函数作用域的,这意味着变量要么在整个程序中可见,要么在整个函数体内可见,即使它们是在诸如 if 语句或 for 循环这样的块中声明的。而let则支持函数作用域、块作用域和全局作用域,比如下面的代码:

1
2
for(let i=0;i<5;i++){}
console.log(i);// Error!

这种方式就跟C比较类似了,循环结束后无法再访问循环中的变量。

正则表达式

跟编译原理里的正则表达式差不多一个意思,使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式,搜索模式可用于文本搜索和文本替换。基本思想即是用有限的字符串(一个)去表示无限的若干个字符串。例子:

1
2
3
var patt = /runoob/i;
// runoob 是一个正则表达式主体 (用于检索)。
// i 是一个修饰符 (搜索不区分大小写)。

在 JavaScript 中,正则表达式通常用于两个字符串方法

  • search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置.
  • replace() 方法用于在字符串中用一些字符串替换另一些字符串,或替换一个与正则表达式匹配的子串。

用法举例:

1
2
3
4
var pattern = /nankai/i;
var target = "Nankai-University";
console.log(target.search(pattern));//output 0
console.log(target.replace(pattern,"Tianjin"));//output TJU

JS异步编程

之前一直没太明白什么是同步I/O,什么是异步I/O。借此机会学习了一下。在同步I/O模型中,一个I/O请求发出后,发起请求的程序要等I/O操作完成才能继续执行后续代码。这意味着在I/O操作进行时,程序将被阻塞,无法执行其他任务。同步I/O的特点是编程简单,逻辑容易理解和实现,但它可能导致程序效率低下;而异步I/O模型允许程序在发起I/O请求后立即继续执行后续代码,不需要等待I/O操作完成。在I/O操作完成后,会通过某种机制(如回调函数、事件、Promise等)通知程序。这意味着程序可以在等待I/O操作完成的同时执行其他任务,从而提高了程序的效率和响应性。如果用过Socket编程就知道,一般情况下的Receive函数就是一种同步I/O,当然也可以设置成非阻塞的,Receive立即返回,但程序就需要不断地轮询Socket,检查是否有数据可读。之前写计网实验的时候,用这种方式近似实现了“定时器”的效果,即轮询while(true)给定的定时时间。而异步I/O则是完成后再“通知”正在运行的程序。事件驱动机制:代码的执行由事件(如用户交互、I/O操作完成等)触发的,而不是顺序执行。在Web服务中,这种模型非常适合处理网络请求,因为服务器可以立即响应各种事件。

JavaScript的执行环境是单线程的,这意味着同一时刻只能执行一个任务。为了不阻塞这个单线程,I/O操作(如数据读写、网络请求)被设计成异步的。当这些操作被发起时,JavaScript代码可以继续执行,而I/O操作在后台进行。当异步I/O操作完成时,相应的回调函数被放入事件队列,等待JavaScript引擎执行。当所有当前的同步任务执行完毕后,再来检查异步任务是否完成,若完成则调用回调函数。如下面的例子:

1
2
3
4
function print() {
console.log("setTimeout操作已结束");
}
setTimeout(print, 3000);//sleep 3s,完成后调用回调函数print

异步编程的例子:

1
2
3
4
5
6
7
setTimeout(function () {
console.log("setTimeout操作已结束");
}, 3000);
console.log(1+2);//先打印
/* output:
3
setTimeout操作已结束 */

JS类

类定义语法,基本思想和C++差不太多。注意JS中采用垃圾回收机制来自动管理内存,所以类中只有构造函数,没有析构函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//class ClassName {
// constructor() { ... }//构造函数
//}
class person{
constructor(name,age){
this.name=name;
this.age=age;
}
output(){
console.log("名字为"+this.name+",今年"+this.age+"岁了");
}
}
var Jack = new person("Jack",18);
Jack.output();

类相关的关键字:

  • extends:继承一个类;
  • static:在类中定义一个静态方法;
  • super:调用父类的构造方法

具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal {
constructor(name) {
this.name = name;
}

eat() {
console.log(this.name + " is eating.");
}
}

class Dog extends Animal {//继承于Animal类
constructor(name, breed) {
super(name);// 调用父类的构造方法
this.breed = breed;
}

bark() {
console.log(this.name + " is barking.");
}
}

const dog = new Dog("Buddy", "Labrador");
dog.eat();
dog.bark();

静态方法即为属于整个类而非某个具体对象的方法,通过className.staticMethod()的方式调用。