JavaScript基础语法

MDN文档

一、JavaScript简介

1.1 JavaScript 代码的书写位置

1.1.1 内部js代码

直接写在html文件中,使用 script 标签包裹,这个 script 标签要放在 body 标签之内,并且要放在最靠近结束标签 </body>的位置。

我们将 script 放在HTML文件的底部附近的原因是浏览器会按照代码在文件中的顺序加载 HTML。如果先加载的 JavaScript 期望修改其下方的 HTML,那么它可能由于 HTML 尚未被加载而失效。因此,将 JavaScript 代码放在 HTML页面的底部附近通常是最好的策略。

=== "外部js代码"
将js代码写在 .js 后缀的文件中,在html文件中使用 script 标签引入。

    <script src=""></script>

???+ note 
    1. script标签中间无需写代码,否则会被忽略!
    2. 外部JavaScript会使代码更加有序,更易于复用,且没有了脚本的混合,HTML 也会更加易读,因此这是个好的习惯。

=== "写在其他html标签内部"
此处不介绍

1.2 注释

单行注释 //,快捷键 ++ctrl+slash++

多行注释 /* 此处代码会被忽略 */,快捷键 ++shift+alt+a++

1.3 结束符

js代码使用 ; 作为每一行代码结束的标志,可写可不写,但是要统一

实际情况: 实际开发中,可写可不写, 浏览器(JavaScript 引擎) 可以自动推断语句的结束位置

现状: 在实际开发中,越来越多的人主张,书写 JavaScript 代码时省略结束符

约定:为了风格统一,结束符要么每句都写,要么每句都不写(按照团队要求.)

1.4 常用的输入输出语句

  • document.write('要输出的内容') 在网页上输出内容,可以传入包含html标签的字符串,这些字符串会被添加到 body 标签内
  • alert() 页面弹出警告对话框
  • console.log('要输出的内容') 在浏览器控制台输出传入的内容
  • prompt('提示信息') 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字,返回的数据是字符串类型,如果输入的是数字一般使用 + 进行隐式转换

???+ note "JavaScript 代码执行顺序"
按HTML文档流顺序执行JavaScript代码

`alert()` 和 `prompt()` 它们会跳过页面渲染先被执行

二、变量

使用 let 关键字来声明变量,一般声明变量就要初始化(对变量赋值)。

!!! warning
不要使用关键字 var 来声明变量,let 的各方面均由于 var

可以一次性声明多个变量,不同变量之间使用 , 分隔,但是不推荐使用。

??? question "算法——交换两个变量的值"
有两个变量a和b,需要交换二者的值?

1. 声明一个临时变量t
2. 将a的值赋值给t,即 `t = a`
3. 将b的值赋值给a,即 `a = b`
4. 将t的值赋值给b,即 `b = t`

```js
// 已知两个变量a,b
let a = 17
let b = 20

// a,b交换之前的输出结果
console.log(a, b) // 17 20

// 通过临时变量进行交换
let c = a
a = b
b = c

// a,b交换之后的输出结果
console.log(a, b) // 20 17
```

???+ note "变量命名规范"
- 不能用js关键字
- 只能用下划线、字母、数字、$组成,且数字不能开头
- 区分大小写
- 见名知意
- 遵守小驼峰命名法,第一个单词首字母小写,后面每个单词首字母大写。例:userName

???+ info "数组array"
类似于python中的列表,但是只能使用正下标,不能使用负数下标,可以存储不同的数据类型

可以对数组变量使用 `.length` 来获取数组长度

???+ "常量"
使用 const 关键字来声明常量,常量声明时必须初始化,命名时常量的变量名全大写。

三、数据类型

  • 基本数据类型:
    • number 数字型
    • string 字符串型
    • boolean 布尔型
    • undefined 未定义型
    • null 空类型
  • 引用数据类型:
    • object 对象

3.1 数字number

js中的数字类型类似于python中的数字类型。NaN 是js中的一个特殊的数字,它代表一个计算错误,它是一个不正确的或者一个未定义的数学操作所得到的结果,任何对 NaN 的操作都会返回 NaN

3.2 字符串string

