0%

单例模式

单例模式下的类只能存在一个实例对象

非单例模式实例

在 JavaScript 中, 我们可以通过 class 类来实例化对象, 我们 new 出两个对象, 但是两个对象是没有直接关联的, 他们储存在不同的内存地址中, 不符合我们的单例模式

1
2
3
4
5
6
7
8
class Singleton {
show() {
console.log('单例对象')
}
}
const s1 = new Singleton()
const s2 = new Singleton()
s1 === s2 // false 引用类型储存在堆的不同内存地址中

单例模式实例

如果要实现单例模式, 我们需要保证只能存在一个实例对象, 这就要求我们的类具有是否已存在实例对象的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 类 - 静态方法模拟
class Singleton {
show() {
consle.log('我是一个单例对象')
}
static getInstance() {
// 静态方法 重点
if(!Singleton.instance){
// 判断是否已存在一个实例
Singleton.instance = new Singleton() // 如果没有就创建一个
}
return Singleton.instance
}
}

// 闭包实现单例模式
function Singleton(){}
Singleton.prototype.getInstance = (function(){
let instance // 定义局部变量模拟 重点
return function() {
if(!instance) {
instance = new Singleton()
}
return instance
}
})()

单例模式应用

通过单例模式管理全局唯一存在的一个 Model 模态框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<style>
div {
width: 200px;
height: 200px;
line-height: 200px;
border: 1px solid pink;
color: pink;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
</style>
<title>Document</title>
</head>
<body>
<button id="show">显示全局模拟框</button>
<button id="hidden">隐藏全局模拟框</button>
<script>
window.onload = function() {
let showbtn = document.getElementById('show')
let hiddenbtn = document.getElementById('hidden')

// 闭包实现
const Modal = (function() {
let modal = null
return function() {
if (!modal) {
modal = document.createElement('div')
modal.innerText = '我一个单例的模态框'
modal.style.display = 'none'
modal.style.color = 'pink'
document.body.appendChild(modal)
}
return modal
}
})()

showbtn.addEventListener(
'click',
function() {
const modal = new Modal()
modal.style.display = 'block'
},
false
)

hiddenbtn.addEventListener(
'click',
function() {
const modal = new Modal()
modal.style.display = 'none'
},
false
)
}
</script>
</body>
</html>

1
知识点 - 数组 - 函数 - 作用域 - 作用域链

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 数组:一组有序的数据
// 数组的作用:可以一次性储存多个数据
// 数组元素:数组中存储的每个数据,都可以叫数组的元素,比如:存储了3个数据,数组中3个元素
// 数组长度:就是数组的元素的个数,比如有3个元素,就说,这个数组的长度是3
// 数组索引(下标):用来存储或者访问数组中的数据的,索引从0开始,到长度减1结束
// 数组的索引和数组的长度的关系:长度减1就是最大的索引值

// 1、构造函数创建数组
var arr = new Array() // 定义了一个叫arr的空数组
var arr = new Array(10) // 定义了一个长度为10的数组,数组中每个数据为undefiend

// 2、字面量方式创建数组
var arr = [] // 创建了一个空数组
var arr = [1, 2, 3, 4] // 创建了一个数组并赋值

// 获取数组中所有值
console.Log(arr) // 打印数组与值
for(var i=0; i<arr.length; i++){ // 循环遍历获取数组中所有元素
console.log(arr[i])
}
// 设置数组的值
arr[1] = 100 // 设置数组中下标为1的元素为100
// 获取数组中某个位置的值
var result = arr[1] // 获取数组中下标为1的元素
// 获取数组的长度
console.log(arr.length) // 打印arr数组的长度

// 总结数组
// 数组:存储一组有序的数据
// 数组的作用:一次性存储多个数据
// 数组的定义方式:
// 1.构造函数定义数组: var 数组名=new Array();
// 2.字面量方式定义数组: var 数组名=[];
// var 数组名=new Array();空数组
// var 数组名=new Array(值);数组定义了,有长度
// var 数组名=new Array(值1,值2,值3....);定义数组并且有多个数据
// var 数组名=[];空数组
// var 数组名=[值1,值2,值3];有三个数据
// 数组元素:就是数组中存储的数据
// 数组长度:就是数组中元素的个数
// 数组索引(下标):从0开始,到数组的长度减1结束
// 通过下标设置数组的元素值: 数组名[索引]=值
// 通过下标访问数组的元素值: 数组名[索引]

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
// 把数据按照一定的顺序排列(从小到大或从大到小)
var arr = [1, 3, 5, 7, 9, 2, 4, 6, 8]
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var tmp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = tmp
}
}
}

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 函数:把重复的代码进行封装,在需要的时候直接调用函数即可。
// 函数作用:代码的重用(重复使用)
// 函数也是一种数据类型 --> function 类型

