积累沉淀

待山花烂漫,化茧成蝶

JavaScript教程

前言:JavaScript(简称 JS)是一种 动态类型、基于原型、多范式 的编程语言。最初由 Brendan Eich 在 1995 年开发,用于在浏览器中实现交互式网页。

如今,它已成为 前端开发的核心语言,并借助 Node.js 扩展到后端、移动端和桌面端开发。

语法基础

注释和分号问题

  1. 单行注释 //
  2. 多行注释 /* ... */
  3. 但为了风格统一,结束符要么每句都写,要么每句都不写 (团队约定)

数据类型

  1. 输入输出
1
2
3
4
5
6
7
8
9
// html 标签输出
document.write('<h1>hello world</h1>')
// 弹窗
alert('hello world');
// 日志
console.log('hello world');
// 输入
let name = prompt('请输入你的名字');
alert('hello ' + name);

注意:JavaScript代码执行顺序:①按HTML文档流顺序执行②代码 alert()prompt()它们会跳过页面渲染先被执行(目前作为了解,后期讲解详细执行过程)

  1. 变量的声明
  • let关键字不允许多次声明一个变量
1
2
3
4
5
6
7
8
9
10
// 变量的声明
let a;
// 赋值
a = 3;
console.log(a);
// 声明的同时直接赋值变量的初始化
let name = '张三';
console.log(name);
// 声明多个变量(不推荐不够清晰)
let age = 18, uname = '迪丽热巴';
  • 数组
1
2
3
4
5
6
7
8
// 声明一个数组
let arr = [1, 2, 3, 4, 5];
console.log(arr[0]);
console.log(arr[4]);
// 声明一个多中类型的数组
let arr2 = [1, 'hello', true, [1, 2, 3]];
// 获得数组的长度
console.log(arr2.length);

注意:①数组可以存储任意类型的数据②长度:数组中数据的个数,通过数组的length属性获得

  1. 常量(注意:常量不允许重新赋值,声明的时候必须赋值 (初始化))
1
2
3
4
5
// 常量
const PI= 3.14;
console.log(PI);
const myName = '王伟';
console.log(myName);

注意:var— 以前的声明变量的方式,会有很多问题。

  1. 数据类型
  • 基本数据类型

number: 数字型

注意:NaN代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果,NaN是粘性的。任何对NaN的操作都会返回NaN。

string: 字符串型

注意:通过单引号"、双引号""或反引号``包裹的数据都叫字符串,单引号和双引号没有本质上的区别,推荐使用单引号。

1
2
3
4
5
6
7
8
9
let str ='pink';
let str1 = "pink";
let str2 = `中文`;
console.log(str2);

let name = 'pink';
let age = 18;
let info = `${name}今年${age}岁`;
console.log(info);

boolean: 布尔型

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

undefined: 未定义型

注意:只声明变量,不赋值的情况下,变量的默认值为undefined,一般很少【直接】为某个变量赋值为undefined。

null: 空类型

注意:null表示空对象,null是一个对象,null对象没有属性和方法。
null和undefined区别:①undefined表示没有赋值②null表示赋值了,但是内容为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//计算有区别
console.log(undefined + 1) //NaN
console.lognull + 1) //1

// 检测数据类型
let num = 10
console.log(typeof num) //number
let str = 'pink'
console.log(typeof str) //string
let str1 ='10'
console.log(typeof str1) //string
let flag = false
console.log(typeof flag) //boolean
let un
console.log(typeof un) //undefined
let obj= null
console.log(typeof obj)//object
  • 隐式转换

+号两边只要有一个是字符串,都会把另外一个转成字符串
除了+以外的算术运算符比如-、*、/等都会把数据转成数字类型
+号作为正号解析可以转换成数字型
任何数据和字符串相加结果都是字符串

1
2
3
4
5
6
7
8
9
console.1og(11 + 11)       // 22
console.log('11'+11) // 1111
console.1og(11 - 11) // 0
console.log('11'-11) // 0
console.log(1 * 1) // 1
console.log('1' * 1) // 1
console.log(typeof '123') // string
console.log(typeof +'123') // number
console.log(+'11' + 11) // 22
  • 引用数据类型

