一、补充知识点
- JavaScript 本身不提供任何与I/O(输入/输出)相关的API,都要靠宿主环境(host)提供,所以 JavaScript只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。目前,已经嵌入JavaScript的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。
①宿主环境是浏览器的话,它提供的额外 API 可以分成三大类。
-
浏览器控制类:操作浏览器
-
DOM 类:操作网页的各种元素
-
Web 类:实现互联网的各种功能
②如果宿主环境是服务器,则会提供各种操作系统的 API
- 比如文件操作 API
- 网络通信 API等等
- 这些你都可以在 Node 环境中找到。
- JavaScript 的核心语法部分相当精简,只包括两个部分
- 基本的语法构造(比如操作符、控制结构、语句)
- 标准库(就是一系列具有各种功能的对象比如Array、Date、Math等)。
- 除此之外,各种宿主环境提供额外的 API(即只能在该环境使用的接口),以便 JavaScript 调用。
- 推荐安装 Chrome 浏览器,它的“开发者工具”(Developer Tools)里面的“控制台”(console),就是运行 JavaScript 代码的理想环境。
- JS区分大小写!
- 大部分空格回车没有实际意义(当不影响浏览器断句时)
var a = 1和var a=1没有区别- 加回车大部分时候也不影响
- 只有一个地方不能加回车,那就是
return后面
二、表达式与语句
1、表达式(expression)
(1)表达式,指一个为了得到一个值的计算式
(2)函数的值也称为返回值
(3)例子
①1+2表达式的值为3
②add(1,2)表达式的值为函数的返回值
③console.log表达式的值为函数本身。因为后面没有加括号,也就是没有调用这个函数。
④console.log(3)表达式的值为函数的返回值Unidifined(源代码里规定了),3只是这个函数打出来的东西。
2、语句(statement)
- 语句是为了完成某种任务而进行的操作
- 语句一般会改变环境(比如声明、赋值)
- 语句以分号或者空格结尾,一个分号或者空格就表示一个语句结束。
- js代码不用写分号不用不用,只有立即执行函数前面需要
- 多个语句可以写在一行内。
- 如果语句间用逗号连接,表示一个语句。
- 举例
var a=1+3
这条语句先用var命令,声明了变量a,然后将1 + 3这个表达式的值赋值给变量a。
3、区别
- 表达式是为了得到值,一定会返回一个值。凡是 JavaScript 语言中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边,预期是一个值,因此可以放置各种表达式。
- 语句主要为了进行某种操作,一般情况下不需要返回值
4、举例
(1)
var a=1+3
这条语句先用var命令,声明了变量a,然后将1 + 3这个表达式的值赋值给变量a。
三、变量
1、定义
变量是对“值”的具名引用。变量就是为“值”起名,然后引用这个名字,就等同于引用这个值。变量的名字就是变量名。
2、声明变量的两种方式
(1)var a;已过时,不准用
var是变量声明命令。它表示通知解释引擎,要创建一个变量a。- 此时只声明了这个变量,但是还未给它赋值,所以该变量的值是
undefined。undefined是一个特殊的值,表示“无定义”。 - 如果变量赋值的时候,忘了写var命令,这条语句也是有效的。
var a = 1;
// 基本等同
a = 1;
但是,不写var的做法,不利于表达意图,而且容易不知不觉地创建全局变量,所以建议总是使用var命令声明变量。
- 可以在同一条var命令中声明多个变量。比如
var a b;
(2)let a=1
① 是目前最新的、更合理的方式
② 用于声明变量
③ 规则
- 遵循块作用域,即使用范围不能超出{ }
- 不能重复申明(不对啊,可以重复声明啊)
- 可以赋值,也可以不赋值
- 必须先声明再使用,否则报错
- 全局声明的let变量,不会变成window的属性
- for循环配合let有奇效
(3)变量声明后,即指定了值也指定了类型,但是值和类型还是可以随意再变化的
3、声明常量const a=1
① 声明常量
② 声明时就要赋值,赋值后不能改
③ 规则
- 遵循块作用域,即使用范围不能超出{ }
- 必须先声明再使用,否则报错
- 全局声明的const常量,不会变成window的属性
- for循环配合const有奇效
4、变量的赋值a = 1+3
- 把
1+3这个表达式的值4赋值给我们刚才声明的变量a - 以后,引用变量名
a就会得到数值4。
5、变量的声明和赋值可以合写成一句var a = 1+3
- 表示先声明变量
a,再将数值1“赋值”给变量a。 - 至此,因为变量
a已被声明,所以可以直接用a='hello'再次给变量a赋值(会覆盖之前的值),无需再次声明
6、name和'name'的区别
- name是变量,值可以变啊,任何类型都可以再变,比如'name','hello'
- 'name'就是一个字符串,是一个常量啊!你怎么去把一个字符串变掉?
四、标识符(identifier)
1、定义
- 标识符指的是用来识别各种值的合法名称。
- 最常见的标识符就是变量名和函数名。
2、标识符命名规则
(1)第一个字符,可以是
- 任意 Unicode 字母(包括英文字母和其他语言的字母)
- 美元符号($)
- 下划线(_)。
- 中文
(2)第二个字符及后面的字符
- 同上
- 数字0-9
(3)JavaScript 有一些保留字,不能用作标识符
arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
3、报错样式
Uncaught SyntaxError: Unexpected token xxx
未捕捉的语法错误:xxx不是我们所期待的字符串
五、注释
- 定义: 源码中被 JavaScript引擎忽略的部分就叫做注释
- 作用:对代码进行解释。
- 写法
- 单行注释,用//起头
- 多行注释,放在/* 和 */之间。
// 这是单行注释
/*
这是
多行
注释
*/
- 不好的注释
- 把代码翻译成中文!
- 过时的注释!
- 发泄不满的注释!
- 好的注释
- 踩坑注解
- 为什么代码会写得这么奇怪,遇到什么bug
六、区块(block)
- 定义:使用大括号,将多个相关的语句组合在一起,称为“区块”。
{
let a=1
let b=1
}
- 对于var命令来说,JavaScript 的区块不构成单独的作用域(scope)。
{
var a = 1;
}
a // 1
上面代码在区块内部,使用var命令声明并赋值了变量a,然后在区块外部,变量a依然有效,区块对于var命令不构成单独的作用域,与不使用区块的情况没有任何区别。
- 在 JavaScript 语言中,单独使用区块并不常见,区块往往用来构成其他更复杂的语法结构,比如for、if、while、function等。
- 大括号{}在语句只有一句的时候可以省略,建议别这样。
七、条件语句
(一)if 结构
- 规则
- 先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。
- 所谓布尔值,指的是 JavaScript 的两个特殊值,true表示真,false表示伪。
- 如果表达式的求值结果为true,就执行紧跟在后面的语句;如果结果为false,则跳过紧跟在后面的语句。
- 基本形式
if (表达式){
语句
}
- 关于=
- 赋值表达式(=):x=y 把y的值赋给x!一直是true!
- 严格相等运算符(===):x===y x等于y 优先采用
- 相等运算符(==): x===y x等于y
(二)if...else 结构
if代码块后面,还可以跟一个else代码块,表示不满足条件时,所要执行的代码。
- 规则:
- 先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。
- 如果表达式的求值结果为true,就执行紧跟在表达式后面的语句1;如果结果为false,则执行else里的语句2
- 基本形式
if (表达式){
语句1
}
else{
语句2
}
- 语句1里面可以有嵌套的if...else 结构
var m = 1;
var n = 2;
if (m !== 1)
if (n === 2) console.log('hello');
else console.log('world');
上面代码不会有任何输出,else代码块不会得到执行,因为它跟着的是最近的那个if语句,相当于下面这样。
if (m !== 1) {
if (n === 2) {
console.log('hello');
} else {
console.log('world');
}
}
- 语句2里面可以有嵌套的if...else 结构
// 对一个变量多次进行判断
if (表达式一) {
语句一;
} else {
if (表达式二) {
语句二;
} else {
if (表达式三) {
语句三;
} else {
语句四;
}
}
}
// 去掉一些大括号,等同于
// 最推荐写法
if (表达式一) {
语句一;
} else if (表达式二) {
语句二;
} else if (表达式三) {
语句三;
} else {
语句四;
}
function fn(){
if (表达式) {
return表达式
}
if (表达式) {
return表达式
}
return表达式
}
- else代码块总是与离自己最近的前面的那个if语句配对。
- 缩进也可以很变态,如面试题常常下套
a=1
if(a===2)
console.log('a')
console.log( 'a等于2')
// a等于2
等同于
a=1
if(a===2){
console.log('a')
}
console.log( 'a等于2')
(三)switch 结构
多个if...else连在一起使用的时候,可以转为使用更方便的switch结构。
- 基本结构
switch (fruit) {
case "banana":
// ...
break;
case "apple":
// ...
break;
default:
// ...
}
- 规则:上面代码根据变量fruit的值,选择执行相应的case。如果所有case都不符合,则执行最后的default部分。
- 注意
- 每个case代码块内部的break语句不能少,否则会接下去执行下一个case代码块,而不是跳出switch结构。
- switch语句内部采用的是“严格相等运算符”———switch语句后面的表达式,与case语句后面的表示式比较运行结果时,采用的是严格相等运算符(===),而不是相等运算符(==),这意味着比较时不会发生类型转换。
var x = 1;
switch (x) {
case true:
console.log('x 发生类型转换');
break;
default:
console.log('x 没有发生类型转换');
}
// x 没有发生类型转换
上面代码中,由于变量x没有发生类型转换,所以不会执行case true的情况。
(四)问号冒号表达式--最简单的if..else结构
- 基本形式
(表达式) ? 语句1 : 语句2
- 规则:如果“表达式”为true,则执行“语句1”,否则执行“语句2”。
- 等同于if..else结构中语句1和语句2都只有一句的情况。
if (表达式){
语句1
}
else{
语句2
}
- 能用这个就不用if..else
- 例子
①
var even = (n % 2 === 0) ? true : false;
上面代码中,如果n可以被2整除,则even等于true,否则等于false。
②
function max(a,b){
if(a>b)return a;
else return b;
}
等同于
function max(a,b){
return a>b ? a : b
}
(五)&&短路逻辑(用最新的?)
-
A && B && C && D取第一个假(布尔值判断真假)值或D,并不会取布尔值true或false,布尔值在这只是用来判断真假 -
如果第一个运算子A的布尔值为true,则返回第二个运算子B的值(注意是值,不是布尔值);如果第一个运算子A的布尔值为false,则直接返回第一个运算子B的值,且不再对第二个运算子求值。
-
A的布尔值为false,就停下,返回A的值。A的布尔值为true,就继续往后,再看B的布尔值,为false就停下,返回B的值,为true就继续往后。知道有个运算子的布尔值为false或者到最后一个运算子了,都返回他的值!
-
false就停下返回值,true就继续。
-
例子
①
var a=1
if(a===1){
console.log(a)
}
等同于
a===1 && console.log('f1存在')
②
console && console.log && console.log('hi')
//防御性编程
//自我保护,没有console就返回undefined,而不会报错
//IE没有console
//如果console不存在,就是undefined,那么布尔值就是false,那么在console就停下了,返回undefined。这样就不会报错了
//如果console存在,console .log不存在,布尔值为undefined,就在此停下,返回undefined,不会报错了
//如果console存在,console .log存在,就可以执行最后一句了
if(console){
if(console.log){
console.log('Hi')
}
}
如果前俩都不存在,布尔值都false,就不执行这个不存在的函数了呗。直接跳过,不会报错影响整个程序
console?.log?('hi')
//最新语法,可选链语法
// ?表示我不知道有没有,有就继续,没就停下
③
fn && fn()
fn存在就调用fn,不存在就不调用
④1&&2 数字1的布尔值为真,所以取最后一个值2
⑤0&&2 数字0的布尔值为假,所以取第一个假值0
⑥
't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""
'' && '' // ""
var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1
(六)||短路逻辑(用最新的n=0)
1、 规则:A||B||C||D取第一个真值或D,并不会取true|false
2、 true就停下返回值,false就继续。注意可以变成false的有六个值,会出现bug
a||b
//等同于
if(!a){b}else{a}
//如果a不存在,就执行b,否则还是a
3、 例子:判断变量a是否存在
a = a||100
//如果a存在就是a,如果a不存在就令a为100
//如果变量a存在,变量a就为第一个真值,再把变量a赋值给变量a,a还是a自己
//a不存在,那么第一个a不是真值,就执行最后一句a=100。给a一个保底值。
等同于
if(a){
a=a
} else{
a=100 // 保底值
}
//如果a不存在就令a为100,如果a存在就令a还是为a
等同于
if(!a){
a=100 // 保底值
} else{
a=a
}
4、有问题!如果a本来就为false的那六个值,不就会被重新赋值为保底值了吗
let b = 0
b = b||100
b //100
5、新语法应运而生
function f1(n){ //定义一个函数,参数为n
n = n||0 //如果参数的布尔值为false(比如不存在),就让参数为0,就让参数还是等于参数
return n+1 //返回参数+1
}
我给的参数为空字符串的话,我这个参数就会被整成0了啊
function f1(n=0){ //如果n是null或者undefined就给它保底值0
return n+1
}
所以a只有不存在的时候才给保底值,空字符串或者其他的false情况还是自己的值
七、循环语句
(一)while
- 基本结构
while (表达式) {循环体语句}
- 规则:
- 判断表达式的真假
- 当表达式为真,执行语句
- 执行完再判断表达式的真假
- 当表达式为假,执行后面的语句
- 例子
①
var i = 0; //初始化表达式
while (i < 100) { //判断表达式
console.log('i 当前为:' + i); //循环体语句
i = i + 1; //递增表达式
}
上面的代码将循环100次,直到i等于100为止。
②
while(a !== 1){
console.log(a)
a=a+0.1
}
会进入死循环。因为浮点数a永远不会等于1
- 初始化、判断、增长有一个不写都会进入死循环。循环体不写就是没有意义。
(二)for循环
- 基本结构
for (初始化表达式; 判断表达式; 递增表达式) {
循环体语句
}
- 规则
- 先执行初始化表达式
- 再判断表达式的真假
- 如果为真,就执行循环体语句,然后执行递增表达式
- 如果为假,就直接退出循环
- 例子
①
var x = 3;
for (var i = 0; i < x; i++) {
console.log(i);
}
// 0
// 1
// 2
// 最后循环执行完i=3
上面代码中,初始化表达式是var i = 0,即初始化一个变量i;判断表达式是i < x,即只要i小于x,就会执行循环;递增表达式是i++,即每次循环结束后,i增大1。
②
for(var i=0;i<3;i++){
setTimeout(()=>{
console.log(i)
},0)
}
//3个3
/*
i=0满足i<3,所以执行循环体打出i,但要过一会。
以此类推
i=1满足i<3,所以执行循环体打出i,但要过一会。
i=2满足i<3,所以执行循环体打出i,但要过一会。
i=3不满足i<3,结束循环。
for循环不结束,就不算过一会!
现在循环结束了,过一会了,终于要打出i了,而且还要打出三次,因为此时i=3了。所以打出了3个3
*/
- 所有for循环,都可以改写成while循环。
(三)do...while循环
- 基本结构
do{
语句
}
while (条件);
- 规则:与while循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件。
- 不管条件是否为真,do...while循环至少运行一次,这是这种结构最大的特点。
- while语句后面的分号注意不要省略。
- 例子。
var x = 3;
var i = 0;
do {
console.log(i);
i++;
} while(i < x);
// 0 1 2
(四)break 语句和 continue 语句
- break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行。
- break语句用于退出所有循环,跳出代码块或循环
- continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环。
- 如果存在多重循环,不带参数的break语句和continue语句都只针对最内层循环。
- 例子
for (var i = 0; i < 5; i++) {
console.log(i);
if (i === 3)
break;
}
// 0
// 1
// 2
// 3
var i = 0;
while (i < 100){
i++;
if (i % 2 === 0) continue;
console.log('i 当前为:' + i);
}
//上面代码只有在i为奇数时,才会输出i的值。如果i为偶数,则直接进入下一轮循环。
(五)标签label
- 基本结构:
标识符:语句 - 举例
foo:{
console.log(1);
break foo;
console.log('本行不会输出');
}
//foo是一个标签,表示一个代码块
//1
{
foo:1;
}
//这是一个代码块,代码块里有一个标签foo,标签foo的内容是1
八、参考资料
- JavaScript 教程
- 进阶:《你不知道的JavaScript》上卷