学的主要版本ES6 兼容以前

取其精华,去其糟粕

注重“主领域”:前端,而不是“主语言”

JS使用最广 门槛极低 生产力

基本语法

表达式与语句

表达式

  • 1 + 2表达式的值为3
  • add(1,2)表达式的值为函数的返回值
  • console.log表达式的值为函数本身(函数没加括号)
  • console.log(3)表达式的值为多少?
  • 值和返回值不同,只有函数有返回值,严谨,函数的值就是返回值
  • console.log()返回一个undefined,可以在控制台查看

语句

  • var a = 1是一个语句,声名语句
  • 声名语句的值是undefined

两者的区别

  • 表达式一般都有值,语句可能有或没有
  • 语句一般会改变环境(声明、赋值)
  • 并不绝对

大小写敏感

  • 变量名等
  • var avar A
  • objectObject
  • functionFunction
  • 都是不一样的
  • 控制台打windowWindow{...},有毛病,不一样

空格

大部分空格没有实际意义

  • var a = 1var a=1没差,只要不影响断句
  • 加回车大部分时候也不影响
  • 只有一个地方不能加回车,就是return后面

控制台

1
2
3
4
5
6
7
8
function fn(){
    return 3
}
fn()//3
function fn(){
    return undefined//return后有回车的话自动补一个`undefined`
    3//忽略
}

标识符

命名规则

  • 第一个字符,可以是Unicode字母或$(dollar),或_(underscore),或中文

变量名是标识符

  • var _ = 1
  • var $ = 8
  • var ______ = 6
  • var 你好 = 'hi'

控制台打var 9$ = 1时,报错Uncaught SyntaxError: Invalid or unexpected token

其他标识符遇到再说

注释

多写注释——才怪

注释的分类

不好的注释
  • 代码翻译成中文
  • 过时的注释
  • 发泄,职业素养
好的注释
  • 踩坑注解
  • 为什么代码会写得这么奇怪,遇到什么bug

“信噪比”要高,有意义的注释

区块block

  • 将代码包在一起
1
2
3
4
{
    let a = 1
    let b = 2
}
  • 常与if/for/while合用

if语句

语法

  • if(表达式){语句1}else{语句2}
  • {}在语句只有一句的时候可以省略,打你

变态情况

表达式里的变态,a=1
  • 表达式必须有个值,=是赋值为,赋值运算符
  • ==垃圾,直接用===,比较运算符

技巧,来自C:表达式里,常量写在左值处,判断,写错直接会报错

1
2
3
if(1 = a){
    // 报错:`Uncaught ReferenceError: Invalid left-hand side in assignment`
}

webStorm会提示,VSCode不堪大用

webStorm教你写代码,规范代码,节省时间

语句1和语句2里的变态,嵌套的if else

else if实际上是两对if else判断,省略花括号,逻辑符合形式,形成分支语句

缩进的变态
1
2
3
4
a = 1
if(a === 2)
    console.log('a')
    console.log('a等于2')

没有花括号,if 只管到之后最近的第一句语句

上面的代码相当于

1
2
3
4
5
a = 1
if(a === 2){
    console.log('a')
}
console.log('a等于2')

再看这句

1
2
3
a = 1
if(a === 2)
    console.log('a'),console.log('a等于2')
  • ,运算符,表示语句没完,console.log('a'),console.log('a等于2')变成一句语句

再看这句

1
2
3
a = 1
if(a === 2)
    console.log('a');console.log('a等于2')
  • ;运算符,表示语句完了

使用最没歧义的写法,最大公约数,程序员第二戒律

第一戒律是不能相信人类,特别是自己,要相信console.log(),墨菲定律

前端的戒律是 图片不能变形,和没有设计稿不写代码

  • 推荐的写法
1
2
3
4
5
6
7
if (表达式){
    语句
}else if(表达式){
    语句
}else{
    语句
}
  • 次推荐写法
1
2
3
4
5
6
7
8
9
function fn(){
    if (表达式){
        return 表达式
    }
    if (表达式){
        return 表达式
    }
    return 表达式
}

return语句,else可以省,因为return返回了返回值后面的语句都不执行,不需要else,分支语句在函数中的写法


switch语句

if else升级版,但不推荐,易用错

switch语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
switch (fruit){
    case "banana":
        // ...
        break;
    case "apple":
        // ...
        break;
    default:
        // ...
}