object: 对象型

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let a = 1
console.log(a++)
// 其他运算符和其他语言一样, 算数运算符,逻辑运算符都是一致的

// 比较运算符
// 比较值是否相等
console.log(2 == '2')
// 比较类型和值是否相等
console.log(NaN == NaN) // false NaN 不等于任何值包括自己

console.log(2 === '2') // false
// 不全等与,包括值和类型有一个不等就是不全等
console.log(2 !== '2') // true
// 不等于判断值是否不等于,'2'隐式转换为数字和数字2比较
console.log(2 != '2') // false
// 字符串的比较
console.log('a' < 'b')
console.log('ab' < 'abc')

语句

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
// 顺序 分支 循环
// 除了''所有的字符串都为真true
if ('pink老师')
console.log('pink老师')
if ('')
console.log('') //不执行 条件为假
else
console.log('没有内容')
if (' ')
console.log('字符串的内容是一个空格')

// switch 的匹配使用的是 === 全等
switch (true) {
case 'pink老师':
console.log('pink老师')
break;
case '':
console.log('')
break;
default:
console.log('没有内容')
break;
}

for (let i = 1; i < 10; ++i) {
for (let j = 1; j <= i; ++j) {
document.write(`<span>${j} x ${i} = ${i * j}</span>`)
}
document.write('<br>')
}

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 数组 声明的两种方式 推荐使用第一种
let arr0 = [1,2,3,4,5]
let arr1 = new Array(1,2,3,4,5, 6, 7, 8, 9, 10)
//
let arr2 = []
// array api
arr1.push(1) // 在末尾追加,返回新的数组长度
arr1.unshift(2) // 将一个或多个元素添加到数组的开头,并返回该数组的新长度
// 删除的操作
console.log(arr1.pop()) // 删除数组末尾的元素,并返回该元素
console.log(arr1.shift()) // 删除数组开头的元素,并返回该元素

// splice(startPos, deleteCount)
arr1.splice(1,2) // 从位置1开始,删除两个元素
arr1.splice(1) // 从位置1开始,删除所有元素
// 遍历
for (let i = 0; i < arr2.length; i++) {
console.log(arr2[i])
}

函数

  1. 两个相同的函数后面的会覆盖前面的函数
  2. Javascript中实参的个数和形参的个数可以不一致
    • 如果形参过多会自动填上undefined(了解即可)
    • 如果实参过多那么多余的实参会被忽略(函数内部有一个arguments,里面装着所有的实参)
  3. 函数一旦碰到return就不会在往下执行了函数的结束用return
  4. 匿名函数
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
// 函数声明
function sayHi() {
console.log('hello')
}
// 调用
sayHi()
// 传参
function sum(num1, num2) {
return num1 + num2
}
console.log(sum(1, 2))
// 默认参数 用法和c++ 相似
function point(x = 0, y = 0) {
return `(${x}, ${y})`
}
// 函数作用域
function scope() {
// 全局变量 函数在调用后生效
notRecommended = 'no'
let num = 3
function inner() {
let num = 4
console.log(num) // 4
}
}
// 匿名函数
let func = function () {
console.log('anonymous function')
}
func(); // output: anonymous function
// 立即执行函数 写法1
(function () {
console.log('immediately invoked function')
console.log('anonymous function')
})();
// 写法2:
(function(x, y){
console.log('immediately invoked function2')
console.log(x + y)
}(3, 4));
// 逻辑中断 注意和c++ 的不同
function logicFunc(x, y) {
x = x || 1
y = y || 1
console.log(`x = ${x}, y = ${y}`)
}
logicFunc() // x = 1, y = 1
logicFunc(0, 0) // x = 1, y = 1
logicFunc(2, 2) // x = 2, y = 2
// Boolean 转换
// '' 、0、undefined、null、false、NaN转换为布尔值后都是false,其余则为true
console.log(Boolean(''))