// 命名函数(有名字的函数叫命名函数)
function f1(){ // 使用function关键字定义了一个叫fn的函数 --> 函数声明
console.log('函数体-->重复的代码')
}
f1() // 函数的调用-->执行函数体中的代码

// 匿名函数(函数如果没名字,就是匿名函数)
var f2 = function(){ // 函数表达式
console.log('匿名函数')
}
f2() // 匿名函数的调用

// 函数的自调用
(function(){
console.log('函数的自调用')
})(); // 函数声明的同时就调用

// 回调函数
// 如果一个函数作为参数,那么这个参数(函数),可以叫做回调函数
function f2(fn){
fn()
}
function f3(){
console.log('f3')
}
f2(f3) // 将 f3 函数作为参数传入 f2 函数,在 f2 函数内部执行 f3 函数


// 形参与实参
// 函数在定义时,函数名后面的小括号里面的变量叫形参
// 函数在调用时,函数名后面的小括号里面的变量或值叫实参
// 函数返回值,在函数内部有return关键字,并且在关键字后面有内容,这个内容被返回了
// 例子
function sum(num1, num2){ // 形参
var sum = num1 + num2
console.log(sum) // 打印结果 30
return sum // return 关键字 函数的返回值
}
var result = sum(10, 20) // (10,20)->实参 result->接受函数返回值

// arguments对象伪数组
// 如果一个函数不确定用户是否传入了参数,或者不知道传入了几个参数,可以使用 arguments 进行处理
function fn(){
// arguments对象可以获取传入的每个参数的值
console.log(arguments.length) // 5
for(var i=0;i<arguments.length; i++){
console.log(arguments[i]) // 输出每个实参的值
}
}
fn(1,2,3,4,5)

// 小结函数
// 如果一个函数中有return ,那么这个函数就有返回值
// 如果一个函数中没有return,那么这个函数就没有返回值
// 如果一个函数中没有明确的返回值,那么调用的时候接收了,结果就是undefined
// 没有明确返回值:函数中没有return,函数中有return,但是return后面没有任何内容
// 函数没有返回值,但是在调用的时候接收了,那么结果就是undefined
// 变量声明了,没有赋值,结果也是undefined
// 如果一个函数有参数,有参数的函数
// 如果一个函数没有参数,没有参数的函数
// 形参的个数和实参的个数可以不一致
// return 下面的代码是不会执行的

作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 作用域:使用范围

// 全局变量:在函数外部使用 var 关键字定义的变量。
// 局部变量:在函数内部定义的变量是局部变量,外面不能使用。
// 隐式全局变量:在函数内部没有使用 var 定义的变量

// 全局作用域:全局变量的使用范围
// 局部作用域:局部变量的使用范围

// 示例
var num1 = 100 // 全局变量
function num() {
var num2 = 200 // 局部变量
num3 = 300 // 隐式全局变量
}
console.log(num1) // 100
console.log(num2) // undefiend
console.log(num3) // 300

作用域链

1
2
3
4
5
6
7
8
9
10
11
var num = 10
function f1() {
function f2() {
function f3() {
console.log(num)
}
f3()
}
f2()
}
f1() // 打印输出 10 一层一层往上寻找 num 的值

预解析(变量(函数)提升)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 预解析:提前解析代码,提升变量的声明与函数的声明
// 预解析做什么事?
// 把变量的声明提前了----提前到当前所在的作用域的最上面
// 函数的声明也会被提前---提前到当前所在的作用域的最上面