break

  • 大部分时候,省略break你就完了
  • 少部分时候,可以利用break
  • 不打breakcase会“贯穿”到下一个case,表示同时满足上下条件,不常用,一般要求一一对应
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
a = 2
switch(a){
    case 1:
    case 3:
        console.log("单数")
        break;
    case 2:
    case 4:
        console.log("双数")
        break;
}

js烂设计,对比Swift switch case

能不写if就不写:三元表达式、短路运算符

问号冒号表达式,即三元运算符

表达式1 ? 表达式2 : 表达式3

简化if else 里只有一句语句时

  • return a>b ? a: b
  • return n>0 ? n: -n

&&短路逻辑

  • A && B && C && D取第一个假值(后面都不看了)或者D
  • 并不会取true/false

控制台

1
2
3
if(f1){
    console.log('存在')
}

报错,fi未声明是不能用的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//bad
if(window.f1){
    console.log('fi存在')
}
//< undefined

//good
window.f1 && console.log('fi存在')

//两者等价

&&且运算

真值表

B B
&&
A B B
A A A

结果栏,表示以A/B作为表达式的值

  • 都为真,直接用B作为表达式的值
  • A假,B真,用A作为表达式的值
  • A真,B假,用B作为表达式的值
  • A假,B假,用A作为表达式的值

第一个A表达式为真,执行并且返回第二个表达式的值

第一个A表达式为假,不计算下去了,直接返回第一个表达式的值

  • &&:‘假’短路
1
2
3
//IE 没`console`
console && console.log && console.log('hi')
//“遇真才算”、“遇假则止”
  • A && B && C && D取第一个出现的假值或者D
  • 不同于&|,并不会取true/false,而是执行表达式,并取表达式的值

||短路逻辑

  • A || B || C || D取第一个真值或者D
  • 并不会取true/false
1
2
3
4
5
6
7
8
9
//bad
if(!a){
    b
}

//good
a || b

//两者等价

||且运算

真值表

B B
||
A A A
A B B

结果栏,表示以A/B作为表达式的值

  • 都为真,直接用A作为表达式的值
  • A假,B真,用B作为表达式的值
  • A真,B假,用A作为表达式的值
  • A假,B假,用B作为表达式的值

第一个A表达式为真,不计算下去了,直接返回第一个表达式的值

第一个A表达式为假,执行并且返回第二个表达式的值

1
2
3
4
5
6
7
8
a = a || 100

//等价于
if(a){
    a = a // 比如a是`undefined`时就会取保底值
}else{
    a = 100 // 保底值
}
  • A || B || C || D取第一个真值(后面都不看了)或者D
  • “遇真则止”、“遇假才算”
  • ||:‘真’短路

条件语句小结

  • if...else.. 代码简短时基本不用
  • switch 不要少break
  • A?B:C
  • A&&B
  • fn && fn() 只有fn存在才调用
  • A||B
  • A = A||B

whilefor循环语句

while语法

  • while(表达式){语句}
  1. 判断表达式的真假
  2. 表达式为真,执行{}里的语句,执行完后,再次判断表达式的真假
  3. 表达式为假,直接跳过花括号,执行后面的语句
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var i = 0
while(i<3){
    console.log(i)
    i = i + 1
}
0
1
2
3
< 4 // 4 不是`console`打印出来的,`while`语句没有返回值,是chrome的内部原因吧
var i = 0
while(i<2){
    console.log(i)
    i = i + 1
}
console.log('done')
0
1
done
< undefined //`while`语句没有返回值,是chrome的内部原因吧
while(false){}
< undefined
while(true){}//死循环

死循环

1
2
3
4
5
var a = 0.1
while(a !== 1){
    console.log(a)
    a = a + 0.1
}//浮点数,加着加着就不精确了,加到跳过`1`的值,永远到不了

其他

do...while用得少,可跳过,能看懂就行


for循环

语法糖
1
2
3
4
5
var a = 0.1 //初始化
while(a !== 1){ //判断
    console.log(a) //循环体
    a = a + 0.1 //增长
}//
  • forwhile循环的方便写法
语法
1
2
3
for(1初始化语句;2判断表达式;3自增语句){
    4循环体
}

1st: 1->2->4->3

nth: 2->4->3->…->LoopOut

1->2->LoopOut

  • 先执行且只执行一遍1初始化语句
  • 然后判断2判断表达式
  • 2判断表达式为真,执行4循环体,然后执行3自增语句
  • 2判断表达式为假,直接退出4循环体体,执行后面的语句