object对象

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
// object声明
let obj = {
name: '小米10青春版',
num: '100012816024',
weight: '0.55kg',
address: '中国大陆',
welcome: function () {
console.log('欢迎来到小米')
}
}
obj.weight = '0.50kg'
obj.price = '6999'
// 等价读取属性
console.log(obj.name)
console.log(obj['name'])

console.log(obj.address)
console.log(obj)
// 调用方法
obj.welcome()

// 遍历对象的属性
for (const key in obj) {
console.log(`key = ${key}, obj[key] = ${obj[key]}`)
}

// 数学类
console.log(Math.PI)
console.log(Math.pow(2,3))
console.log(Math.ceil(2.1)) // 向上取整 3
console.log(Math.floor(2.9)) // 向下取整 2
console.log(Math.round(-1.5)) // -1
console.log(Math.round(-1.51)) // -2
// [0, 1)
console.log(Math.random())
// [0, 10]
console.log(Math.floor(Math.random() * 11))
// 如何生成N-M之间的随机数
let N = 5, M = 10
console.log(Math.floor(Math.random() * (M - N + 1)) + N)

注意:

简单数据类型存放到栈里面(操作系统自动分配释放存放函数的参数值、局部变量的值)
引用数据类型存放到堆里面(存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收)

Js & Web

WebAPI基本认知

  1. 作用和分类
  • 作用:就是使用JS去操作html和浏览器
  • 分类:DOM(文档对象模型)、BOM(浏览器对象模型)
  1. DOM
  • 将HTML文档以树状结构直观的表现出来,我们称之为文档树或DOM树
  • 描述网页内容关系的名词
  • 作用:文档树直观的体现了标签与标签之间的关系
  1. DOM对象
  • 浏览器根据html标签生成的JS对象
  • 所有的标签属性都可以在这个对象上面找到
  • 修改这个对象的属性会自动映射到标签身上
  • DOM的核心思想,把网页内容当做对象来处理

获取DOM对象操作元素内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 根据CSS选择器来获取DOM元素 (重点)

// 选择匹配的第一个元素
// 参数: 包含一个或多个有效的CSS选择器字符串
// 返回值:CSS选择器匹配的第一个元素,一个HTMLElement对象。
const box = document.querySelector('div')
for (const key in box) {
console.log(`key = `)
console.log(`${key}: ${box[key]}`)
}
console.log(box.textContent)
const pNav = document.querySelector('#nav')
console.log(pNav)
const li = document.querySelector('ul li:first-child')
console.log(li)
// 选择匹配的多个元素
const list = document.querySelectorAll('ul li')
console.log(list)
for (let i = 0; i < list.length; ++i) {
list[i].style.color = 'pink'
}
// 得到的是一个伪数组:
// 有长度有索引号的数组
// 但是没有pop()push()等数组方法

操作元素属性

  1. 操作元素的普通属性
1
2
3
4
5
6
7
8
const box = document.querySelector('.box')
for (const key in box) {
console.log(`${key}: ${box[key]}`)
}
// 纯文本不识别标签
box.innerText = '我是一个盒子'
// 识别标签
box.innerHTML = '<strong>我是一个盒子</strong>'
  1. 操作元素的样式属性
1
2
3
4
const box = document.querySelector('.box')
box.style.width = '300px'
box.style.backgroundColor = 'hotpink'
box.style.border = '2px solid blue'
  1. 操作类名(className)操作CSS
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.nav {
color: red;
}
.box {
width: 300px;
height: 300px;
background-color: blue;
margin: 100px auto;
padding: 10px;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="nav">123</div>
<script>
// 获取元素
const box = document.querySelector('div')
// 添加类名
box.className = 'nav box'
</script>
</body>
</html>
  1. 通过classList操作类控制CSS(最推荐的方式)
  • 为了解决className容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
  • 语法:
    classList语法
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
color: #333
}
.active {
color: red;
background-color: pink;
}
</style>
</head>
<body>
<div class="box active">文字</div>
<script>
const box = document.querySelector('.box')
box.classList.add('active')
box.classList.remove('active')
// 切换类 toggLe()有还是没有啊,有就删掉,没有就加上
box.classList.toggle('active') // 此时相当于删掉 active, 原因是 div.class 中有box active
</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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" value="默认值">
<div data-id="1">one</div>
<div data-id="2">two</div>
<div data-id="3">three</div>
<div data-id="4">four</div>
<div data-id="5">five</div>