console.log(num) // 打印 undefined 不报错
var num = 10

f1() // 打印 '函数预解析'
function f1() {
console.log('函数预解析')
}

var num1 = 100
function f1() {
// var num1 等价于增加了此行代码
console.log(num1) // 变量提升到了作用域最上面 --> 输出 undefined
var num1 = 200
}

// 预解析小结
// 预解析中,变量的提升,只会在当前的作用域中提升,提前到当前的作用域的最上面
// 函数中的变量只会提前到函数的作用域中的最前面,不会出去
// 预解析会分段(多对的script标签中函数重名,预解析的时候不会冲突)

1
知识点: -分支 - 循环

流程控制

1
2
3
4
// 流程控制:控制代码的执行过程
// 1、顺序结构:从上到下,从左到右执行的顺序,就是顺序结构(不严谨)
// 2、分支结构:if语句、if-else语句、switch-case语句
// 3、循环结构:while循环、do-while循环、for循环、for-in循环

分支语句-主要用于判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// if语句-(如果)
// 语法:
if (1 < 3) {
console.log('1是小于3的')
}
// 执行过程:
// 先判断表达式结果是否成立(true/false),如果是true则执行代码块,如果是false,大括号中的代码不执行

// if-else语句-(如果,否则)
// 语法:
if (1 < 3) {
console.log('1小于3') // 代码1
} else {
console.log('1大于3') // 代码2
}
// 执行过程:
// 如果表达式结果成立(true/false),这执行代码1,如果表达式结果是false,这执行代码2

// 三元表达式
// 语法
var result = 1 < 3 ? '小于' : '不小于'
// 执行过程:
// 表达式(1<3)的结果是true还是false,如果是true,则result = '小于',如果是false,则result = '不小于'

// if-else if-else if-else
// 语法
if (表达式1) {
代码1
} else if (表达式2) {
代码2
} else if (表达式3) {
代码3
} else {
代码4
}
// 执行过程
// 先判断表达式1的结果,如果为true,则执行代码1,如果是false,再判断表达式2的结果,如果为true就执行代码2,如果是false再判断表达式3的结果,如果为true就执行代码3,如果上面表达式都是false,就执行代码4

// switch-case语句
// 语法:
switch (表达式) {
case1:
代码1
break
case2:
代码2
break
case3:
代码3
break
default:
代码4
}
// 执行过程
// 获取表达式的值,和值1比较,如果一样,就执行代码1,遇到break,跳出整个语句。后面代码不执行,
// 如果和值1不一样,就和值2比较,然后执行代码2,遇到break,跳出整个语句,后面代码不执行。
// 以此类推。。。
// 如果都不一样,执行代码4

// 总结分支语句
// if语句:一个分支
// if-else语句:两个分支,最终只执行一个分支
// if-else if-else if...语句: 多个分支,也是只会执行一个
// switch-case语句:多分支语句,最终也会一个(必须要有break)
// 三元表达式:和if-else语句是一样的
// 什么时候使用if-else if...: 一般是对范围的判断
// 什么时候使用switch-case语句:一般是对具体的值的判断
// 如果有多个分支,是针对范围的判断一般选择if-else if的语句
// 如果有多个分支,是针对具体的值判断,一般选择用switch-case语句

循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 循环:一件事不停的或反复的去做
// 循环要有结束的条件,循环还应该有记录循环次数的计数器

// while循环
// 语法:
var num = 0 // 计数器
while (num <= 5) {
console.log('循环体')
num++
}
// 执行过程
// 先判断条件是否成立,如果为true,那么执行循环体中的代码,计数器加一,再继续判断条件是否成立,如果为true,那么继续执行循环体中的代码,计数器继续加一,再判断循环条件,直到循环条件不成立,循环结束。
// 如果循环的条件一直为true,那么循环体中代码会一直执行,这种称为死循环。我们应该尽量避免死循环。

