前端基础学习之JavaScript part one

Posted by OuterCyrex on July 20, 2024

一.变量、数组和流程控制

一.变量

JavaScript中变量的定义常常使用下述三种:

变量类型 特点
var 函数作用域,作用域提升,且全局变量可以被挂载到window上(不建议使用)
let 块级作用域,存在暂时性死区
const 常量,声明就需要初始化 通常用于声明数组和对象

下边对上述内容进行解释:

暂时性死区即从代码块开始变量声明语句执行完毕之间的区域内,该变量不可被使用。

1
2
3
4
5
console.log(name)
{
    var name = "Outer"
}
console.log(name)

var的作用域是全局的,不存在暂时性死区,即在声明变量的语句之前即可使用变量,且作用域是全局的。

实际开发中常常不使用var来声明变量。

let则存在暂时性死区,且作用域是块级

1
2
3
4
5
{
    let x = "Outer"
    console.log(x)
}
console.log(x)

上述代码中,第一个log会输出,第二个由于不在let的作用域内因此不会输出。

const定义常量,一旦声明便不可被修改

1
2
const a = 12
a = 18

Javascript是一种动态弱类型语言,因此对变量的类型要求不高。在JS中存在两种等号

等号类型 作用
== 判断数值是否相同
=== 判断数值和类型是否都相同

如:

1
2
3
4
5
6
7
let a = 1
let b = '1'
console.log(a==b)
console.log(a===b)
//输出
true
false

二.数据类型

Javascript中的数据类型有8种:

类型 作用
number 数值类型,范围为 ± 2^53 - 1
string 字符串类型
boolean 布尔类型
null 空值
undefined 未定义
object 对象(类似结构体)
symbol 表示唯一标识
bigInt 安全地存储和操作大整数

1.number和bigInt

number类型的数据范围是± 2^53 - 1,可以存储小数但存在一定精度误差。且注意作为一个函数时Number()的首字母N要大写

1
2
3
4
5
6
7
let a = 1
let b = Number(2)
console.log(a,typeof a)
console.log(b,typeof b)
//输出结果
1 'number'
2 'number'

且对于其他进制的数字,存入number类型的变量后会变为对应的十进制

1
2
3
4
5
6
const a2 = 0b110 //输出6
console.log(a2)
const a3 = 0o11 //输出9
console.log(a3)
const a4 = 0xa1 //输出161
console.log(a4)

其中对于Number类型的变量存在一些方法来实现一些效果:

方法或函数 作用
toFixed(n) 是方法,保留n为小数
Number.parseInt() 是函数,取整数部分,但一般是针对字符串类型
Number.isInteger() 判断一个数是否为整数
Math.round() 四舍五入保留整数
Math.floor() 上取整保留整数
Math.ceil() 下取整保留整数

注意,Number.parseInt()常常是针对字符串而言,因此对Number类型转换时编译会出现警告,但javascript弱类型语言,因此仍然可以进行转换。

1
2
3
4
5
6
7
8
let a = 3.1415926
console.log(a.toFixed(2))
console.log(Number.parseInt(a))
console.log(Number.isInteger(a))
//输出
3.14
3
false

此外,javascriptMath库还具有取整上取整下取整

1
2
3
4
5
6
console.log(Math.round(3.5)) // 四舍五入 4
console.log(Math.round(3.4)) // 四舍五入 3
console.log(Math.ceil(1.0000001)) // 向上取整 2
console.log(Math.ceil(1.9)) // 向上取整 2
console.log(Math.floor(1.9)) // 向下取整 1
console.log(Math.floor(1.001)) // 向下取整 1

Number不同的是,BigInt只能存储整数,且其字节大小是动态变化的,数值越大字节数越大,且BigInt类型不能直接与Number进行运算。

2.String

javascript中,""''是完全等价的,不需要做区分。

javascript中的String类型与Go中的有异曲同工之处。

1
2
3
4
5
6
7
const x="Outer"
console.log(x,typeof x)
const y=String("Cyrex")
console.log(y,typeof y)
//输出结果
Outer string
Cyrex string

go中的字符串相似,可以直接进行下标的索引。

1
2
3
const x="Outer"
console.log(x,typeof x)
console.log(x[1],typeof x[1])

go不同的一点是,javascript支持依据字符内容来查询下标,若未查询到则输出-1