<script>
const input = document.querySelector('input')
console.log(input.value)
input.value = '修改后的值'
// 获取表单的形式
console.log(input.type)
//
input.type = 'password'

const one = document.querySelector('div')
console.log(one.dataset)
console.log(one.dataset.id)
</script>
</body>
</html>

定时器-间歇函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let i = 0
// lambda 表达式方式调用
let n = setInterval(() => {
i++
console.log(`${i}`)
}, 1000)

let m = setInterval(function () {
console.log('匿名函数的方式调用')
}, 1000)
function func() {
console.log('具名函数的方式调用')
}
let t = setInterval(func, 1000)
// 关闭定时器
console.log(n)
// 实际上事件触发关闭
clearInterval(n)
clearInterval(m)
clearInterval(t)

事件监听 (绑定)

  1. 绑定事件监听
  • 语法

  • 事件监听三要素:
    事件源:那个dom元素被事件触发了,要获取dom元素
    事件类型:用什么方式触发,比如鼠标单击click、鼠标经过mouseover等
    事件调用的函数:要做什么事

1
2
3
4
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert('点击了')
})

事件类型

事件对象

  • 事件对象是什么

也是个对象,这个对象里有事件触发时的相关信息
例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息

  • 使用场景

可以判断用户按下哪个键,比如按下回车键可以发布新闻
可以判断鼠标点击了哪个元素,从而做相应的操作

  • 部分常用属性

环境对象

  • 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境
  • 弄清楚this的指向,可以让我们代码更简洁
  • 能够分析判断函数运行在不同环境中this所指代的对象
  • 谁调用,this就是谁—> 判断this指向的粗略规则
1
2
3
4
5
6
7
8
9
10
function func() {
console.log(this)
}
// this -> window
func();
const btn = document.querySelector('.btn')
btn.addEventListener('click', function (e) {
// this -> btn
console.log(this)
});

事件流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 山东济南 蓝翔 目标(pink老师) 捕获阶段 
// 蓝翔 济南 山东 冒泡阶段
// true --- 捕获阶段
// 默认为false --- 冒泡阶段
document.addEventListener('click', function (e) {
console.log('我是爷爷')
}, true)
const father = document.querySelector('.father')
father.addEventListener('click', function (e) {
console.log('我是爸爸')
}, true)
const son = document.querySelector('.son')
son.addEventListener('click', function (e) {
console.log('我是儿子')
// 阻止传播
e.stopPropagation()
}, true)
// output:我是爷爷 我是爸爸 我是儿子
// 不加参数true, 输出为:我是儿子 我是爸爸 我是爷爷
  1. 事件流与两个阶段说明

①事件捕获
②事件冒泡

  1. 事件捕获

事件捕获阶段:从最外层元素开始,依次向内调用所有同名事件

  1. 事件冒泡阻止冒泡

当一个元素触发事件后,会依次向上调用所有父级元素的同名事件

  1. 解绑事件

鼠标经过事件:

mouseovermouseout会有冒泡效果
mouseentermouseleave没有冒泡效果(推荐)

事件委托

原理:事件委托其实是利用事件冒泡的特点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<ul>
<li>第一个孩子</li>
<li>第二个孩子</li>
<li>第三个孩子</li>
<li>第四个孩子</li>
<li>第五个孩子</li>
<p>我不需要变色</p>
</ul>
<script>
// 点击每个小当前文字变为红色
// 按照事件委托的方式委托给父级,事件写到父级身上
// 1.获得父元素
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// alert('点击了')
// console.log(e.target) // 获得点击的元素
// e.target.style.color = 'red'
// 我的需求,我们只要点击才会有效果工
if (e.target.tagName === 'LI') {
e.target.style.color = 'red'
}

});
</script>
  • 阻止默认行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<form action="https://www.itcast.cn">
