文章目录
1.JavaScript简介
1.1 JavaScript的起源
JavaScript诞生于1995年,它的出现主要是用于处理网页中的前端验证。所谓的前端验证,就是指检查用户输入的内容是否符合一定的规则。比如:用户名的长度,密码的长度,邮箱的格式等。但是,有的同学可能会有疑问,这些验证,后端不也可以进行验证吗?确实,后端程序的确可以进行这些验证,但你要清楚,在1995年那个年代,网速是非常慢的,向后端发送一个请求,浏览器很久才能得到响应,那这无疑是一种非常不好的用户体验。
为了解决前端验证的问题,当时的浏览器巨头NetScape(网景)公司就开发出一种脚本语言,起初命名为LiveScript,后来由于SUN公司的介入更名为了JavaScript。但是你要清楚,Java和JavaScript是没有什么关系的,只不过当时Java非常流行,为了蹭热度,才将LiveScript更名为JavaScript,它们的关系就像雷锋和雷峰塔的关系一样,没啥关系。
但是,浏览器开发商不止网景一家,还有一个大家都知道的公司,微软公司,它们的主打产品是IE(Internet Explorer)浏览器,当网景公司的Netscape Navigator浏览器推出JavaScript语言时,微软就急了啊,好家伙,人网景都推出了专门用于前端验证的语言,不仅大大减少了后端程序的压力,还提高了用户的体验。我微软这么大的公司不也得整一个,在1996年,微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript。
于是在市面上存在两个版本的JavaScript,一个网景公司的JavaScript和微软的JScript,虽然当时浏览器的巨头是网景,但是网景的浏览器是收费的,虽然微软的IE浏览器在全球的市场份额远远不及网景,但是微软的拳头产品是Windows操作系统,每一个操作系统都自带一个IE浏览器并且免费,那么,未来的发展大家可能也想到了,网景让微软给干倒闭了,1998年11月,网景被美国在线(AOL)收购。
老大哥就是老大哥,为了抢先获得规则制定权,网景最先将JavaScript作为草案提交给欧洲计算机制造商协会,也就是ECMA组织,希望能将JavaScript做成行业标准,最终在网景、SUN以及微软等公司的参与下,由一众程序员和相关组织人员组成的第39技术委员会也就是TC39发布了ECMA-262标准,这个标准定义了名为ECMAScript的全新脚本语言,为啥又来了个ECMAScript?
因为Java是SUN的商标,SUN授权了NetScape可以叫JavaScript,但是ECMA没有SUN的授权就不能叫JavaScript,哪怕NetScape成员特别希望ECMA把它叫做JavaScript,但是ECMA也有成员并不希望这个标准就叫JavaScript,总之经过几轮磋商和博弈,ECMAScript这个名字就定下来。
1.2 JavaScript的组成
ECMAScript是一个标准,而这个标准需要由各个厂商去实现,不同的浏览器厂商对该标准会有不同的实现。
我们已经知道ECMAScript是JavaScript标准,所以一般情况下这两个词我们认为是一个意思。但是实际上JavaScript的含义却要更大一些。一个完整的JavaScript实现应该由以下三个部分构成:
1.3 JavaScript的特点
解释型语言
JavaScript是一门解释型语言,所谓解释型值语言是指不需要被编译为机器码在执行,而是直接执行。由于少了编译这一步骤,所以解释型语言开发起来尤为轻松,但是解释型语言运行较慢也是它的劣势。不过解释型语言中使用了JIT技术,使得运行速度得以改善。
动态语言
JavaScript是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。当然这个问题我们以后再谈。不过在补充一句动态语言相比静态语言性能上要差一些,不过由于JavaScript中应用的JIT技术,所以JavaScript可能是运行速度最快的动态语言了。
类似于 C 和 Java 的语法结构
JavaScript的语法结构与C和Java很像,向for、if、while等语句和Java的基本上是一模一样的。所以有过C和Java基础的同学学习起来会轻松很多。不过JavaScript和与Java的关系也仅仅是看起来像而已。
基于原型的面向对象
JavaScript是一门面向对象的语言。啥是对象?下次聊。
Java也是一门面向对象的语言,但是与Java不同JavaScript是基于原型的面向对象。啥是原型?下次聊。
严格区分大小写
JavaScript是严格区分大小写的,也就是abc和Abc会被解析器认为是两个不同的东西。
1.4 JavaScript本使用方式
1.在HTML中在script标签中就可以编写JavaScript代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
alert("hello world")
</script>
</body>
</html>
2.在一个单独的js文件中也可以编写JavaScript代码,然后在HTML文件中使用script标签进行引用
<script src="main.js"></script>
1.5 JavaScript的输出方式
1.向页面输出内容
<script>
document.write("hello world")
</script>
2.在控制台输出信息
<script>
console.log("日志信息"); // 调试中经常使用
console.info("普通信息");
console.warn("警告信息");
console.error("错误信息");
</script>
3.弹出框输出
<script>
alert("温馨提示!")
</script>
4.确认弹出框
<script> // 点击确定返回true ,取消返回false
if(confirm("是否要删除?")){
document.write("删除成功")
}else{
document.write("取消删除")
}
</script>
5.用户输入弹出框
<script>
// prompt(提示信息,用户输入的内容) 该函数返回用户输入的内容
console.log(prompt('请输入姓名:','eg:admin'))
</script>
1.6 JavaScript注释
<script>
// 单行注释: 【注释内容不会被解析执行,只是用户描述代码的作用,单行注释只能注释一行的内容】
/*
多行注释: 可以包含多行注释文本
*/
</script>
2.JavaScript的基本语法
2.1 变量命名的正确方式
所谓标识符,就是指给变量、函数、属性或函数的参数起名字。
标识符可以是按照下列格式规则组合起来的一或多个字符:
第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ )。
其它字符可以是字母、下划线、美元符号或数字。
按照惯例,ECMAScript 标识符采用驼峰命名法。
标识符不能是关键字和保留字符。
**个人意见:**命名一定要规范,否则项目大了,命名不规范很难维护,不利于后期扩展,尽量和团队沟通达成一个规范,命名一定要见名知意。
保留字:
不建议使用的标识符:
正确命名的方式: 后面标注的变量和常量先忽略,后面在解释
<script>
// 单个单词: student,user,teacher 【变量】
// 多个单词:userName,userAge,userPassword 【变量】
// 单个单词:ERROR INFO 【常量】
// 多个单词:ERROR_INFO CLASS_NAME 【常量】
</script>
2.2 变量的使用方式
什么是变量? 从名字上就可以看出,这是可以改变的一个量,也是某些值,赋值给了一个容器,然后给这个容器起了个名字,然后呢?这个容器啊,里面放的东西是可以随时改变的,你就可以把这个容器啊想像成一个变量。
1.声明变量:
<script>
var student; // 声明单个变量
var username,password; // 【使用var关键字声明变量】 声明多个变量
let userAge,userEmail; // 【使用let关键字声明变量】 声明多个变量
const USER_INFO,USER_PHONE; // 【使用const关键字声明常量】
</script>
**2.使用变量:**如果声明了变量,单没有赋值,那么该变量的值就是 undefined
<script>
var username,password; // 【使用var关键字声明变量】
console.log(username,password) //输出: undefined undefined
</script>
3.个变量重新赋值:
<script>
var username="admin",password="123456"; // 【使用var关键字声明变量】
console.log(username,password) //输出: undefined undefined
// 给变量重新赋值
username="root";
password="456789";
console.log(username,password) //输出: undefined undefined
</script>
2.3 数据类型的分类
数据类型决定了一个数据的特征,比如:123和”123”,直观上看这两个数据都是123,但实际上前者是一个数字,而后者是一个字符串。
对于不同的数据类型我们在进行操作时会有很大的不同。
JavaScript中一共有5种基本数据类型:
- 字符串型(String)
- 数值型(Number)
- 布尔型(Boolean)
- undefined型(Undefined)
- null型(Null)
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型
2.4 检测一个变量的数据类型
使用typeof操作符可以用来检查一个变量的数据类型。
<script>
var studentName =1;
console.log( typeof studentName) // 输出:number
</script>
2.5 String
String用于表示一个字符序列,即字符串。字符串需要使用 单引号 或 双引号 括起来。
<script>
var studentName = "admin"
var userName = 'root'
console.log(typeof studentName)
console.log(typeof userName)
</script>
2.6 Number
Number 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。
Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。
最大值:+1.7976931348623157e+308
最小值:-1.7976931348623157e+308
0以上的最小值:5e-324
特殊的数字:
Infinity:正无穷
-Infinity:负无穷
NaN:非法数字(Not A Number)
其它的进制:
二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
八进制:0 开头表示八进制
十六进制:0x 开头表示十六进制
注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。
2.7 Boolean
布尔型也被称为逻辑值类型或者真假值类型。
布尔型只能够取真(true)和假(false)两种数值。除此以外, 其它的值都不被支持。
2.8 Undefined
Undefined 类型只有一个值,即特殊的 undefined。
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
注意:使用typeof对没有初始化和没有声明的变量,会返回“undefined”。
2.9 Null
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。
注意:从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。
3.0 强制类型转换
强制类型转换指将一个数据类型强制转换为其它的数据类型。一般是指,将其它的数据类型转换为String、Number、Boolean
转换为String类型
将其它数值转换为字符串有三种方式:toString()、String()、 拼串。
-
方式一:调用被转换数据类型的toString()方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null和undefined这两个值没有toString()方法,如果调用它们的方法,会报错。
-
方式二:调用String()函数,并将被转换的数据作为参数传递给函数,使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。
-
方式三:为任意的数据类型 + " " 推荐的方式
转换为Number类型
有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。parseInt()只会将字符串转换为整数,而parseFloat()可以将字符串转换为浮点数。
有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。parseInt()只会将字符串转换为整数,而parseFloat()可以将字符串转换为浮点数。
方式一:使用Number()函数
字符串 --> 数字
如果是纯数字的字符串,则直接将其转换为数字
如果字符串中有非数字的内容,则转换为NaN
如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
布尔 --> 数字
true 转成 1
false 转成 0
null --> 数字
null 转成 0
undefined --> 数字
undefined 转成 NaN
方式二:这种方式专门用来对付字符串,parseInt() 把一个字符串转换为一个整数
var a = "123";
a = parseInt(a);
console.log(a);
console.log(typeof a);
方式三:这种方式专门用来对付字符串,parseFloat() 把一个字符串转换为一个浮点数
var a = "123.456";
a = parseFloat(a);
console.log(a);
console.log(typeof a);
注意:如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作
转换为Boolean类型
将其它的数据类型转换为Boolean,只能使用Boolean()函数。
使用Boolean()函数
数字 —> 布尔
除了0和NaN,其余的都是true
字符串 —> 布尔
除了空串,其余的都是true
null和undefined都会转换为false
对象也会转换为true
3.1 算术运算符
运算符也叫操作符,通过运算符可以对一个或多个值进行运算并获取运算结果。
算术运算符用于表达式计算
y=5,下面的表格解释了这些算术运算符:
运算符 | 描述 | 例子 | x 运算结果 | y 运算结果 |
---|---|---|---|---|
+ | 加法 | x=y+2 | 7 | 5 |
- | 减法 | x=y-2 | 3 | 5 |
* | 乘法 | x=y*2 | 10 | 5 |
/ | 除法 | x=y/2 | 2.5 | 5 |
% | 取模(求余数) | x=y%2 | 1 | 5 |
++ | 自增 [++在前,先自增再赋值,++在后选赋值再自增] | x=++y | 6 | 6 |
- - | 自减 - -在前,先自减再赋值,- -在后选赋值再自减] | x= y- - | 4 | 4 |
3.2 关系运算符
关系运算符在逻辑语句中使用,以测定变量或值是否相等。
x=5,下面的表格解释了比较运算符:
运算符 | 描述 | 比较 | 返回值 |
---|---|---|---|
> | 大于 | x>8 | false |
< | 小于 | x<8 | true |
>= | 大于或等于 | x>=8 | false |
<= | 小于或等于 | x<=8 | true |
3.3 赋值运算符
赋值运算符用于给 JavaScript 变量赋值。
x=10 和 y=5,下面的表格解释了赋值运算符:
运算符 | 例子 | 等同于 | 运算结果 |
---|---|---|---|
= | x=y | x=5 | |
+= | x+=y | x=x+y | x=15 |
-= | x-=y | x=x-y | x=5 |
*= | x*=y | x=x*y | x=50 |
/= | x/=y | x=x/y | x=2 |
%= | x%=y | x=x%y | x=0 |
3.4 逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑。
给定 x=6 以及 y=3,下表解释了逻辑运算符:
运算符 | 描述 | 例子 |
---|---|---|
&& | and | (x < 10 && y > 1) 为 true |
|| | or | (x5 || y5) 为 false |
! | not | !(x==y) 为 true |
&& 与:&&可以对符号两侧的值进行与运算并返回结果,运算规则如下:
两个值中只要有一个值为false,就返回false,只有两个值都为true时,才会返回true
JS中的“与”属于短路的与,如果第一个值为false,则不会检查第二个值
非布尔值时:如果两个都为true,则返回第二个值,如果两个值中有false,则返回靠前的false的值
|| 或:||可以对符号两侧的值进行或运算并返回结果,运算规则如下:
两个值中只要有一个true,就返回true,只有两个值都为false,才会返回false
JS中的“或”属于短路的或,如果第一个值为true,则不会检查第二个值
非布尔值时:如果两个都为false ,则返回第二个值,如果两个值中有true,则返回靠前的true的值
! 非:!可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,true变false,false变true,运算规则如下:
如果对一个值进行两次取反,它不会变化
非布尔值时:先会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其它的数据类型转换为布尔值,可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和Boolean()函数一样
6.5 条件运算符
avaScript 还包含了基于某些条件对变量进行赋值的条件运算符。
语法: variablename=(condition)?value1:value2;
例子:
<script>
var userAge = 10;
// 表达式为真返回?号后的值,未夹返回:号后的值,总结:真前假后
console.log(userAge>=18 ? "成年":"未成年")
</script>
6.7 逗号运算符
使用逗号可以在一条语句中执行多次操作。
比如:var num1=1, num2=2, num3=3;
使用逗号运算符分隔的语句会从左到右顺 序依次执行。
6.8 运算符优先级
运算符优先级由上到下依次减小,对于同级运算符,采用从左向右依次执行的方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8o3WYEGI-1656822319479)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C695dff6b3d3117760c6fba7c0b09895b.png)]
6.8 条件语句
我们先来了解下语句块的的概念:语句块就是多条语句,被 { } 包裹起来的多条语句
{
var userAge = 10;
var userName = admin"";
}
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句,最基本的条件语句:
- if…else
- switch…case
if条件语句: 如果if后面只有一条语句可以省略 { } 号
var userAge = 10;
if(userAge>=18){
console.log("成年")
}
**if eles 条件语句 ** 如果else后面只有一条语句可以省略 { } 号
var userAge = 10;
if(userAge>=18){
console.log("成年")
}else{
console.log("未成年")
}
switch…case
switch…case是另一种流程控制语句。
switch语句更适用于多条分支使用同一条语句的情况
var today = prompt("请输星期几?","eg:1")
switch (today) {
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
case 4:
console.log("星期四");
break;
case 5:
console.log("星期五");
break;
case 6:
console.log("星期六");
break;
case 7:
console.log("星期日");
break;
default: // 当上面所有条件都不满足时走这里
console.log("输入错误");
}
6.9 循环语句
循环语句和条件语句一样,也是基本的控制语句,只要满足一定的条件将会一直执行,最基本的循环语句:
- while
- do…while
- for
while
while语句是一个最基本的循环语句,while语句也被称为while循环
var i = 1;
while (i <= 10) {
console.log(i);
i++;
}
do…while
do…while和while非常类似,只不过它会在循环的尾部而不是顶部检查表达式的值,因此,do…while循环会至少执行一次。相比于while,do…while的使用情况并不 是很多。
var i = 1;
do {
console.log(i);
i++;
} while (i <= 10);
for
for语句也是循环控制语句,我们也称它为for循环。大部分循环都会有一个计数器用以控制循环执行的次数, 计数器的三个关键操作是初始化、检测和更新。for语句 就将这三步操作明确为了语法的一部分。
for (var i = 1; i <= 10; i++) {
console.log(i);
}
跳转控制
- break:结束最近的一次循环,可以在循环和switch语句中使用。
- continue:结束本次循环,执行下一次循环,只能在循环中使用。
3.面向对象基础
3.1 声明对象
// 创建对象的第一种方式
var object = new Object()
object.name = "admin";
// 创建对象的第二种方式
var user = {}
user.name = "admin2"
3.2 给对象赋值
var user = {}
// 给对象赋值的第一种方式
user.name = "admin"
user['password'] = "root"
// 创建对象的时候就开始赋值
function Person(name,age) {
this.name = name;
this.age = age;
this.say = function() {
alert(name + ':::' + age);
}
}
var person = new Person('张三', 24);
person.say();
3.3 删除对象属性
// 声明一个对象并且赋值
var user = {name:"admin",password:"123456"}
// 删除对象的属性方式1
delete user['name']
// 删除对象的属性方式2
delete user.password
3.4 遍历一个对象的所有属性
// 声明一个对象并且赋值
var user={userName:"admin",userAge:20,userEmai:"admin@qqq.com",userSex:"男"}
for(userkey in user){
console.log(user[userkey])
}
3.4 访问一个对象的属性
var user={userName:"admin",userAge:20,userEmai:"admin@qqq.com",userSex:"男"}
// 方式1:
console.log(user.userName)
// 方式2:
console.log(user['userName'])
3.5 数据类型梳理
基本数据类型
- JavaScript中的变量可能包含两种不同数据类型的值:基本数据类型和引用数据类型。
- JavaScript中一共有5种基本数据类型:String、Number、 Boolean、Undefined、Null。
- 基本数据类型的值是无法修改的,是不可变的。
- 基本数据类型的比较是值的比较,也就是只要两个变量的值相等,我们就认为这两个变量相等。
引用数据类型
- 引用类型的值是保存在内存中的对象。
- 当一个变量是一个对象时,实际上变量中保存的并不是对象本身,而是对象的引用。
- 当从一个变量向另一个变量复制引用类型的值时,会将对象的引用复制到变量中,并不是创建一个新的对象。
- 这时,两个变量指向的是同一个对象。因此,改变其中一个变量会影响另一个。
栈和堆梳理
- JavaScript在运行时数据是保存到栈内存和堆内存当中的。
- 简单来说栈内存用来保存变量和基本类型,堆内存是用来保存对象。
- 我们在声明一个变量时,实际上就是在栈内存中创建了一个空间用来保存变量。
- 如果是基本类型则在栈内存中直接保存,如果是引用类型则会在堆内存中保存,变量中保存的实际上对象在堆内存中的地址。
3.6 合并两个对象
// Object.assign()方法用于对象的合并
// 将源对象的属性,复制到目标对象中
// 第一个参数是目标对象,后面的参数都是源对象
// 会修改目标对象,不会修改源对象
var obj1 = {
age: 18
};
var obj2 = {
name: "user"
};
var obj = Object.assign(obj1, obj2);
//(目标对象,源对象)
console.log(obj1); //{age: 18, name: "user"}
console.log(obj2); //{name: "user"}
console.log(obj); //{age: 18, name: "user"}
4.函数
4.1 函数的概念
函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用,向函数传递参数之后,函数可以返回一定的值。
通常情况下,JavaScript代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行,只有在调用函数时才会执行函数体内部的代码。
这里要注意的是JavaScript中的函数也是一个对象,使用typeof检查一个函数对象时,会返回function。
4.2 函数的创建
方式1:几乎不使用
var user={userName:"admin",userAge:20,userEmai:"admin@qqq.com",userSex:"男"}
var fun = new Function("console.log(user);");
// 调用函数
fun()
方式2:
函数的声明方式:
function 函数名([形参1,形参2,...,形参N]) {
语句...
}
例如:
var user={userName:"admin",userAge:20,userEmai:"admin@qqq.com",userSex:"男"}
// 声明函数
function fun(){
console.log(user)
}
// 调用函数
fun()
方式3:
函数的声明方式
var 变量名 = function ([形参1,形参2,...,形参N]) {
语句...
}
例如:
var user = {
userName: "admin",
userAge: 20,
userEmai: "admin@qqq.com",
userSex: "男"
}
var fun = function() {
console.log(user)
}
// 调用函数
fun()
4.3 函数返回值
使用 return 来返回函数执行完后需要返回给调用者的值,如果写了 return 并没有返回任何值 ,默认是 undefined
var fun = function( a ,b) {
return a+b;
}
// 调用函数
console.log( fun(5,15) )
4.4 嵌套函数
嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。
var fun = function(userName, userAge,userSex,userEmai) {
var user = {
userName: userName,
userAge: userName,
userEmai: userEmai,
userSex: userSex
}
var changeUserAge = function (changeUserAgeValue){
user.userAge = changeUserAgeValue;
return user;
}
// 将内部函数返回给外部调用
return {changeUserAge:changeUserAge};
// 如果属性名称和属性值名称一样则可以这样写: return {changeUserAge};
}
// 调用函数
console.log( fun("admin",20, "admin@qqq.com","男").changeUserAge(25) )
4.5 立即执行函数
// 不需要别人调用,自己会调用自行
// 接收外部传递过来的值
( function(userName, userAge,userSex,userEmai) {
var user = {
userName: userName,
userAge: userName,
userEmai: userEmai,
userSex: userSex
}
console.log(user)
// 将参数传递给立即执行函数
})("admin",20,"男","admin@qq.com")
4.6 对象中的函数
写方法方式1:
var user = {
username:"admin",
userId:101,
userAge:25,
showProperty: function(){
console.log(this)
}
}
user.showProperty()
写法方式2:
var user = {
username:"admin",
userId:101,
userAge:25,
// 执行window
showProperty1:()=>{
console.log(this)
},
// 指向user对象
showProperty2:function(){
console.log(this)
}
}
user.showProperty1()
user.showProperty2()
4.5 this指向问题
this对象
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
以函数的形式调用时,this永远都是window
以方法的形式调用时,this就是调用方法的那个对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fivh1MTL-1656822319480)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C20200703113559339.png)]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn" type="button">按钮</button>
<script type="text/javascript">
// 1.普通函数的this指向的是window
function method1() {
console.log("普通函数this:", this)
}
method1();
// 2.对象中的函数执行的是obj对象,如果是箭头函数,指向的还是window
let obj = {
id: '101',
show: function() {
console.log(this)
}
}
obj.show();
// 3.构造函数指向的是实例化对象
function Star(id, name) {
this.id = id;
this.name = name;
this.show = function() {
console.log('构造函数方法的this:', this)
}
Star.prototype.sing = function() {
console.log('构造函数方法的this:', this)
}
};
let star = new Star('101', 'admin');
star.sing();
star.show();
// 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象
var btn = document.getElementById('btn');
btn.onclick = function() {
console.log('绑定事件函数的this:', this);
};
// 5. 定时器函数 this 指向的也是window
window.setTimeout(function() {
console.log('定时器的this:', this);
}, 1000);
// 6. 立即执行函数 this还是指向window
(function() {
console.log('立即执行函数的this', this);
})();
</script>
</body>
</html>
4.6改变this指向问题
//1. function.call(需要绑定的对象,参数) 会直接调用该方法
// call 第一个可以调用函数 第二个可以改变函数内的this 指向
// call 的主要作用可以实现继承
function Father(uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son(uname, age, sex) {
Father.call(this, uname, age, sex);
}
var son = new Son('小明', 18, '男');
console.log(son);
// 2. apply() 应用 运用的意思
var o = {
id: '101',
name: 'jack'
};
function fun(arg1,arg2) {
console.log(this);
console.log(arg1 ,arg2); // 'admin', 'root'
};
fun.apply(o, ['admin', 'root']);
// 1. 也是调用函数 第二个可以改变函数内部的this指向
// 2. 但是他的参数必须是数组(伪数组)
// 3. apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求数组最大值
// Math.max();
var arr = [-1, 66, 3, 99, 4];
var max = Math.max.apply(Math, arr);
var min = Math.min.apply(Math, arr);
console.log(max, min);
// 默认指向的是window
// 现在我想让他执行student对象
let student = {
id:'102',
name:'小明'
}
let fun = function (){
console.log("this[bind]:",this)
}.bind(student); // 通过bind改变fun函数的this指向为student,并不会立即调用
fun();
5.原型链
5.1 prototype
那原型(prototype)到底是什么呢?
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
function Person(id,name,age) {
this.id = id;
this.name = name;
this.age = age;
}
let person = new Person(101,"admin","20")
// 在对象的原型上添加属性
person.__proto__.flag = "deleted"
// 原型对象上挂属性
Person.prototype.INSTANCE = "hello world!"
// 在原型对象上添加方法
person.__proto__['show'] = function(){
console.log("__proto__:",this.id, this.name, this.age)
}
// 原型对象上挂方法
Person.prototype.showProties = function() {
console.log("prototype:",this.id, this.name, this.age)
}
// 在原型对象上添加方法
person.__proto__['log'] = function(){
console.log("__proto__:","log")
}
// 原型对象上挂方法
Person.prototype.log = function() {
console.log("prototype:","log")
}
// 调用链 先找对象本身->prototype->__proto__
// 调用对象原型上添加方法
person.showProties()
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log('name' in person)
console.log(person.INSTANCE)
person.log()
console.log(person) // 输出:prototype: log
5.2 使用 prototype 实现继承
// 定义父类型构造函数
function SupperType() {
this.supProp = 'Supper property';
}
// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
};
// 定义子类型的构造函数
function SubType() {
this.subProp = 'Sub property';
}
// 创建父类型的对象赋值给子类型的原型 这样子类就可以访问父类的方法和属性
SubType.prototype = new SupperType();
// 将子类型原型的构造属性设置为子类型
SubType.prototype.constructor = SubType;
// 给子类型原型添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subProp)
};
// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();
原型链+借用构造函数的组合继承
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function (name) {
this.name = name;
};
function Student(name, age, price) {
Person.call(this, name, age); // 为了得到父类型的实例属性和方法
this.price = price; // 添加子类型私有的属性
}
Student.prototype = new Person(); // 为了得到父类型的原型属性和方法
Student.prototype.constructor = Student; // 修正constructor属性指向
Student.prototype.setPrice = function (price) { // 添加子类型私有的方法
this.price = price;
};
var student = new Student("admin", 24, 15000);
console.log(student.name, student.age, student.price);
student.setName("root");
student.setPrice(16000);
console.log(student.name,student.age, student.price);
6.JavaScript常用对象
6.1 数组对象
数组也是对象的一种,数组是一种用于表达有顺序关系的值的集合的语言结构,也就是同类数据元素的有序集合。
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。但是在JavaScript中是支持数组可以是不同的元素,这跟JavaScript的弱类型有关,此处不用纠结,我们大多数时候都是相同类型元素的集合。数组内的各个值被称作元素,每一个元素都可以通过索引(下标)来快速读取,索引是从零开始的整数。
使用typeof检查一个数组对象时,会返回object。
1.创建数组的方式
// 使用字面量创建 通类型的数组
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// 不同类型有序数组创建
var arr = [1, "2", 3, "4", 5, "6", 7, "8", 9];
// 使用 Array对象创建数组
var arr = new Array();
arr[0] = 'admin'
2.数组的遍历
var arr = [1, "2", 3, "4", 5, "6", 7, "8", 9];
// 遍历数组的方式1:
for(var i=0;i<arr.length;i++){
console.log(arr[i])
}
// 遍历数组的方式2: function(数组中的每一项元素,数组下标,当前数组本身)
arr.forEach(function(item,index,slef){
console.log(item,index,slef)
})
// 遍历数组的方式3:
for(var index in arr){
console.log(arr[index])
}
6.2 数组的方法
push()方法:该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
pop()方法:该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
unshift()方法:该方法向数组开头添加一个或多个元素,并返回新的数组长度
shift()方法:该方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回
slice()方法:该方法可以用来从数组提取指定元素,该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
参数:
- 第一个参数:截取开始的位置的索引,包含开始索引
- 第二个参数:截取结束的位置的索引,不包含结束索引,第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
- 注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个
splice()方法:该方法可以用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
参数:
- 第一个参数:表示开始位置的索引
- 第二个参数:表示要删除的元素数量
- 第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
concat()方法:该方法可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响
reverse()方法:该方法用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组
sort()方法:该方法可以用来对数组中的元素进行排序,也会影响原数组,默认会按照Unicode编码进行排序
json(分隔符)方法:该方法可以用来对数组中的元素按照指定分隔符,将数组分割为一个新的字符串
6.2 函数对象
call()和apply()
call()和apply()这两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()和apply()都会调用函数执行,在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this,call()方法可以将实参在对象之后依次传递,apply()方法需要将实参封装到一个数组中统一传递。
call()方法演示:
function fun(name,password) {
console.log("name = " ,name);
console.log("password= " , password);
console.log("fun = ", this);
}
var user = {
name: "admin",
sayName: function (name,password) {
console.log(name,password,this);
}
};
fun("admin", "101") // 指向window
console.log("---------------")
fun.call(user, "admin", "101"); // 指向user对象
注意:默认fun()函数调用,this指向的是window对象,你可以使用call()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,实参将会依次传递
apply()方法演示:
function fun(name,password) {
console.log("name = " ,name);
console.log("password= " , password);
console.log("fun = ", this);
}
var user = {
name: "admin",
sayName: function (name,password) {
console.log(name,password,this);
}
};
fun("admin", "101") // 指向window
console.log("---------------")
fun.apply(user, ["admin", "101"]); // 指向user对象
注意:默认fun()函数调用,this指向的是window对象,你可以使用apply()调用函数,在调用的时候传入一个对象,这个对象就是this所指向的对象,也就是说,可以自己指定this的指向,然后从第二个参数开始,需要制定一个实参数组进行参数传递
bind()方法演示:在不调用原函数的情况下,改变this的指向
function fun(name,password) {
console.log("fun:", this);
}
var user = {
name: "admin",
sayName: function (name,password) {
console.log(name,password,this);
}
};
fun("admin", "101") // 指向window
console.log("---------------")
// 不调用源函数,但是会把this指向 user对象
var funThisUser = fun.bind(user,"admin", "101");
funThisUser()// 指向user对象
6.3 arguments参数
在调用函数时,浏览器每次都会传递进两个隐含的参数:
函数的上下文对象: this
封装实参的对象: arguments
this对象我们已经学习过了,那arguments对象是什么呢?
arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度,在调用函数时,我们所传递的实参都会在arguments中保存,比如:arguments.length 可以用来获取实参的长度,我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦,例如:
arguments[0]:表示第一个实参
arguments[1]:表示第二个实参
…
它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。
arguments对象演示:
function fun(a, b) {
// 通过下标获取第一个参数
console.log(arguments[0]);
// 通过下标获取第二个参数
console.log(arguments[1]);
// 获取实参的个数
console.log(arguments.length);
// 看看它的函数对象
console.log(arguments.callee);
console.log(arguments.callee == fun);
}
fun("Hello", "World");
6.4 Date对象
在JavaScript中使用Date对象来表示一个时间,如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间。
var date = new Date();
console.log(date);
console.log(date.getFullYear()); //获取当前日期对象的年份(四位数字年份)
console.log(date.getMonth()); //获取当前日期对象的月份(0 ~ 11)
console.log(date.getDate()); //获取当前日期对象的日数(1 ~ 31)
console.log(date.getHours()); //获取当前日期对象的小时(0 ~ 23)
console.log(date.getMinutes()); //获取当前日期对象的分钟(0 ~ 59)
console.log(date.getSeconds()); //获取当前日期对象的秒钟(0 ~ 59)
console.log(date.getMilliseconds()); //获取当前日期对象的毫秒(0 ~ 999)
日期格式案例:
Date.prototype.format = function(fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
}
// 调用方式
var time = new Date().format("yyyy-MM-dd hh:mm:ss");
console.log(time);
6.5 Math对象
Math和其它的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法。
/*固定值*/
console.log("PI = " + Math.PI);
console.log("E = " + Math.E);
console.log("===============");
/*正数*/
console.log(Math.abs(1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(1.1)); //可以对一个数进行向上取整,小数位只有有值就自动进1
console.log(Math.floor(1.99)); //可以对一个数进行向下取整,小数部分会被舍掉
console.log(Math.round(1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*负数*/
console.log(Math.abs(-1)); //可以用来计算一个数的绝对值
console.log(Math.ceil(-1.1)); //可以对一个数进行向上取整,小数部分会被舍掉
console.log(Math.floor(-1.99)); //可以对一个数进行向下取整,小数位只有有值就自动进1
console.log(Math.round(-1.4)); //可以对一个数进行四舍五入取整
console.log("===============");
/*随机数*/
//Math.random():可以用来生成一个0-1之间的随机数
//生成一个0-x之间的随机数:Math.round(Math.random()*x)
//生成一个x-y之间的随机数:Math.round(Math.random()*(y-x)+x)
console.log(Math.round(Math.random() * 10)); //生成一个0-10之间的随机数
console.log(Math.round(Math.random() * (10 - 1) + 1)); //生成一个1-10之间的随机数
console.log("===============");
/*数学运算*/
console.log(Math.pow(12, 3)); //Math.pow(x,y):返回x的y次幂
console.log(Math.sqrt(4)); //Math.sqrt(x) :返回x的平方根
6.6 String对象
在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
- String():可以将基本数据类型字符串转换为String对象
- Number():可以将基本数据类型的数字转换为Number对象
- Boolean():可以将基本数据类型的布尔值转换为Boolean对象
但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果,在这一章节中,我们重点介绍String()对象的属性和方法。
字符串属性
constructor属性演示:返回创建字符串对象的原型函数
var str = "Hello,World!";
console.log(str.constructor);
length属性演示:可以用来获取字符串的长度
var str = "Hello,World!";
console.log(str.length);
charAt()方法演示:该方法可以根据索引获取指定位置的字符
var str = "Hello,World!";
console.log(str.charAt(1));
charCodeAt()方法演示:该方法获取指定位置字符的字符编码(Unicode编码)
var str = "Hello,World!";
console.log(str.charCodeAt(1));
concat()方法演示:该方法可以用来连接两个或多个字符串
var str = "Hello,World!";
console.log(str.concat("你好,", "世界!"));
Indexof()方法演示:该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引,如果没有找到指定的内容,则返回-1,可以指定一个第二个参数,指定开始查找的位置
var str = "Hello,World!";
console.log(str.indexOf("o"));
console.log(str.indexOf("o", 5));
lastIndexOf()方法演示:该方法的用法和indexOf()一样,不同的是indexOf是从前往后找,而lastIndexOf是从后往前找,也可以指定开始查找的位置
var str = "Hello,World!";
console.log(str.lastIndexOf("o"));
console.log(str.lastIndexOf("o", 5));
slice()方法演示:可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到内容返回
参数:
- 第一个参数:开始位置的索引(包括开始位置)
- 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
- 注意:也可以传递一个负数作为参数,负数的话将会从后边计算
var str = "Hello,World!";
var result = str.slice(1, 4);
console.log(result)
substring()方法演示:可以用来截取一个字符串,它和slice()类似
参数:
- 第一个参数:开始截取位置的索引(包括开始位置)
- 第二个参数:结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
- 注意:不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用0,而且它还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
var str = "Hello,World!";
var result = str.substring(1, 4);
console.log(result);
substr()方法演示:该方法用来截取字符串
参数:
- 第一个参数:截取开始位置的索引
- 第二个参数:截取的长度
var str = "Hello,World!";
var result = str.substr(6, 6);
console.log(result);
split()方法演示:该方法可以将一个字符串拆分为一个数组,需要一个字符串作为参数,将会根据该字符串去拆分数组[常用]
var str = "Hello,World!";
var result = str.split(",");
console.log(result);
toUpperCase()方法演示:将一个字符串转换为大写并返回
var str = "Hello,World!";
var result = str.toUpperCase();
console.log(result);
toLowerCase()方法演示:将一个字符串转换为小写并返回
var str = "Hello,World!";
var result = str.toLowerCase();
console.log(result);
6.7 RegExp对象
正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来。
使用typeof检查正则对象,会返回object。
语法格式:
var 变量名 = new RegExp("正则表达式","匹配模式");
匹配模式:
- i:忽略大小写
- g:全局匹配模式
- ig:忽略大小写且全局匹配模式
案例演示:
// 这个正则表达式可以来检查一个字符串中是否含有a
var reg = new RegExp("ab", "i");
var str = "Abc";
var result = reg.test(str);
console.log(result);
使用字面量创建RegExp对象
语法格式:
var 变量名 = /正则表达式/匹配模式;
注意:可以为一个正则表达式设置多个匹配模式,且顺序无所谓
7.JavaScript DOM
7.1 DOM概述
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。
HTML DOM 模型被结构化为 对象树 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UqJMTy5v-1656822319481)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C910bb72bbde0f438991f02a6cda866d7.png)]
通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:
JavaScript 能改变页面中的所有 HTML 元素
JavaScript 能改变页面中的所有 HTML 属性
JavaScript 能改变页面中的所有 CSS 样式
JavaScript 能删除已有的 HTML 元素和属性
JavaScript 能添加新的 HTML 元素和属性
JavaScript 能对页面中所有已有的 HTML 事件作出反应
JavaScript 能在页面中创建新的 HTML 事件
dmo文档节点:
节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点。
比如:html标签、属性、文本、注释、整个文档等都是一个节点。
虽然都是节点,但是实际上它们的具体类型是不同的。
比如:标签我们称为元素节点、属性称为属性节点、文本称为 文本节点、文档称为文档节点。
节点的类型不同,属性和方法也都不尽相同。
节点:Node——构成HTML文档最基本的单元。
常用节点分为四类:
文档节点:整个HTML文档
元素节点:HTML文档中的HTML标签
属性节点:元素的属性
文本节点:HTML标签中的文本内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qbOwYxEB-1656822319482)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C6c58b926da64501f486305b5afe3d9f8.png)]
节点属性:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6gvzuEl-1656822319483)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C2271dc3f0f61e890fe4cb76817fc6942.png)]
文档节点:
文档节点(Document)代表的是整个HTML文 档,网页中的所有节点都是它的子节点。
document对象作为window对象的属性存在的,我们不用获取可以直接使用。
通过该对象我们可以在整个文档访问内查找节点对象,并可以通过该对象创建各种节点对象。
元素节点:
HTML中的各种标签都是元素节点(Element),这也是我们最常用的一个节点。
浏览器会将页面中所有的标签都转换为一个元素节点, 我们可以通过document的方法来获取元素节点。
例如:document.getElementById(),根据id属性值获取一个元素节点对象。
属性节点:
属性节点(Attribute)表示的是标签中的一个一个的属 性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。可以通过元素节点来获取指定的属性节点。
例如:元素节点.getAttributeNode(“属性名”),根据元素节点的属性名获取一个属性节点对象。
注意:我们一般不使用属性节点。
文本节点:
文本节点(Text)表示的是HTML标签以外的文本内容,任意非HTML的文本都是文本节点,它包括可以字面解释的纯文本内容。文本节点一般是作为元素节点的子节点存在的。获取文本节点时,一般先要获取元素节点,在通过元素节点获取文本节点。
例如:元素节点.firstChild;,获取元素节点的第一个子节点,一般为文本节点。
7.2 查找 HTML 元素
方法 | 描述 |
---|---|
document.getElementById(id) | 通过元素 id 来查找元素。 |
document.getElementsByClassName(name) | 通过类名来查找元素。 |
document.querySelector(CSS选择器) | 通过CSS选择器选择一个元素。 |
document.querySelectorAll(CSS选择器) | 通过CSS选择器选择多个元素。 |
document.getElementsByTagName(name) | 通过标签名来查找元素。 |
获取元素节点演示:
document.getElementById(id)
<body>
<button id="btn">按钮</button>
<script>
var btn = document.getElementById("btn");
console.log(btn)
</script>
</body>
document.getElementsByTagName(name)
<body>
<button id="btn">按钮</button>
<script>
var btn = document.getElementsByTagName("button");
console.log(btn)
</script>
</body>
document.querySelectorAll(CSS选择器)
<body>
<ul class="list">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<script>
var list = document.querySelectorAll(".list li");
for(let i = 0; i <list.length;i++){
console.log(list[i])
}
</script>
</body>
7.3 获取 HTML 的值
方法 | 描述 |
---|---|
元素节点.innerText | 获取 HTML 元素的 inner Text。 |
元素节点.innerHTML | 获取 HTML 元素的 inner HTML。 |
元素节点.属性 | 获取 HTML 元素的属性值。 |
元素节点.getAttribute(attribute) | 获取 HTML 元素的属性值。 |
元素节点.style.样式 | 获取 HTML 元素的行内样式值。 |
案例:获取ul 里面的html 和文本值
<body>
<ul class="list" id="app">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<script>
var list = document.querySelectorAll(".list li");
// 获取标签内文本
for(let i = 0; i <list.length;i++){
// string 类型
console.log( list[i].innerText)
}
// 获取 html值 string类型
console.log( document.getElementById("app").innerHTML)
</script>
</body>
演示:获取获取和设置
<body>
<ul class="list1 list2" id="app" data-message="hello world">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>列表项4</li>
</ul>
<div id="content">
</div>
<script>
// 元素节点.属性 获取 HTML 元素的属性值。
var app = document.getElementById("app")
console.log(app.className)
console.log(app.classList)
// 元素节点.getAttribute(attribute) 获取 HTML 元素的属性值。
console.log(app.getAttribute("id"))
console.log(app.getAttribute("data-message"))
// 元素节点.style.样式 获取 HTML 元素的行内样式值。
console.log(app.style.color='red')
var content = document.getElementById("content")
// 修改div内部的html代码 将ul添加到div的里面
content.innerHTML = app.innerHTML
// 重新设置元素的属性值
app.setAttribute("id","newApp")
console.log(app.getAttribute("id"))
</script>
</body>
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式或者说正在应用的样式,如果想要读取当前正在应用的样式属性我们可以使用元素.currentStyle.样式名来获取元素的当前显示的样式,它可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持,其它的浏览器都不支持,在其它浏览器中可以使用getComputedStyle()这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用,但是需要两个参数:
第一个参数:要获取样式的元素
第二个参数:可以传递一个伪元素,一般都传null
该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过 对象.样式名 来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,比如:没有设置width,它不会获取到auto,而是一个长度,但是该方法不支持IE8及以下的浏览器。通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性,因此,我们可以写一个适配各个浏览器的读取元素样式的方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*样式表的样式*/
#box {
width: 200px;
height: 200px;
background-color: green;
}
</style>
</head>
<body>
<div style="width: 100px;height: 100px;" id="box"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/*通用的获取元素样式的方法*/
function getStyle(elementNode, name) {
if (window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(elementNode, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return elementNode.currentStyle[name];
}
}
var box = document.getElementById("box");
console.log(getStyle(box, "width"));
console.log(getStyle(box, "height"));
console.log(getStyle(box, "background-color"));
</script>
</body>
</html>
编写一段兼容性代码,用来获取任意标签的文本内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
console.log(getInnerText(a));
/*获取任意标签的内容*/
function getInnerText(element) {
// 判断浏览器是否支持textContent,如果支持,则使用textContent获取内容,否则使用innerText获取内容。
if(typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
</script>
</body>
</html>
7.4 改变 HTML 的值
方法 | 描述 |
---|---|
元素节点.innerText = new text content | 改变元素的 inner Text。 |
元素节点.innerHTML = new html content | 改变元素的 inner HTML。 |
元素节点.属性 = new value | 改变 HTML 元素的属性值。 |
元素节点.setAttribute(attribute, value) | 改变 HTML 元素的属性值。 |
元素节点.style.样式 = new style | 改变 HTML 元素的行内样式值。 |
元素节点.removeAttribute(‘属性名’) | 移除 HTML 元素的属性值。 |
7.5 修改 HTML 元素
方法 | 描述 |
---|---|
document.createElement(element) | 创建 HTML 元素节点 |
document.createAttribute(attribute) | 创建 HTML 属性节点 |
document.createTextNode(text) | 创建 HTML 文本节点 |
元素节点.removeChild(element) | 删除 HTML 元素 |
元素节点.appendChild(element) | 添加 HTML 元素 |
元素节点.replaceChild(element) | 替换 HTML 元素 |
元素节点.insertBefore(element) | 在指定的子节点前面插入新的子节点 |
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var ul = document.createElement("ul");
// 创建 HTML 元素节点
var li1 = document.createElement("li");
// 给元素节点设置属性值
li1.setAttribute("id","app")
// 创建 HTML 文本节点
var text1 = document.createTextNode("列表项1");
// 添加 HTML 元素
li1.appendChild(text1);
ul.appendChild(li1);
// 将新创建好的ul添加到body中
document.getElementsByTagName("body")[0].appendChild(ul);
</script>
</body>
</html>
拓展:
修改节点的内容除了常用的innerHTML和innerText之外,还有insertAdjacentHTML和insertAdjacentText方法,可以在指定的地方插入内容。insertAdjacentText方法与insertAdjacentHTML方法类似,只不过是插入纯文本,参数相同。
语法说明:
- object.insertAdjacentHTML(where,html);
- object.insertAdjacentText(where,text)
参数说明:
where:
beforeBegin:插入到开始标签的前面
beforeEnd:插入到结束标签的前面
afterBegin:插入到开始标签的后面
afterEnd:插入到结束标签的后面
7.6 查找 HTML 父子
方法 描述 | |
---|---|
元素节点.parentNode | 返回元素的父节点 |
元素节点.parentElement | 返回元素的父元素 |
元素节点.childNodes | 返回元素的一个子节点的数组(包含空白文本Text节点) |
元素节点.children | 返回元素的一个子元素的集合(不包含空白文本Text节点) |
元素节点.firstChild | 返回元素的第一个子节点(包含空白文本Text节点) |
元素节点.firstElementChild | 返回元素的第一个子元素(不包含空白文本Text节点) |
元素节点.lastChild | 返回元素的最后一个子节点(包含空白文本Text节点) |
元素节点.lastElementChild | 返回元素的最后一个子元素(不包含空白文本Text节点) |
元素节点.previousSibling | 返回某个元素紧接之前节点(包含空白文本Text节点) |
元素节点.previousElementSibling | 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点) |
元素节点.nextSibling | 返回某个元素紧接之后节点(包含空白文本Text节点) |
元素节点.nextElementSibling | 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点) |
方法封装:
/*获取任意一个父级元素的第一个子元素*/
function getfirstElementChild(element) {
if (element.firstElementChild) {
return element.firstElementChild;
} else {
var node = element.firstChild;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
/*获取任意一个父级元素的最后一个子元素*/
function getLastElementChild(element) {
if (element.lastElementChild) {
return element.lastElementChild;
} else {
var node = element.lastChild;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的前一个兄弟元素*/
function getPreviousElementSibling(element) {
if (element.previousElementSibling) {
return element.previousElementSibling;
} else {
var node = element.previousSibling;
while (node && node.nodeType != 1) {
node = node.previousSibling;
}
return node;
}
}
/*获取任意一个子元素的后一个兄弟元素*/
function getNextElementSibling(element) {
if (element.nextElementSibling) {
return element.nextElementSibling;
} else {
var node = element.nextSibling;
while (node && node.nodeType != 1) {
node = node.nextSibling;
}
return node;
}
}
8.DOM文档事件
HTML事件可以触发浏览器中的行为,比方说当用户点击某个 HTML 元素时启动一段 JavaScript。
8.1 窗口事件
由窗口触发该事件 (同样适用于 标签):
属性 | 描述 |
---|---|
onblur | 当窗口失去焦点时运行脚本。 |
onfocus | 当窗口获得焦点时运行脚本。 |
onload | 当文档加载之后运行脚本。 |
onresize | 当调整窗口大小时运行脚本。 |
onstorage | 当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。 |
例如:
window.onload = function () {
console.log("dom节点加载完成");
};
8.2 表单事件
表单事件在HTML表单中触发 (适用于所有 HTML 元素,但该HTML元素需在form表单内):
属性 | 描述 |
---|---|
onblur | 当元素失去焦点时运行脚本。 |
onfocus | 当元素获得焦点时运行脚本。 |
onchange | 当元素改变时运行脚本。 |
oninput | 当元素获得用户输入时运行脚本。 |
oninvalid | 当元素无效时运行脚本。 |
onselect | 当选取元素时运行脚本。 |
onsubmit | 当提交表单时运行脚本。 |
例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<select id="courseList" name="course">
<option data-message="A" value="mathematics">数学</option>
<option data-message="B" value="chinese">语文</option>
<option data-message="C" value="english">英语</option>
</select>
<script>
// 给courseList绑定onchange事件 当值修改的时候获取到当前的value
var course = document.getElementById("courseList").onchange = function() {
console.log(course)
// 如果当前选中的是英语那就在添加一个选项 其他
if (this.value = 'english') {
var content = ' <option data-message="D" value="other">其他</option>'
var courseNode = document.getElementById("courseList")
courseNode.insertAdjacentHTML('beforeEnd',content)
}
}
// 获取courseList中的所有option 并且得到option的value值和文本值,自定义属性值
var courseOption = document.querySelectorAll("#courseList option")
courseOption.forEach(function(current, index, slef) {
console.log(current.getAttribute("value"))
console.log(current.getAttribute("data-message"))
console.log(current.innerText)
console.log("----------------")
})
</script>
</body>
</html>
7.8显示和隐藏元素
// 不显示元素--隐藏 特点:脱离文档流--不占据页面空间
document.getElementById('type').style.display = 'none'
// 像块级元素一样显示元素 场合:将行内元素变为块级元素
document.getElementById('type').style.display = 'block'
// 像行内元素一样显示元素 注意:不要块级元素(div/p..)改变成行内元素
document.getElementById('type').style.display = 'inline-block'
// 默认值,元素可见
document.getElementById('type').style.visibility = 'visible'
// 元素不可见,但依然占据页面空间
document.getElementById('type').style.visibility = 'hidden'
// dispaly:none 与 visibility:hidden之间的区别
// display:none;脱离文档流,所以不占空间
// visibility:hidden;没有脱离文档流,所以元素隐藏,空间保留
8.3 键盘事件
通过键盘触发事件,类似用户的行为:
属性 | 描述 |
---|---|
onkeydown | 当按下按键时运行脚本。 |
onkeyup | 当松开按键时运行脚本。 |
onkeypress | 当按下并松开按键时运行脚本。 |
/* 当键盘按下判断当前的按键是不是 回车 ,如果是就输出true,否则输出false */
window.onkeydown = function(event) {
/* 解决兼容性问题 */
event = event || window.event;
console.log("当前按下的keyCode值:",event.keyCode)
if (event.keyCode == 13) {
console.log("true");
} else {
console.log("false");
}
};
案例实战:控制div的移动方向
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
//为document绑定一个按键按下的事件
document.onkeydown = function (event) {
event = event || window.event;
console.log(event.keyCode)
// 定义移动速度
var speed = 10;
// 选择移动方向
switch (event.keyCode) {
case 37: // 左
box.style.left = box.offsetLeft - speed + "px";
break;
case 39: // 右
box.style.left = box.offsetLeft + speed + "px";
break;
case 38: // 上
box.style.top = box.offsetTop - speed + "px";
break;
case 40: // 下
box.style.top = box.offsetTop + speed + "px";
break;
}
};
</script>
</body>
</html>
拓展知识:
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数。
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标的状态。
在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的。
解决事件对象的兼容性问题:event = event || window.event;
属性 | 描述 |
---|---|
ctrlKey | 返回当事件被触发时,“CTRL” 键是否被按下。 |
altKey | 返回当事件被触发时,“ALT” 是否被按下。 |
shiftKey | 返回当事件被触发时,“SHIFT” 键是否被按下。 |
clientX | 返回当事件被触发时,鼠标指针的水平坐标。 |
clientY | 返回当事件被触发时,鼠标指针的垂直坐标。 |
screenX | 返回当某个事件被触发时,鼠标指针的水平坐标。 |
screenY | 返回当某个事件被触发时,鼠标指针的垂直坐标。 |
8.4 鼠标事件
通过鼠标触发事件,类似用户的行为:
属性 | 描述 |
---|---|
onclick | 当单击鼠标时运行脚本。 |
ondblclick | 当双击鼠标时运行脚本。 |
onmousedown | 当按下鼠标按钮时运行脚本。 |
onmouseup | 当松开鼠标按钮时运行脚本。 |
onmousemove | 当鼠标指针移动时运行脚本。 |
onmouseover | 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。 |
onmouseout | 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。 |
onmouseenter | 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。 |
onmouseleave | 当鼠标指针移出元素时运行脚本,可以阻止冒泡。 |
onmousewheel | 当转动鼠标滚轮时运行脚本。 |
onscroll | 当滚动元素的滚动条时运行脚本。 |
写一个通用的拖拽元素函数,创建两个div,进行拖拽演示,要求兼容IE8、火狐、谷歌等主流浏览器
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box1" style="width: 100px;height: 100px;background: red;position: absolute;"></div>
<div id="box2" style="width: 100px;height: 100px;background: green;position: absolute;"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
drag(box1);
drag(box2);
/*
* 提取一个专门用来设置拖拽的函数
* 参数:开启拖拽的元素
*/
function drag(obj) {
//当鼠标在被拖拽元素上按下时,开始拖拽
obj.onmousedown = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 设置obj捕获所有鼠标按下的事件
/**
* setCapture():
* 只有IE支持,但是在火狐中调用时不会报错,
* 而如果使用chrome调用,它也会报错
*/
obj.setCapture && obj.setCapture();
// obj的偏移量 鼠标.clentX - 元素.offsetLeft
// obj的偏移量 鼠标.clentY - 元素.offsetTop
var ol = event.clientX - obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
// 为document绑定一个鼠标移动事件
document.onmousemove = function (event) {
// 解决事件的兼容性问题
event = event || window.event;
// 当鼠标移动时被拖拽元素跟随鼠标移动
// 获取鼠标的坐标
var left = event.clientX - ol;
var top = event.clientY - ot;
// 修改obj的位置
obj.style.left = left + "px";
obj.style.top = top + "px";
};
// 为document绑定一个鼠标松开事件
document.onmouseup = function () {
// 取消document的onmousemove事件
document.onmousemove = null;
// 取消document的onmouseup事件
document.onmouseup = null;
// 当鼠标松开时,取消对事件的捕获
obj.releaseCapture && obj.releaseCapture();
};
/*
* 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
* 此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
* 如果不希望发生这个行为,则可以通过return false来取消默认行为,
* 但是这招对IE8不起作用
*/
return false;
};
}
</script>
</body>
</html>
8.5 媒体事件
属性 | 描述 |
---|---|
onabort | 当发生中止事件时运行脚本。 |
oncanplay | 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本。 |
oncanplaythrough | 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本。 |
ondurationchange | 当媒介长度改变时运行脚本。 |
onemptied | 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本。 |
onended | 当媒介已抵达结尾时运行脚本。 |
onerror | 当在元素加载期间发生错误时运行脚本。 |
onloadeddata | 当加载媒介数据时运行脚本。 |
onloadedmetadata | 当媒介元素的持续时间以及其它媒介数据已加载时运行脚本。 |
onloadstart | 当浏览器开始加载媒介数据时运行脚本。 |
onpause | 当媒介数据暂停时运行脚本。 |
onplay | 当媒介数据将要开始播放时运行脚本。 |
onplaying | 当媒介数据已开始播放时运行脚本。 |
onprogress | 当浏览器正在取媒介数据时运行脚本。 |
onratechange | 当媒介数据的播放速率改变时运行脚本。 |
onreadystatechange | 当就绪状态(ready-state)改变时运行脚本。 |
onseeked | 当媒介元素的定位属性不再为真且定位已结束时运行脚本。 |
onseeking | 当媒介元素的定位属性为真且定位已开始时运行脚本。 |
onstalled | 当取回媒介数据过程中(延迟)存在错误时运行脚本。 |
onsuspend | 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本。 |
ontimeupdate | 当媒介改变其播放位置时运行脚本。 |
onvolumechange | 当媒介改变音量亦或当音量被设置为静音时运行脚本。 |
onwaiting | 当媒介已停止播放但打算继续播放时运行脚本。 |
8.6其它事件
属性 | 描述 |
---|---|
onshow | 当 |
ontoggle | 当用户打开或关闭 元素时触发。 |
8.7 事件冒泡
事件的冒泡(Bubble):所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。
案例演示1:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,会触发两个div的单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
};
</script>
</body>
</html>
案例演示2:创建两个div,叠放在一起,分别绑定单击事件,点击最里边的div,不会触发两个div的单击事件,只会触发自己的单击事件,这时候我们可以取消事件冒泡
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function (event) {
console.log("div1 的单击事件触发了!");
stopBubble(event);
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
stopBubble(event);
};
// 取消事件冒泡
function stopBubble(event) {
// 如果提供了事件对象,则这是一个非IE浏览器
if (event && event.stopPropagation) {
// 因此它支持W3C的stopPropagation()方法
event.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
</body>
</html>
案例演示3:当点击a标签的时候,阻止a标签的默认跳转事件,采用事件阻止
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<a href="https://www.baidu.com" id="a">打开百度,你就知道!</a>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var a = document.getElementById("a");
// 为a绑定单击事件
a.onclick = function () {
stopDefault();
};
// 阻止浏览器的默认行为
function stopDefault(event) {
if (event && event.preventDefault) {
// 阻止默认浏览器动作(W3C)
event.preventDefault();
} else {
// IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}
return false;
}
</script>
</body>
</html>
8.8 事件委派
我们希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的,我们可以尝试将其绑定给元素的共同的祖先元素,也就是事件的委派。事件的委派,是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了事件冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。
案例演示:为ul列表中的所有a标签都绑定单击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="u1">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var u1 = document.getElementById("u1");
// 为ul绑定一个单击响应函数
u1.onclick = function (event) {
event = event || window.event;
// 如果触发事件的对象是我们期望的元素,则执行,否则不执行
if (event.target.className == "link") {
console.log("我是ul的单击响应函数");
}
};
</script>
</body>
</html>
8.9 事件绑定
我们以前绑定事件代码只能一个事件绑定一个函数,那我们要是想一个事件对应多个函数,并且不存在兼容性的问题该如何解决呢?
接下来,我会直接提供两个已经编写好的事件绑定和事件解绑的兼容性代码,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function f1() {
console.log("output1 ...");
};
function f2() {
console.log("output2 ...");
};
// 为按钮1的单击事件绑定两个函数
addEventListener(document.getElementById("btn1"), "click", f1);
addEventListener(document.getElementById("btn1"), "click", f2);
// 点击按钮2取消按钮1的单机事件绑定函数f1
document.getElementById("btn2").onclick = function () {
removeEventListener(document.getElementById("btn1"), "click", f1);
};
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if (element.addEventListener) {
element.addEventListener(type, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if (element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
</script>
</body>
</html>
9. JavaScript BOM
BOM概述
浏览器对象模型(BOM)使 JavaScript 有能力与浏览器"对话"。
浏览器对象模型(Browser Object Model (BOM))尚无正式标准。
由于现代浏览器已经(几乎)实现了 JavaScript 交互性方面的相同方法和属性,因此常被认为是BOM的方法和属性。
浏览器对象模型(BOM)可以使我们通过JS来操作浏览器,在BOM中为我们提供了一组对象,用来完成对浏览器的操作,常见的BOM对象如下:
- Window:代表的是整个浏览器的窗口,同时window也是网页中的全局对象
- Navigator:代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
- Location:代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
- History:代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录,由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效
- Screen:代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用。
9.1 定时事件
JavaScript 可以在时间间隔内执行,这就是所谓的定时事件( Timing Events)。
window 对象允许以指定的时间间隔执行代码,这些时间间隔称为定时事件。
通过 JavaScript 使用的有两个关键的方法:
- setTimeout(function, milliseconds) 在等待指定的毫秒数后执行函数。 【只执行一次】
- setInterval(function, milliseconds) 等同于 setTimeout(),但持续重复执行该函数。 【会重复执行多次】
- setTimeout() 和 setInterval() 都属于 window 对象的方法
9.2 常用窗口属性
两个属性可用用于确定浏览器窗口的尺寸。
这两个属性都以像素返回尺寸:
window.innerHeight - 浏览器窗口的内高度(以像素计)
window.innerWidth - 浏览器窗口的内宽度(以像素计)
浏览器窗口(浏览器视口)不包括工具栏和滚动条。
对于 Internet Explorer 8, 7, 6, 5:
document.documentElement.clientHeight
document.documentElement.clientWidth
或
document.body.clientHeight
document.body.clientWidth
一个实用的 JavaScript 解决方案(包括所有浏览器):该例显示浏览器窗口的高度和宽度(不包括工具栏和滚动条)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var w = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
console.log(w);
console.log(h);
</script>
</body>
</html>
9.3 其它窗口方法
window.open() :打开新的窗口
语法介绍:
window.open(URL,name,specs,replace);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hU7MfxX3-1656822319483)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5C786bc5b7eacc1a8aba149c0741484aee.png)]
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=500,height=500');
myWindow.document.write("<p>这是新建窗口</p>");
}
</script>
</body>
</html>
window.close() :关闭当前窗口
语法介绍:
window.close();
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="closeWin()">关闭窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=200,left=500','');
myWindow.document.write("<p>这是新建窗口</p>");
}
function closeWin() {
myWindow.close();
}
</script>
</body>
</html>
window.moveTo() :移动当前窗口
语法介绍:
window.moveTo(x,y);
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="moveWin()">移动窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100,left=500','');
myWindow.document.write("<p>这是新建窗口</p>");
}
function moveWin() {
myWindow.moveTo(300, 300);
myWindow.focus();
}
</script>
</body>
</html>
window.resizeTo() :调整当前窗口
语法介绍:
window.resizeTo(width,height);
案例演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick="openWin()">打开窗口</button>
<button onclick="resizeWin()">调整窗口</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function openWin() {
myWindow = window.open('', '', 'width=200,height=100,left=500');
myWindow.document.write("<p>这是新建窗口</p>");
}
function resizeWin() {
myWindow.resizeTo(300, 300);
myWindow.focus();
}
</script>
</body>
</html>
9.4 Navigator对象
Navigator代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器,由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了,一般我们只会使用userAgent来判断浏览器的信息,userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,不同的浏览器会有不同的userAgent,如下代码:
var ua = navigator.userAgent;
console.log(ua);
对浏览器的判断
var ua = navigator.userAgent;
if (/firefox/i.test(ua)) {
alert("你是火狐浏览器");
} else if (/chrome/i.test(ua)) {
alert("你是谷歌浏览器");
} else if (/msie/i.test(ua)) {
alert("你是IE5-IE10浏览器");
} else if ("ActiveXObject" in window) {
alert("你是IE11浏览器");
}
注意:在IE11中已经将微软和IE相关的标识都已经去除了,所以我们基本已经不能通过UserAgent来识别一个浏览器是否是IE了,如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如:ActiveXObject
9.4 Location对象
Location对象中封装了浏览器的地址栏的信息,如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)
function myFunction(){
console.log(location); //输出location对象
console.log(location.href); //输出当前地址的全路径地址
console.log(location.origin); //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host); //输出当前地址的主机
console.log(location.port); //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search); //输出当前地址的?后边的参数部分
// 跳转地址
location.href = "https://www.baidu.com";
}
常用方法
assign():用来跳转到其它的页面,作用和直接修改location一样
location.assign("https://www.baidu.com");
reload():用于重新加载当前页面,作用和刷新按钮一样,如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
location.reload(true);
replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面,它不会生成历史记录,不能使用回退按钮回退
location.replace("https://www.baidu.com");
9.5 History对象
History对象可以用来操作浏览器向前或向后翻页
console.log(history); //输出history对象
console.log(history.length); //可以获取到当成访问的链接数量
常用方法
back():可以回退到上一个页面,作用和浏览器的回退按钮一样
history.back();
forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样
history.forward();
go():可以用来跳转到指定的页面,它需要一个整数作为参数
history.go(-2);
1:表示向前跳转一个页面,相当于forward()
2:表示向前跳转两个页面
-1:表示向后跳转一个页面,相当于back()
-2:表示向后跳转两个页面
例如: history.go(-2);
9.6 Screen对象
Screen 对象包含有关客户端显示屏幕的信息。
注意:没有应用于 screen 对象的公开标准,不过所有浏览器都支持该对象。
Screen对象描述
每个 Window 对象的 screen 属性都引用一个 Screen 对象。Screen 对象中存放着有关显示浏览器屏幕的信息。JavaScript 程序将利用这些信息来优化它们的输出,以达到用户的显示要求。例如,一个程序可以根据显示器的尺寸选择使用大图像还是使用小图像,它还可以根据显示器的颜色深度选择使用 16 位色还是使用 8 位色的图形。另外,JavaScript 程序还能根据有关屏幕尺寸的信息将新的浏览器窗口定位在屏幕中间。
Screen对象属性
属性 | 描述 |
---|---|
availHeight | 返回显示屏幕的高度 (除 Windows 任务栏之外)。 |
availWidth | 返回显示屏幕的宽度 (除 Windows 任务栏之外)。 |
bufferDepth | 设置或返回调色板的比特深度。 |
colorDepth | 返回目标设备或缓冲器上的调色板的比特深度。 |
deviceXDPI | 返回显示屏幕的每英寸水平点数。 |
deviceYDPI | 返回显示屏幕的每英寸垂直点数。 |
fontSmoothingEnabled | 返回用户是否在显示控制面板中启用了字体平滑。 |
height | 返回显示屏幕的高度。 |
logicalXDPI | 返回显示屏幕每英寸的水平方向的常规点数。 |
logicalYDPI | 返回显示屏幕每英寸的垂直方向的常规点数。 |
pixelDepth | 返回显示屏幕的颜色分辨率(比特每像素)。 |
updateInterval | 设置或返回屏幕的刷新率。 |
width | 返回显示器屏幕的宽度。 |
10.Exception
10.1 异常概述
在ES3之前JavaScript代码执行的过程中,一旦出现错误,整个JavaScript代码都会停止执行,这样就显的代码非常的不健壮。
在Java或C#等一些高级语言中,都提供了异常处理机制,可以处理出现的异常,而不会停止整个应用程序。
从ES3开始,JavaScript也提供了类似的异常处理机制,从而让JavaScript代码变的更健壮,即使执行的过程中出现了异常,也可以让程序具有了一部分的异常恢复能力。
当错误发生时,JavaScript 提供了错误信息的内置 error 对象。
error 对象提供两个有用的属性:name 和 message 。
error 对象属性
属性 | 描述 |
---|---|
name | 设置或返回错误名 |
message | 设置或返回错误消息(一条字符串) |
Error Name Values
error 的 name 属性可返回六个不同的值:
错误名 | 描述 |
---|---|
EvalError | 已在 eval() 函数中发生的错误 |
RangeError | 已发生超出数字范围的错误 |
ReferenceError | 已发生非法引用 |
SyntaxError | 已发生语法错误 |
TypeError | 已发生类型错误 |
URIError | 在 encodeURI() 中已发生的错误 |
10.2 异常捕捉
ES3开始引入了 try-catch 语句,是 JavaScript 中处理异常的标准方式。
语法格式:
try {
// 可能发生异常的代码
} catch (error) {
// 发生错误执行的代码
} finally {
// 无论是否出错都会执行的代码
}
10.3 异常抛出
在大部分的代码执行过程中,都是出现错误的时候,由浏览器(javascript引擎)抛出异常,然后程序或者停止执行或被try…catch 捕获。
然而有时候我们在检测到一些不合理的情况发生的时候也可以主动抛出错误,请使用 throw 关键字抛出来主动抛出异常。
注意事项:
- thow后面就是我们要抛出的异常对象,在以前的时候都是出现错误的时候浏览器抛出异常对象,只是现在是我们自己主动抛出的异常对象。
- 只要有异常对象抛出,不管是浏览器抛出的,还是代码主动抛出,都会让程序停止执行。如果想让程序继续执行,则有也可以用try…catch来捕获。
- 每一个错误类型都可以传入一个参数,表示实际的错误信息。
- 我们可以在适当的时候抛出任何我们想抛出的异常类型。throw new SyntaxError(“语法错误…”);
/*该函数接收一个数字,返回它的平方。*/
function foo(num) {
if (typeof num == "number") {
return num * num;
} else {
throw new TypeError("您输入的是一个非法数字!")
}
}
console.log(foo(4));
console.log(foo("abc"));
10.4 主动抛出自定义异常
我们不仅仅可以抛出js内置的错误类型的对象,也可以自定义错误类型,然后抛出自定义错误类型的对象。
如果要自定义错误类型,只需要继承任何一个自定义错误类型都可以,一般直接继承Error即可。
/*自定义错误*/
function MyError(message) {
this.message = "注意:这是自定义的错误"
this.name = "自定义错误";
}
MyError.prototype = new Error();
try {
throw new MyError("注意:这是自定义错误类型")
} catch (error) {
console.log(error.message)
}
11. JSON
11.1 JSON概述
SON:JavaScript Object Notation(JavaScript 对象标记法),它是一种存储和交换数据的语法。
当数据在浏览器与服务器之间进行交换时,这些数据只能是文本,JSON 属于文本并且我们能够把任何 JavaScript 对象转换为 JSON,然后将 JSON 发送到服务器。我们也能把从服务器接收到的任何 JSON 转换为 JavaScript 对象。以这样的方式,我们能够把数据作为 JavaScript 对象来处理,无需复杂的解析和转译。
SON语法
在json中,每一个数据项,都是由一个键值对(或者说是名值对)组成的,但是键必须是字符串,且由双引号包围,而值必须是以下数据类型之一:
字符串(在 JSON 中,字符串值必须由双引号编写)
数字
对象(JSON 对象)
数组
布尔
null
JSON 的值不可以是以下数据类型之一:
函数
日期
undefined
因为 JSON 语法由 JavaScript 对象标记法衍生而来,所以很少需要其它额外的软件来处理 JavaScript 中的 JSON。
通过 JavaScript,您能够创建对象并向其分配数据,就像这样:
<script>
// js对象
var person = {name: "admin", age: 20, city: "重庆"};
// js对象转化为 JSON字符串
console.log( JSON.stringify(person));
// JSON 字符串
var personString = '{"name":"admin","age":20,"city":"重庆"}';
// JSON 字符串转化为js对象
console.log( JSON.parse(personString));
</script>
12.Ajax
12.1 AJAX概述
传统的web交互是用户触发一个http请求服务器,然后服务器收到之后,在做出响应到用户,并且返回一个新的页面,每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的HTML页,而用户每次都要浪费时间和带宽去重新读取整个页面。这个做法浪费了许多带宽,由于每次应用的交互都需要向服务器发送请求,应用的响应时间就依赖于服务器的响应时间,这导致了用户界面的响应比本地应用慢得多。
AJAX 的出现,刚好解决了传统方法的缺陷,AJAX 是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新
AJAX的XMLHttpRequest对象
AJAX 的核心是 XMLHttpRequest 对象。 所有现代浏览器都支持 XMLHttpRequest 对象。
XMLHttpRequest 对象用于幕后同服务器交换数据,这意味着可以更新网页的部分,而不需要重新加载整个页面。
所有现代浏览器(Chrom、IE7+、Firefox、Safari 以及 Opera)都有内建的 XMLHttpRequest 对象。
创建 XMLHttpRequest 的语法是:
variable = new XMLHttpRequest();
老版本的 Internet Explorer(IE5 和 IE6)使用 ActiveX 对象:
variable = new ActiveXObject("Microsoft.XMLHTTP");
为了应对所有浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,创建 XMLHttpRequest 对象,如果不支持,则创建 ActiveX 对象:
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
12.2 XMLHttpRequest对象方法
方法 | 描述 |
---|---|
new XMLHttpRequest() | 创建新的 XMLHttpRequest 对象 |
abort() | 取消当前请求 |
getAllResponseHeaders() | 返回头部信息 |
getResponseHeader() | 返回特定的头部信息 |
open(method, url, async, user, psw) | 规定请求method:请求类型 GET 或 POST |
url:文 | 件位置 |
async:true | (异步)或 false(同步) |
user: | 可选的用户名称 |
psw: | 可选的密码 |
send() | 将请求发送到服务器,用于 GET 请求 |
send(string) | 将请求发送到服务器,用于 POST 请求 |
setRequestHeader() | 向要发送的报头添加标签/值对 |
12.3XMLHttpRequest对象属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAjGBgKR-1656822319483)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220417195902817.png)]
封装ajax
var Ajax = {
get: function (url, fn) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
fn.call(this, xhr.responseText);
}
};
xhr.send();
},
post: function (url, data, fn) {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
fn.call(this, xhr.responseText);
}
};
xhr.send(data);
}
};
// 演示GET请求
Ajax.get("url", function (response) {
console.log(response);
});
// 演示POST请求
Ajax.post("url", "", function (response) {
console.log(response);
});
13.Cookie
13.1 Cookie概述
Cookie 是一些数据,存储于你电脑上的文本文件中,当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息,Cookie 的作用就是用于解决 “如何记录客户端的用户信息”:
当用户访问 web 页面时,它的名字可以记录在 cookie 中。
在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。
Cookie 以名/值对形式存储,如下所示:
username=admin
当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie 会被添加到该请求中,服务端通过这种方式来获取用户的信息。
JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 Cookie。
13.2 Cookie创建
JavaScript 中,创建 cookie 如下所示:
document.cookie = "username=zhangsan";
您还可以为 cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除。
document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT";
您可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
document.cookie = "username=zhangsan; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
13.3 Cookie读取
JavaScript 中,读取 cookie 如下所示:
document.cookie 将以字符串的方式返回所有的 cookie,类型格式: cookie1=value; cookie2=value; cookie3=value;
document.cookie = "username=zhangsan";
var cookies = document.cookie;
console.log(cookies);
13.4 Cookie修改
JavaScript 中,修改 cookie 如下所示:
使用 document.cookie 将旧的 cookie 将被覆盖就是修改。
document.cookie = "username=zhangsan";
document.cookie = "username=lisi";
var cookies = document.cookie;
console.log(cookies);
13.5 Cookie删除
JavaScript 中,删除 cookie 如下所示:
删除 cookie 非常简单,您只需要设置 expires 参数为以前的时间即可,如下所示,设置为 Thu, 01 Jan 1970 00:00:00 GMT:
document.cookie = "username=zhangsan";
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
var cookies = document.cookie;
console.log(cookies);
13.6 cookie操作工具方法
function cookieUtils() {
/**
* 添加cookie
* @param cname cookie名称
* @param cvalue cookie值
* @param exdays 过期天数
*/
function addCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
/**
* 删除所有cookie
*/
function clear() {
var arrCookie = document.cookie.split('; ');
var arrLength = arrCookie.length;
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() - 1);
for (var i = 0; i < arrLength; i++) {
var str = arrCookie[i].split('=')[0];
document.cookie = str + '=' + ';expires=' + expireDate.toGMTString();
}
}
/**
* 获取所有的cookie
* @param returnType 返回的是json还是object 默认object
*/
function getCookies(returnType) {
var cookies = {};
if (!document.cookie == '') {
//用spilt('; ')切割所有cookie保存在数组arrCookie中
var arrCookie = document.cookie.split(';');
for (var i = 0; i < arrCookie.length; i++) {
var arr = arrCookie[i].split("=");
cookies[arr[0] ]= arr[1]
}
if(returnType == "json"){
return JSON.stringify(cookies)
}
return cookies;
}
}
/**
* 获取指定cookie
* @param cname cookie名称
* @returns {string}
*/
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
}
return "";
}
/**
* 根据cookie的名称删除指定的cookie
* @param name cookie的名称
*/
function removeCookie(name) {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval = getCookie(name);
if (cval != null)
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}
// 将这些方法进行返回 供外部使用
return { addCookie,clear,getCookies,getCookie,removeCookie }
}
14.WebStorage
14.1 WebStorage概述
WebStorage是HTML5中本地存储的解决方案之一,在HTML5的WebStorage概念引入之前除去IE User Data、Flash Cookie、Google Gears等看名字就不靠谱的解决方案,浏览器兼容的本地存储方案只有使用Cookie。有同学可能会问,既然有了Cookie本地存储,为什么还要引入WebStorage的概念?那就要说一说Cookie的缺陷了:
- 数据大小:作为存储容器,Cookie的大小限制在4KB左右这是非常坑爹的,尤其对于现在复杂的业务逻辑需求,4KB的容量除了存储一些配置字段还简单单值信息,对于绝大部分开发者来说真的不知指望什么了。
- 安全性问题:由于在HTTP请求中的Cookie是明文传递的(HTTPS不是),带来的安全性问题还是很大的。
- 网络负担:我们知道Cookie会被附加在每个HTTP请求中,在HttpRequest和HttpResponse的header中都是要被传输的,所以无形中增加了一些不必要的流量损失。
虽然WebStorage是HTML5新增的本地存储解决方案之一,但并不是为了取代Cookie而制定的标准,Cookie作为HTTP协议的一部分用来处理客户端和服务器通信是不可或缺的,session正是依赖于实现的客户端状态保持。WebStorage的意图在于解决本来不应该Cookie做,却不得不用Cookie的本地存储的应用场景。
14.2 WebStorage分类
Web Storage又分为两种: sessionStorage 和localStorage ,即这两个是Storage的一个实例。从字面意思就可以很清楚的看出来,sessionStorage将数据保存在session中,浏览器关闭也就没了;而localStorage则一直将数据保存在客户端本地; 不管是sessionStorage,还是localStorage,使用的API都相同。
localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理,
14.3 localStorage方法
对象介绍:
localStorage在本地永久性存储数据,除非显式将其删除或清空。
常见方法:
- 保存单个数据:localStorage.setItem(key,value);
- 读取单个数据:localStorage.getItem(key);
- 删除单个数据:localStorage.removeItem(key);
- 删除所有数据:localStorage.clear();
- 获取某个索引的key:localStorage.key(index);
14.4 sessionStorage方法
对象介绍:
sessionStorage对象存储特定于某个对话的数据,也就是它的生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。存储在sessionStorage中的数据可以跨越页面刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可以使用(注意:Firefox和Weblit都支持,IE则不行)。
因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在sessionStorage中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。
不同浏览器写入数据方法略有不同。Firefox和Webkit实现了同步写入,所以添加到存储空间中的数据是立刻被提交的。而IE的实现则是异步写入数据,所以在设置数据和将数据实际写入磁盘之间可能有一些延迟。
常见方法:
- 保存单个数据:sessionStorage.setItem(key,value);
- 读取单个数据:sessionStorage.getItem(key);
- 删除单个数据:sessionStorage.removeItem(key);
- 删除所有数据:sessionStorage.clear();
- 获取某个索引的key:sessionStorage.key(index);
15.闭包
闭包(closure)是指有权访问另一个函数作用域中变量的函数。学习了闭包,可以在函数外部访问函数内部的变量,闭包的作用是延伸了变量的作用范围。
闭包基础案例:
// 闭包(closure)指有权访问另一个函数作用域中变量的函数。
// 闭包: 我们fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
function father() {
var num = 10;
function son() {
console.log(num);
}
son();
}
father();
闭包的作用:主要是延伸变量的作用范围
<!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">
<title>Document</title>
</head>
<style type="text/css">
</style>
<body>
<ul class="nav">
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
<script>
// 闭包应用-点击li输出当前li的索引号
// 方式1. 我们可以利用动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function() {
// console.log(i);
console.log(this.index);
}
}
// 方式2. 利用闭包的方式得到当前小li 的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {
// console.log(i);
lis[i].onclick = function() {
console.log(i);
}
})(i);
}
</script>
</body>
</html>
利用闭包给window对象添加全局方法
(function(window) {
//私有数据
var a = 10;
var b = 5;
//操作数据的函数
function add(a, b) {
return a + b;
}
function product(a, b) {
return a * b;
}
//向外暴露对象(给外部使用的方法)
window.slefMetond = {
add,
product
}
})(window);
// 全局函数
window.__proto__['log'] = function() {
console.log("__proto__:", "log")
return "log()"
}
// this 默认执行window 所以可以直接进行调用
console.log(slefMetond.add(2, 5))
console.log(slefMetond.product(2, 5))
console.log(log())
16.JavaScript新特性
16.1 let 关键字
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
- 不允许重复声明
- 块儿级作用域
- 不存在变量提升
- 不影响作用域链
注意:以后声明变量使用 let 就对了
16.2 const 关键字
const 关键字用来声明常量,const 声明有以下特点:
- 不允许重复声明
- 块儿级作用域
- 声明必须赋初始值
- 值不允许修改
- 标识符一般为大写
注意:声明对象类型使用 const,非对象类型声明选择 let
16.3 变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式
数组的解构赋值:
var arr = ['admin','20','admin@qq.com'];
var [name,age,email] = arr;
console.log(name)
console.log(age)
console.log(email)
对象的解构赋值:
//对象的解构赋值
const admin = {
name: "admin",
hobby: ["爬山", "歌手", "玩游戏", "看书"],
tages: ["老男人", "单身狗"]
};
let {
name,
hobby,
tages: [tage1, tage2]
} = admin;
console.log(name);
console.log(hobby);
console.log(tage1);
console.log(tage2);
16.4 模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符
- 可以使用 ${xxx} 形式输出变量
var message = "admin"
var content = `<h1> 你好: ${message}</h1>`
document.write(content)
16.5 对象的简写
let name = "admin";
let age = 18;
let show = function () {
console.log(this);
};
//属性和方法简写
let person = {
name,
age,
show
};
console.log(person.name);
console.log(person.age);
person.show();
16.7 箭头函数
ES6 允许使用「箭头」(=>)定义函数,通用写法如下:
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数 this 指向声明时所在作用域下 this 的值,箭头函数不会更改 this 指向,用来指定回调函数会非常合适
- 箭头函数不能作为构造函数实例化
- 不能使用 arguments 实参
function fun(name,add){
console.log(name+":",add())
}
// 调用函数
fun("admin",()=>{
return 10+5;
})
16.8 rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments 参数。
function fun(...args) {
for (var index in args) {
console.log(args[index])
}
}
// 调用函数
fun("admin", "root", "jack")
16.9 spread 扩展运算符
扩展运算符(spread)也是三个点(…),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
function fun() {
for (var index in arguments) {
console.log(arguments[index])
}
}
var arr = ["admin", "root", "jack"]
// 调用函数
fun( ...arr)
16.10 展开对象
// 展开对象
let admin = {
name: "admin"
};
let root = {
age: "20"
};
let user = {...admin, ...root};
console.log(user);
16.11 迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费,原生具备 iterator 接口的数据:
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
注意:需要自定义遍历数据的时候,要想到迭代器
工作原理:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
案例演示:自定义遍历数据
//声明一个对象
const obj = {
name: "五班",
stus: [
"张三",
"李四",
"王五",
"小六"
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = {value: _this.stus[index], done: false};
//下标自增
index++;
//返回结果
return result;
} else {
return {value: undefined, done: true};
}
}
};
}
}
//遍历这个对象
for (let value of obj) {
console.log(value);
}
16.12 生成器
生成器函数使用
代码说明:
-
- 的位置没有限制
- 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
- yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
- next 方法可以传递实参,作为 yield 语句的返回值
16.13 promise函数
多个prmoise函数同步执行
//创建 promise 对象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("导航条数据");
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("轮播图数据");
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("表单数据");
}, 3000)
})
//await 要放在 async 函数中.
async function fun() {
try {
let result1 = await p1;
let result2 = await p2;
let result3 = await p3;
console.log(result1);
console.log(result2);
console.log(result3);
} catch (e) {
console.log(e);
}
}
//调用函数
fun();
promise执行异步任务
new Promise((resolve,reject)=>{
// 模拟一个异步任务
setTimeout(()=>{
console.log('执行完promise')
// 模拟异步任务的返回结果,偶数代表成功,奇数代表失败
let result= Math.floor(Math.random()*(50-10+1)+10);
console.log('result='+result)
if (result%2==0){
// 执行这个函数将异步任务的返回值传递过去在then中继续处理(成功时调用)
resolve('模拟执行成功返回的数据');
}else {
// 执行这个函数将异步任务的返回值传递过去在catch中继续处理(失败时调用)
reject('模拟执行失败返回的数据');
}
},1000)
}).then((data)=>{
// 得到resolve传递过来的参数进行处理
console.log(data)
}).catch((error)=>{
//得到reject传递过来的参数进行处理
console.log(error)
})
promiseAll 执行多个异步任务
all方法的使用,当我们需要执行多个异步任务,需要多个任务返回成功数据才去执行下一步操作的时候,就可以使用到该方法
<script type="text/javascript">
let result1=4;
let result2=2;
Promise.all([
// 异步任务1
new Promise((resolve, reject) => {
setTimeout(() => {
let flag=result1%2==0
if (flag){
resolve(flag)
}else {
reject(flag)
}
}, 1000)
}),
// 异步任务2
new Promise( (resolve, reject) => {
setTimeout(() => {
let flag=result2%2==0
if (flag){
resolve(flag)
}else {
reject(flag)
}
}, 2000)
})
]).then( success=>{
// 多个异步任务执行成功,在此处执行下一步操作
console.log('success:',success)
}
).catch( error=>{
// 多个异步任务执行失败,在此处执行下一步操作
console.log("error:",error)
})
</script>
prmoise链式调用
链式调用,也就是说多个异步任务,需要上一次执行成功,才可以继续执行下一部这样的情况
<script type="text/javascript">
new Promise((resolve, reject) => {
// 第一次网络请求的代码
setTimeout(() => {
resolve("success1")
// 处理失败的情况
// reject('failed1')
}, 1000)
}).then((result) => {
// 第一次处理的代码
console.log('第一次网络请 result='+result);
return new Promise((resolve, reject) => {
// 第二次网络请求的代码
setTimeout(() => {
resolve("success2")
}, 1000)
})
}).then((result) => {
// 第二次处理的代码
console.log('第二次网络请求 result='+result);
return new Promise((resolve, reject) => {
// 第三次网络请求的代码
setTimeout(() => {
resolve("success3")
})
})
}).then((result) => {
console.log('第三次网络请求 result='+result);
}).catch(( error)=>{
// 处理失败的情况
console.log('调用失败')
})
--------------------------------------------链式调用的方式2------------------------------------------------------
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('000')
}, 1000)
}).then(result => {
console.log(result, '第一层');
return Promise.resolve(result + '111')
}).then(result => {
console.log(result, '第二层');
return Promise.resolve(result + '222')
}).then(result => {
console.log(result, '第三层');
}).catch(err => {
console.log(err);
})
</script>
race的用法: all是等所有的异步操作都执行完了再执行then方法,那么race方法就是相反的,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调
<script type="text/javascript">
function Async1(){
let promise = new Promise(function(resolve, reject){
setTimeout(function(){
let num = Math.floor(Math.random()*(10-1+1)+1);
console.log('Async1 随机数生成的值:',num)
if(num<=8){
resolve(num);
}
else{
reject('执行失败回调');
}
}, 1000);
})
return promise
}
function Async2(){
let promise = new Promise(function(resolve, reject){
setTimeout(function(){
let num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('Async2 随机数生成的值:',num)
if(num<=8){
resolve(num);
}
else{
reject('执行失败回调');
}
}, 2000);
})
return promise
}
function Async3(){
let promise = new Promise(function(resolve, reject){
setTimeout(function(){
let num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('Async3 随机数生成的值:',num)
if(num<=8){
resolve(num);
}
else{
reject('执行失败回调');
}
}, 3000);
})
return promise
}
Promise.race([Async1(), Async2(), Async3()])
.then(function(results){
console.log('执行成功',results);
},function(error){
console.log('执行失败',error);
</script>
16.14 Set
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
- size:返回集合的元素个数
- add():增加一个新元素,返回当前集合
- delete():删除元素,返回 boolean 值
- has():检测集合中是否包含某个元素,返回 boolean 值
- clear():清空集合,返回 undefined
//创建一个空集合
let set = new Set();
//创建一个非空集合
let setList = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(setList.size);
//添加新元素
console.log(setList.add(4));
//删除元素
console.log(setList.delete(1));
//检测是否存在某个值
console.log(setList.has(2));
// 遍历集合
setList.forEach((key,val,slef) => console.log(key + ": " + val,slef));
// 获取所有key
for(let key of setList.keys()) {
console.log(key);
}
// 获取所有value
for(let val of setList.values()) {
console.log(val);
}
// key and value
for(let item of setList.entries()) {
console.log(item);
}
// 获取所有value
for(let item of setList) {
console.log(item);
}
//清空集合
console.log(setList.clear());
16.15 Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:
- size:返回 Map 的元素个数
- set():增加一个新元素,返回当前 Map
- get():返回键名对象的键值
- has():检测 Map 中是否包含某个元素,返回 boolean 值
- clear():清空集合,返回 undefined
//创建一个空 map
let map= new Map();
//创建一个非空 map
let mapList = new Map([
["name", "张三"],
["gender", "女"]
]);
//属性和方法
//获取映射元素的个数
console.log(mapList.size);
//添加映射值
console.log(mapList.set("age", 20));
//获取映射值
console.log(mapList.get("age"));
//检测是否有该映射
console.log(mapList.has("age"));
// 遍历map集合
mapList.forEach(function(value,key,slef){
console.log(value,key)
})
// 遍历map集合
for (let [k,v] of mapList.entries()) {
console.log(k + ":" + v)
}
//清除
console.log(mapList.clear());
16.16 class 类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,它的一些如下:
- class:声明类
- constructor:定义构造函数初始化
- extends:继承父类
- super:调用父级构造方法
- static:定义静态方法和属性
//父类
class Father {
//构造方法
constructor(id, name, age) {
this.id = id;
this.name = name;
this.age = age;
}
//对象方法
show() {
console.log("我是父类show()");
}
}
//子类
class Son extends Father {
constructor(id, name, age, email) {
// 调用父类构造
super();
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
//子类方法
sonSelf() {
console.log("子类独有sonSelf()");
}
//方法重写
show() {
console.log("我是子类show()");
}
// 我是子类独有方法
static staticMethod() {
console.log("子类staticMethod()")
}
}
//实例化对象
const father = new Father("101", "小明", 20);
const son = new Son("101", "admin", 20, "admin@qq.coom");
//调用子类方法
son.sonSelf();
//调用重写方法
son.show();
//调用静态方法
Son.staticMethod();
father.show();
console.log(father);
console.log(son);
16.17 数值扩展
ES6 新增了二进制和八进制的表示方法
let b = 0b10//二进制
let o = 0o10;//八进制
let d = 10;//十进制
let x = 0xf;//十六进制
console.log(b);
console.log(o);
console.log(d);
console.log(x);
Number.EPSILON
Number.EPSILON:它是 JavaScript 表示的最小精度,EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
function equals(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true;
} else {
return false;
}
}
console.log(0.1 + 0.2 === 0.3);
console.log(equals(0.1 + 0.2, 0.3));
Number.isFinite
Number.isFinite:检测一个数值是否为有限数
console.log(Number.isFinite(100));
console.log(Number.isFinite(100 / 0));
console.log(Number.isFinite(Infinity));
console.log(Number.isFinite(-Infinity));
Number.isNaN
Number.isNaN:检测一个数值是否为 NaN
console.log(Number.isNaN(123));
console.log(Number.isNaN(NaN));
Number.parseInt
Number.parseInt:将一个字符串转换为整数
console.log(Number.parseInt("5454abc"));
Number.parseFloat
Number.parseFloat:将一个字符串转换为浮点数
console.log(Number.parseFloat("3.1415926asds"));
Number.isInteger
Number.isInteger:判断一个数是否为整数
console.log(Number.isInteger(5));
console.log(Number.isInteger(2.5));
Math.trunc
Math.trunc:将数字的小数部分抹掉
console.log(Math.trunc(3.5));
Math.sign
Math.sign:判断一个数到底为正数、负数、还是零
// 正数返回1
console.log(Math.sign(100));
// 0返回本身
console.log(Math.sign(0));
// 负数返回-1
console.log(Math.sign(-20000));
16.18对象扩展
ES6 新增了一些 Object 对象的方法,例如:
- Object.is:比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
- Object.assign:对象的合并,将源对象的所有可枚举属性,复制到目标对象
- proto、setPrototypeOf、 setPrototypeOf可以直接设置对象的原型
Object.is
Object.is:判断两个值是否完全相等
console.log(Object.is(120, 120));// ===
console.log(Object.is(NaN, NaN));// ===
console.log(NaN === NaN);// ===
设置原型对象
- Object.setPrototypeOf:设置原型对象
- Object.getPrototypeof:获取原型对象
const protoObject = {
name: "objectName"
};
const subjoin = {
area: ["北京", "上海", "深圳"],
show: function() {
console.log("show()");
}
};
Object.setPrototypeOf(protoObject, subjoin);
console.log(Object.getPrototypeOf(protoObject));
console.log(protoObject)
protoObject.show();
16.19 模块化开发
模块化的语法
模块功能主要由两个命令构成:export 和 import。
- export 命令用于规定模块的对外接口
- import 命令用于输入其它模块提供的功能
模块化导出
m1.js
//方式一:分别暴露
export let school = "华北理工大学";
export function study() {
console.log("我们要学习!");
}
m2.js
//方式二:统一暴露
let school = "华北理工大学";
function findJob() {
console.log("我们要找工作!");
}
export {school, findJob};
m3.js
//方式三:默认暴露
export default {
school: "华北理工大学",
change: function () {
console.log("我们要改变自己!");
}
}
模块化的导入
<script type="module">
// 引入 m1.js 模块内容
import * as m1 from "./m1.js";
// 引入 m2.js 模块内容
import * as m2 from "./m2.js";
// 引入 m3.js 模块内容
import * as m3 from "./m3.js";
m1.study();
m2.findJob();
m3.default.change();
</script>
模块化导入的结构赋值
// 引入 m1.js 模块内容
import {school, study} from "./m1.js";
// 引入 m2.js 模块内容
import {school as s, findJob} from "./m2.js";
// 引入 m3.js 模块内容
import {default as m3} from "./m3.js";
16.20 数组方法扩展
Array.prototype.includes:此方法用来检测数组中是否包含某个元素,返回布尔类型值
const hero = ["林冲", "鲁智深", "李逵", "宋江"];
console.log(hero.includes("林冲"));
<Table rowSelection={{type:"checkbox",selectedRowKeys:selectedUserKeys,
onChange:(selectedKeys,selectedRows) => selectedUserOnChange(selectedKeys,selectedRows)}}
columns={slackColumns} dataSource={unApproved} pagination={false} scroll={{y:80}} size="small" rowKey={record => record.userId}/>
16.21 symbol
ES6新增数据类型。符号是原始值,且符号实例是唯一、不可变的。
符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
symbol并不是为了提供私有属性的行为而新增的。
符号就是用来·创建唯一记号,进而用作非字符串形式的对象属性。
Object.getOwnPropertySymbols
方法,可以获取对象的所有 Symbol 属性名
Reflect.ownKeys
可以返回所有类型的键名,包括常规键名和 Symbol 键名
symbol特点
- symbol的值是唯一的,用来解决命名冲突的问题
- symbol不能与其他数据类型进行运算
- symbol定义的类型不能用 for … in 进行遍历,但是可以使用Reflect.ownkeys获取所有的键值
symbol的基本使用:
let sym = Symbol();
console.log(typeof sym); // 'symbol'
symobl的创建模式
// 可以在创建时,指定一个标识符,并且是唯一的,即时同名也不相等
let sym1 = Symbol('sym1');
let sym2 = Symbol('sym1')
console.log(sym1,sym2,sym1 == sym2)
// 可以在创建时,指定一个标识符,并且是唯一的,创建时如果已经存在该标识符,直接返回,不会新建
let sym3 = Symbol.for('sym3');
let sym4 = Symbol.for('sym3')
console.log(sym3,sym4,sym3 == sym4)
首先补充一个知识点:属性名中的[]作用。
属性名中的中括号有的作用就是应用变量所在代表的值作为属性名。
来演示一个:
var name="nickName";
var obj={
id:"101",
[name]:'admin' // 此时obj多了个数据名叫nickName
}
获取symbol键值对
// Reflect.ownKeys()获取所有键值对
var sym = Symbol('sym');
var obj={
name:"小明",
[sym]:"symbolValue",
}
console.log(Reflect.ownKeys(obj))
console.log(obj[sym])
实战案例:解决对象属性命名冲突问题
有问题的添加或修改属性
// 使用场景 解决变量命名属性冲突,如果假设我们不知道这个对象有什么属性,
//然后去给他添加,可能会覆盖调用原有的属性,但是我们使用symbol去添加就不会出现这种问题
let user = {
id:'1005',
run:function(){
console.log("run")
}
}
console.log(user)
// 在我们不知道user对象有id和run属性的时候去新增就会覆盖掉原有的属性
user.id='106';
user.run=function(){
console.log("run-new")
}
// 以前的user对象被我们在不知情的情况下污染了
console.log(user)
解决方案:使用Symbol
// 使用场景 解决变量命名属性冲突,如果假设我们不知道这个对象有什么属性,
//然后去给他添加,可能会覆盖调用原有的属性,但是我们使用symbol去添加就不会出现这种问题
let user = {
id:'1005',
run:function(){
return "run";
}
}
console.log(user)
// 解决方案使用sysbom去添加 ,这样并不会覆盖掉原有的属性
//并且属性名都是和以前一样的,因为Symbol是唯一的,不会覆盖掉原来的属性
var symId = Symbol('id');
var symRun = Symbol('run');
user[symId] = '106';
user[symRun] = function(){
return "run-new";
};
console.log(user);
// 原来的
console.log(user.id);
console.log(user.run());
// 新增的
console.log(user[symId]);
console.log(user[symRun]());
ect = {
name: “objectName”
};
const subjoin = {
area: [“北京”, “上海”, “深圳”],
show: function() {
console.log(“show()”);
}
};
Object.setPrototypeOf(protoObject, subjoin);
console.log(Object.getPrototypeOf(protoObject));
console.log(protoObject)
protoObject.show();
## 16.19 模块化开发
**模块化的语法**
模块功能主要由两个命令构成:export 和 import。
- export 命令用于规定模块的对外接口
- import 命令用于输入其它模块提供的功能
**模块化导出**
m1.js
```javascript
//方式一:分别暴露
export let school = "华北理工大学";
export function study() {
console.log("我们要学习!");
}
m2.js
//方式二:统一暴露
let school = "华北理工大学";
function findJob() {
console.log("我们要找工作!");
}
export {school, findJob};
m3.js
//方式三:默认暴露
export default {
school: "华北理工大学",
change: function () {
console.log("我们要改变自己!");
}
}
模块化的导入
<script type="module">
// 引入 m1.js 模块内容
import * as m1 from "./m1.js";
// 引入 m2.js 模块内容
import * as m2 from "./m2.js";
// 引入 m3.js 模块内容
import * as m3 from "./m3.js";
m1.study();
m2.findJob();
m3.default.change();
</script>
模块化导入的结构赋值
// 引入 m1.js 模块内容
import {school, study} from "./m1.js";
// 引入 m2.js 模块内容
import {school as s, findJob} from "./m2.js";
// 引入 m3.js 模块内容
import {default as m3} from "./m3.js";
16.20 数组方法扩展
Array.prototype.includes:此方法用来检测数组中是否包含某个元素,返回布尔类型值
const hero = ["林冲", "鲁智深", "李逵", "宋江"];
console.log(hero.includes("林冲"));
<Table rowSelection={{type:"checkbox",selectedRowKeys:selectedUserKeys,
onChange:(selectedKeys,selectedRows) => selectedUserOnChange(selectedKeys,selectedRows)}}
columns={slackColumns} dataSource={unApproved} pagination={false} scroll={{y:80}} size="small" rowKey={record => record.userId}/>
16.21 symbol
ES6新增数据类型。符号是原始值,且符号实例是唯一、不可变的。
符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
symbol并不是为了提供私有属性的行为而新增的。
符号就是用来·创建唯一记号,进而用作非字符串形式的对象属性。
Object.getOwnPropertySymbols
方法,可以获取对象的所有 Symbol 属性名
Reflect.ownKeys
可以返回所有类型的键名,包括常规键名和 Symbol 键名
symbol特点
- symbol的值是唯一的,用来解决命名冲突的问题
- symbol不能与其他数据类型进行运算
- symbol定义的类型不能用 for … in 进行遍历,但是可以使用Reflect.ownkeys获取所有的键值
symbol的基本使用:
let sym = Symbol();
console.log(typeof sym); // 'symbol'
symobl的创建模式
// 可以在创建时,指定一个标识符,并且是唯一的,即时同名也不相等
let sym1 = Symbol('sym1');
let sym2 = Symbol('sym1')
console.log(sym1,sym2,sym1 == sym2)
// 可以在创建时,指定一个标识符,并且是唯一的,创建时如果已经存在该标识符,直接返回,不会新建
let sym3 = Symbol.for('sym3');
let sym4 = Symbol.for('sym3')
console.log(sym3,sym4,sym3 == sym4)
首先补充一个知识点:属性名中的[]作用。
属性名中的中括号有的作用就是应用变量所在代表的值作为属性名。
来演示一个:
var name="nickName";
var obj={
id:"101",
[name]:'admin' // 此时obj多了个数据名叫nickName
}
获取symbol键值对
// Reflect.ownKeys()获取所有键值对
var sym = Symbol('sym');
var obj={
name:"小明",
[sym]:"symbolValue",
}
console.log(Reflect.ownKeys(obj))
console.log(obj[sym])
实战案例:解决对象属性命名冲突问题
有问题的添加或修改属性
// 使用场景 解决变量命名属性冲突,如果假设我们不知道这个对象有什么属性,
//然后去给他添加,可能会覆盖调用原有的属性,但是我们使用symbol去添加就不会出现这种问题
let user = {
id:'1005',
run:function(){
console.log("run")
}
}
console.log(user)
// 在我们不知道user对象有id和run属性的时候去新增就会覆盖掉原有的属性
user.id='106';
user.run=function(){
console.log("run-new")
}
// 以前的user对象被我们在不知情的情况下污染了
console.log(user)
解决方案:使用Symbol
// 使用场景 解决变量命名属性冲突,如果假设我们不知道这个对象有什么属性,
//然后去给他添加,可能会覆盖调用原有的属性,但是我们使用symbol去添加就不会出现这种问题
let user = {
id:'1005',
run:function(){
return "run";
}
}
console.log(user)
// 解决方案使用sysbom去添加 ,这样并不会覆盖掉原有的属性
//并且属性名都是和以前一样的,因为Symbol是唯一的,不会覆盖掉原来的属性
var symId = Symbol('id');
var symRun = Symbol('run');
user[symId] = '106';
user[symRun] = function(){
return "run-new";
};
console.log(user);
// 原来的
console.log(user.id);
console.log(user.run());
// 新增的
console.log(user[symId]);
console.log(user[symRun]());