1
2
3
4
5
6
const x="Outer"
console.log(x.indexOf(`t`))
console.log(x.indexOf(`z`))
//输出结果
2
-1

对于字符串类型,常见的方法包括:

方法 作用
indexOf 可以根据内部的字符串来检索对应的下标
split 根据某个特殊标识符来分割字符串,并返回数组
slice 可以根据下标获取对应的切片
includes 判断字符串内是否有匹配的子串
trim 去除字符串中的空格

对上述的一些方法进行简要的介绍,首先介绍split

1
2
3
4
5
const x="Outer,Cyrex"
const a=x.split(',')
console.log(a[0])
//输出结果
Outer

其次是slice,会根据输入的下标进行切片,如果输入负数,则意味着从字符串末尾开始计数,即负数代表的下标 = 字符串长度 + 负数

1
2
3
4
5
const x="OuterCyrex"
const a = x.slice(5,10)
console.log(a)
//输出结果
Cyrex

3.Boolean

布尔值只有两种情况:truefalse

除了上述两种情况外,也可以将特定的值强转为布尔值

1
2
3
4
5
6
7
8
console.log(Boolean(0))
console.log(Boolean(''))
console.log(Boolean(null))
console.log(Boolean(undefined))
console.log(Boolean(false))
console.log(Boolean(NaN))
//注意区分,上述均为false,下方的为true
console.log(Boolean(' '))

4.object和symbol

javascript中的object结构体相似但差别也较大,更像是一个映射组。

1
2
3
4
5
let Obj={
    name:"Outer",
    age:19,
}
console.log(Obj)

也可以为其字段名加上双引号或单引号

对于内容的检索存在两种方法,一种是类似结构体的方案,即对象名.字段名,另一种是类似映射的方案,即对象名["字段名"]

1
2
3
console.log(Obj.name,Obj["name"])
//输出结果
Outer Outer

对象的字段进行增删改的操作如下:

javascript中,由于是弱类型语言,其并不严格要求对象的字段信息,定义之后仍然可以进行增删字段。

增改的操作是一样的,只需要赋值即可。

若字段不存在则增加新的字段,若存在则对已有的字段进行更改

1
2
3
4
5
6
7
let Obj={
    name:"Outer",
    age:19,
}
Obj.sex="male"
Obj.name="Cyrex"
console.log(Obj)

删除操作则需要使用delete函数。

1
2
3
4
5
6
let Obj={
    name:"Outer",
    age:19,
}
delete Obj.age
console.log(Obj)

此外,我们如果想让某个变量的内容作为字段名,可以使用[]来实现

1
2
3
4
5
const x = "name"
const obj = {
    [x]: "Outer"
}
console.log(obj)

上述代码便实现了将x的内容name作为字段名的效果。

javascript中存在一种symbol类型,可以使字段名重复存在

1
2
3
4
5
6
7
8
9
const x = Symbol("name")
const y = Symbol("name")
let Obj = {
    [x]:"Outer",
    [y]:"Cyrex",
}
console.log(Obj)
//输出结果
{Symbol(name): 'Outer', Symbol(name): 'Cyrex'}

可以理解为Symbolstring不同点在于Symbol给每个字符串一个特殊的标识来区分内容一样的字符串。

5.null和undefined

null值类似于go语言中的nil,即空值空值确切存在的一个值,但是其内容为空。

注意,null的数据类型为Object,类似与go中的空接口interface{}

null不同的是,undefined表示值根本不存在或违背定义。

出现undefined的情况包括:

  • 变量被声明了,但没有赋值时,就等于undefined
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined
  • 对象没有赋值的属性,该属性的值为undefined
  • 函数没有返回值时,默认返回undefined

比较有意思的是,nullundefined被认为是数值相等的。

1
2
3
4
5
console.log(null == undefined)
console.log(null === undefined)
//输出结果
true
false

三.数组

1.定义数组

javascript中创建数组有两种方式:一种是直接创建,另一种是通过Array函数来创建

1
2
3
4
5
6
7
8
9
const a = ["Outer","Cyrex"]
console.log(a)
const b = Array("Outer","Cyrex")
console.log(b)
//输出结果
Array(2)
0: "Outer"
1: "Cyrex"
length: 2

2.遍历数组

javascript中,有两种数组遍历方式:

1
2
3
4
5
6
7
let a = Array(1,2,3,4,5,6,7,8,9,10)
for(let i=0;i<a.length;i++){
    console.log(a[i])
}
for(const value of a){
    console.log(value)
}

其中,for of方法类似于go语言中的for range方法,但其不能获取索引,因此存在一定弊端。

3.数组方法

首先介绍一个函数:

javascript中存在很多判断类型的函数,数组对应的判断类型函数便是isArray。也可以使用instanceof来实现判断。

注意typeof获取的数组类型实际是Object,这也就是为什么数组并不是八大数据类型之一,其本质是一种以下标为key的对象。

1
2
3
4
5
6
7
console.log(Array.isArray(a1))
console.log(Array.isArray(1))
console.log(a1 instanceof Array)
//输出结果
true
false
true

下边介绍javascript中的数组方法:

方法 作用
push() 在数组的末端添加一个或多个元素,并返回添加之后数组的长度
pop() 移除数组末端的元素并返回其值
shift() 从数组的开头移除元素,并返回其值
unshift() 从数组的开头插入新元素,并返回添加之后数组的长度
join() split方法互逆,添加指定的字符串内容到数组元素之间,并返回插入之后的字符串
sort() 将数组进行升序排列,通过reverse()方法反转可以获得降序
splice() 在数组中截取一个切片,并返回被截取的数组内容
forEach() 迭代每一个元素,不可被终止且无返回值
map() 迭代每一个元素并进行指定从操作,返回值为进行操作后的数组
filter() 迭代每一个元素,获取满足函数内条件的元素并组建一个新数组,返回该新数组
every() 判断数组是否每一个元素都满足条件
some() 判断数组是否存在某一元素满足条件
find() 获取数组中满足条件的数据,如果有 就是满足条件的第一个数据;如果没有就是undefined

前四个方法是基于数据结构中的队列来使用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let a = Array(1,2,3,4,5,6,7,8,9,10)
let last = a.pop()
console.log(last)
a.push(11)
console.log(a)
let first = a.shift()
console.log(first)
a.unshift(0)
console.log(a)
//输出结果
last:10
push后的数组[1, 2, 3, 4, 5, 6, 7, 8, 9, 11]
first:1
unshift后的数组[0, 2, 3, 4, 5, 6, 7, 8, 9, 11]

之后是join的用途,用于在数组的各个元素之间插入指定的字符:

1
2
3
4
5
let a = Array(1,2,3,4,5,6,7,8,9,10)
let str = a.join(',')
console.log(str)
//输出结果
1,2,3,4,5,6,7,8,9,10

sort用于将元素按升序进行排列,但在JavaScript中,Array.prototype.sort() 方法默认是将所有元素转换为字符串,然后按照字符串的Unicode码点进行排序。

1
2
3
4
5
let a = Array('c','w','a','f','g','h','v','c','w')
a.sort()
console.log(a)
//输出结果
['a', 'c', 'c', 'f', 'g', 'h', 'v', 'w', 'w']

为了解决这种情况,实现按照数字大小来排序,则需要再sort()内传入一个函数。

sort()内函数传回的是负值,则将元素进行调换,若为正值或0则不进行调换。

1
2
3
4
5
6
7
let numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort(function(a, b) {  
  return a - b
})
console.log(numbers)
//输出结果
[1, 1, 2, 3, 4, 5, 6, 9]

此时sort()会将每相邻两个元素放入函数function(a, b) {return a - b}中,若传回负值则调换顺序,否则不变。以此来实现按数字大小排序

1
2
3
4
5
6
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
b = numbers.splice(4,7)
console.log(b,numbers)
//输出结果
[5, 9, 2, 6, 41, 55]
[3, 1, 4, 1]

接下来进行介绍的是ES6版新增的方法

首先是forEach(),其参数为一个函数(很神奇的是,和go一样,javascript中函数也是第一公民),可以对数组的所有元素进行迭代并放入该函数中,且不可终止

1
2
3
4
5
6
7
8
9
10
numbers.forEach(function(value, index,array){
    numbers.pop()
    console.log(value,index,array)
})
//输出结果
3 0 (9) [3, 1, 4, 1, 5, 9, 2, 6, 41]
1 1 (8) [3, 1, 4, 1, 5, 9, 2, 6]
4 2 (7) [3, 1, 4, 1, 5, 9, 2]
1 3 (6) [3, 1, 4, 1, 5, 9]
5 4 (5) [3, 1, 4, 1, 5]