<input type="submit" value="免费注册">
</form>
<a href="https://www.baidu.com">百度一下</a>
<script>
const form = document.querySelector('form')
form.addEventListener('submit', function (e) {
// 阻止默认行为 提交
e.preventDefault()
console.log('提交表单')
})
const a = document.querySelector('a')
a.addEventListener('click', function (e) {
// 阻止默认跳转行为
e.preventDefault()
console.log('点击超链接')
})
</script>

其他事件

  1. 页面加载事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 等待页面所有资源加载完毕,就回去执行回调函数
window.addEventListener('load', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
console.log('点击了')
})
console.log('页面加载完毕')
})
// 等待某个图片资源加载完毕,就回去执行回调函数
const img = new Image()
img.src = 'https://picsum.photos/id/237/200/300'
img.addEventListener('load', function () {
console.log('图片加载完毕')
})
// 给document加, 无需等待样式表、图像等完全加载
// 无需等待样式表、图像等完全加载
document.addEventListener('DOMContentLoaded', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
console.log('点击了')
})
})
  1. 元素滚动事件
1
2
3
4
// 页面滚动事件
window.addEventListener('scroll', function () {
console.log('我滚动了')
})
  1. 页面尺寸事件
  • 会在窗口尺寸改变的时候触发事件:resize
  • 检测屏幕宽度:

获取元素的可见部分宽高(不包含边框,margin,滚动条等)
clientWidthclientHeight

日期对象

  1. 实例化
1
2
3
4
5
6
7
8
9
// 实例化date对象
const date = new Date()
console.log(date)
// 指定一个事件
const date1 = new Date('2025-5-1')
console.log(date1)
console.log(date.getMonth())
console.log(date.getDay())
console.log(date.toLocaleString())
  1. 时间对象方法

  2. 时间戳

是指1970年01月01日00时00分00秒起至现在的秒数(javascript中的时间戳是毫秒数),它是一种特殊的计量时间的方式

1
2
3
4
// 获取时间戳的三种方法
console.log(Date.now())
console.log(new Date().getTime())
console.log(+new Date())

JS进阶

ES6 新的语法

作用域

  • 局部作用域分为函数作用域和块作用域。

let声明的变量会产生块作用域,var不会产生块作用域
const声明的常量也会产生块作用域

  • 全局作用域

全局作用域中声明的变量,任何其它作用域都可以被访问

  • 垃圾回收机制(GarbageCollection)简称GC

JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收

  • 闭包

一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包=内层函数+外层函数的变量
闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量

1
2
3
4
5
6
7
8
9
10
// 闭包
function outer() {
let a = 10
function inner() {
console.log(a)
}
return inner
}
const func = outer()
func()
  • 变量提升

变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 把所有var声明的变量提升到当前作用域的最前面
// 只提升声明,不提升赋值
console.log(num + '件') // undefined件
var num = 10

// Uncaught ReferenceError: Cannot access 'a' before initialization
// console.log(a)
// let a = 10
// 注意这就是var 提升的作用,比较容易理解

// 提升到当前作用域
// function func() {
// console.log(num) // undefined
// var num = 10
// }

函数进阶

  • 函数参数

动态参数, arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参

1
2
3
4
5
6
7
8
9
10
function getSum() {
console.log(arguments)
let sum = 0
for (let i = 0; i < arguments.length; ++i) {
sum += arguments[i]
}
return sum
}
let ret = getSum(1, 2, 3, 4, 5)
console.log(ret)

剩余参数,充许我们将一个不定数量的参数表示为一个数组

1
2
3
4
5
6
function config(baseUrl, ...others) {
console.log(baseUrl) // https://www.baidu.com
console.log(others) // [ 'get', 'json' ]
}

config('https://www.baidu.com', 'get', 'json')
  • 展开表达式

展开运算符(…)将一个数组进行展开
不会修改原数组

