JS基础
JavaScript是一种运行在客户端(浏览器)的编程语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画等交互效果。
JS书写位置:
1.行内JS:直接标签内部写·JS代码。
2.内部JS:写在html文件里,用script标签包住,script标签写在</body>上面。
3.外部JS:写在以 .js 结尾的文件里,通过script标签,引入html页面中。此时script标签里不要写代码,否则会被忽略 。
<body>
<script src = "my.js"></script>
</body>
注释:
1.单行注释(//):ctrl + /
2.块注释(/* */):shift + alt + a
结束符:英文的 ; ,可写可不写。
变量
定义:变量是计算机中用来存储数据的“容器”(盒子)。变量不是数据本身,仅仅是一个用于储存数值的容器。
作用:记录计算机中数据的不同状态。
基本使用:
1.变量的声明(创建变量)
声明变量有两部分构成:声明关键字(let)、变量名(标识符)。
2.变量的赋值(把数据存储到变量里面)
声明变量的时候直接完成赋值操作,这种操作称为变量初始化。
注意:数字直接存储,字符用单引号引起来,表示一段信息。
3.更新变量
let age = 18
age =19 //变量更新
注意:let不允许对同一个变量多次声明。
4.声明多个变量:多个变量中间用逗号隔开,但不推荐这样写。
变量的本质:
内存:计算机中存储数据的地方,相当于一个空间。
变量本质:是程序在内存中申请的一块用来存放数据的小空间。
变量命名规则与规范:
1.规则(必循遵守):
不能用关键字,如let、var、if 等JS内置的有特殊含义的字符;
只能用下划线、字母、数字、$组成,且数字不能开头;
字母严格区分大小写。
2.规范(建议):遵守小驼峰命名法,如userName;起名要有意义。
常量:
也是一个容器,用来保存数据。
注意:常量里面的值不允许改变;常量必须要初始化。
当某个值永远不会改变的时候,可以使用常量来保存,目的为了程序的安全。
常量声明关键字:const
基本数据类型
JS数据类型分为两大类:基本数据类型(简单数据类型)& 引用数据类型(复杂数据类型)
基本数据类型:number数字型、string字符串型、boolean布尔型、undefined未定义型、null空类型。
引用数据类型:Object、Function、Array.
typeof 关键字检测数据类型。
Number数字型:
数字:可用于运算操作。
JS中的整数、小数等统一称为数字类型。
string字符串型:
字符串:被引号包裹的一段文字信息。
JS中的字符串:通过单引号('')、双引号("")、反引号(``)包裹的都属于字符串。单引、双引没有本质区别,推荐使用单引号。
字符串嵌套:不能嵌套自己,外单内双 或 外双内单。
boolean布尔型:
用于判断真假的数据类型,通常用来判断条件是否成立。它有两个固定值 true 和 false。
undefined未定义型:
未定义是比较特殊的类型,只有一个值undefined。
只声明变量,不赋值的情况下,变量的默认值为undefined,一般很少直接为某个变量赋值为undefined。
使用场景:开发中经常声明一个变量,等待传过来的数据。如果外面不知道整个数据是否传递过来,此时可以通过检测这个变量是否为undefined,来判断用户是否有数据传过来。
null空类型:
JS中的null仅仅是一个代表‘无’、‘空’ 或 ‘值未知’ 的特殊值。
null和undefined 区别:undefined表示没有赋值,不存在;null表示赋值了,但内容为空。
注意:typeof null 返回的是 ‘object’,返回的是对象类型。这是JS的一个bug,不代表null就是引用数据类型,null属于基本数据类型。
模板字符串:
字符串拼接:‘+’ 可以实现字符串的拼接,最常见是字符串拼接变量。(数字相加,字符相连)
模板字符串:
使用场景:拼接字符串和变量
语法:1. `` 2.内容拼接变量时,用${}包住变量(反引中间套变量,直接dollar大括号)
``里的内容可以换行。
操作符
五种运算符
算术运算符:
也叫数学运算符,主要包括加减乘除、取余(求模)等。+ - * / %(取余)
取余使用场景:用来判断某个数能否被整除。
注意事项:如果计算失败,返回的结果是NaN。
赋值运算符:
对变量进行赋值的运算符。
=:将等号右边的值赋予给左边,要求左边必须是一个容器。
+=,-=,*=,/=,%=:对变量里的值进行运算,再把结果存到原来的变量里。
自增/自减运算符:
++:自增,变量自身的值加1,如x++;
--:自减,变量自身的值减1,如x--。
++/--写在变量前后都可以,只能给变量使用。
++/--在变量前后的区别:如果单独使用,没有区别;如果参与运算,有区别:
1.++在前(前缀式):先对变量值加1,再拿着变量值做运算。
2.++在后(后缀式):先拿着变量值做运算,再对变量值加1。
比较运算符(关系运算符):
使用场景:比较两个数据大小,返回一个布尔值(true/false)。
>,<,>=,<=,
===:左右两边是否类型和值都相等(全等),
==:左右两边值是否相等,
!==:左右两边是否不全等,
!=:左右两边值不相等。
注意:=是赋值,===是全等,==是判断。
逻辑运算符:
使用场景:可以把多个布尔值放到一起运算,最后返回一个布尔值。
运算符优先级
类型转换
数据类型转换可分为:显示转换和隐式转换。
显示转换
转换为数字型:
//通过显示转换把其它类型转换为数字型
// 1.Number()
// 1.1把字符串转换为数字型
console.log(typeof Number('1')) //1 number
console.log(Number('abcd')) //NaN 无法完成转换则返回NaN
// 1.2把布尔值转换为数字型
console.log(Number(true)) //1
console.log(Number(false)) //0
// 1.3把 null undefined 转换为数字型
console.log(Number(null)) //0
console.log(Number(undefined)) //NaN
// 2.parseInt() parseFloat() 固定使用场景:以数字开头的字符串,只需要保留数字。
console.log(parseInt('100.5px')) //100 只保留整数
console.log(parseFloat('100.5px')) //100.5 可以返回小数
转换为布尔型:
有六种情况为false:false,0,'',null,undefined,NaN,其余都为true。
转换为字符串型见表格。
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。
1.隐式转换为数字型的运算符
算术运算符 -,*,/;比较运算符 >,==;+作为正号使用时
2.隐式转换为字符串型的运算符
+作为拼接字符串(只要两侧有一个字符串型,+就是拼接作用)
3.隐式转换为布尔型的运算符
!逻辑非
分支语句
表达式:可以被求值的代码,并将其计算出一个结果。
语句:一段可以执行的代码,是一个行为,例如分支语句和循环语句。
程序三大流程控制语句:顺序结构(依次执行),分支结构(选择执行),循环结构(重复执行)。
分支语句:
分支语句可以根据条件判断真假,来选择性的执行需要的代码。
分支语句包括:if分支语句,三元运算符,switch语句。
if分支语句
if语句
使用语法:
if (条件) {
满足条件要执行的代码
}
//小括号里的条件结果是布尔值,为true时,进入大括号执行代码;为false,则不执行。
//小括号内的结果若不是布尔类型,会发生类型转换为布尔值,类似Boolean()。
//如果大括号只有一个语句,大括号可以省略,但不提倡。
if双分支语句
if (条件) {
满足条件执行的代码
} else {
不满足条件执行的代码
}
if多分支语句
适用于有多个条件的时候
if (条件1) {
代码1
} else if (条件2) {
代码2
} else if (条件3) {
代码3
} else {
代码n
}
//else if 可以有任意多个,else不是必须的
三元运算符
一些简单的双分支,可以使用三元运算符(三元表达式),写起来比if else 双分支简单。
语法:
条件 ? 表达式1 : 表达式2
执行过程:如果条件为真,则执行表达式1,否则执行表达式2。
switch语句
适于多个条件的时候,也属于分支语句,大部分情况下和if分支语句功能相同。
语法:
switch (表达式) {
case 值1:
代码1
break
case 值2:
代码2
break
// 可以有多个case
default:
代码n
}
//表达式的值要求全等于 === case的值,才能匹配上。
//break如果落下了,容易造成case穿透。
switch适合于等值判断;if多分支适合于区间判断 。
循环语句
使用场景:重复执行指定的一段代码。
while循环
语法:
初始值
while (循环条件) {
要重复执行的代码 //循环体
变量计数
}
//条件为true时进入循环体执行循环;代码执行完毕不会退出,而是继续回到小括号判断条件是否满足,若满足继续执行,知道括号内条件不满足,即跳出。
while循环三要素:初始值(经常用变量),循环条件,变量计数(常用自增或自减)
for循环
for (初始值; 循环条件; 变量计数) {
//满足条件执行的循环体
}
终止循环
1.break 终止整个循环,一般用于结果已得到,后续的循环不需要的时候。
2.continue 终止本次循环,一般用于排除或跳过某一个选项的时候。
无限循环
1.while(true) 来构造无限循环,需要用break退出循环。
2.for(;;)也可以构造无限循环,需要用break退出循环。
循环嵌套
一个循环语句里面有包含另一个循环语句。
for (初始化; 循环条件; 变量计数) {
for (初始化; 循环条件; 变量计数) {
//要重复执行的代码
}
}
特点:外部循环每执行依次,内部循环执行所有次。
数组和数组操作
数组(Array):是一种数据类型,属于引用数据类型。
作用:在单个变量名下存储多个数据。
基本使用
1.声明语法:
let 数组名 = [数据1,数据2,. . .,数据n]
数组是按照顺序来存储的,每个数据有自己的编号,编号从0开始,也称为索引或下标。
数组里可以存放任意数据类型。
2.数组取值 :数组名[索引]
3.遍历数组:利用for循环
数组长度:数组中数据的个数,通过 数组名.length 属性获得。
数组元素:数组中数据,也称为数组元素。
操作数组
1.查:数组名[索引],如果查询不到返回undefined。
2.改:数组名[索引] = 新值
3.增:
①数组.push(新增数据):将一个或多个数据添加到数组末尾,返回值:新数组长度,
②数组.unshift(新增数据):将一个或多个数据添加到数组开头,返回值:新数组长度。
4.删:
①数组名.pop():删除最后一个元素,返回值:该元素的值。
②数组名.shift():删除第一个元素,返回值:该元素的值。
以上增加和删除方法都会修改元素组。
数组名.splice()方法:
可以从任意指定位置删除或增加数组元素,会修改原数组。
数组sort排序
语法:数组名.sort() 会修改原数组,默认按照每个元素最左边的元素大小进行升序排序。
let arr = [88, 78, 100, 34, 99]
// 1.升序排序
arr.sort(function (a, b) {
return a - b
})
console.log(arr) //[34, 78, 88, 99, 100]
// 2.降序排序
arr.sort(function (a, b) {
return b - a
})
console.log(arr) //[100, 99, 88, 78, 34]
// 3.获取最大最小值
document.write(`数组的最大值:${arr[0]}`)
document.write(`数组的最小值:${arr[arr.length - 1]}`)
可以利用排序获得数组最大最小值。
函数及应用
函数-Function:是可以被重复使用的代码块
作用:函数可以把具有相同或相似逻辑的代码“包裹”起来,这么做的优势是有利于代码复用。
基本使用
1.定义函数—利用关键字function定义函数(声明函数)
2.调用函数
// 1.定义函数
function 函数名() {
函数体
}
// 2.调用函数
函数名() //小括号是调用函数
函数的命名跟变量基本一致,采用小驼峰命名法,建议函数名采用动词。
定义好函数不会自动执行函数,必须要调用才会执行,每调用一次,会重新执行函数里面的代码。
alert() prompt() parseInt() 本质都是在调用函数
函数整体认知
传递数据给函数——函数内部处理——返回一个结果给调用者
函数语法:
function sum(x, y) {
return x + y //return关键字把结果返回给调用者
}
console.log(sum(10, 20)) //输出函数返回的结果
函数参数
形参和实参:
形参:声明函数时小括号里的参数
实参:调用函数时小括号里的参数
执行过程:把实参的数据传递给形参,从而提供给函数内部使用,可以把形参理解为变量。
alert('打印') parseInt('11px') Number('11') 本质都是函数调用的传参
在JS中实参和形参的个数可以不一致,形参个数过多,会自动补充undefined;实参个数过多,则多余的实参会被忽略。 开发中尽量保持一致
默认参数:
可以给形参设置默认值,这个默认值只会在缺少实参传递或者实参是undefined才会被执行 。
function sum(x = 0, y = 0) {
return x + y
}
console.log(sum()) //0
console.log(sum(undefined, undefined)) //0
console.log(sum(1, 2)) //3
逻辑中断
存在于逻辑运算符 && 和 || 中,左边如果满足一定条件会中断代码执行,也称为逻辑短路。
解释:
false && anything //逻辑与左边false则中断,如果左边为true,则返回右边代码的值
true || anything //逻辑或左边true则中断,如果左边为false,则返回右边代码的值
function sum(x, y) { //没有实参时,x y = undefined
x = x || 0
y = y || 0
return x + y
}
console.log(sum(1, 2)) //3
console.log(sum()) //0
//没有实参时,形参x y = undefined,结果会返回NaN,利用逻辑中断,将x y的值变为0进行计算
默认参数和逻辑中断使用场景的区别:
默认参数主要针对的是形参;逻辑中断除了处理参数之外,还可以做更多。
函数返回值return
返回值:把处理结果返回给调用者
函数返回值细节:
1.return结束函数,return后面的代码不会执行了
2.return和被返回的结果不要换行
3.如果函数没有return,则默认返回的是undefined
作用域和立即执行函数
作用域(Scope)
定义:变量或者值在代码中可用性的范围。
作用:作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
全局作用域:全局有效
作用于所有代码执行的环境(整个script标签内部)或者一个独立的js文件。
局部作用域:局部有效
1.函数作用域:作用于函数内的代码环境
2.块级作用域:{}大括号内部
let/const声明会产生块作用域,var不会产生块作用域。
全局变量和局部变量
在JS中,根据作用域的不同,变量可以分为:全局变量和局部变量。
- 全局变量(全局作用域的变量):在任何区域都可以访问和修改。
- 局部变量(局部作用域内变量):只能在当前局部内部访问修改。
注意:
1.函数内部的变量,如果没有声明直接赋值,则当全局变量来看。(避免这种写法,要先声明,再赋值)
2.函数内部的形参,当局部变量来看。
变量的访问原则
在不同作用域下,可能存在变量命名冲突的情况:
在能够访问到的情况下先局部,局部没有再找全局,总结:就近原则。
匿名函数
函数根据有没有名字分为两种:
匿名函数两种使用方式:函数表达式 & 立即执行函数
1.函数表达式
将匿名函数赋值给一个变量,并且通过变量名称进行调用,这称为函数表达式。
let fn = function () {}
使用场景:后期web api 阶段会使用。
语法:
let fn = function () {
//函数体
}
调用:
fn() //函数名(),实际上是变量名加()调用,但变量里存的是函数,称之为函数名加()
注意:
1.函数也是一种数据类型,属于引用数据类型(数组 函数)
2.函数表达式必须先声明后使用
3.函数表达式可以传递参数还可以有返回值,使用方法和具名函数类似。
let sum = function (x, y) { //形参
return x + y
}
let re = sum(1, 2) //实参
console.log(re) //3
2.立即执行函数(IIFE)
IIFE(立即执行函数表达式)Immediately Invoked Function Expression
语法:
//方式1
(匿名函数)();
(function () { console.log(11) })();
//方式2
(匿名函数())
(function () { console.log(11) }());
//不需要调用,立即执行。第二个()是调用的意思
场景:避免全局变量之间的污染
注意:如果有多个立即执行函数,则需要用分号隔开。
对象和对象操作
对象-Object:JavaScript里的一种数据类型(引用类型),也是用于存储数据的。
好处:可以用来详细描述某个事物,是用键值对形式存储语义更明了。
特点:对象数据是无序的,数组是有序的。
注意:声明对象使用大括号,大括号就是对象字面量
对象组成:属性 & 方法
对象使用-属性
1.定义对象属性:属性都是成对出现,包括属性名和属性值。
属性就是依附在对象上的变量(对象外是变量,对象内是属性)
2.访问对象属性
//对象.属性名
console.log(pig.age)
点 . 可以理解为 的,对象的某个属性
对象使用-方法
1.定义对象方法:方法成对出现,包括方法名和匿名函数。
方法就是依附在对象上的函数(对象外是函数,对象内是方法)
2.调用对象方法
通过 对象.方法() 调用
方法可以传递参数也可以有返回值,跟函数使用类似。
操作对象
对象本质是无序的数据集合,操作对象数据是 增 删 改 查 语法:
操作对象 | 语法 |
增加对象内容 | 对象.新属性 = 新值 对象.新方法名 = function(){} |
删除对象内容 | delete 对象名.属性名 |
修改对象 | 对象.属性 = 新值 |
查询对象 | 对象.属性 |
查的另一种写法:
对于多词属性如中横线分割的属性,点操作就不能用了。
可以采取:对象['属性'] 方式,单双引号都可以,也可以用于其它正常属性。
总结:多次属性或者需要解析变量的时候使用[]语法,其余直接用点语法。
遍历对象
for循环遍历对象的问题:对象没有长度且无序。
利用for in 遍历对象
for (let 变量 in 对象) {
console.log(变量) //属性名
console.log(对象[变量]) //属性值
}
for (let key in pig) {
console.log(key) //key是属性
console.log(pig[key]) //对象[变量] 是值
}
注意: 数组遍历用传统for,用for in 会得到字符串型索引号;for in 主要用来遍历对象。
let arr = ['red', 'green', 'blue']
for (let k in arr) {
console.log(k) //得到字符串型的索引号
}
总结:
for in 语法中的k是一个变量,在循环的过程中依次代表对象的属性名;
由于k是变量,所以必须用 [] 语法解析;
k是获得对象的属性名,对象名[k] 是获得属性值;
一般不用这种方式遍历数组,主要用来遍历对象。
Math内置对象
内置对象:JS内部提供的对象,包含各种属性和方法给开发者调用。
内置对象-Math:
math对象是JS提供的一个“数学”对象,提供了一系列数学运算的方法。
Math-生成任意范围随机数:
Math.random() 随机数,返回一个 [0,1)之间的随机小数。
取 n~m之间的随机整数:
Math.floor(Math.random() * (差值 + 1)) + 最小值
// 1.取0~10之间的一个随机整数
// Math.random() * (10 + 1)
// (0 ~ 0.9999) * 11 → (0 ~ 10.9999)
// 取整
// Math.floor(Math.random() * (10 + 1))
console.log(Math.floor(Math.random() * (10 + 1)))
// 2.取5~15之间的随机整数
// Math.floor(Math.random() * (10 + 1)) + 5
console.log(Math.floor(Math.random() * (10 + 1)) + 5)
// 3.取 n~m之间的随机整数
// Math.floor(Math.random() * (差值 + 1)) + 最小值
数据类型储存和+声明变量const优先
数据类型存储
JS数据类型整体分为两大类:
1.基本数据类型(简单数据类型):number \ string \ boolean \ undefined \ null
2.引用数据类型(复杂数据类型):Object \ Function \ Array
内存中堆栈空间分配区别:
- 栈:访问速度快,基本数据类型存放到栈里
(变量的数据直接存放在栈空间中)
- 堆:存储容量大,引用数据类型存放到堆里面
(栈空间里存放的是地址,真正的数据存放在堆空间中)
变量声明
变量声明选择 let or const ?
const优先,尽量使用const。(有了变量先给const,如果发现它后面是要被修改的,再改为let)
const声明的变量不允许被修改,更安全,而且const声明变量的时候里面需要进行初始化;
但是const声明的对象可以修改里面的属性,因为对于引用数据类型,const声明的变量,里面存的不是值,是地址。
所以:const声明的变量不可以改变,本质是说,const声明的变量 地址不能修改。建议数组和对象使用const来声明
-------------------
WebAPIs
查阅文档:MDN
API:应用程序接口
接口:无需关心内部如何实现,只需要调用就可以很方便的实现某些功能。
作用:开发人员使用JS提供的接口来操作网页元素和浏览器
DOM
文档对象模型
作用:操作网页文档,开发网页特效和实现用户交互
DOM的核心思想是把网页内容当作对象来处理,通过对象的属性和方法对网页内容操作。比如DOM会把标签解析成DOM对象,修改这个对象的属性会自动修改到标签身上,从而修改标签的样式、结构或内容
document对象:
是DOM顶级对象,作为网页内容的入口,它提供的属性和方法都是用来访问和操作网页内容的。
获取DOM元素
获取第一个DOM元素:querySelector
语法:
document.querySelector('css选择器')
返回文档中与指定选择器匹配的第一个Element(元素)对象;找不到返回null。
console.dir() 打印的结果是对象格式,可以看到对象的相关属性。
获取多个DOM元素:querySelectorAll及伪数组
document.querySelectorAll('css选择器')
得到的是一个伪数组:有长度有索引号但没有pop() push()等数组方法,想到得到里面的每一个元素对象需要利用for循环遍历。
即使只有一个元素对象,querySelectorAll获得的也是一个伪数组,里面只有一个元素而已。
操作元素内容
先获取dom对象,在通过innerText / innerHTML属性进行内容处理(增删改查)
1. 对象.innerText 属性
渲染文本内容到标签里,不能解析标签。
2. 对象.innerHTML 属性
渲染文本内容到标签里,会解析标签。
样式操作
操作元素常见属性
还可以通过Dom操作元素属性,如通过src更换图片地址,最常见的属性如:herf、title、src等。
先获取元素,再操作Dom元素属性。
语法:
对象.属性 = 值
操作元素样式属性
还可以通过DOM对象修改标签元素的样式属性,如轮播图小圆点自动更换颜色属性,点击按钮滚动图片(移动的位置translateX属性)。
1.通过style属性操作CSS
语法:
对象.style.样式属性 = 值
const box = document.querySelector('.box')
box.style.width = '200px'
box.style.backgroundColor = 'pink'
注意:修改样式通过style属性引出;不要忘了加单位;如果有- 的样式,采取小驼峰命名法,如marginTop
其他:页面就一个body标签,获取body可以通过document.body。
2.通过类名(className)操作元素样式(理解)
适用于要修改的样式较多的情况,核心是把多个样式放到CSS一个类中,然后把这个类添加到这个元素身上。
语法:
对象.className = 'active' //active是一个类名
注意:由于class是关键字,所以使用className来代替
使用类名操作样式添加的新类名会覆盖原先的类名。
3.通过classList操作元素样式(推荐)
为了解决className容易覆盖以前的类名,可以通过classList方式追加和删除类名。
语法:
//新增一个类名
对象.classList.add('类名')
//移除一个类名
对象.classList.remove('类名')
//切换一个类名,如果元素身上有这个类名就删除,没有就添加
对象.classList.toggle('类名')
注意:这三个是方法要加小括号
操作表单元素属性
1.比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框。
获取:DOM对象.属性
设置:DOM对象.属性 = 新值
2.表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示,比如禁用按钮,勾选按钮等。
<body>
<input type="text" name="username" value="用户名">
<button>按钮</button>
<!-- <button disabled>按钮</button> disabled可以禁用按钮 -->
<input type="checkbox" name="agree">
<script>
// 操作表单属性
// 1.操作表单type 和value 属性
// 1.1修改type属性
const username = document.querySelector('[name=username]') //通过属性选择器获得,要用[]包含
console.log(username)
// username.type = 'password'
// 1.2操作表单value属性
console.log(username.value) //查
username.value = '123456' //改&增
username.value = '' //删
// 2.禁用按钮或勾选复选框
// 2.1禁用按钮
const button = document.querySelector('button')
button.disabled = true
// 2.2勾选复选框
const agree = document.querySelector('[name=agree]')
console.log(agree)
agree.check = true
</script>
</body>
自定义属性
标准属性:标签天生自带属性,比如class、id、title等,可以直接使用点语法操作如:对象.title。
自定义属性:在html5中推出了专门的data-自定义属性
使用场景:通过自定义属性可以存储数据,后期可以使用这个数据
使用:在标签上一律以data- 开头;在DOM对象上一律以dataset对象方式获取。
<body>
<div class="box" data-id="1" data-name="box">盒子</div>
<script>
// 自定义属性
// 1.获取盒子
const box = document.querySelector('.box')
// 2.得到自定义属性值
console.log(box.dataset) //得到一个对象集合
console.log(box.dataset.id)
</script>
</body>
定时器及轮播图定时切换案例
定时器-间隔函数
定时器函数可以开启和关闭定时器
1.开启定时器
setInterval(函数,间隔时间)
每间隔一段时间调用这个函数,,间隔时间单位是毫秒。
注意:函数如果是调用外面的函数,不需要加小括号;
定时器返回的是一个id数字。
2.关闭定时器
clearInterval(变量名)
需要定时器变量名来关闭,开启一个定时器时要声明一个变量把它接受过来,变量返回的是一个唯一的id数字。
<script>
// 定时器之间隔函数
// 1.开启定时器
/* setInterval(function () {
console.log('我一秒钟执行一次')
}, 1000) */
function repeat() {
console.log('我是一个间隔函数')
}
let timer = setInterval(repeat, 1000) //注意调用的时候直接写函数名字不需要小括号
console.log(timer) //1
// 2.关闭定时器
clearInterval(timer)
</script>
事件监听
事件监听
事件是程序在运行时发生的特定动作或特定的事情,比如点击按钮、鼠标经过菜单等。
语法:
元素对象.addEventListener('事件类型',事件处理函数)
事件发生后,想要执行的代码写道事件处理函数里;当触发指定的事件时,事件处理函数就会被执行。
事件监听是将事件处理函数注册到元素对象身上。事件监听也称为事件注册、事件绑定。
注意:事件类型必须是字符串,且都是小写字母。
三要素:事件源(哪个元素上触发)、事件类型(什么情况下触发:鼠标单击click、鼠标经过mouseenter等)、事件处理函数(把要做的事放到事件处理函数里面)
回调函数
当一个函数当作参数来传递给另外一个函数的时候,这个函数就是回调函数(回头调用的函数).
回调函数本质还是函数,只不过把它当作参数使用,使用匿名函数作为回调函数比较常见,作用是完成某些特定任务。
常见使用场景:setInterval(function(){},1000);box.addEventListener('click',function() {})
事件类型
自动获得焦点 focus() / 自东失去焦点 blur()
语法:元素.focus()
鼠标经过/离开事件的区别:
mouseover和mouseout 会有冒泡;
mouseenter和mouseleave 没有冒泡(常用)。
事件对象
事件对象里有事件触发时的相关信息,包含属性和方法。
使用场景:可以判断用户按下哪个键,如按下回车发布信息;可以判断鼠标点击了哪个元素,从而做相应操作。
语法:注册事件中,回调函数的第一个参数就是事件对象,一般命名为event、ev、e。
元素.addEventListener('click' ,function (e) {
})
想要得到用户按下了键盘的哪个键:事件对象.key属性
环境对象this
指的是函数内部特殊的this,它指向一个对象,并且受当前环境影响。
作用:弄清除this的指向,可以让代码更简洁。
函数的调用方式不同,this指代的对象也不同,[谁调用,this就是谁]是判断this指向的粗略规则。
排他思想
排除其他人,保留我自己。
目的是突出显示某个元素,如有多个元素,当鼠标经过时,只有当前元素会添加高亮样式,其余的元素移除样式。
事件流和事件委托
事件流
事件完整执行过程中的流动路径。
当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段。
1.事件捕获:当一个元素的事件被触发时,会从DOM的根元素开始依次调用同名事件(从外到里)。
事件捕获需要写相应代码才能看到效果。
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
说明: addEventListener第三个参数传入true代表是捕获阶段触发(很少使用),若传入false代表冒泡阶段触发,默认就是false。
2.事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。即当一个元素触发事件后,会依次向上调用所有父级元素的同名事件。
事件冒泡是默认存在的,或者第三个参数传入false都是冒泡。
阻止冒泡
前提:需要拿到事件对象
语法:
事件对象.stopPropagation()
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效。
事件委托
(Event Delegation)是JS中注册事件的常用技巧,也称为事件委派、事件代理。
简单理解:原本需要注册在子元素的事件委托给父元素,让父元素承担事件监听的职务。
优点:减少注册次数,可以提高程序性能。
原理:利用事件冒泡的特点,给父元素注册事件,当触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件。
如何利用事件委托方式找到真正触发的元素:事件对象.target.tagName。
<script>
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// console.dir(e.target)
if (e.target.tagName === 'LI') {
e.target.style.color = 'pink'
}
})
</script>
client、scroll、offset
阻止默认行为
如点击提交按钮是阻止对表单的提交、阻止链接的跳转等。
语法:
事件对象.preventDefault()
移除事件监听
页面加载事件
1.加载外部资源(如图片、外联CSS和JS等)加载完毕时触发的事件。
事件名:load
监听页面所有资源加载完毕:给window添加load事件
window.addEventListener('load',function () {
//执行的操作
})
2.当初始HTML文档被完全加载和解析完成之后就触发,而无需等待样式表、图像等完全加载。
事件名:DOMContentLoaded
监听页面DOM加载完毕:给document添加 DOMContentLoaded 事件
document.addEventListener('DOMContentLoaded',function () {
//执行的操作
})
页面滚动事件
滚动条在滚动的时候持续触发的事件
使用场景:固定导航栏、返回顶部等
事件名:scroll
监听整个页面滚动:给window或document添加scroll事件
window.addEventListener('scroll',function () {
//执行的操作
})
监听某个元素的内部滚动直接给某个元素添加即可。
小技巧:实现回到顶部需求时,让页面滑动效果滚动到页面顶部可以给html标签添加CSS属性scroll-behavior:
html {
/* 让滚动条丝滑滚动 */
scroll-behavior:smooth;
}
页面尺寸事件
会在窗口尺寸改变的时候触发事件。
window.addEventListener('resize',function () {
//执行的代码
}
获取元素宽高 clientWidth 和 clientHeight
获取元素可见部分宽高,不包括border、margin、滚动条等,只读
元素尺寸和位置 offset家族
日期对象
日期对象使用 Date
用来表示日期和时间的对象,可以得到当前系统日期和时间。Date是JS内置对象。
1.实例化 日期对象使用必须先实例化:创建一个对象并获取时间
在代码中发现了new关键字时,一般将这个操作称为实例化。
获得当前日期:
const date = new Date()
获得指定日期:
const date = new Date('2099-9-9)
2.格式化日期对象 将日期对象的数据转换为实际开发中常用的格式。
格式化日期的另外方法:
时间戳
时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数(数字型)。
使用场景:计算倒计时。
算法:将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数;剩余时间毫秒数转换为年月日时分秒就是倒计时时间。
三种方法获得时间戳:
时分秒转换公式:
h = parseInt(总秒数 / 60 / 60 % 24) //计算小时
m = parseInt(总秒数 / 60 % 60) //计算分钟
s = parseInt(总秒数 / 60) //计算当前秒数
DOM节点操作
DOM树:DOM将HTML文档以树状结构直观的表现出来,称之为DOM树或节点树。
节点(Node)是DOM树中的单个点。包括文档本身、元素、文本以及注释都属于节点。
元素节点:所有的标签,html是根节点;
属性节点:所有的属性,如herf;
文本节点:所有的文本。
查找节点
利用节点的关系查找节点,返回的都是对象。
1.查找父节点:parentNode,返回最近一级的父节点对象,找不到返回Null。
子元素.parentNode
2.查询子节点:children,返回所有子节点元素,是一个伪数组。
3.查询兄弟节点:previousElementSibling—上一个兄弟;nextElementSibling—下一个兄弟。
增加节点
使用场景:需要在页面中增加元素,如点击发布按钮,新增一条信息。
步骤:
1.创建节点
document.createElement('标签名')
2.追加节点:把创建的节点插入到某个父元素中。
append:放到父元素最后面;
prepend:放到父元素最前面。
<script>
// 1.创建节点
const firstli = document.createElement('li')
firstli.innerText = '我是放到前面的'
const lastli = document.createElement('li')
lastli.innerText = '我是放到后面的'
// 2.追加给父元素
const ul = document.querySelector('ul')
// 放到ul最前面
ul.prepend(firstli)
// 放到ul最后面
ul.append(lastli)
console.log(ul)
</script>
删除节点
element.remove(),会从DOM树中删除这个元素。
<script>
const box = document.querySelector('.box')
box.remove()
</script>
M端事件
M端 (移动端)有自己独特的地方,比如触屏事件touch(也称触摸事件),Android和IOS都有。
touch对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
常见的触屏事件:
touch事件 | 说明 |
touchstart | 手指触摸到一个DOM元素时触发 |
touchmove | 手指在一个DOM元素上滑动时触发 |
touchend | 手指从一个DOM元素上移开时触发 |
swiper插件
触摸滑动插件
1.看官网Swiper中文网-轮播图幻灯片js插件,H5页面前端开发
2.查看基本使用流程
3.写个小demo
4.应用到开发中
AlloyFinger插件
Web手势插件,为元素注册各种手势事件。
github地址:https://github.com/AlloyFinger
forEach遍历数组
数组.forEach(function (element, index) { })
element是数组元素,index是索引号
BOM
BOM和window对象
JS的组成:
ECMAScript:规定了js基础语法核心知识,如变量、分支语句、循环对象、对象等等。
WebAPIs:
- DOM 文档对象模型,定义了一套HTML文档的API
- BOM 浏览器对象模型,定义了一套操作浏览器窗口的API
定时器之延迟函数
JS内置的一个用来让代码延迟执行的函数,叫setTimeout。
语法:
setTimeout(回调函数,等待的毫秒数)
setTimeout仅仅只执行一次,可以理解为把一段代码延迟执行,平时省略window。
间歇函数setInterval:每隔一段时间执行一次,平时省略window。
清除延迟函数:
let timer = setTimeout(回调函数,等待的毫秒数)
clearTimerout(timer)
定时器开启后,不必等待,直接执行下面的代码,定时器时间到了后,再回来执行里面的代码。
<script>
// 1.开启延迟函数
/* setTimeout(function () {
console.log('我只执行一次')
}, 3000) */
let timerId = setTimeout(function () {
console.log('我只执行一次')
}, 3000)
// 1.1延迟函数返回的还是一个正整数,表示延迟函数的编号
console.log(timerId)
// 1.2延迟函数需要等待时间,所以下面的代码优先执行
// 2.关闭延迟函数
clearTimeout(timerId)
</script>
location、navigator、history对象和本地存储
location对象
location(地址)它拆分并保存了URL地址的各个组成部分,他是一个对象。
常用属性和方法:
navigator对象
navigator是对象,该对象下记录了浏览器自身的相关信息。
常用属性和方法:
通过userAgent检测浏览器的版本及平台。
<script>
// 检测userAgent(浏览器信息)
(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iphone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动战点
if (android || iphone) {
location.href = 'https://m.itcast.cn'
}
})()
</script>
history对象
history是对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应,如前进、后退等。
使用场景:在实际开发中比较少用,会在一些OA办公系统中见到。
常见方法:
history对象方法 | 作用 |
back() | 后退功能 |
forward() | 前进功能 |
go(参数) | 前进后退功能,参数如果是1,前进一个页面,如果是-1,后退一个页面 |
本地存储 localStorage
将数据存储在本地浏览器中,页面刷新数据不丢失。
好处:
1.页面刷新或关闭数据不丢失,实现数据持久化;
2.容量较大,sessionStorage 和 localStorage 约5M左右。
sessionStorage 和 localStorage用法基本相同,区别是当浏览器被关闭时,存储在sessionStorage的数据会被清除。
localStorage
特性:以键值对的形式存储,并且存储的是字符串,省略了window
语法:
//存储数据
localStorage.setItem(key, value)
//读取数据
localStorage.getItem(key)
//删除数据
localStorage.removeItem(key)
<script>
// 1.存储
localStorage.setItem('age', 18)
// 2.获取
console.log(localStorage.getItem('age'))
// 3.删除
localStorage.removeItem('age')
</script>
localStorage存储复杂数据类型
本地只能存储字符串,无法存储复杂字符类型。
解决方案:将复杂数据类型转换为JSON字符串,存储到本地。
JSON字符串:首先是一个字符串;属性名用双引号引起来,不能单引号;属性值如果是字符串也必须双引号。
<script>
// 本地存储复杂数据类型
const goods = {
name: '小米',
price: 1999
}
// 1.把对象转换为JSON字符串 JSON.stringify
localStorage.setItem('goods', JSON.stringify(goods))
console.log(typeof localStorage.getItem('goods'));
// 2.把JSON字符串转换为对象 JSON.parse
JSON.parse(localStorage.getItem('goods'))
console.log(JSON.parse(localStorage.getItem('goods')))
</script>
正则表达式和元字符
vscode正则插件 any-rule:可以快速生成正则表达式。
正则表达式(Regular Expression)是一种字符串匹配的模式(规则)。
使用场景:如手机号表单要求只能输入11位数字(匹配),过滤页面内容中的敏感词(替换),从字符串中获取需要的特定部分(提取)等。
基本使用
正则基本使用分为两步:
元字符
是一些具有特殊含义的字符,可以极大地提高灵活性和强大的匹配功能。如,规定用户只能输入英文的26个字符,换成元字符写法:/[a-z]/。
元字符分为四类:边界符、量词、范围、字符类。
1.边界符:提示字符所处的位置,主要有两个字符。
^:表示匹配行首的文本(以谁开始)
$:表示匹配行尾的文本(以谁结束)
如果^和$在一起,则表示必须是精确匹配。
2.量词:设定某个元素的重复次数。
注意:逗号两侧千万不要出现空格
3.范围:表示字符的范围,定义的规则限定在某个范围,比如只能是字母或数字等。用 [] 表示范围。
//想要包含小写字母,大写字母,数字
const reg = /^[a-zA-Z0-9]$/
4.字符类:某些常见模式的简写方式,区分字母和数字。
替换和修饰符
replace 替换方法
语法:
字符串.replace(/正则表达式/,'替换的文本')
只能替换匹配到的第一个字符,返回值是新的替换完毕的字符串,并不修改原来的字符串
修饰符
约束正则的某些细节行为,如是否区分大小写,是否支持多行匹配。
语法:
/表达式/修饰符
i 是单词ignore的缩写,正则匹配时字母不区分大小写 ;
g 是global的缩写,匹配所有满足正则表达式的结果;
-------------------
JS进阶
作用域链+JS垃圾回收机制+闭包
作用域链
嵌套关系的作用域串联起来形成了作用域链。
作用:作用域链本质上是底层的变量查找机制(就近原则)。
函数被执行时,会优先在当前函数作用域中查找变量,找不到会逐级向上查找父级作用域直到全局作用域。子作用域能够访问父作用域,父作用域无法访问子作用域。
垃圾回收机制
Garbage Collection简称GC
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
内存的生命周期:
JS环境中分配的内存,一般有如下生命周期:
1.内存分配:当声明变量、函数、对象时,系统自动为他们分配内存;
2.内存使用:即读写内存,也就是使用变量、函数等;
3.内存回收:使用完毕,有垃圾回收器自动回收不在使用的内存。
说明:全局变量一般不会回收(关闭页面回收);一般情况下局部变量的值不用了会被自动回收。
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏。
闭包
一个函数对周围状态的引用捆绑在一起,闭包让开发者可以从内部函数访问外部函数的作用域。
闭包 = 内层函数 + 外层函数的变量
作用:实现数据的私有,避免全局污染,外层函数也可以访问里层函数变量。
function outer() {
let count = 1
function fn() {
count++
console.log(`函数被调用${count}次`)
}
return fn
}
const re = outer()
re()
闭包存在的问题:可能造成内存泄漏。re是全局变量,等页面关闭才回收,re不销毁,count++一直被re使用,那么count++也不会被销毁。
var变量提升&函数提升
var变量提升:
允许变量在声明之前即被访问(仅存在于var声明变量)
变量提示会提升到当前作用域的最前面,只提升变量声明,不提升变量赋值。
函数提升:
函数提升到当前作用域的最前面,只提升声明,不提升调用。函数表达式不存在提升的现象。
展开运算符和箭头函数
函数参数(arguments对象&剩余参数)
1.arguments对象
使用场景:不确定传递多少个实参时。
是函数内部内置的对象(伪数组);只存在于函数中,外面无法使用;作用是动态获取调用函数时传递的所有实参,可以通过for循环依次得到传递过来的实参。
function sum() {
//arguments可以得到传递过来的所有实参[1,2,3]
let sum = 0
for(let i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
console.log(sum) //6
}
sum(1,2,3)
2.剩余参数
允许将一个不定数量的参数表示为一个数组,即用于获取多余的实参,并形成一个真数组。
使用场景:解决形参和实参个数不匹配的问题。
function sum(...other) {
let sum = 0
other.forEach(function (ele) {
sum += ele
})
console.log(sum)
}
sum(1,2)
sum(1,2,3)
sum(1,2,3,4)
剩余参数和arguments区别:
...是语法符号,置于最末函数形参之前,用于获取多余的实参,是个真数组;
箭头函数不支持arguments,但是可以使用剩余数组;
提倡多使用剩余参数。
展开运算符 ...
将一个数组/对象进行展开,不会修改原数组
使用场景:求数组最大/最小值、合并数组等。
不会修改原数组
const arr = [1,2,3]
console.log(...arr) //1 2 3
//求数组最大/最小值
console.log(Math.max(...arr)) //3
console.log(Math.min(...arr)) //1
//合并数组
const arr1 = [1,2,3]
const arr2 = [4,5,6]
console.log([...arr1, ...arr2]) //[1,2,3,4,5,6]
剩余参数和展开运算符区别:
剩余参数:函数参数使用,把多个元素收集起来生成一个真数组(凝聚);
展开运算符:将数组展开成各个元素(拆散)
箭头函数
使用场景:箭头函数更适用于那些本来需要匿名函数的地方,写法更简单。
语法:
如果只有一个形参可以省略小括号,没有参数则写空的小括号;
如果函数体只有一句代码,则可以省略大括号,这句代码就是返回值(省略return);
如果返回的是一个对象,则需要用小括号把对象包起来;
箭头函数里没有arguments,但是有剩余参数
//普通函数
const fn = function () {
console.log('我是普通函数')
}
fn()
//箭头函数
//1.基本语法
const fn1 = () => {
console.log('我是箭头函数')
}
fn1()
//2.细节使用
//2.1 如果只有一个形参可以省略小括号,没有参数则写空的小括号
const sum = x => {
console.log(x + x)
}
sum(2)
//2.2 如果函数体只有一句代码,则可以省略大括号,这句代码就是返回值(省略return)
const sum1 = (x) => {
return x + x
}
//可以写成:
const sum2 = x => x + x
sum2(5) //10
//2.3 如果返回的是一个对象,则需要用小括号把对象包起来
const fn = () => ({name: '汪汪'})
console.log(fn())
//2.4 箭头函数里没有arguments,但是有剩余参数
const fn = (...other) => {
console.log(other)
}
fn(1,2)
箭头函数本身没有this,它只会沿用上一层作用域的this。
根据需求来选择是否需要箭头函数:dom元素注册事件,需要用this,就不要箭头函数;定时器里面需要用到this,就需要箭头函数(定时器setTimeout是window调用的,定时器里的this指向window,用箭头函数让this指向上一层)
ES6数组和对象解构
ES6中对象和属性方法简写
1.在对象中,如果属性名和属性值一致,可以简写只写属性名。
2.在对象中,方法(函数)可以简写
const obj = {
uname: uname,
age: age
}
//可简写为:
const obj = {
uname,
age
}
const obj = {
sayHi: function() {
console.log('hi')
}
}
//可简写为:
const obj = {
sayHi() {
console.log('hi')
}
}
解构赋值
解构赋值:将数组中的值或对象的属性取出,赋值给其他变量。
结构:就是把一个事物的结构进行拆解
数组解构:
语法:右侧数组的值将被赋值给左侧的变量;变量的顺序对象数组值的位置依次进行赋值操作。
const [a,b,c] = [1,2,3]
//同时将数组值1 2 3依次赋值给变量a b c
JS两个特殊情况需要加分号:小括号 / 中括号开头的需要加分号。
数组解构值和变量不匹配的情况:
1.变量多值少的情况:多的变量结果是undefined,防止undefined传值,可以设置默认值。
const [a,b,c,d = 4] = [1,2,3]
2.变量少值多的情况:多的值传不过来,利用剩余参数解决
const [a,b,...c] = [1,2,3,4] //c = [3,4]
3.按需导入,忽略某些值。
const [a, ,c,d] = [1,2,3,4] //a = 1, c = 3, d = 4
多维数组解构赋值:
const [a, [b, c]] = [1, [2, 3]]
对象解构:
const user = {
name: '汪汪',
age: 18
}
// 1.对象解构赋值基本使用
const { name, age } = user
console.log(name); //汪汪
console.log(age); //18
// 1.1要求变量名和属性名必须一致,如果不一致默认为undefinded
// 1.2变量名不要和外面的变量名冲突,否则报错
// 2.更改解构变量名(重命名) 变量名:新变量名
const { name: n, age: a } = user
console.log(n); //汪汪
console.log(a); //18
// 3.对象数组解构
const arr = [
{ name: '汪汪', age: 18 }
]
const [{ name: uname, age: uage }] = arr
console.log(uname); //汪汪
console.log(uage); //18
filer方法筛选数组
filter()方法创建一个新的数组,新数组中的元素是符合条件的所有元素。
使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组,不影响原数组。
语法:
const newArr = arr.filter(function (element, index) {
return 筛选条件
})
构造函数
创建对象三种方式:
1.利用对象字面量创建对象 const o = {name: '汪汪'}
2.利用Object 创建对象 const o = new Object({ name: '汪汪' })
3.利用构造函数创建对象
构造函数
是一种特殊的函数,主要用来创建对象(初始化对象)。
使用场景:常规{}语法允许创建一个对象,可以通过构造函数来快速创建多个类似的对象。
说明:
1.构造函数命名以大写字母开头;
2.通过new关键字来调用构造函数,可以创建对象,这种行为被称为实例化;
3.实例化构造函数时没有参数可以省略();
4.构造函数里无需写return,自动返回创建的新的对象。
function Pig(name, age, gender) {
this.name = name //this.name是属性,name是形参也是属性值
this.age = age
this.gender = gender
}
const peiqi = new Pig('佩奇', 6, '女')
const kala = new Pig('卡卡', 7, '男')
new实例化执行过程:
1.创建新空对象
2.构造函数this指向新对象
3.执行构造函数代码
4.返回新对象
实例成员&静态成员
实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实力方法)
说明:
1.为构造函数传入参数,创建结构相同但值不相同的对象;
2.构造函数创建的实例对象彼此独立互不影响。
静态成员:构造函数的属性和方法被称为静态成员(静态属性和静态方法)
说明:静态成员只能构造函数来访问;静态方法中的this指向构造函数。
instanceof:作用是判断其左边对象是否为其右边类的实例,返回boolean类型的数据。
Array instanceof Object //true
Function instanceof Object //true
一切皆对象
包装类型:字符串、数值、布尔等基本数据类型也都有专门的构造函数,称为包装类型。
JS中几乎所有的数据都可以基于构造函数创建,不同的构造器创建出来的数据拥有不同的属性和方法。
内置构造函数—Object三种静态方法
静态方法就是只有构造函数Object可以调用的。
Object是内置的构造函数,用来创建普通对象。推荐使用字面量方式声明对象。
1.Object.keys()静态方法获取对象中所有属性(键)
语法:
const o = {name: '汪汪', age: 18}
//获得对象所有键,并且返回一个数组
const arr = Object.keys(o)
console.log(arr); //['name', 'age']
2.Object.values()静态方法获取对象中所有属性值,返回一个数组。
3.Object.assign()静态方法常用于拷贝对象。
语法:
//拷贝对象,把o拷贝给obj
const o = {name: '汪汪', age: 18}
const obj = {}
Object.assign(obj, o)
console.log(obj) //{name: '汪汪', age: 18}
内置构造函数—Array
Array是内置的构造函数,用于创建数组。const arr = new Array(3, 5)
数组常见实例方法:
map:返回处理之后的数组
在JavaScript中,Array.prototype.map
方法是一种用于遍历数组并创建一个新数组的方法。对于原数组中的每一个元素,map
方法都会调用一个提供的函数,并将这个函数的返回值作为新数组中的对应位置的元素。
语法:
const newArray = array.map(callback(element, index, array), thisArg);
callback
: 必需。一个函数,其执行结果会作为新数组中的元素。此函数接受三个参数:
element
: 当前正在处理的元素。index
: 可选。当前元素的索引。array
: 可选。调用 map 方法的原始数组。
thisArg
: 可选。指定在回调函数中使用的this
值。
thisArg
参数是指定在回调函数中使用的this
值。当使用Array.prototype.map
或其他数组方法时,可以通过thisArg
参数来控制回调函数内部this
的指向。具体来说:- 默认情况下,如果未提供
thisArg
参数,回调函数中的this
通常指向全局对象(在浏览器中是window
,在 Node.js 中是global
)。- 如果提供了
thisArg
参数,则回调函数中的this
会指向thisArg
指定的对象。
const numbers = [1, 4, 9];
const roots = numbers.map(function(num) {
return Math.sqrt(num);
});
console.log(roots); // 输出: [1, 2, 3]
reduce:返回累计处理的结果
语法:
arr.reduce(function(上一次值,当前值) {},初始值)
参数:如果有初始值,则把初始值累加到里面.
const arr = [1, 5, 8]
const total = arr.reduce(function(prev, current) {
return prev + current
})
console.log(total) //14
const total = arr.reduce((prev, current) => prev + current)
其他常见实例方法:
Array.from()方法转换为真数组
内置构造函数—String
常见实例方法:
split('分隔符'):将字符串拆分成数组;
join('分隔符'):将数组转换为字符串。
面向对象和原型
两种编程思想—面向过程和面向对象
1.面向过程编程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个依次调用。
优点:性能比面向对象高,适合跟硬件联系很紧密的东西。
缺点:不灵活,复用性差。
2.面向对象编程(oop):把事务分解成一个个对象,然后由对象之间分工合作。以对象功能来划分问题,而不是步骤。
面向对象的特性:封装性;继承性;多态性。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特点,可以设计出低耦合的系统,使系统更灵活、更易于维护。
缺点:性能比面向过程低。
构造函数实现封装
JS 实现面向对象中的封装借助于构造函数来实现,但存在浪费内存的问题,构造函数中的函数会多次创建,占用内存。
原型对象 prototype 和原型
JS规定,每一个函数都有一个prototype属性,指向另一个对象,也称为原型对象。
使用场景:可以解决构造函数封装时函数(方法)会多次创建,占用内存的问题。
原型对象可以挂载函数,对象实例化不会多次创建原型对象里面的函数,节约内存。
实例对象可直接访问原型对象中函数。
执行过程:先找实例对象属性或函数,找不到会再找原型对象中属性和函数。
this指向
构造函数和原型对象中的this都指向实例化的对象。
注意:箭头函数不能做构造函数,因为箭头函数没有this;原型对象里面的函数如果需要用到this,也不要用箭头函数。
constructor属性
每个原型对象里都有个constructor属性(constructor构造函数)
作用:该属性指向该原型对象的构造函数。如果对象有多个方法,可以给原型对象采取对象赋值形式,但这样会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,可以在修改后的原型对象中,添加一个constructor指向原来的构造函数。
function Person(name) {
this.name = name
}
console.log(Person.prototype.constructor === Person) //true
Person.prototype = {
constructor: Person,
sing() {console.log('我会唱歌')},
dance() {console.log('我会跳舞')}
}
原型__proto__
对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以对象可以使用构造函数prototype原型对象的方法,就是因为对象有__proto__原型的存在。
注意:prototype 原型对象,__proto__ 原型 ;
__proto__非标准属性,在浏览器里显示的是[[Prototype]];
__proto__尽量不要修改它,否则会影响性能。
构造函数、原型对象、实例对象三者关系:
原型链
__proto__属性链状结构称为原型链
作用:原型链为对象成员查找机制提供一个方向,或者说一条路线。
查找规则:当访问一个对象成员(属性/方法)时,首先查找这个对象自身有没有该成员(属性/方法);如果没有就查它的原型对象(__proto__指向的prototype原型对象);如果还没有就查找原型对象的原型对象(Object的原型对象);依此类推一直找到Object为止(null).
instanceof运算符
语法:
实例对象 instanceof 构造函数
作用:检测构造函数.prototype 是否存在于实例对象的原型链上。
原型继承
继承是面向对象编程的另一个特性。有些公共的属性和方法可以写到父级身上,子级提通过继承也可以使用这些属性和方法。
JS中大多是借助原型对象实现继承的特性。
字面量对象继承的缺点:使用同一个对象,修改任何一个会影响其他,对象不独立。
解决:构造函数实例化对象继承,因为new每次都会创建一个新的对象。
浅拷贝和递归
浅拷贝
把对象拷贝给一个新的对象,开发中经常需要复制一个对象。如果直接赋值,则复制的是地址,修改任何一个对象,另一个对象都会变化。
常见方法:
1.拷贝对象:
1.1Object.assign()
1.2展开运算符 {...obj}
2.拷贝数组:
2.1Array.prototype.concat()
2.2展开运算符 [...arr]
注意:如果是基本数据类型拷贝值,如果是引用数据类型拷贝地址。
浅拷贝单层对象没问题,如果遇到多层拷贝还是会影响原来的对象。
深拷贝
拷贝多层,不再拷贝地址。
常见方法:
1.通过JSON序列化实现
JSON.stringify() 序列化为JSON字符串,然后再JSON.parse() 转回对象格式。
注意:JSON.stringify()序列化的时候会忽略函数和undefinded。
2.lodash库 实现
js库lodash里面_.cloneDeep() 内部实现了深拷贝。
官网地址:https://www.lodashjs.com/
3.通过递归实现
递归
递归是一种函数调用自身的操作。
函数内部自己调用自己,递归函数的效果和循环效果类似;由于递归很容易发生“栈溢出”错误(stack overflow),所以记得添加退出条件return。
//1.利用函数递归打印三句话
let i = 1
function fn() {
console.log(`我是第${i}句话`)
if (i >= 3) return
i++
fn() //递归
}
fn()
通过递归函数实现深拷贝:
const obj = {
name: '佩奇',
family: {father: '猪爸爸'},
hobby: ['唱歌', '跳舞']
}
// 封装深拷贝函数 cloneDeep()
function cloneDeep(oldObj) {
// 先判断拷贝的是数组还是对象
const newObj = Array.isArray(oldObj) ? [] : {}
// 遍历拷贝属性和值
for (let k in oldObj) {
// 把旧对象的值给新对象的属性
// 如果属性值是引用数据类型,则需要递归再次拷贝;
if(typeof oldObj[k] === 'object') {
newObj[k] = cloneDeep(oldObj[k])
} else { // 否则属性值是基本数据类型,直接赋值即可
newObj[k] = oldObj[k] //相当于newObj.name = '佩奇'
}
}
// 返回新对象
return newObj
}
const newObj = cloneDeep(obj)
throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度地避免错误的发生导致整个程序无法继续运行。
1.throw抛出异常信息,程序也会终止执行;
2.throw后面跟的是错误提示信息;
3.Error对象配合throw使用,能够设置更详细的错误信息。
function count(x,y) {
if(!x || !y) {
// throw '参数不能为空'
throw new Error('参数不能为空')
}
return x + y
}
count()
try / catch 捕获错误信息
想要测试某些代码是否有异常,可以通过 try / catch捕获错误信息(浏览器提供的错误信息)
try 试试;catch 拦住;finally 最后
1.将预估可能发生错误的代码写在try代码段中;
2.如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息;
3.finally 不管是否有错误,都会执行。
try {
// 要写测试的代码
const p = document.querySelector('p')
p.style.color = 'red'
} catch(err) { // 参数err是错误信息
// 如果try里面的代码错误则进入catch
throw err
} finally {
// 不管代码是否有错误都会执行
console.log('不管代码是否有错误都会执行');
}
call、apply、bind 改变this指向
JS中允许指定(改变)函数中this的指向,有3个方法可以动态指定普通函数中this的指向。
call() - 了解
使用call方法调用函数,同时指定被调用函数中this的指向。
语法:
fun.call(thisArg, arg1, arg2, ...)
thisArg:在fun函数运行时指定的this值
arg1, arg2:传递的其它参数(fun的实参)
返回值就是函数的返回值,因为它就是调用函数
const obj = { name: '汪汪' }
// call()作用:第一调用函数;第二改变this指向
function fun(x, y) {
console.log(this);
return x + y
}
fun() // 此时this指向window
fun.call(obj, 1, 2) //此时this指向obj对象
console.log(fun.call(obj, 1, 2)); // 返回值就是函数返回值
使用场景:Object.prototype.toString.call(数据) 检测数据类型。
// call()使用场景 - 检测数据类型
// 1.typeof 检测数据类型不够精确
console.log(typeof '123'); //string
console.log(typeof []); //object
console.log(typeof null); //object
// 2.Object.prototype.toString() 返回的结果是[object xxx类型]
console.log(Object.prototype.toString('123')); //[object Object]
console.log(Object.prototype.toString.call('123')); //[object String]
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call(null)); //[object Null]
apply() - 理解
使用apply方法调用函数,同时指定被调用函数中this的指向。
语法:
fun.apply(thisArg, [argsArray])
thisArg:在fun函数运行时指定的this值
argsArray:传递的实参,必须是数组,传递时将数组里的元素拆出来一个一个传。
返回值就是函数的返回值,因为它就是调用函数
使用场景:求数组最大/小值
// apply()使用场景 - 求数组最大 / 小值
// 方法一 展开运算符
Math.max(...[1, 2, 3])
// 方法二 apply() 能将数组参数拆分
Math.max.apply(null, [1, 2, 3]) // 这里this指向max函数,不需要改变this指向,参数写null.
bind() - 重点
bind() 方法不会调用函数,但能改变函数内部this指向
语法:
fun.bind(thisArg, arg1, arg2, ...)
thisArg:在fun函数运行时指定的this值
arg1, arg2:传递的其它参数
返回由指定的this值和初始化参数改造的 原函数拷贝(新函数)
const obj = { name: '汪汪' }
// 1.基本使用
function fun() {
console.log(this);
}
fun() //this指向window
fun.bind() //没有输出,bind不会调用函数
// const fn = fun.bind() //将fun函数拷贝了一份
console.log(fn === fun); //false
const fn = fun.bind(obj) //fn:{ name: '汪汪' } 可以改变this指向
this指向总结
1.普通函数
this的取值不取决于函数的定义,而是取决于怎么调用的(this指向调用者)
全局内调用: fn() 指向window;
对象内的方法调用: obj.fn() 指向调用对象;
构造函数调用: new Person()指向实例对象;
事件处理函数中调用:指向当前触发事件的DOM元素;
特殊调用比如call、apply、bind可以改变this指向,fun.cal(obj) 指向 obj。
2.箭头函数 没有this,沿用上一级作用域的this。
防抖和节流
防抖(debounce)
单位时间内,频繁触发事件,只执行最后一次。
使用场景:搜索框搜索输入,只需用户最后一次输入完,再发送请求;手机号、邮箱验证输入检测
利用lodash库提供的防抖函数实现防抖
节流(throttle)
单位时间内,频繁触发事件,只执行一次。
使用场景:高频事件:鼠标移动 mousemove、页面尺寸缩放resize、滚动条滚动scroll 等。
利用lodash库提供的节流函数实现节流