这个函数可以选择三个参数,分别是元素的值value,下标index和当前迭代操作完成之后的数组array

注意,上述代码进行到5结束是由于pop()导致数组最后无法继续迭代

之后是map,其与forEach不同的是,他会返回操作后的新数组,而并不是在原数组上进行修改

1
2
3
4
5
6
7
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
let newArray = numbers.map(function(value,index,array){
    return value - index
})
console.log(newArray)
//输出结果
[3, 0, 2, -2, 1, 4, -4, -1, 33, 46]

filter方法会筛选出满足函数内条件的元素,将这些元素组建出新的数组并返回,函数内return ture的元素会被纳入数组

1
2
3
4
5
6
7
8
9
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
let newArray = numbers.filter(function(value){
    if(value > 10){
        return true
    }
})
console.log(newArray)
//输出结果
[4, 5, 9, 6, 41, 55]

every()some()的返回值都是布尔值Boolean,用于判断数组内元素是否满足函数内条件,every()只有在均满足时返回true,而some()则是存在满足的元素即返回true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
let output = numbers.every(function(value){
    if(value > 3){
        return true
    }
})
console.log(output)
output = numbers.some(function(value){
    if(value > 3){
        return true
    }
})
console.log(output)
//输出结果
false
true

find则类似ORM框架中的take,会获取第一个满足条件的元素的值

1
2
3
4
5
6
7
8
9
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
let output = numbers.find(function(value,index){
    if(value > 3){
        return true
    }
})
console.log(output)
//输出结果
4

也可以使用findIndex来返回第一个满足条件的元素的下标,若没找到对应元素则返回-1

1
2
3
4
5
6
7
8
9
let numbers = [3, 1, 4, 1, 5, 9, 2, 6,41,55]
let index = numbers.findIndex(function(value,index){
    if(value > 3){
        return true
    }
})
console.log(index)
//输出结果
2

四.流程控制

1.逻辑运算符

javascript中的的与、或、非分别为&&||!

1
2
3
console.log(true && false)
console.log(true || false)
console.log(!true)

javascript中,存在一种特殊的机制,逻辑短路,即若已经能确定该逻辑语句的值,javascript便不会读取之后的内容

  • &&: 如果前面的条件是false,那就不会去判断后面的条件了

  • ||:如果前端的条件是true,那就不会去判断后面的条件了

1
2
3
4
5
6
7
8
9
function t(){
    console.log("t")
    return true
}
console.log(false && t())
console.log(true || t())
//输出结果
false
true

其中若函数被运行时会输出't',以此来检测是否会运行该函数。

2.流程控制语句

由于javascript中的大部分流程控制语句与C语言的内容一致,因此不过多讲解

选择语句仍然是if ... else ...

1
2
3
4
if (condition)  
    console.log("条件为真");  
else  
    console.log("条件为假");

switch语句也与C语言中的完全一致。如若无break便会继续运行其余分支。

1
2
3
4
5
6
7
8
9
10
11
switch (表达式或变量) {
    case  值1:
        代码块;
        break;
    case  值2:
        代码块;
        break;
    ....
    default:
        以上条件都不成立执行此代码块
}

javascript中的特色在于其for循环语句,在javascript中有三种for循环的方法:

for循环语句 运用情景
for(初始条件;循环条件;迭代条件) 最基础最传统的for循环语句
for in语句 用来遍历对象的可枚举属性列表
for of语句 用于快速的遍历数组和对象

传统的for循环与C语言基本一致,在此不作说明。

for ... in是用来检索对象中的key值(或称为字段名),对于数组而言便是输出下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
    name:"Outer",
    age: 18
}
for (const objKey in obj) {
    console.log(objKey)
}
a = Array(1,2)
  for (const index in a) {
      console.log(index)
  }
//输出结果
name,age
0,1

for of在之前的数组知识里已经进行过介绍,在此不作重复介绍。

javascript中的whiledo ... whilecontinuebreak也与C语言基本一致,不再介绍。

1
2
3
4
5
6
7
while (条件){
    循环体
}

do {
	循环体
} while (条件表达式)

do ... while相较于while而言会先运行一次循环题体。