通过单引号(') 、双引号(")或反引号(`) 包裹的数据都叫字符串,单引号和双引号没有本质上的区别,推荐使用单引号。字符串可以使用 + 拼接。字符串中有变量时,可以用 ${ } 包住变量,类似于python中是 f字符串

  1. 无论单引号或是双引号必须成对使用
  2. 单引号/双引号可以互相嵌套,但是不以自已嵌套自已(口诀:外双内单,或者外单内双)
  3. 必要时可以使用转义符 \,输出单引号或双引号

3.3 布尔值boolean

表示肯定或否定时在计算机中对应的是布尔类型数据。

它有两个固定的值 true 和 false,表示肯定的数据用 true(真),表示否定的数据用 false(假)。

3.4 未定义类型undefined

未定义是比较特殊的类型,只有一个值 undefined

只声明变量,不赋值的情况下,变量的默认值为 undefined,一般很少直接为某个变量赋值为 undefined。我们开发中经常声明一个变量,等待传送过来的数据。如果我们不知道这个数据是否传递过来,此时我们可以通过检测这个变量是不是undefined,就判断用户是否有数据传递过来。

3.5 空类型null

JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值。常把 null 作为尚未创建的对象,即将来有个变量里面存放的是一个对象,但是对象还没创建好,可以先给个null

???+ null 和 undefined 区别
undefined 表示没有赋值

null 表示赋值了,但是内容为空

3.6 使用typeof判断数据类型

typeof 运算符可以返回被检测的数据类型。它支持两种语法形式(用x表示变量):

  1. 作为运算符: typeof x (常用的写法)
  2. 函数形式: typeof(x)

3.7 类型转换

=== "隐式转换"
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。

`+` 号两边只要有一个是字符串,都会把另外一个转成字符串。除了+以外的算术运算符 比如 - * / 等都会把数据转成数字类型

???+ tip
    - `+` 号作为正号解析可以转换成数字型
    - 任何数据和字符串相加结果都是字符串

=== "强制转换"
转换成数字类型:

- `Number(数据)` 转成数字类型。如果字符串内容里有非数字,转换失败时结果为 NaN,NaN也是number类型的数据,代表非数字
- `parseInt(数据)` 只保留整数,可以以数字开头的字符串中提取整数

    ???+ tip "将秒数转换为时分秒"
        - 小时数: `h = parseInt(总秒数 / 60 / 60 % 24)`
        - 分钟数: `m = parseInt(总秒数 / 60 % 60)`
        - 秒数: `s = parseInt(总秒数 % 60)`

- `parseFloat(数据)` 可以保留小数,可以以数字开头的字符串中提取数字

转换成字符串类型:

`String(数据)`
`变量.toString(进制)`

转换为布尔值:

- 强制转换 `Boolean()` 函数 将 `''`, `0`, `undefined`, `null`, `false`, `NaN` 转换为false
- 进行数学加减法运算时 `''`, `null`, `false` 会转换为数字0

四、运算符

???+ info "优先级"
1. 小括号 ()
2. 一元运算符 自增 ++,自减 --,逻辑非 !
3. 算术运算符 按照数学运算顺序
4. 关系运算符 >, >=, <, <=
5. 相等运算符 ==, !=, ===, !==
6. 逻辑且 &&
7. 逻辑或 ||
8. 赋值运算符 =
9. 逗号运算符 ,

=== "算术运算符"
- +求和
- -求差
- *求积
- /求商
- %取模(取余数)

=== "赋值运算符"
- =
- +=
- -=
- *=
- /=
- %=

=== "自增自减运算符"
自增自减运算符可以放在变量之前,也可以放在变量之后,一般放在变量之后。

- `++` 自增运算符
- `--` 自减运算符

=== "比较运算符"
- >
- <
- >=
- <=
- == 值是否相等,不推荐使用
- != 不等于,不推荐使用
- === 类型和值是否都相等
- !== 左右两边是否不全等

???+ note
    涉及到NaN的比较返回false

=== "逻辑运算符"
- && 且
- || 或
- ! 非

五、分支语句

5.1 if语句

=== "单分支"
js if (条件) { 条件成立时执行的代码 }

=== "双分支"
js if (条件) { 条件成立时执行的代码 } else { 条件不成立时执行的代码 }

!!! tip "三目运算符"
    三目运算符可以替代简单的if双分支语句。

    语法:

    `条件? true时执行的代码: false时执行的代码`

=== "多分支"
js if (条件) { 代码 } else if (条件) { 代码 } else { 代码 }

5.2 switch语句

switch (变量名) {
    case 值1:
        代码
        break
    case 值2:
        代码
        break
    default:
        代码
        break          
}

???+ note
- 找到跟小括号里数据全等的case值,并执行里面对应的代码。若没有全等 === 的则执行default里的代码
- switch case语句一般用于等值判断,不适合于区间判断(if语句)
- switch case一般需要配合break关键字使用 没有break会造成case穿透

六、循环语句

6.1 while循环

while (循环终止条件) {
    循环体
    使循环终止的代码(一般是自增自减运算)
}

???+ note "循环语句三要素"
- 变量起始值
- 循环终止条件:当条件为true时才执行循环中的代码
- 使循环终止的代码:常用自增自减运算

6.2 for循环

for (变量起始值; 循环终止条件; 使循环终止的代码) {
    循环体
}

6.3 退出循环

break 语句,退出本次循环

continue 语句,退出整个循环语句

七、数组Array

使用let声明一个数组的两种方式:

  • let myArr = [元素1, 元素2, 元素3]
  • let myArr = new Array(元素1, 元素2, 元素3)

和python中的列表类似,不过下标索引只支持正向索引。通过数组的 length 属性,可以获取数组的长度。

使用for循环遍历数组

// let arr = new Array('js', 'html', 'css')
let arr = ['js', 'html', 'css']
for (let i = 0; i < arr.length; i++) {
    alert(arr[i])
}

??? question "算法——寻找数组中的最大值和最小值"
寻找数组[2, 6, 1, 77, 52, 25, 7]中的最大值和最小值。

```js
let arr = [2, 6, 1, 77, 52, 25, 7]
// 第一步:假设下标为0的元素就是最大值,也是最小值
let max = arr[0]
let min = arr[0]
// 第二步:for循环遍历,让假定的最值依次和其他元素比较
for (let i = 1; i < arr.length; i++) {
    // 第三步:如果找到比假定的最大值还要大的数,将这个数假定为最大值
    if (arr[i] > max) {
        max = arr[i]
    }
    // 第三步:如果找到比假定的最小值还要小的数,将这个数假定为最小值
    if (arr[i] < min) {
        min = arr[i]
    }
}
console.log(max) // 77
console.log(min) // 1
```

操作数组:

  • 重新赋值 arr[i] = 新值
  • 在末尾追加元素 arr.push('元素1', '元素2', '元素3') 方法,在数组末尾追加一个或多个元素
  • 在开头插入元素 arr.unshift('元素1', '元素2', '元素3') 方法,在数组开头插入一个或多个元素,返回新数组长度
  • 删除元素
    • arr.pop() 删除最后一个元素,返回这个被删除的元素
    • arr.shift() 删除第一个元素,返回这个被删除的元素
    • arr.splice(start, deleteCount) 其中start参数表示开始删除位置的下标,可选参数deleteCount表示删除元素的个数,此方法从start下标开始,删除deleteCount个元素,未传入deleteCount时,删除之后的所有元素
  • 排序 arr.sort() 数组元素从小到大排序,此操作会改变原数组
    // 默认,从小到大排序
    arr.sort(function(a, b) {
        return a - b
    })
    // 从大到小排序
    arr.sort(function(a, b) {
        return b - a
    })

??? question "算法——冒泡排序"
将数组[5, 4, 3, 2, 1]中的元素按照从小到大排序:

冒泡排序依次将最大的书往右边移动,每执行一趟,就会有一个数移动到最右侧。{==|==} 右侧是已经排好了的数字。

**分析:**

- 原顺序 ++5++ ++4++ ++3++ ++2++ ++1++
- 第0趟(需要比较4次):
    - 第0次比较 ++4++ ++5++ ++3++ ++2++ ++1++ {==|==}
    - 第1次比较 ++4++ ++3++ ++5++ ++2++ ++1++ {==|==}
    - 第2次比较 ++4++ ++3++ ++2++ ++5++ ++1++ {==|==}
    - 第3次比较 ++4++ ++3++ ++2++ ++1++ ++5++ {==|==}
- 第1趟(需要比较3次):
    - 第0次比较 ++3++ ++4++ ++2++ ++1++ {==|==} ++5++
    - 第1次比较 ++3++ ++2++ ++4++ ++1++ {==|==} ++5++
    - 第2次比较 ++3++ ++2++ ++1++ ++4++ {==|==} ++5++
- 第2趟(需要比较2次):
    - 第0次比较 ++2++ ++3++ ++1++ {==|==} ++4++ ++5++
    - 第1次比较 ++2++ ++1++ ++3++ {==|==} ++4++ ++5++
- 第3趟(需要比较1次):
    - 第0次比较 ++1++ ++2++ {==|==} ++3++ ++4++ ++5++


第一步:外层for循环(趟数是4趟,需要循环4次)数组总长度arr.length是5,因此循环终止条件是arr.length-1

    for (let i = 0; i < arr.length-1; i++) {
        内层for循环
    }

第二步:内层for循环(每趟需要比较的次数,需要比较 arr.length-1-趟序号 次)

    for (let j = 0; j < arr.length-i-1; j++) {
        相邻两数,如果左大于右,交换两数的值
    }

第三步:交换两个数的值

    if (arr[j] > arr[j + 1]) {
        let t = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = t
    }

```js
let arr = [5, 4, 3, 2, 1]
console.log(arr) // [5, 4, 3, 2, 1]

// 将此数组元素从小到大排序
for (let i=0; i < arr.length-1; i++) {
    for (let j = 0; j < arr.length-i-1; j++) {
        if (arr[j] > arr[j+1]) {
            let t = arr[j]
            arr[j] = arr[j+1]
            arr[j+1] = t
        }
    }
}
console.log(arr) // [1, 2, 3, 4, 5]
```

???+ tip "map方法(常用)"
数组的 .map(function (item, index) {}) 方法, 类似于python列表推导式,可以用于产生新数组. 也类似于python列表遍历的 enumerate() 函数,可以同时遍历数组的元素和下标(但是这种遍历方法不推荐使用,因为 .map() 方法是用来产生新元组的).

语法:

```js
const arr = ['py', 'js', 'html', 'css']
const newArr = arr.map(function (item, index) {
    console.log(item, index) // 遍历数组,依次输出数组元素和下标
    return item + '语言'
})
console.log(newArr) // ['py语言', 'js语言', 'html语言', 'css语言']
```

???+ info ".join()方法"
数组的 .join() 可以用来将数组中的元素拼接为一个字符串, 类似于python中的 ','.join(list) 方法,只不过js中的 .join() 不传参时,相当于传入了默认值 ','

```js
const arr = ['py', 'js', 'html', 'css']
const newArr1 = arr.join()
console.log(newArr1) // py,js,html,css
const newArr2 = arr.join('')
console.log(newArr2) // pyjshtmlcss
```

???+ tip ".forEach()方法"
.forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数. 这个方法没有返回值,它的作用就是遍历数组, 类似于python的enumerate()函数

语法:

```js
arr.forEach(function(item, index) {
    // 函数体
})

// 或
arr.forEach(function(item) {
    // 函数体
})
```

=== "Python"
    ```py
    mylist = ['py', 'c', 'js', 'css']
    for index, item in enumerate(mylist):
        print(index) # 遍历下标
        print(item) # 遍历列表
    ```

=== "JavaScript"
    ```js
    const arr = ['py', 'c', 'js', 'css']

    // forEach可以同时遍历数组元素和下标
    arr.forEach(function(item, index) {
        console.log(item) // 遍历数组
        console.log(index) // 遍历下标
    })
    ```

???+ tip ".filter()方法"
.filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素. 用于筛选数组符合条件的元素,并返回筛选之后元素的新数组

语法:

```js
arr.filter(function(item, index){
    return 筛选条件
})

// 或
arr.filter(function(item){
    return 筛选条件
})
```

返回值:返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组. 因为返回新数组,所以不会影响原数组

```js
const arr = [51, 2, 7, 15, 37, 20]

const newArr = arr.filter(function(item, index){
    return item > 20
})
console.log(newArr) // [51, 37]
```

八、函数

8.1 具名函数

function 函数名(参数) {
    函数体
    return 返回值
}
  • 定义函数时,参数可以为空,如果有多个参数,用逗号分隔
  • 如果定义函数时,没有设置默认值,那么此参数的默认值是undefined
  • 函数可以不设置返回值,无返回值的函数的返回值是undefined
  • 函数内部使用let声明的变量只在函数内部生效(作用域是函数内部),如果函数内外有同名的变量,先访问局部变量再访问全局变量(就近原则)

???+ info "逻辑中断"
另一种设置函数默认值的方式:
js function getSum(a, b) { a = a || 0 b = b || 0 return a + b } // 相当于 function getSum(a=0, b=0) { return a + b }
使用且&&、或||,当满足一定条件时只让左边执行,而右边不执行。无论 && 还是 || ,运算结果都是最后被执行的表达式值,一般用在变量赋值

- `&&` 左边为false就短路
- `||` 左边为true就短路

???+ note "函数命名规范"
- 小驼峰命名法
- 以动词开头,以下为常用动词及其含义:

| 动词 | 含义 |
|:---:|---|
| can | 判断是否可以执行某个操作 |
| has | 判断是否包含某个值 |
| is | 判断是否为某个值 |
| get | 获取某个值 |
| set | 设置某个值 |
| load | 加载某些数据 |

8.2 匿名函数

两种匿名函数:

  1. 函数表达式:将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为函数表达式
    // 定义匿名函数
    let 变量名 = function () {
        函数体
    }
    
    // 调用匿名函数
    变量名()
  2. 立即执行函数:不需要调用就能直接执行的匿名函数。多个立即执行函数之间使用分号隔开
    // 写法一
    (function () { 函数体 })();
    
    // 写法二
    (function () { 函数体 }());

九、对象

声明对象的两种方式:

  • 方式一 let 对象名 = {}
  • 方式二 let 对象名 = new Object()

对象由属性和方法组成:

let 对象名 = {
    属性名: 属性值,
    方法名: 函数
}
  • 对象中的属性名和属性值类似于python中的字典
  • 属性名值对使用逗号分隔
  • 属性名可以使用 "" 或 ''包裹,一般情况下省略,除非名称遇到特殊符号如空格、连字符、下划线等

操作对象:

  • 查询属性值:类似于python中的对象,可以使用 .属性名 访问对象属性值;也类似于python中的字典,可以使用 对象名[属性名] 来访问属性值,属性名需要使用单引号或双引号包裹
  • 更改属性值:类似于python中的对象和字典,可以对属性重新赋值
  • 新增属性:操作同上,如果属性不存在,则会新增属性
  • 删除属性:通过属性名删除属性 delete 对象名.属性名
  • 对象中的方法:定义在对象中的函数就是方法,方法中也可以定义函数参数。调用方法类似于python中的方法
    let obj = {
        sayHi: function() {
            alert('hi')
        }
    }

使用 for in 语句遍历对象,类似于python字典的遍历,此处获取属性值必须使用类似于python字典的写法:

let obj = {
    name: 'jack',
    age: 10,
    gender: 'male'
}
for (let k in obj) {
    console.log(k) // 属性名
    console.log(obj[k]) // 属性值
}

内置对象:

类似于python中的标准库。

内置对象 Math 提供一系列和数学计算相关的方法

  • random:生成0-1之间的随机数(包含0不包括1)
  • ceil:向上取整
  • floor:向下取整
  • max:找最大数
  • min:找最小数
  • pow:幂运算
  • abs:绝对值

???+ tip "生成随机整数"
js // 生成0到10之间的随机整数 Math.floor(Math.random * 11) // 生成5到10之间的随机整数 Math.floor(Math.random * 6) + 5 // 生成N到M之间的随机整数 Math.floor(Math.random * (M - N + 1)) + N