1
2
3
let arr = [1, 2, 3, 4, 5]
console.log(...arr) // 1 2 3 4 5
console.log(Math.max(...arr)) // 5
  • 箭头函数(重要)

使用场景:箭头函数更适用于那些本来需要匿名函数的地方
箭头函数没有arguments动态参数,但是有剩余参数...args

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
// 箭头函数
const fn = (a) => {
console.log('这是一个箭头函数')
console.log(a)
}
fn(1)
// 只有一个形参的时候,可以省略小括号
const fn1 = a => {
console.log('只有一个形参的时候,可以省略小括号')
console.log(a)
}
fn1(2)
// 只有一行代码的时候,我们可以省略大括号
const fn2 = a => console.log(a)
fn2(3)
// 只有一行代码的时候,可以省略return
const fn3 = x => x * x
console.log(fn3(4))
// 箭头函数可以直接返回一个对象
const fn4 = (uname) => ({uname: uname})
console.log(fn4('刘德华')) // {uname: '刘德华'}
// 利用箭头函数来求和
const sum = (a, b, ...args) => {
const s1 = a + b
let s2 = 0
for (let i = 0; i < args.length; ++i) {
s2 += args[i]
}
return s1 + s2
}
const s = sum(1, 2, 3, 4, 5)
console.log(s)

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

解构赋值

  • 数组解构

数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法

1
2
3
4
5
6
7
const arr = [1, 2, 3]
const [a, b, c] = arr
console.log(a, b, c)
// 交换两个变量
let m = 10, n = 20;
[m, n] = [n, m]
console.log(m, n)
  • 对象解构

对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 解构的语法
const {uname, age} = {uname: '张三', age: 18}
console.log(uname, age)
// 支持改名
const {uname: name, age: age0 } = {uname: '张三', age: 18}
console.log(name, age0)
//解构数组对象
const pig = [
{
uname:'佩奇',
age:6
}
]
const [{ uname, age }] = pig
console.log(uname, age)

构造函数

1
2
3
4
5
6
7
// 构造函数
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
const person = new Person('张三', 18)
console.log(person)

实例成员&静态成员

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
// 构造函数
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
const person = new Person('张三', 18)
console.log(person)

// 添加实例成员
person.gender = 'male'
// 添加实例方法
person.sayHi = () => {
console.log(person)
}
// 调用实例方法
person.sayHi()

// 添加静态成员
Person.country = '中国'
// 添加静态方法
Person.show = () => {
console.log(Person.country)
}
// 调用静态方法
Person.show()

内置构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const obj = {
uname: '张三',
age: 18,

sayHi: () => {
console.log('hello world')
}
}
// 获得所有的属性名
const keys = Object.keys(obj)
// 获得所有的属性值
const values = Object.values(obj)
console.log(keys) // ["uname", "age", "sayHi"]
console.log(values) // ["张三", 18, f]]
// 深度拷贝
const copyObj = {}
Object.assign(copyObj, obj)

常用API

Array Api

reduce 执行过程:

①如果没有起始值,则上一次值以数组的第一个数组元素的值
②每一次循环,把返回值给做为下一次循环的上一次值
③如果有起始值,则起始值做为上一次值

面向对象

  • 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
  • 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作
  • 面向对象的特性: ①封装性 ②继承性 ③多态性

原型

  • constructor属性