变态情况1

1
2
3
4
5
6
7
for(let i = 0; i < 5; i++){
    console.log(i)
} //i  undefined

for(var i = 0; i < 5; i++){
    console.log(i)
} //退出厚,i  5 ,即循环体内最大数加1

变态情况2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for(var i = 0; i < 5; i++){
    setTimeout(()=>{
        console.log(i)
        },0)
} //打印五次`5`

for(var i = 0; i < 5; i++){
    setTimeout(()=>{
        console.log(i + '随机数' + Math.random())
        },0)
}

原因

1
2
3
for(var i = 0; i < 5; i++){
}
i === 5 //true

执行完,跳出循环,i的值为5setTimeout指过一会再执行里面的函数,过一会,i的值还是5,过多长,等到执行完for循环,再打印,此时i的值为5,即打印五次5

1
2
3
4
5
6
7
8
var a = 1
function fn(){
    console.log(a)
}
fn()
a = 2
fn()
...

因为a的值可能会变,即不确定,a的值在定义函数时,不确定,在执行函数时确定

即如果不确定函数是什么时候执行的,就不能确定a的值是多少,a的值可能会随着后面的代码运行而改变

即不确定fn()的调用时间,就不能确定a的值

1
2
3
4
5
6
7
8
var a = 1
function fn(){
    console.log(a)
}
setTimeout(fn)
a = 5 //光速赋值
< 5 //返回值 5
5 //龟速打印 5

会打印5setTimeout(fn)表示过一段时间执行

1
2
3
for(var i = 0; i < 5; i++){}
i === 5 //true
setTimeout(()=>(console.log(i)))

会打印5setTimeout(fn)表示过一段时间执行

1
2
3
4
for(var i = 0; i < 5; i++){
    setTimeout(()=>(console.log(i)))
}
i === 5 //true

for循环是当前任务,先执行完规定的循环次数后,再执行setTimeout

* setTimeout执行机制队列之后再讲

var改成let,满足直觉的幻想

1
2
3
4
5
6
7
8
for(let i = 0; i < 5; i++){
    setTimeout(()=>(console.log(i)))
}
0
1
2
3
4

其他变态情况

1
2
3
4
5
6
7
8
9
let i = 0
for(; i < 5; i++){
    setTimeout(()=>(console.log(i)))
}
0
1
2
3
4

判断语句一定要起效,自增长一定要写,没有就会死循环

breakcontinue

退出所有循环 V.S. 退出当前一次循环

  • 退出所有循环
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for(var i = 0; i < 10; i++){
    if(i%2 === 1){//i为奇数
        break//循环体内,不执行自增长`i++`,且跳出当前`for`循环
        console.log(i + "第一次")
    }
    console.log(i + "第二次")
}
console.log(i + "第三次")
0
1
  • 跳过,即退出当前一次循环,继续自增长语句,直到判断语句跳出循环
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
for(var i = 0; i < 10; i++){
    if(i%2 === 1){
        continue//执行自增长`i++`,且跳过本次循环,即跳过奇数次
        console.log(i + "第奇数次")
    }else{
        console.log(i + "第偶数次")
    }
}
0
2
4
6
8

continue在其他语言中是next,即下一个的意思,更符合原意

  • 两层循环
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for(var i = 0; i < 3; i++){
    for(var j = 101; j < 110; j++){
        if(i === 5){
            break
        }
    }
    console.log(i)
}
0
1
2

break只会退出当前外层的for循环


label语句

用的很少,面试概率5%

label语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
合法标识符A:{
    //代码...
}
console.log(2);

合法标识符B:1
console.log(2);

{
合法标识符B:1
console.log(2);
}

合法标识符A是一个标签,表示:后面的代码块

1
2
3
4
5
6
foo:{
    console.log(1);
    break foo;
    console.log('本行不会输出');
}
console.log(2);

面试

1
2
3
4
5
{
foo:1
}

foo:1

上面的是什么,注意Chromesafarifirefox控制台打出的不一致,Chrome做了优化

加了;,不是对象,测试下面

1
2
3
4
5
6
7
{
foo:1
}

{
foo:1
}

下面这样才是对象

1
2
3
var a ={
    foo:1
}

回答:是一个label语句,语句就是1,完了



参考文章

JS 语法.pdf

入门《网道 JavaScript 教程》

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名