// do-while循环
// 语法:
var num = 1
do {
console.log('循环体')
} while (num <= 5) // 条件
// 执行过程:
// 先执行一次循环体,然后判断条件是否成立,不成立就跳出循环,成立就执行循环体,然后再判断。。以此类推。。直到条件不成立,跳出循环。

// while循环特点:先判断,后循环,有可能一次循环体都不执行
// do-while循环特点:先循环,后判断,至少执行一次循环体

// for循环
// 语法:
for (var i = 0; i < 5; i++) {
// 表达式1、2、3
console.log('循环体')
}
// 执行过程
// 先执行一次表达式1,然后判断表达式2,如果不成立,就跳出循环,如果成立,就执行循环体的代码,执行完毕后跳到表达式3,然后跳到表达式2,判断表达式2是否成立。。以此类推。。直到表达式2不成立后跳出循环

break 与 continue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//break关键字:如果在循环中使用,遇到了break,则立刻跳出当前所在的循环
for (var i = 0; i < 10; i++) {
while (true) {
console.log('哈哈')
break // 遇到break 跳出循环
}
}

//continue:在循环中如果遇到continue关键字,直接开始下一次循环
//案例:求100-200之间所有的奇数的和
var sum = 0
var i = 100
while (i <= 200) {
//判断是不是偶数
if (i % 2 == 0) {
//如果是偶数----->跳过这个数字
i++ //102
continue
}
sum += i
i++
}
console.log(sum)

1
知识点: -分支 - 循环

流程控制

1
2
3
4
// 流程控制:控制代码的执行过程
// 1、顺序结构:从上到下,从左到右执行的顺序,就是顺序结构(不严谨)
// 2、分支结构:if语句、if-else语句、switch-case语句
// 3、循环结构:while循环、do-while循环、for循环、for-in循环

分支语句-主要用于判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// if语句-(如果)
// 语法:
if (1 < 3) {
console.log('1是小于3的')
}
// 执行过程:
// 先判断表达式结果是否成立(true/false),如果是true则执行代码块,如果是false,大括号中的代码不执行

// if-else语句-(如果,否则)
// 语法:
if (1 < 3) {
console.log('1小于3') // 代码1
} else {
console.log('1大于3') // 代码2
}
// 执行过程:
// 如果表达式结果成立(true/false),这执行代码1,如果表达式结果是false,这执行代码2

// 三元表达式
// 语法
var result = 1 < 3 ? '小于' : '不小于'
// 执行过程:
// 表达式(1<3)的结果是true还是false,如果是true,则result = '小于',如果是false,则result = '不小于'

// if-else if-else if-else
// 语法
if (表达式1) {
代码1
} else if (表达式2) {
代码2
} else if (表达式3) {
代码3
} else {
代码4
}
// 执行过程
// 先判断表达式1的结果,如果为true,则执行代码1,如果是false,再判断表达式2的结果,如果为true就执行代码2,如果是false再判断表达式3的结果,如果为true就执行代码3,如果上面表达式都是false,就执行代码4

// switch-case语句
// 语法:
switch (表达式) {
case1:
代码1
break
case2:
代码2
break
case3:
代码3
break
default:
代码4
}
// 执行过程
// 获取表达式的值,和值1比较,如果一样,就执行代码1,遇到break,跳出整个语句。后面代码不执行,
// 如果和值1不一样,就和值2比较,然后执行代码2,遇到break,跳出整个语句,后面代码不执行。
// 以此类推。。。
// 如果都不一样,执行代码4

// 总结分支语句
// if语句:一个分支
// if-else语句:两个分支,最终只执行一个分支
// if-else if-else if...语句: 多个分支,也是只会执行一个
// switch-case语句:多分支语句,最终也会一个(必须要有break)
// 三元表达式:和if-else语句是一样的
// 什么时候使用if-else if...: 一般是对范围的判断
// 什么时候使用switch-case语句:一般是对具体的值的判断
// 如果有多个分支,是针对范围的判断一般选择if-else if的语句
// 如果有多个分支,是针对具体的值判断,一般选择用switch-case语句

循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 循环:一件事不停的或反复的去做
// 循环要有结束的条件,循环还应该有记录循环次数的计数器