每个原型对象里面都有个constructor属性(constructor构造函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Star(uname, age) {
this.uname = uname
this.age = age
}
// 节省内存空间
Star.prototype.show = function () {
console.log(`姓名:${this.uname},年龄:${this.age}`)
}

const star0 = new Star('刘德华', 18)
const star1 = new Star('周杰伦', 19)
star0.show()
star1.show()

  • prototype属性
1
2
3
4
5
6
7
8
9
10
11
12
13
// 添加比较多的共享方法
Star.prototype = {
// 重新指会原对象
constructor: Star,
sing: function () {
console.log('正在唱歌...')
},
dance: function () {
console.log('正在跳舞...')
}
}
const star2 = new Star('王源', 18)
star2.sing()

对象原型

对象都会有一个属性_proto_指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有_proto_原型的存在。

高阶技巧

深浅拷贝

  1. 浅拷贝:拷贝的是地址,特别是遇到嵌套对象时,对象属性的引用地址会相同,修改一个对象属性,另一个对象属性也会改变。
  • 拷贝对象:Object.assgin()/展开运算符{…obj}拷贝对象
  • 拷贝数组:Array.prototype.concat()或者[…arr]

浅拷贝可以用在简单对象或数组上,不要有嵌套对象也是可以的。

  1. 深拷贝:拷贝的是对象,不是地址
  • 通过递归实现深拷贝
  • lodash/cloneDeep
  • 通过JSON.stringify()实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// deepcopy
function deepCopy(newObj, oldObj) {
for (const k in oldObj) {
if (oldObj[k] instanceof Array ) {
newObj[k] = []
deepCopy(newObj[k], oldObj[k])
}
else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
}
else {
newObj[k] = oldObj[k]
}
}
}

异常处理

  1. throw抛异常
  2. try/catch捕获异常
  3. debugger

处理this

  1. JavaScript中还允许指定函数中this的指向,有3个方法可以动态指定普通函数中this的指向 call()apply()bind()
1
2
3
4
5
6
7
8
9
10
11
12
13
// apply
const anObj = {
name: '张三',
age: 18
}
function func(a, b) {
console.log(this) // window
console.log(a, b)
}
func.apply(anObj, [1, 2]) // this -> anObj
// 求数组最大值
const max = Math.max.apply(null, [1, 2, 3, 4, 5])
console.log(max)
  1. bind()-重点
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击发送短信</button>
<script>
const obj = {
uname: '张三',
age: 18
}
function fn() {
console.log(this)
}
// 1. bind不会调用函数
// 2. 能改变this指向
// 3. 返回值是个函数,但是这个函数里面的this是更改过的
const newfn = fn.bind(obj)
newfn() // {uname: '张三', age: 18}

// 需求,有一个按钮,点击里面就禁用,2秒钟之后开启
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
this.disabled = true
setTimeout(() => {
this.disabled = false
}, 2000) // ok 非常好,箭头函数本身没有this, 它指向外层函数的this-> btn

// 下面这句也是ok的,通过bind绑定this, 不够简洁
// setTimeout(function () {
// this.disabled = false
// }.bind(this), 2000)
})

</script>
</body>
</html>

性能优化

  1. 防抖(debounce
  • 防抖:单位时间内,频繁触发事件,只执行最后一次
  • 使用场景:

搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测

  • 实现方式:

lodash提供的防抖来处理
手写一个防抖函数来处理

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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 400px;
height: 400px;
background-color: pink;
text-align: center;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box')
let i = 1
function moveMouse() {
box.innerHTML = i++
}
// box.addEventListener('mousemove', moveMouse)

// 手写防抖函数
// 核心是利用setTimeout定时器来实现
function debounce(fn, t) {
let timer
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
fn()
}, t)
}
}
// 使用
box.addEventListener('mousemove', debounce(moveMouse, 500))
</script>
</body>
</html>
  1. 节流-throttle
  • 节流:单位时间内,频繁触发事件,只执行一次
  • 实现方式:

lodash提供的节流函数来处理
手写一个节流函数来处理

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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

<style>
.box {
width: 400px;
height: 400px;
background-color: pink;
text-align: center;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box')
let i = 1
function moveMouse() {
box.innerHTML = i++
}
// 节流
// 节流的核心就是利用定时器(setTimeout)来实现
function throttle(fn, t) {
let timer
return function () {
if (timer) return
timer = setTimeout(function () {
fn()
// 在setTimeout中是无法删除定时器的,因为定时器还在运作,所以使用timer=null 而不是 clearTimeout(timer)
timer = null
}, t)
}
}
// 调用
box.addEventListener('mousemove', throttle(moveMouse, 500))
</script>
</body>
</html>

  1. 总结:
Buy me a coffee please.