本文只是教程的部分摘录笔记,如想系统学习JavaScript,请访问现代JavaScript教程
0 JavaScript代码的插入
使用JavaScript代码有两种形式
一种是在<script>和</script>标签之间直接写入JavaScript代码。
<script>
//代码部分
</script>
另一种是将JavaScript代码写入.js文件,再通过src属性导入。
<script src="test.js"></script>
注意:两种方法不能同时使用!
一般多使用第二种方法导入JavaScript代码。这样浏览器会将JavaScript代码文件下载到缓存中,再次使用该代码文件时浏览器会直接从缓存中调用,极大提高了加载速度。
1 语句
JavaScript语句和C语言类似,“ ; ”标志着语句的结束。
alert("Hello World!"); // alert语句可以在浏览器中弹出提示框,重点是;结尾
2 注释
JavaScript的注释也和C语言类似,使用" // "作为一行的注释,使用" /* "和" */ "组合来进行跨行多段注释。
alert("test") // 这是一行注释
/*
这是大块注释的第一行
这是第二行
第三行
*/
在大多数编辑器中,选中一行代码按下“Ctrl + /”可以进行快速行注释。按下“Ctrl + Shift + /”可以进行快速多段代码注释。
3 现代模式
由于JavaScript的不断迭代更新,许多旧的特性或被修改,或被取消。为了保证兼容性,JavaScript默认不启用这些的修改。但使用“use strict”语句可以强制启用这些修改。
“use strict”;
“use strict”语句必须放在整个文件的最顶端,这样才能启用现代模式。
4 变量
JavaScript的变量声明和C语言类似,但也有些许不同。JavaScript使用“ let "来声明变量,但它不需要像C语言一样准确的声明诸如” int “, " char "等变量类型。
let message; // 语句同样使用;结尾
message = "test"; // 给变量赋值
alert(message); // 还记得alert的作用吗?
在查看其他程序员的代码时,可能会遇见使用“ var ”来声明变量。这是比较老派的做法,目前推荐使用let来声明变量。
变量命名规则同C语言,这里不再赘述。
使用" const "可以声明常量,其值不能改变,在代码中适当的使用可以增强程序的可读性,
const number = 1;
5 数据类型
JavaScript中有8中数据类型。
5.0 Number类型
Number类型代表整数和浮点数。
Infinity代表数学中的无穷大。
NaN代表错误的计算结果。
alert("This is not a number" / 2) // 返回NaN
BigInt代表大整数,即大于(253-1)
(即 9007199254740991
),或小于 -(253-1)
的整数。
5.1 String类型
即字符串类型,其有三种表示
单引号表示:'String'
双引号表示:"String"
反引号表示:`String`
其中,反引号允许在字符串中引入变量:
const name = "LiMing"; // const定义常量
let test; // let定义变量
test = `My name is ${name}`; // 在字符串中引入name
alert(test); // 弹出提示框测试
如果嵌入的是表达式,则会先计算表达式的值再嵌入:
let test; // 声明变量
test = `1 + 2 = ${1 + 2}`; // 嵌入表达式
alert(test); // 弹出测试框测试
值得注意的是,只有反引号才允许引入变量,在单引号和双引号中引入变量均会报错。
5.2 Boolean类型(逻辑类型)
即" true "和" False ",这里不再赘述。
5.3 Null值
Null是一个空值,它代表什么都没有。
let age = null; // 代表age是未知的
5.4 undefined值
undefined代表变量已声明但未赋值。
5.5 object类型和symbol类型
后续会详细介绍。
5.6 typeof运算符
typeof运算符可以返回参数的类型,它有两种调用形式:
typeof x; // 运算符形式
typeof(x); // 函数形式
两者返回的结果没有区别。
6 交互
6.0 alert
在前面例子中已经出现过,其会产生一个提示框,用户在解决完提示框之前不能点击网页的其他位置。对alert的调用会返回undefined。
alert("Hello World!"); // 梦回初始
6.1 prompt
prompt函数的作用是弹出提示框,并要求用户输入信息并返回,如果用户不输入信息或者点击取消,则会返回null。
它接受两个参数:
reuslt = prompt(title, [default]);
其中,title代表弹出提示框中的提示信息,[default]参数是可选的,它代表默认返回该值,如果不设置,则返回null。
let age;
age = prompt("How old are you?", 100); // 向用户索要输入
alert(`You are ${age} years old!`); // 默认返回 You are 100 years old.
注意:一般来说为保证兼容性,最好加上default参数。
6.2 confirm
confirm函数可以弹出提示信息,要求用户选择“确定”或“取消”,并相应的返回“true”或“false”。
result = confirm(question);
此函数接受一个参数question,代表弹出的提示信息。
例如:
let result = confirm("Do you want to exit?");
alert(result);
7 类型转换
7.1 字符类型转换
使用String()可以将变量强制转换为字符类型的变量。
let number = 3;
number = String(number);
alert(typeof number);
7.2 数字类型转换
使用Number()可以将变量强制转换为数字类型的变量。
let string1 = "1";
string1 = Number(string1);
alert(typeof string1);
如果该字符不是有效的数字,则会返回NaN。
let string2 = "This is not a numebr";
string2 = Number(string2);
alert(string2); // NaN
Number转换规则:
转换前 | 转换后 |
---|---|
undefined | NaN |
null | 0 |
true | 1 |
false | 0 |
string | 去掉首尾空格后进行转换,如果剩余为空字符,则返回0。如果剩余字符中仍有非数字,则返回NaN。 |
7.3 布尔类型转换
使用boolean()可以将变量转换为布尔类型。
只有0、空字符串、null、NaN、undefined会被转换为false,
其余均会被转换为true。
8 基础运算符
8.0 ' + '的特殊用法
在JS中,' + '可以用来连接两个字符:
let string1 = 'My',
string2 = 'string',
string3 = string1 + string2;
alert(string3); // Mystring
只要任意一个元素是字符,则另一个元素也会被转化成字符,然后进行连接;
let string1 = 'string' + 1;
alert(string1); // string1
8.1 神奇的"++"
"++"运算符有前置和后置两种形式。"++"在后则返回操作前的值,"++"在前则返回操作后的值。最后a, b的值都是1。
let a = 0;
alert(a++); // 显示0
let b = 0;
alert(++b); // 显示1
alert(a); // 1
alert(b); // 1
9 值的比较
0)在进行字符串比较时,JS会按照Unicode编码顺序逐位进行比较。
1)对于不同类型的变量之间的比较,JS会将其全部转化为Number类型再判定大小
2)使用' === '可以要求JS不进行变量转换的比较,即严格比较。如果被比较的数据类型不同,则会返回false。
3)null==undefined 返回的是true
10 条件语句
if, else, else if 同C语言。
10.0 三元运算符" ? "
let result = condition ? value1 : value2;
conditon代表条件,如果" ? "之前为true,则执行value1,否则执行value2。
在某些情况下," ? "可以减少代码量,比如:
let access;
let age = prompt("How old are you?", 18);
if(age>=18)
{
access = true;
}
else{
access = false;
}
可以简化为:
let access;
let age = prompt("How old are you?", 18);
access = (age > 18) ? true: false;
" ? "同样也可以进行嵌套使用。
11 逻辑运算符
11.0 ||
" || "运算符可以返回表达式中第一个真值,如果没有真值,则返回最后一个值。
let name = "" || "LiMing" || "LiHua"; // 空字符串会被转换为false
alert(name); // LiMing
let number = false || false || 0;
alert(number); // 0
11.1 &&
" && "运算符可以返回表达式中第一个假值,如果没有假值,则返回最后一个值。
11.2 ??
" ?? "是空值合并运算符。表达式
a ?? b
如果a未定义,则表达式返回b的值;
如果a已定义,则表达式返回a的值。
常用来设置默认值。
12 循环
for, while, switch 同C语言
12.0 switch
放在一起的case语句可以共享同一段代码,本质是利用case后未加break导致的代码粘连。
let a = 0;
switch(3)
{
case 1:
case 2: // 1和2共享同一段代码
alert(wrong!);
break;
case 3:
alert(right!);
break;
}
case的相等是严格相等,即数据类型也必须相同
13 函数
13.0 函数格式
function name(parameters) {
...body...
}
其先使用function声明这是一个函数,后面是此函数的名字,圆括号内是函数需要传入的参数,花括号内是函数体。
13.1 函数的参数
调用函数时如果传入的参数不够,则未传入参数的变量的值为undefined
function ShowMessage(user, message)
{
alert(typeof message);
}
ShowMessage("LiMing"); // undefined
同样,函数也可以设定参数的默认值。
function ShowMessage(message = "200 OK")
{
alert(message);
}
ShowMessage(); // 200 OK
ShowMessage("404 Not Found") // 404 not found
13.2 函数的返回值
如果return后面没有其他语句,则其等价于return undefined,函数会返回undefined。
function Test()
{
return;
}
alert(typeof(Test())); // undefined
13.3 函数的声明和表达式
JS引擎在执行脚本时,首先会搜索整个脚本中的函数的声明并创建函数,这使得函数声明创建的函数无论在脚本的哪个位置都能使用。
但函数表达式只有在被执行到时会被创建,因此会出现顺序问题。
现代模式下,函数声明只在当前所在的代码块中可用。
13.4 箭头函数
对于一些简单的函数,我们可以使用箭头函数来替代。其形式如下:
let func = (arg1, arg2, ...argN) => expression
等价于:
let func = function(arg1, arg2, ...argN) {
return expression;
};
例如,求两个数之和的函数如下:
let sum = function(a, b) {
return a + b;
};
使用箭头函数则可以表示为:
let sum = (a, b) => a + b;
如果只有一个参数,则可以省略括号:
let func = n => n ** 2;
如果没有参数,则必须加上括号:
let func = () => alert("Hello!");
因此,在使用一些简单的函数时可以用箭头函数减小代码量。
箭头函数的多行形式
如果 => 右边有多行表达式,则应该用 { } 包裹并使用return语句返回值。
let func = (a, b) => {
let result = a + b;
return result;
};
14 对象
JS中对象包括键值对,键必须是字符串类型或Symbol类型,使用其他类型的变量会被自动转换为字符类型,值可以使任意类型的数据。
14.0 对象的基本知识
创建对象
使用 {} 即可创建对象:
let user = {};
这里的user就是一个对象。
创建对象时可以给其直接添加属性, :
let user = {
name : "LiMing",
age : 18,
};
键也可以使用长字符,但必须使用引号包裹:
let user = {
name : "LiMing",
"like bird" : false, // 使用长字符作为键
}
属性必须以 , 结尾,这样可以方便的显式添加或删除属性:
user.weight = 60; // 添加属性
user.name = "LiHua"; // 修改属性
delete user.age; // 删除属性
一般来说,通过 . 操作符就可以访问对象的属性,其要求属性的键是合法的字符串。因此,对于不合法的字符串,我们需要通过 [ ] 来进行访问:
alert( user.like bird ) // 错误的写法
alert( user["like bird"] ) // √
使用 [ ] 还可以通过变量名来访问属性,而 . 操作符不可以:
let user = {
name : "LiMing",
}
let key = prompt("What do you want to know about the user?", "name");
alert( user[key] ); // LiMing
alert( user.key ) // 错误
计算属性
因为 [ ] 操作符,我们可以动态的创建属性:
let fruit = prompt("which fruit do you have?", "apple");
let user = {
[fruit] : 5,
}
alert( user[fruit] );
[ ] 中还支持字符运算:
let fruit = prompt("which fruit do you have?", "apple");
let user = {
[fruit + "computer"] : 5, // applecomputer : 5
}
alert( user[fruit] );
属性值简写
在实际应用中,常用变量名来作为属性值:
function make_user(name, age){
return {
name : name,
age : age,
}
}
let user = make_user("LiMing", 18);
alert( user.name ); // LiMing
其可以简写如下:
function make_user(name, age){
return {
name, // 这里的name代表 name : name,即键和值同名
age,
}
}
let user = make_user("LiMing", 18);
alert( user.name ); // LiMing
属性存在测试:in 操作符
在JS中,如果读取的属性不存在,则会返回undefined。
使用 in 操作符可以测试该对象中有无此属性。
function make_user(name, age){
return {
name,
age,
}
}
let user = make_user("LiMing", 18);
alert( "name" in user ); // true
值得注意的是,in 左边必须是字符串,如果是形参,则该形参所指向的实参必须为字符类型。
for...in...循环
为了遍历对象中的所有属性,我们需要用到循环,这里的for...in...循环和之前的for(...;...;...)循环形式不同,其形式如下:
for (key in object) {
// 对此对象属性中的每个键执行的代码
}
例如:
let user = {
name : "LiMing",
age : 18,
is_admin : false,
}
for (let key in user) { // 在for循环中声明变量
alert(key); // name, age, is_admin
alert(user[key]) // 使用 [ ] 取得值,为 LiMing, 18, false
}
如果键可以被转换为整数,则键会按照整数大小进行展示:
let codes = {
"49" : "Germany",
"48" : "USA",
//...
"1" : "China",
}
for (let code in codes) {
alert( code );
alert( codes[code] );
}
在这里展示的顺序并非是创建时的"49" : "Germany", "48" : "USA", ... , "1" : "China"
而是从1开始,即先是"1" : "China", ... , "48" : "USA", "49" : "Germany"
对象的存储
JS中,普通变量存储的是值本身。但对象不同,它存储的是对变量的引用,因此具有拷贝和克隆的性质。
可以使用Object.assign方法来进行克隆,其语法如下:
Object.assign(dest, [src1, src2, src3...])
其中,dest代表目标对象,src1, src2,...代表源对象,该方法可以将源对象中所有的属性克隆到目标对象中,并返回dest目标对象。
如果克隆的属性已经存在,则其会被覆盖:
let user = {
name : 'LiMing',
}
Object.assign(user, {name : 'LiHua'});
alert(user.name); // LiHua
但如果是嵌套对象,则还需要进行嵌套处理。
内存管理
JavaScript采用自动内存管理,只有当一个值不能经过任何有效途径访问,即值是不可达时,该内存才会被释放。类似图的可达性。
14.1 对象方法
基本知识
从本质来说,对象的方法就是一个函数。
let user = {
name : "LiMing",
}
user.say_hi = function () { // 添加方法
alert("Hello");
};
user.say_hi(); // Hello
/*
现在的user实际为
user = {
name : "LiMing",
sayhi : function () {
alert("Hello");
},
}
*/
也可以将预先声明的函数作为对象的方法:
let user = {
name : "LiMing",
}
function say_hi() { // 预先声明函数
alert("Hello");
}
user.say_hi = say_hi; // 将其添加为对象的方法
user.say_hi(); // Hello
也可以对方法进行简写:
let user = {
say_hi() { // 等价于say_hi : function () {
alert("Hello");
},
}
user.say_hi(); // Hello
this
有时候,方法需要用到当前对象中的属性。比如,say_hi()方法就有可能调用user中的name属性。这时,就需要用到 this,它代表当前方法所指向的对象。
let user = {
name : "LiMing",
say_hi() {
alert(`Hello ${this.name}`);
},
}
user.say_hi(); // Hello LiMing
14.2 构造器和操作符"new"
在实际运用中,我们常需要构造同一类型的多个对象,比如构造多个用户。这时就需要用到构造器。
一般来说,构造函数有两个约定:
1. 一般以大写字母开头
2. 只能用 new 操作符执行
function User(name) { // 构造器
this.name = name;
this.admin = false;
}
let user = new User("LiMing"); // 创建对象
alert( user.name ); // LiMing
alert( user.admin ); // false
构造器同样也可以创建方法:
function User(name) { // 构造器
this.name = name;
this.admin = false;
this.say_hi = function() {
alert(`Hello ${this.name}`);
};
}
let user = new User("LiMing"); // 创建对象
alert( user.name ); // LiMing
alert( user.admin ); // false
user.say_hi(); // Hello LiMing
14.3 可选链 ?.
在实际使用时,我们可能会遇见访问对象的属性不存在的情况,JS在默认情况下会返回一个错误,而使用可选链 ?. 可以帮助我们返回undefined或者null。其语法如下:value?.prop
:
- 如果
value
存在,则结果与value.prop
相同, - 否则(当
value
为undefined/n
ull
时)则返回undefined
例如:
let user = {};
alert( user?.address ); // 返回undefined而不是报错
如果 ?. 左边的部分不存在,则代码会停止执行并返回undefined,因此,该语句后的代码不会被执行。
let user = {};
function sayhi(x) {
x++;
}
let x = 0;
user?.sayhi(x); // 不会执行sayhi中的x++
alert( x ); // 0
变体 ?.() 和 ?.[]
同理 ?.() 可以用来调用一个可能不存在的方法:
let user_admin = {
say_admin() {
alert("I'm admin.");
},
};
let user_guest = {};
user_admin.say_admin?.(); // I'm admin
user_guest.say_admin?.(); // undefined
?.[] 可以用来调用一个可能不存在的属性:
let user_admin = {
password : '123456',
};
let user_guest = {};
let key = 'password';
alert( user_admin?.[key]); // 123456
alert( user_guest?.[key]); // undefined
14.4 Symbol类型
在JS中,对象的键只能是字符类型或Symbol类型。使用下面方法可以创建一个Symbol类型的变量:
let id = Symbol(); // id是Symbol的一个实例化对象
我们可以对Symbol类型的变量添加描述,这在调试代码时非常有用:
let id = Symbol("id"); // 以id为描述的名称为id的Symbol类型变量
Symbol类型的变量具有唯一性,即每个Symbol类型的变量都是不同的。
隐藏属性
使用Symbol可以隐式的添加属性,代码的其他部分都不能意外访问或重写此属性。
let user = {
name : 'LiMing',
};
let id = Symbol("id"); // 创建Symbol类型的变量
user[id] = 1; // 还记得[]的作用吗?
alert( user[id] ); // 1
如果要在创建对象时就引入Symbol类型的变量,应该使用 [id] 而非 "id",因为后者是字符类型:
let id = Symbol("id"); // 创建Symbol类型的变量
let user = {
name : 'LiMing',
[id] : 1,
};
Symbol类型的属性不会参与for...in...循环,但Object.assign可以克隆Symbol类型的属性。
全局Symbol
如果想在文件的不同地方访问到同一个Symbol实体,那就得使用Symbol.for(key)方法。
该方法会在全局注册表中查找一个描述为key的Symbol实体,如果该实体存在,则返回。如果不存在,则创建该实体并将其添加到全局注册表中。
let id2 = Symbol.for("id"); // 全局注册表中不存在描述为id的Symbol实体,则创建
let id3 = Symbol.for("id"); // 全局注册表中已存在描述为id的Symbol实体,则直接赋值
alert( id2 == id3 ); // true
Symbol.keyFor()方法可以通过Symbol变量名来找到对应的描述,这里的Symbol必须为全局Symbol:
let sym= Symbol.for("id");
alert( Symbol.keyFor(sym) ); // id