// while循环
// 语法:
var num = 0 // 计数器
while (num <= 5) {
console.log('循环体')
num++
}
// 执行过程
// 先判断条件是否成立,如果为true,那么执行循环体中的代码,计数器加一,再继续判断条件是否成立,如果为true,那么继续执行循环体中的代码,计数器继续加一,再判断循环条件,直到循环条件不成立,循环结束。
// 如果循环的条件一直为true,那么循环体中代码会一直执行,这种称为死循环。我们应该尽量避免死循环。

// do-while循环
// 语法:
var num = 1
do {
console.log('循环体')
} while (num <= 5) // 条件
// 执行过程:
// 先执行一次循环体,然后判断条件是否成立,不成立就跳出循环,成立就执行循环体,然后再判断。。以此类推。。直到条件不成立,跳出循环。

// while循环特点:先判断,后循环,有可能一次循环体都不执行
// do-while循环特点:先循环,后判断,至少执行一次循环体

// for循环
// 语法:
for (var i = 0; i < 5; i++) {
// 表达式1、2、3
console.log('循环体')
}
// 执行过程
// 先执行一次表达式1,然后判断表达式2,如果不成立,就跳出循环,如果成立,就执行循环体的代码,执行完毕后跳到表达式3,然后跳到表达式2,判断表达式2是否成立。。以此类推。。直到表达式2不成立后跳出循环

break 与 continue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//break关键字:如果在循环中使用,遇到了break,则立刻跳出当前所在的循环
for (var i = 0; i < 10; i++) {
while (true) {
console.log('哈哈')
break // 遇到break 跳出循环
}
}

//continue:在循环中如果遇到continue关键字,直接开始下一次循环
//案例:求100-200之间所有的奇数的和
var sum = 0
var i = 100
while (i <= 200) {
//判断是不是偶数
if (i % 2 == 0) {
//如果是偶数----->跳过这个数字
i++ //102
continue
}
sum += i
i++
}
console.log(sum)

1
2
3
4
5
知识点:
- 变量
- 注释
- 数据类型
- 运算符

变量

变量—区分大小写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 变量的作用: 用来操作数据的(可以存储,可以读取)
var admin // 变量的声明->只声明,没有赋值
var name = '周杰伦' // 变量的初始化->声明并赋值

// 1.使用var关键字(variable:可变的量)
var uname // undefined
uname = '周杰伦' //赋值

// 2.声明变量即赋值(推荐)
var unam = '周杰伦'

// 3.声明多个变量
var unam = '周杰伦',
age = 20

变量名的注意问题—变量名的命名规范,要遵循驼峰命名法

* 1.变量的名字要有意义,
* 2.变量名有一定的规范:一般以字母,$符号,下划线开头,中间或者后面可以有$符号,字母,数字
* 3.变量名一般都是小写的
* 4.变量名如果是多个单词,第一个单词的首字母是小写的,后面的所有的单词的首字母都是大写的,这种命名方式称为:驼峰命名法
* 5.不能使用关键字(系统自带的一些单词,不能使用)
* 6.不会单词用拼音,拼音也要遵循驼峰命名法

注释

1
2
3
4
5
6
7
8
9
10
11
12
13
//注释是解释代码的含义,浏览器不会对注释内容进行解析执行

// 单行注释

/*
* 多行注释
* 建议在代码关键处添加注释
* 不写注释-->写的时候只有你和上帝看的懂,写完之后你的代码只有上帝看得懂
**/

/**
* 另外一种多行注释
*/

数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
// 原始数据类型(6种)
var num = 10 //number 数字类型(整数和小数)
var str = '周杰伦' //string 字符串类型(一般用单引号或双引号包裹)
var flag = true //boolean 布尔类型(值只有两个 true(真/1) false(假/0))
var nll = null //null 空类型 值只有一个null 一个对象指向为空,可以赋值为null
var undef //undefined 未定义值只有一个undefined(变量声明了,没有赋值 函数没有返回值)
var obj = new Object() //object 对象

NaN // 特殊(not an number) undefined加数字会出现NaN

// 获取数据类型
typeof num // number
typeof num // number

### 类型转行

其他类型转数字类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 转整数
parseInt('10') // 10 转整数
parseInt('12abc') // 12
parseInt('1a1') // 1 遇到非数字就停止
parseInt('abc12') // NaN

// 转数字
Number('10') // 10
Number('12abc') // NaN
Number('1.1') // 1.1
Number('abc12') // NaN

// 转小数
parseFloat('12') // 10
parseFloat('12.12') // 12.12
parseFloat('abc12') // NaN

其他类型转字符串

1
2
3
4
5
// 如果变量有意义调用.tostring()使用转换  // undefined(无意义)
// 如果变量没有意义使用string()转换
var num = 20
num.toString() // '20'
String(num) // '20'

其他类型转布尔类型

1
2
3
4
5
6
7
8
// console.log(Boolean(1)) //true
// console.log(Boolean(0)) //false
// console.log(Boolean(11)) //true
// console.log(Boolean(-10)) //true
// console.log(Boolean("哈哈")) //true
// console.log(Boolean("")) //false
// console.log(Boolean(null)) //false
// console.log(Boolean(undefined)) //false

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 算数运算符:  +  -  *  /  %
// 算数运算表达式:由算数运算符连接起来的表达式

// 一元运算符: 这个操作符只需要一个操作数就可以运算的符号 ++ --
// 二元运算符: 这个操作符需要两个操作数就可以运算,
// 三元运算符: 10 == 10 ? true:false

// 复合运算符: += -= *= /= %=
// 复合运算表达式:由复合运算符连接起来的表达式

// 关系运算符: > < >= <= ==不严格的 ===严格的 !=不严格的不等 !==严格的不等
// 关系运算表达式:由关系运算符连接起来的表达式
// 关系运算表达式的结果是布尔类型

// 逻辑运算符:
// &&---逻辑与--并且
// ||---逻辑或---或者
// !---逻辑非---取反--取非
// 逻辑运算表达式:由逻辑运算符连接起来的表达式
// 表达式1 && 表达式2
// 如果有一个为false,整个的结果就是false
// 表达式1 || 表达式2
// 如果有一个为true,整个的结果为true

// !表达式1 取反
// 表达式1的结果是true,整个结果为false
// 表达式1的结果是false,整个结果为true

// 赋值运算符: =
var num = 12

一元运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 基础  ++与--
var num = 10
num++ // 相当于num = num + 1 输出 11
num-- // 相当于num = num - 1 输出 9

// 进阶
var num = 10
var sum = num++ + 10 // sum = 20
// 如果++在后面:如上: num++ + 10参与运算
// 先运算 再加加

var sum1 = ++num + 10 // sum1 = 21
// 如果++在前面 如上: ++num + 10
// 先加加 再运算加10

JavaScript –> 简称 js

JavaScript 历史

​ 1995 年,网景公司希望能在静态 HTML 页面上添加一些动态效果, 让 Brendan Eich 设计出了 JavaScript 语言(用时 2 周)

JavaScript 分为三个部分:

  • ECMAScript 标准—-js 的基本语法
  • DOM—Document Object Model 文档对象模型
  • BOM—Browser Object Model 浏览器对象模型

JavaScript 是什么?

  • 是一门脚本语言
  • 是一门解释性语言
  • 是一门弱类型的语言
  • 是一门动态类型的语言
  • 是一门基于对象的语言

JavaScript 代码写在哪里

1
2
3
4
5
第一种:写在html文件中-->使用script标签包裹代码
<script>
alert('这对script标签中的就是js代码)
</script>
第二种:可以单独写在js文件中-->js文件中不需要使用<script>标签

<script>标签的属性:

  • src 表示要引入的外部文件
  • type 表示脚本语言的类型 text/javascript,默认值就是它.
  • language 已废弃。原来用于代码使用的脚本语言。由于大多数浏览器忽略它,所以不要用了。
  • defer:可选。(等页面加载完成后,才执行 js)表示脚本可以延迟到文档完全被解析和显示之后再执行。由于大多数浏览器不支持,故很少用。
  • charset:可选。表示通过 src 属性指定的字符集。由于大多数浏览器忽略它,所以很少有人用它。
  • async:可选,能简单实现 js 的异步加载.

注意事项

  • <script>标签尽量放在<body>标签内,放在结束的</body>标签前
  • 一个<script>标签如果用于引入外部 js 文件,就不要在标签内写 script 代码
  • html 文件中可以同时存在多对<script>标签,浏览器会依次解析执行

  本文主要通过实现一个简单的 Form 组件来学习 Vue 中相关知识点.

相关知识点

  • v-model 指令的实质
  • 父子组件定义属性传值 props $emit
  • 子孙组件传值 provide inject
  • slot 插槽
  • 在生命周期中监听自定义事件

需求分析

  • 数据双向绑定
  • 单个规则校验
  • label
  • error 错误提示
  • 数据获取
  • 数据传递
  • 全局校验

组件拆分(单一性)

  • Input 组件
  • FormItem 组件
  • Form 组件

组件功能划分


Input 组件

数据双向绑定
触发规则校验


FormItme 组件

label
prop
error


Form 组件

数据模型持有
校验规则持有
数据传递
全局校验


功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Input 组件
<template>
<div>
<input type="text" :value="value" @input="onInput" />
</div>
</template>

<script>
/**
* 职能
* 1.双向绑定
* 2.触发校验
*/
export default {
name: 'KInput',
props: {
value: {
type: String,
default: ''
}
},
methods: {
onInput(e) {
// v-model 监听 input
this.$emit('input', e.target.value)
// 触发校验
this.$parent.$emit('validate')
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// FormItem 组件
<template>
<div>
<label v-if="label">{{ label }}</label>
<slot />
<span v-show="error">{{ error }}</span>
</div>
</template>

<script>
/**
* 职能
* 1.校验
* 2.label
* 3.error
*/
import Schema from 'async-validator'
export default {
inject: ['form'],
props: {
label: {
type: String,
default: ''
},
prop: {
type: String,
default: ''
}
},
data() {
return {
error: ''
}
},
mounted() {
this.$on('validate', () => {
this.validate()
})
},
methods: {
validate() {
/**
* 引入 sync-validate 插件
* 获取校验规则与校验值
*/
const value = this.form.model[this.prop]
const rule = this.form.rules[this.prop]

const schema = new Schema({ [this.prop]: rule })

return schema.validate(
{ [this.prop]: value.trim() },
errors => (this.error = errors ? errors[0].message : '')
)
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Form 组件
<template>
<div>
<slot />
</div>
</template>

<script>
/**
* 职能
* 1.数据持有丶传递
* 2.全局校验
*/
export default {
props: {
model: {
type: Object
},
rules: {
type: Object
}
},
provide() {
return {
form: this
}
},
methods: {
validate(cb) {
/**
* 全局校验
* 获取所有子组件
* 找到带有prop验证的
* 调用子组件自身的校验方法
*/
const tasks = this.$children
.filter(item => item.prop)
.map(item => item.validate())

Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false))
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 测试
<template>
<div>
<KFrom ref="form" :model="model" :rules="rules">
<KFormitem lable="用户名" prop="uname">
<KInput v-model="model.uname"></KInput>
</KFormitem>
<KFormitem lable="密码" prop="pwd">
<KInput v-model="model.pwd"></KInput>
</KFormitem>
<KFormitem>
<button @click="submit">校验</button>
</KFormitem>
</KFrom>
</div>
</template>

<script>
import KFrom from './KForm'
import KFormitem from './KFormitem'
import KInput from './KInput'
export default {
components: { KFrom, KFormitem, KInput },
data() {
return {
model: {
uname: '',
pwd: ''
},
rules: {
uname: [{ required: true, message: '用户名不能为空' }],
pwd: [{ required: true, message: '密码不能为空' }]
}
}
},
methods: {
submit() {
this.$refs.form.validate(isValidate => {
if (isValidate) {
alert('校验通过')
} else {
alert('校验失败')
}
})
}
}
}
</script>