JavaScript引入
1.CSS书写格式
1.1.行内样式: 写在标签内部
1.2.内嵌样式(内联样式) : 写在一对head标签中
1.3.外链样式: 写在一个单独的.css文件中, 再导入进来
2.JavaScript书写格式
1.行内样式: 写在标签内部
2.内嵌样式(内联样式) : 写在一对head标签中
3.外链样式: 写在一个单独的.js文件中, 再导入进来
3.JavaScript书写格式注意点
- 不推荐直接将JavaScript代码书写到标签内部
- 默认情况下浏览器会从上至下的解析网页, 所以如果将JavaScript写到一对head标签中, 并且需要通过JavaScript代码操作界面上的元素, 那么就不能直接书写JavaScript代码, 否则无效
2.1如果想将JavaScript写到一对head标签中, 并且需要在JavaScript代码中操作界面上的元素, 那么必须加上window.onload
=function(){操作界面元素的JavaScript}
2.2window.onload
的含义: 等界面上所有的内容都加载完毕再执行{}中代码
2.3由于默认情况下浏览器会从上至下的解析网页, 所以如果想通过JavaScript操作界面上的元素只需要等到元素被加载解析之后操作就可以了, 所以我们还可以将JavaScript代码写到body结束标签的前面 - 如果通过外链式导入.js文件, 并且需要在.js文件中操作界面上的元素, 那么如果是在head标签中导入的, 必须在.js文件中加上window.onload. 如果是在body结束标签前面导入的, 那么就不用添加window.onload
- 如果需要在一对script标签中编写JavaScript代码, 那么就不能同时通过script标签再导入其它的.js文件, 否则书写的JavaScript代码无效
javascript输出形式
- 通过弹窗的形式来输出
alert(需要输出的内容);//弹出 confirm(需要输出的内容);//确认 prompt(需要输出的内容);//提供输入的文本框
注意点:
- 如果需要输出的内容不是数字, 那么就必须通过单引号或者双引号括起来
- 在JavaScript中是严格区分大小写的, alert()和ALERT()不是一回事 在编写JavaScript代码的时候, 一定要记住每一句代码后面都需要添加一个分号, 并且这个分号必须是英文的分号
- 我们会发现有时候不写分号程序也能够运行, 这里并不是因为不需要分号, 而是浏览器自动帮助我们添加了分号, 浏览器自动添加会消耗一定的性能, 并且有可能会添加错误
- 通过网页内容区域的形式来输出
document.write(需要输出的内容);
注意点: 如果需要输出的内容不是数字, 那么就必须通过单引号或者双引号括起来
- 通过开发者工具控制台的形式来输出
console.log(需要输出的内容); // 普通输出 console.warn(需要输出的内容); // 警告输出 console.error(需要输出的内容); // 错误输出
注意点: 如果需要输出的内容不是数字, 那么就必须通过单引号或者双引号括起来
常量和变量
- 什么是常量?常量表示一些固定不变的数据,现实生活中人的性别其实就可以看做是常量, 生下来是男孩一辈子都是男孩, 生下来是女孩一辈子都是女孩
2.JavaScript中常量的分类
- 2.1整型常量:整型常量其实就是正数, 在JavaScript中随便写一个整数都是整型常量,例如:1 / 666 / 99
- 2.2实型常量:实型常量其实就是小数, 在JavaScript中随便写一个小数都是实型常量,例如: 3.14 6.66
- 2.3字符串常量:字符串常量其实就是用单引号或者双引号括起来的内容, 我们就称之为字符串常量,例如:'a'/ 'abc'/ "1"/ "知播渔教育",无论用单引号或者双引号括起来了多少个字符, 在JavaScript中都是字符串常量
- 2.4布尔常量: 布尔常量其实就是真或者假, 在JavaScript中通过true和false来表达,在JavaScript中布尔常量只有两个取值, 真(true)或者假(false)
- 2.5自定义常量: 在ES6中新增的,const 常量名称 = 常量取值;
- 什么是变量?: 变量表示一些可以被修改的数据, 在现实生活中超市的储物格就是变量, 在不同的时间段里面, 储物格中存储的数据也不一样
- 如何定义一个变量: 在JavaScript中可以通过定义变量的方式来生成储物格, 也就是告诉浏览器, 我们需要一块内存空间,
var 变量名称
; - 如何使用变量?使用变量就是往申请的那块内存空间中存储数据, 和获取存储的数据
- 如何存储数据?变量名称 = 需要存储的数据;可以将等号右边需要存储的数据放到等号左边变量申请的那块存储空间中
- 如何获取存储在变量中的数据,即变量名称。
- 如果一个变量没有进行初始化, 那么变量中存储的是什么呢?在JavaScript中如果定义了一个变量,但是没有进行初始化, 那么变量中存储的是
undefined
- 给变量初始化有很多种形式
4.1先定义变量, 再对变量进行初始化
var num; num = 321; // 先定义再初始化
4.2可以在定义变量的同时对变量进行初始化
var value = 666; // 定义的同时初始化
1.注意点
1.在JavaScript中变量之间是可以相互赋值的
2.在JavaScript中如果定义了同名的变量, 那么后定义的变量会覆盖先定义的变量
3.在老版本的标准的(ES6之前)JavaScript中可以先使用变量, 再定义变量, 并不会报错, 由于JavaScript是一门解释型的语言, 会边解析边执行, 浏览器在解析JavaScript代码之前还会进行一个操作"预解析(预处理)"
预解析(预处理)步骤:
将当前JavaScript代码中所有变量的定义和函数的定义放到所有代码的最前面
var value; num = 123; value = num; // 将Num中的值拷贝一份给value console.log(num); console.log(value); var num = 666; num = 888; // 如果num前面没有var, 那么就是修改变量中存储的值 var num = 888;// 如果num前面有var, 那么就不是修改变量中存储的值, 而是重新定义一个新的变量 console.log(num);
为了解决老版本标准的的两个注意点
- 在JavaScript中如果定义了同名的变量, 那么后定义的变量会覆盖先定义的变量
- 在老版本的标准的(ES6之前)JavaScript中可以先使用变量, 再定义变量, 并不会报错
在ES6中就推出了一种新的定义变量的方式,格式:
ES6之前: var 变量名称;
ES6开始: let 变量名称;
javascript关键字
什么是关键字?
被JavaScript语言赋予了特殊含义的单词
关键字在开发工具中会显示特殊颜色
关键字不能用作变量名、函数名等
关键字严格区分大小写, var和Var前者是关键字, 后者不是
只需要记住一点: 在JavaScript中所有的关键字都是小写的
关键字在开发工具中会显示特殊颜色 标识符命名规则(必须遵守)
只能由26个英文字母的大小写、10个阿拉伯数字0~9、下划线_、美元符号$组成
不能以数字开头
** 严格区分大小写**,比如test和Test是2个不同的标识符
不可以使用关键字、保留字作为标识符
JavaScript注释
- 什么是JS的注释?
和HTML/CSS的注释一样, 都是用来注解解释某一段程序的含义的, 都是用来提升代码的阅读性的, 都是为了方便程序员之间沟通的 - JS注释的格式
单行注释: // 被注释的内容
注意点: 单行注释的有效范围是从第二个斜杠开始一直直到这一行的末尾, 也就是被注释的内容不能换行
多行注释: /星 被注释的内容 星/
注意点: 多行注释的有效范围是从第一颗星开始直到第二颗星结束, 也就是说被注释的内容可以换行的 - JS中注释的嵌套规则
3.1单行注释可以嵌套单行注释, 但是必须在一行
3.2单行注释可以嵌套多行注释, 但是必须在一行
3.3多行注释可以嵌套单行注释
3.4多行注释不可以嵌套多行注释
// 被注释的内容 被注释的内容 /* 被注释的内容 */
基本数据类型
- Number 数值类型
在JavaScript中无论是整数还是小数都是属于数值类型的
- String 字符串类型
在JavaScript中无论是通过单引号还是通过双引号括起来的内容都是属于字符串类型的
- Boolean 布尔类型
在JavaScript中布尔类型比较特殊, 只有两个取值true/false
- Undefined 未定义类型
在JavaScript中未定义类型比较特殊, 只有一个取值undefined
- Null 空类型
引用数据类型
- Object 对象类型
在JavaScript中为了方便我们检测某一种数据是属于哪一种数据类型的, JavaScript提供了一个名称叫做typeof的操作符,格式: typeof 需要检测的数据
;
数据类型转换
1.将Number类型转换为字符串类型
2.将Boolean类型转换为字符串类型
3.将undefined类型转换为字符串类型
4.将null类型转换为字符串类型
在JavaScript中如果想将以上的四种基本数据类型转换为字符串类型, 常用的方法有三种
1.对于Number类型和Boolean类型来说, 可以通过 变量名称.toString()的方式来转换
2.可以通过String(常量or变量);转换为字符串
3.还可以通过 变量or常量 + "" / 变量or常量 + ''转换为字符串
注意点: 不能使用常量直接调用toString方法, 因为常量是不能改变的
注意点: 变量名称.toString()的方式前面不能是常量, 因为常量不能被改变
String(常量or变量), 因为是根据传入的值重新生成一个新的值, 并不是修改原有的值
- 将String类型转换为数值类型
- 如果字符串中都是数值, 那么就正常转换
- 如果字符串是一个空串""/" ", 那么转换之后是0
- 如果字符串中不仅仅是数字, 那么转换之后是NaN
- 将Boolean类型转换为数值类型
- true转换之后是1
- false转换之后是0
- 将undefined类型转换为数值类型
- 转换之后是NaN
- 将null类型转换为数值类型
- 转换之后是0
总结:空字符串/false/null转换之后都是0
字符串中不仅仅是数字/undefined转换之后是NaN
其它的正常转换
在JavaScript中如果想将以上的四种基本数据类型转换为数值类型, 常用的方法有三种
- 通过Number(常量or变量);方式来转换
2. 还可以通过数学运算中的+号和-号来转换
3. 还可以通过parseInt(需要转换的字符串)/parseFloat(需要转换的字符串)
将String类型转换为布尔类型
只要字符串中有内容都会转换为true, 只有字符串中没有内容才会转换为false
将Number类型转换为布尔类型
只有数值是0才会转换为false, 其它的都会转换为true
如果是NaN也会转换为false
将undefined类型转换为布尔类型
undefined会转换为false
将null类型转换为布尔类型
null会转换为false
总结: 空字符串/0/NaN/undefined/null 会转换成false, 其它的都是true
在JavaScript中如果想将基本数据类型转换为布尔类型, 那么只需要调用Boolean(常量or变量)
注意
: === !==和== !=的区别
=== !==会同时判断取值和数据类型
== !=只会判断取值
关系运算符的结合性和优先级
- 关系运算符都是左结合性(从左至右的运算)
- 关系运算符中 > < >= <= 的优先级高于 == != === !==
- == 只会判断取值是否相等, 不会判断数据类型是否相等
let res = 123 == "123"; // let res = 123 == 123; true
=== 不仅会判断取值是否相等, 并且还会判断数据类型是否相等
只有取值和数据类型都相等, 才会返回true
- 什么是逻辑运算符?
名称?格式?返回值?特点?
1.1 逻辑与 &&
格式: 条件表达式A && 条件表达式B
返回值: true false
特点: 一假则假
1.2逻辑或 ||
格式: 条件表达式A || 条件表达式B
返回值: true false
特点: 一真则真
1.3逻辑非 !
格式: !条件表达式
返回值: true false
特点: 真变假, 假变真
- 逻辑运算符的优先级和结合性
逻辑运算符的结合性是左结合性(从左至右的运算)
在逻辑运算中&&的优先级高于|| - 逻辑运算符的注意点
2.1在逻辑运算中如果不是布尔类型, 那么会先转换成布尔类型, 再参与其它的运算
2.2在逻辑与运算中, 如果参与运算的不是布尔类型, 返回值有一个特点
格式: 条件A && 条件B
如果条件A不成立, 那么就返回条件A
如果条件A成立, 无论条件B是否成立, 都会返回条件B
2.3在逻辑或运算中, 如果参与运算的不是布尔类型, 返回值有一个特点
格式: 条件A || 条件B
如果条件A成立, 那么就返回条件A
如果条件A不成立, 无论条件B是否成立, 都会返回条件B
2.4在逻辑与运算中,有一个逻辑短路现象
格式: 条件A && 条件B
由于逻辑与运算的规则是一假则假, 所以只要条件A是假, 那么条件B就不会运算
2.5在逻辑或运算中,有一个逻辑短路现象
格式: 条件A || 条件B
由于逻辑或运算的规则是一真则真, 所以只要条件A是真, 那么条件B就不会运算
1.逗号运算符 ,
在JavaScript中逗号运算符一般用于简化代码
2.逗号运算符优先级和结合性
逗号运算符的结合性是左结合性(从左至右的运算)
逗号运算符的优先级是所有运算符中最低的 - 逗号运算符也是一个运算符, 所以也有运算符结果
逗号运算符的运算符结果就是最后一个表达式的结果
表达式1, 表达式2, 表达式3, ....;
a = 10, b = 5;
1.什么是三目运算符
三目运算符又称之为条件运算符
2.三目运算符格式
条件表达式 ? 结果A : 结果B;
在三目运算符中当条件为真的时候, 就会返回结果A
在三目运算符中当条件为假的时候, 就会返回结果B
3.三目运算符注意点
在三目运算符中?:不能单独出现, 要么一起出现, 要么一起不出现
if条件判断
- if第一种形式
if第一个形式的特点: 当条件表达式为真的时候就会执行{}中所有的代码, 并且只会执行一次
if(条件表达式){ 条件满足执行的语句; }
- if第二种形式
if第二种形式的特点
当条件满足就执行if后面{}中的代码
当条件不满足就执行else后面{}中的代码
并且两个{}只有一个会被执行, 并且只会被执行一次
if(条件表达式){ 条件成立执行的语句; }else{ 条件不成立执行的语句; }
3.if第三种形式:if第三种形式的特点
- 会从上至下的依次判断每一个条件表达式, 哪一个条件表达式满足, 就执行哪一个条件表达式后面{}中的代码
- 如果前面所有的条件表达式都不满足, 就会执行else后面{}中的代码,并且众多的大括号只有一个会被执行, 并且只会执行一次
if(条件表达式A){ 条件A满足执行的语句; }else if(条件表达式B){ 条件B满足执行的语句; } ... ... else{ 前面所有条件都不满足执行的语句; }
1.对于非布尔类型的数据, 会先转换成布尔类型再判断
2.对于==/===判断, 将常量写在前面
3.if/else if/else后面的大括号都可以省略, 但是省略之后只有紧随其后的语句受到控制
4.在JavaScript中分号(;)也是一条语句(空语句)
5.if选择结构可以嵌套使用
6.当if选择结构省略大括号时, else if/else会自动和距离最近没有被使用的if匹配
在企业开发中, 如果条件满足之后只有一句代码需要执行, 那么就使用三目运算符
在企业开发中, 如果条件满足之后有多句代码需要执行, 那么就使用选择结构
选择结构
1. switch:
switch特点:会从上至下的依次判断每一个case是否和()中表达式的结果相等, 如果相等就执行对应case后面的代码, 如果前面所有的case都不匹配, 那么就会执行default后面的代码并且所有的case和default只有一个会被执行, 并且只会被执行一次
1. case判断的是===, 而不是==
2. ()中可以是常量也可以是变量还可以是表达式
5. default不一定要写在最后, switch中的default无论放到什么位置, 都会等到所有case都不匹配再执行
6. 和if/else中的else一样, default也可以省略
switch(表达式){ case 表达式A: 语句A; break; case 表达式B: 语句B; break; ... ... default: 前面所有case都不匹配执行的代码; break; }
在企业开发中如果是对区间进行判断, 那么建议使用if
在企业开发中如果是对几个固定的值的判断, 那么建议使用switch
原则: 能用if就用if
2.if
if的格式
if(条件表达式){ 条件满足执行的语句; }
if的特点: 只有条件表达式为真才会执行后面{}中的代码 大括号中的代码只会被执行一次
3. while
while的特点: 只有条件表达式为真才会执行后面{}中的代码
大括号中的代码有可能会被执行多次
while的执行流程
3.1首先会判断条件表达式是否为真, 如果为真就执行后面{}中的代码
3.2执行完后面{}中的代码, 会再次判断条件表达式是否还为真
3.3如果条件表达式还为真, 那么会再次执行后面{}中的代码
3.4重复3.1~3.3, 直到条件表达式不为真为止
while的格式 while(条件表达式){ 条件满足执行的语句; }
1.什么是死循环?
条件表达式永远为真的循环结构我们称之为死循环
2.什么是循环体?循环结构后面的{}我们称之为循环体
3.和if一样对于非Boolean类型的值, 会先转换为Boolean类型再判断
4.和if一样while 后如果只有一条语句它可以省略大括号和if一样如果省略了后面的{}, 那么只有紧随其后的那条语句受到控制
5.和if一样, 不能在()后面写分号(;)
书写循环结构代码的规则
1.不管三七二一先写上循环结构的代码
2.将需要重复执行的代码拷贝到{}中
3.在()中指定循环结束的条件
1.while循环的格式
while(条件表达式){
需要重复执行的代码;
}
while循环的特点: 只有条件表达式为真, 才会执行循环体
2.dowhile循环的格式 do{ 需要重复执行的代码; }while(条件表达式); dowhile循环的特点: 无论条件表达式是否为真, 循环体都会被执行一次
1.在企业开发中大部分情况下while循环和dowhile循环是可以互换的
2.在企业开发中如果循环体中的代码无论如何都需要先执行一次, 那么建议使用dowhile循环
3.在企业开发中其它的情况都建议使用while循环
let和var
- 在JavaScript中定义变量有两种方式
ES6之前: var 变量名称;
ES6开始: let 变量名称; - 两种定义变量方式的区别
如果通过var定义变量, 可以重复定义同名的变量, 并且不会报错, 并且后定义的会覆盖先定义的,可以先使用后定义(预解析)
如果通过let定义变量, 不可以重复定义同名的变量,不可以先使用再定义, 因为浏览器不会对let定义的变量进行预解析 - 什么是全局变量
全局变量就是定义在{}外面的变量, 我们就称之为全局变量 - 什么是局部变量
局部变量就是定义在{}里面的变量, 我们就称之为局部变量 - 全局变量和局部变量的区别
如果是全局变量, 那么有效范围是从定义变量的那一行开始直到文件的末尾都可以使用
如果是局部变量, 那么有效范围是从定义变量的那一行开始直到大括号结束为止(只有在大括号中才能使用)
循环结构
- for循环的格式
for(初始化表达式;条件表达式;循环后增量表达式){ 3/6 需要重复执行的代码; }
- for循环的特点
for循环的特点和while循环的特点一样, 只有条件表达式为真, 才会执行循环体 - for循环的执行流程
3.1首先会执行初始化表达式, 并且只会执行一次
3.2判断条件表达式是否为真, 如果条件表达式为真, 就执行循环体
3.3执行完循环体就会执行循环后增量表达式
3.4重复3.2~3.3, 直到条件表达式不为真为止
在企业开发中由于for循环比while循环要灵活, 所以能用for循环就用for循环
1.在for循环中"初始化表达式""条件表达式""循环后增量表达式"都可以不写
如果不写就相当于while(1);
while (){ // while循环不能省略条件表达式 console.log("123"); } for(;;){ //for循环是可以省略条件表达式的, 默认就是真 console.log("123"); }
2.其它注意点和while循环一样
break 和 continue
- 什么是break关键字?
break关键字可以用于switch语句和循环结构中
在switch语句中break关键字的作用是立即结束当前的switch语句
在循环结构中break关键字的作用也是立即结束当前的循环结构 - break关键字的注意点
2.1break关键字后面不能编写任何的语句, 因为永远执行不到
2.2如果在循环嵌套的结构中, break结束的是当前所在的循环结构 - 什么是continue关键字?
continue关键字只能用于循环结构
在循环结构中continue关键字的作用是跳过本次循环, 进入下一次循环 - continue关键字的注意点
2.1continue关键字后面和break一样, 不能编写其它的代码, 因为执行够不到
2.2和break一样, 如果continue出现在循环嵌套结构中, 只会跳过当前所在的循环
数组
- 什么是数组?
数组就是专门用于存储一组数据的
注意点: 和我们前面学习的Number/String/Boolean/Null/undefined不同(基本数据类型)
数组(Array)不是基本数据类型, 是引用数据类型(对象类型) - 如何创建一个数组?
let 变量名称 = new Array(size); - 如何操作数据
3.1如何往数组中存储数据
变量名称[索引号] = 需要存储的数据;
3.2如何从数组中获取存储的数据
变量名称[索引号];
1.和其它编程语言不同, 如果数组对应的索引中没有存储数据, 默认存储的就是undefined,其它编程语言中默认保存的是垃圾数据或者0
2.和其它编程语言不同, JavaScript中访问了数组中不存在的索引不会报错, 会返回undefined,其它编程语言一旦超出索引范围就会报错或者返回脏数据
3.和其它编程语言不同, 当JavaScript中数组的存储空间不够时数组会自动扩容,其它编程语言中数组的大小是固定的
4.和其它编程语言不同, JavaScript的数组可以存储不同类型数据,在其它编程语言中数组只能存储相同类型数据(要么全部都是字符串, 要么全部都是数值等)
5.和其它编程语言不同, JavaScript中数组分配的存储空间不一定是连续的,其它语言数组分配的存储空间都是连续的, JavaScript数组是采用"哈希映射"方式分配存储空间;什么是哈希映射? 好比字典可以通过偏旁部首找到对应汉字, 我们可以通过索引找到对应空间
在浏览器中各大浏览器也对数组分配存储空间进行了优化
如果存储的都是相同类型的数据, 那么会尽量分配连续的存储空间
如果存储的不是相同的数据类型, 那么就不会分配连续的存储空间
- 创建数组的其它方式
- 通过构造函数创建数组
let 变量名称 = new Array(size); 创建一个指定大小数组 let 变量名称 = new Array(); 创建一个空数组 let 变量名称 = new Array(data1, data2, ...); 创建一个带数据的数组
- 通过字面量创建数组
let 变量名称 = []; 创建一个空数组 let 变量名称 = [data1, data2, ...]; 创建一个空数组
数组遍历
- 什么是数组的遍历
数组的遍历就是依次取出数组中存储的所有数据, 我们就称之为数组的遍历 - 什么是数组的解构赋值?
解构赋值是ES6中新增的一种赋值方式 - 数组解构赋值的注意点
2.1在数组的解构赋值中, 等号左边的格式必须和等号右边的格式一模一样, 才能完全解构
var a,b,c,d; let [a, b, c] = [1, 3, 5]; //a=1,b=3,c=5 let [a, b, c] = [1, 3, [2, 4]]; //a=1,b=3,c=[2,4] let [a, b, [c, d]] = [1, 3, [2, 4]]; //a=1,b=3,c=2,d=4 console.log("a = " + a); console.log("b = " + b); console.log("c = " + c); console.log("d = " + d);
运行截图
在数组的解构赋值中, 如果左边的个数和右边的个数不一样, 那么如果设置默认值会被覆盖
let [a, b = 666] = [1, 3, 5]; console.log("a = " + a); console.log("b = " + b);
- 在数组的解构赋值中, 还可以使用ES6中新增的扩展运算符来打包剩余的数据
- 在数组的解构赋值中, 如果使用了扩展运算符, 那么扩展运算符只能写在最后
- ES6中新增的扩展运算符: ...
let [a, b] = [1, 3, 5]; //a=1,b=3 // let [a, ...b] = [1, 3, 5]; //a=1,b=[3,5] console.log("a = " + a); console.log("b = " + b);
image.png
JavaScript-数组增删改查
1.splice函数
// 需求: 将索引为1的数据修改为d, 索引为2的修改为e (改)
// 参数1: 从什么位置开始
// 参数2: 需要替换多少个元素
// 参数3开始: 新的内容
let arr = ["a", "b", "c"]; arr.splice(1, 2, "d", "e"); //arr=["a","d","e"]
2.push函数
// 需求: 要求在数组最后添加一条数据 (增)
- push方法可以在数组的最后新增一条数据, 并且会将新增内容之后数组当前的长度返回给我们
let res = arr.push("d"); console.log(res);//4 console.log(arr);//arr=["a","b","c","d"]
- 数组的push方法可以接收1个或多个参数
let arr=["a","b","c"] arr.push("d", "e"); console.log(arr); //arr=["a","b","c","d","e"]
3.unshift函数
需求: 要求在数组最前面添加一条数据 (增)----arr[-1] = "m";
unshift方法和push方法一样, 会将新增内容之后当前数组的长度返回给我们
unshift方法和push方法一样, 可以接收1个或多个参数
let arr=["a","b","c","d"] arr.unshift("d") console.log(arr) //["d","a","b","c"]
pop函数
// 需求: 要求删除数组最后一条数据 (删)
数组的pop方法可以删除数组中的最后一条数据, 并且将删除的数据返回给我们
let arr=["a","b","c","d"] let res = arr.pop(); console.log(res);//"d" console.log(arr);//arr=["a","b","c"]
shift方法
需求: 要求删除数组最前面一条数据 (删)
数组的shift方法可以删除数组中的最前面一条数据, 并且将删除的数据返回给我们
let arr=["a","b","c","d"] let res = arr.shift(); console.log(res);//"a" console.log(arr);//arr=["b","c","d"]
splice函数
// 需求: 要求删除数组中索引为1的数据 (删)
// 以下代码的含义: 从索引为1的元素开始删除1条数据
// 参数1: 从什么位置开始
// 参数2: 需要删除多少个元素
arr.splice(1, 1);//arr=["a","c"]
如何清空数组
方法一:arr=[]
方法二:arr.length=0
方法三:arr.splice(0,arr.length)
如何将数组转换为字符串
let str = arr.toString();
let arr = [1, 2, 3, 4, 5]; let str = arr.toString(); console.log(str);//1,2,3,4,5 console.log(typeof str);//string
join函数
如何将数组转换成指定格式字符串
join方法默认情况下如果没有传递参数, 就是调用toString();
let str = arr.join(""); console.log(str);//12345 console.log(typeof str);//string
join方法如果传递了参数, 就会将传递的参数作为元素和元素的连接符号
let str = arr.join("+"); console.log(str);//1+2+3+4+5 console.log(typeof str);//string
concat函数
如何将两个数组拼接为一个数组?
注意点: 数组不能够使用加号进行拼接, 如果使用加号进行拼接会先转换成字符串再拼接
注意点: 扩展运算符在解构赋值中(等号的左边)表示将剩余的数据打包成一个新的数组
let arr1 = [1, 3, 5]; let arr2 = [2, 4, 6]; let res = arr1 + arr2; let res = arr1.concat(arr2); //[1,2,3,4,5,6]
扩展运算符在等号右边, 那么表示将数组中所有的数据解开, 放到所在的位置
注意点: 不会修改原有的数组, 会生成一个新的数组返回给我们
let res = [...arr1, ...arr2]; // let res = [1, 3, 5, 2, 4, 6];
reverse函数
如何对数组中的内容进行反转
注意点: 会修改原有的数组
let res = arr.reverse(); console.log(res); // 注意点: 会修改原有的数组 console.log(arr);
image.png
slice函数
如何截取数组中指定范围内容
slice方法是包头不包尾(包含起始位置, 不包含结束的位置)
let res = arr.slice(1, 3) console.log(res); console.log(arr);
image.png
indexOf方法
如何查找元素在数组中的位置
indexOf方法如果找到了指定的元素, 就会返回元素对应的位置
indexOf方法如果没有找到指定的元素, 就会返回-1
注意点: indexOf方法默认是从左至右的查找, 一旦找到就会立即停止查找
let arr = [1, 2, 3, 4, 5, 3]; let res1 = arr.indexOf(3); let res2 = arr.indexOf(6); console.log(res1); console.log(res2);
参数1: 需要查找的元素
参数2: 从什么位置开始查找
let arr = [1, 2, 3, 4, 5, 3]; let res = arr.indexOf(3, 4);//5
lastIndexOf()函数
注意点: lastIndexOf方法默认是从右至左的查找, 一旦找到就会立即停止查找
// let res = arr.lastIndexOf(3); // let res = arr.lastIndexOf(3, 4);
includes函数
何判断数组中是否包含某个元素
let res = arr.includes(4); //true
fill函数
// 数组的fill方法的作用: 设置数组中所有元素的值为指定的数据
函数
- 什么是函数?函数是专门用于封装代码的, 函数是一段可以随时被反复执行的代码块
- 函数格式
function 函数名称(形参列表){ 被封装的代码; }
- 不使用函数的弊端
3.1冗余代码太多
3.2需求变更, 需要修改很多的代码 - 使用函数的好处
4.1冗余代码变少了
4.2需求变更, 需要修改的代码变少了
函数定义步骤:
1.1书写函数的固定格式
1.2给函数起一个有意义的名称
- 为了提升代码的阅读性
- 函数名称也是标识符的一种, 所以也需要遵守标识符的命名规则和规范
1.3确定函数的形参列表 - 看看使用函数的时候是否需要传入一些辅助的数据
1.4将需要封装的代码拷贝到{}中
1.5确定函数的返回值 - 可以通过return 数据; 的格式, 将函数中的计算结果返回给函数的调用者
一个函数可以有形参也可以没有形参(零个或多个)
什么是形参? 定义函数时函数()中的变量我们就称之为形参
一个函数可以有返回值也可以没有返回值
函数没有通过return明确返回值, 默认返回undefined return;
break作用立即结束switch语句或者循环语句,return作用立即结束当前所在函数
调用函数时实参的个数和形参的个数可以不相同
什么是实参? 调用函数时传入的数据我们就称之为实参
JavaScript中的函数和数组一样, 都是引用数据类型(对象类型)
既然是一种数据类型, 所以也可以保存到一个变量,将一个函数保存到一个变量中,将来可以通过变量名称找到函数并执行函数
arguments参数
- 因为console.log();也是通过()来调用的, 所以log也是一个函数
- log函数的特点,可以接收1个或多个参数
- 为什么log函数可以接收1个或多个参数?内部的实现原理就用到了arguments
- arguments的作用:保存所有传递给函数的实参
1.扩展运算符在等号左边, 将剩余的数据打包到一个新的数组中
注意点: 只能写在最后
let [a, ...b] = [1, 3, 5]; a = 1; b = [3, 5];
2.扩展运算符在等号右边, 将数组中的数据解开
let arr1 = [1, 3, 5];
let arr2 = [2, 4, 6];
let arr = [...arr1, ...arr2]; let arr = [1, 3, 5, 2, 4, 6];
3.扩展运算符在函数的形参列表中的作用
将传递给函数的所有实参打包到一个数组中
注意点: 和在等号左边一样, 也只能写在形参列表的最后
function getSum(a, b) { // 在ES6之前可以通过逻辑运算符来给形参指定默认值 // 格式: 条件A || 条件B // 如果条件A成立, 那么就返回条件A // 如果条件A不成立, 无论条件B是否成立, 都会返回条件B a = a || "111"; b = b || "222"; console.log(a, b); } getSum(123, "abc");
// 从ES6开始, 可以直接在形参后面通过=指定默认值
注意点: ES6开始的默认值还可以从其它的函数中获取
function getSum(a = "111", b = getDefault()) { console.log(a, b); } getSum(); function getDefault() { return "单佳兰" } let say = function () { console.log("hello world"); } say(); let fn = say; fn();
// 将函数作为其他函数的参数
function test(fn) { // let fn = say; fn(); } test(say); */ // 将函数作为其他函数的返回值 function test() { // 注意点: 在其它编程语言中函数是不可以嵌套定义的, // 但是在JavaScript中函数是可以嵌套定义的 let say = function () { console.log("hello world"); } return say; } let fn = test(); // let fn = say;
匿名函数
- 什么是匿名函数?匿名函数就是没有名称的函数
- 匿名函数的注意点:匿名函数不能够只定义不使用
- 匿名函数的应用场景
3.1作为其他函数的参数
test(function () { console.log("hello world"); });
3.2作为其他函数的返回值
function test() { return function () { console.log("hello lnj"); }; } let fn = test(); // let fn = say; fn();
3.3作为一个立即执行的函数
(function () { console.log("hello it666"); })();
注意点: 如果想让匿名函数立即执行, 那么必须使用()将函数的定义包裹起来才可以
箭头函数
- 什么是箭头函数?
箭头函数是ES6中新增的一种定义函数的格式,目的: 就是为了简化定义函数的代码 - 在ES6之前如何定义函数
function 函数名称(形参列表){ 需要封装的代码; } let 函数名称 = function(形参列表){ 需要封装的代码; }
- 从ES6开始如何定义函数
let 函数名称 = (形参列表) =>{ 需要封装的代码; }
- 箭头函数的注意点
4.1在箭头函数中如果只有一个形参, 那么()可以省略
4.2在箭头函数中如果{}中只有一句代码, 那么{}也可以省略
let say = name => console.log("hello " + name); say("shanjialan");
递归函数
- 什么是递归函数?
递归函数就是在函数中自己调用自己, 我们就称之为递归函数,递归函数在一定程度上可以实现循环的功能 - 递归函数的注意点
每次调用递归函数都会开辟一块新的存储空间, 所以性能不是很好
1.在JavaScript中定义变量有两种方式
ES6之前: var 变量名称;
ES6开始: let 变量名称;
2.两种定义变量方式的区别
// 2.1是否能够定义同名变量
1. 通过var定义变量,可以重复定义同名的变量,并且后定义的会覆盖先定义的
2.2如果通过let定义变量, "相同作用域内"不可以重复定义同名的变量
2.3通过var定义变量, 可以先使用后定义(预解析)
2.4通过let定义变量, 不可以先使用再定义(不会预解析)
// 2.3是否能被{}限制作用域
2.5无论是var还是let定义在{}外面都是全局变量
2.6将var定义的变量放到一个单独的{}里面, 还是一个全局变量
1.在JavaScript中{}外面的作用域, 我们称之为全局作用域
2.在JavaScript中函数后面{}中的的作用域, 我们称之为"局部作用域"
3.在ES6中只要{}没有和函数结合在一起, 那么应该"块级作用域"
4.块级作用域和局部作用域区别
4.1在块级作用域中通过var定义的变量是全局变量
4.2在局部作用域中通过var定义的变量是局部变量
5.无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量
// 注意点: 只要出现了let, 在相同的作用域内, 就不能出现同名的变量
// 注意点: 在不同的作用域范围内, 是可以出现同名的变量的
注意点: 初学者在研究"作用域链"的时候最好将ES6之前和ES6分开研究:
1.需要明确:
1.ES6之前定义变量通过var
2.ES6之前没有块级作用域, 只有全局作用域和局部作用域
3.ES6之前函数大括号外的都是全局作用域
4.ES6之前函数大括号中的都是局部作用域
2.ES6之前作用域链
1.1.全局作用域我们又称之为0级作用域
2.2.定义函数开启的作用域就是1级/2级/3级/...作用域
2.3.JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
0 ---> 1 ----> 2 ----> 3 ----> 4
2.4.除0级作用域以外, 当前作用域级别等于上一级+1
3.变量在作用域链查找规则
3.1先在当前找, 找到就使用当前作用域找到的
3.2如果当前作用域中没有找到, 就去上一级作用域中查找
3.3以此类推直到0级为止, 如果0级作用域还没找到, 就报错
注意点: 初学者在研究作用域的时候最好将ES6之前和ES6分开研究
1.需要明确:
1.ES6定义变量通过let
2.ES6除了全局作用域、局部作用域以外, 还新增了块级作用域
3.ES6虽然新增了块级作用域, 但是通过let定义变量并无差异(都是局部变量)
2.ES6作用域链
1.1.全局作用域我们又称之为0级作用域
2.2.定义函数或者代码块都会开启的作用域就是1级/2级/3级/...作用域
2.3.JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
0 ---> 1 ----> 2 ----> 3 ----> 4
2.4.除0级作用域以外, 当前作用域级别等于上一级+1
3.变量在作用域链查找规则
3.1先在当前找, 找到就使用当前作用域找到的
3.2如果当前作用域中没有找到, 就去上一级作用域中查找
3.3以此类推直到0级为止, 如果0级作用域还没找到, 就报错
预解析
- 什么是预解析?
浏览器在执行JS代码的时候会分成两部分操作:预解析以及逐行执行代码
也就是说浏览器不会直接执行代码, 而是加工处理之后再执行,
这个加工处理的过程, 我们就称之为预解析 - 预解析规则
2.1将变量声明和函数声明提升到当前作用域最前面
2.2将剩余代码按照书写顺序依次放到后面 - 注意点
通过let定义的变量不会被提升(不会被预解析)
对象和类
- JavaScript中提供了一个默认的类Object, 我们可以通过这个类来创建对象
- 由于我们是使用系统默认的类创建的对象, 所以系统不知道我们想要什么属性和行为, 所以我们必须手动的添加我们想要的属性和行为
第一种方式
let obj = new Object(); obj.name = "shanjialan"; obj.age = 20; obj.say = function () { console.log("hello world"); }
创建对象的第二种方式
let obj = {}; // let obj = new Object();
创建对象的第三种方式
注意点: 属性名称和取值之间用冒号隔开, 属性和属性之间用逗号隔开
let obj = { name: "lnj", age: 33, say: function () { console.log("hello world"); } };
JavaScript-函数和方法区别
- 什么是函数?
函数就是没有和其它的类显示的绑定在一起的, 我们就称之为函数 - 什么是方法?
方法就是显示的和其它的类绑定在一起的, 我们就称之为方法 - 函数和方法的区别
3.1函数可以直接调用, 但是方法不能直接调用, 只能通过对象来调用
3.2函数内部的this输出的是window, 方法内部的this输出的是当前调用的那个对象 - 无论是函数还是方法, 内部都有一个叫做this的东东
this是什么? 谁调用了当前的函数或者方法, 那么当前的this就是谁
工厂函数
- 什么是工厂函数?
工厂函数就是专门用于创建对象的函数, 我们就称之为工厂函数
function createPerson(myName, myAge) { let obj = new Object(); obj.name = myName; obj.age = myAge; obj.say = function () { console.log("hello world"); } return obj; } let obj1 = createPerson("lnj", 34); let obj2 = createPerson("zs", 44); console.log(obj1); console.log(obj2);
image.png
构造函数
- 什么是构造函数
构造函数和工厂函数一样, 都是专门用于创建对象的
构造函数本质上是工厂函数的简写 - 构造函数和工厂函数的区别
2.1构造函数的函数名称首字母必须大写
2.2构造函数只能够通过new来调用
function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = function () { console.log("hello world"); } // return this; // 系统自动添加的 }
- 当我们new Person("zs", 44);系统做了什么事情
1.1会在构造函数中自动创建一个对象
1.2会自动将刚才创建的对象赋值给this
1.3会在构造函数的最后自动添加return this;
由于两个对象中的say方法的实现都是一样的, 但是保存到了不同的存储空间中
所以有性能问题
通过三个等号来判断两个函数名称, 表示判断两个函数是否都存储在同一块内存中
console.log(obj1.say === obj2.say); // false
优化【上】
- 当前这种方式解决之后存在的弊端
1.1阅读性降低了
1.2污染了全局的命名空间
function mySay() { console.log("hello world"); } function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = mySay; // return this; // 系统自动添加的 } console.log(obj1.say === obj2.say); // true
优化【中】
优化[上]虽然是TRUE但是可读性不好,污染全局空间,因此把方法存放在一个对象里,由于test函数都是属于同一个对象, 所以返回true
let fns = { mySay: function () { console.log("hello world"); } } function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = fns.mySay; // return this; // 系统自动添加的 } let obj1 = new Person("sjl", 20); let obj2 = new Person("zs", 44); console.log(obj1.say === obj2.say); // true
优化【下】
Person.prototype = { say: function () { console.log("hello world"); } }
- prototype特点
1.1存储在prototype中的方法可以被对应构造函数创建出来的所有对象共享
1.2prototype中除了可以存储方法以外, 还可以存储属性
1.3prototype如果出现了和构造函数中同名的属性或者方法, 对象在访问的时候, 访问到的是构造函中的数据 - prototype应用场景
prototype中一般情况下用于存储所有对象都相同的一些属性以及方法
如果是对象特有的属性或者方法, 我们会存储到构造函数中
function Student(name,age) { this.name=name; this.age = age; } Student.prototype={ say:function (name,age) { console.log(name); }, currentType:"person" } let student=new Student("hanjialan",19); student.say("shanshun"); let student2=new Student("shuanahus",30); console.log(student.currentType === student.currentType);
function Person(myName, myAge) { this.name = myName; this.age = myAge; this.currentType = "构造函数中的type"; this.say = function () { console.log("构造函数中的say"); } } Person.prototype = { currentType: "人", say: function () { console.log("hello world"); } } let obj1 = new Person("sjl", 20); obj1.say();//构造函数中的say console.log(obj1.currentType);//构造函数中的type let obj2 = new Person("zs", 44); obj2.say();//构造函数中的say console.log(obj2.currentType);//构造函数中的type
- 每个"构造函数"中都有一个默认的属性, 叫做prototype
prototype属性保存着一个对象, 这个对象我们称之为"原型对象" - 每个"原型对象"中都有一个默认的属性, 叫做constructor
constructor指向当前原型对象对应的那个"构造函数" - 通过构造函数创建出来的对象我们称之为"实例对象"
每个"实例对象"中都有一个默认的属性, 叫做proto
proto指向创建它的那个构造函数的"原型对象"
image.png
Function函数
- JavaScript中函数是引用类型(对象类型), 既然是对象,
所以也是通过构造函数创建出来的,"所有函数"都是通过Function构造函数创建出来的对象 - JavaScript中只要是"函数"就有prototype属性
"Function函数"的prototype属性指向"Function原型对象" - JavaScript中只要"原型对象"就有constructor属性
"Function原型对象"的constructor指向它对应的构造函数 - Person构造函数是Function构造函数的实例对象, 所以也有proto属性,Person构造函数的proto属性指向"Function原型对象"
hanshu
Object函数
- JavaScript函数是引用类型(对象类型), 所以Function函数也是对象
2."Function构造函数"也是一个对象, 所以也有proto属性
"Function构造函数"proto属性指向"Function原型对象" - JavaScript中还有一个系统提供的构造函数叫做Object
只要是函数都是"Function构造函数"的实例对象
4.只要是对象就有proto属性, 所以"Object构造函数"也有proto属性
"Object构造函数"的proto属性指向创建它那个构造函数的"原型对象"
5.只要是构造函数都有一个默认的属性, 叫做prototype
prototype属性保存着一个对象, 这个对象我们称之为"原型对象"
6.只要是原型对象都有一个默认的属性, 叫做constructor
constructor指向当前原型对象对应的那个"构造函数"
console.log(Function.__proto__); console.log(Function.__proto__ === Function.prototype); // true console.log(Object); console.log(Object.__proto__); console.log(Object.__proto__ === Function.prototype); // true console.log(Object.prototype); console.log(Object.prototype.constructor); console.log(Object.prototype.constructor === Object); // true console.log(Object.prototype.__proto__); // null
图示
1.所有的构造函数都有一个prototype属性, 所有prototype属性都指向自己的原型对象
2,所有的原型对象都有一个constructor属性, 所有constructor属性都指向自己的构造函数
3.所有函数都是Function构造函数的实例对象
4.所有函数都是对象, 包括Function构造函数
5.所有对象都有proto属性
6.普通对象的proto属性指向创建它的那个构造函数对应的"原型对象"
7.所有对象的proto属性最终都会指向"Object原型对象"
8."Object原型对象"的proto属性指向NULL
【总结】
- 所有对象都有一个proto属性;
- Function对象是所有函数的祖先函数;
- 每个构造函数都有一个prototype属性,指向该函数的“原型对象”
- 每个构造函数的原型对象都有一个constructor属性,指向其构造函数;
- 所有函数都是对象;
【原型链】
- 对象中proto组成的链条我们称之为原型链
- 对象在查找属性和方法的时候, 会先在当前对象查找
如果当前对象中找不到想要的, 会依次去上一级原型对象中查找
如果找到Object原型对象都没有找到, 就会报错
Person.prototype = { // 注意点: 为了不破坏原有的关系, 在给prototype赋值的时候, 需要在自定义的对象中手动的添加constructor属性, 手动的指定需要指向谁 constructor: Person, currentType: "人", say: function () { console.log("hello world"); } }
注意点
使用优化【下】的方法,即使用构造函数名.prototype,会破坏原型链的关系,因为这相当于重新指定了构造函数的prototype覆盖掉,会导致之前的原型链关系被破坏,因此使用prototype属性时一定要指定constructor来确保原型链的一致性。
原型链是指通过__ proto __ 属性来串起来的链,当访问一个属性或者方法时,自己有的用自己的,自己没有的从原型链上去找,原型链上有用原型链上的,原型链没有则报错
【注意点】:
操作的是私有属性(局部变量)
注意点:
在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
由于私有属性的本质就是一个局部变量, 并不是真正的属性, 所以如果通过 对象.xxx的方式是找不到私有属性的, 所以会给当前对象新增一个不存在的属性
终结版
终结版.png
function Person(myName, myAge) { this.name = myName; this.age = myAge; } Person.prototype = { say: function() { console.log("小单,加油!!每天都是美好的一天,小豌豆,别放弃~!"); } } let yiya = new Person("shanjialan", 19); console.log(yiya.__proto__ === Person.prototype); console.log(Person.__proto__ === Function.prototype); console.log(Function.prototype.__proto__ === Person.prototype.__proto__); console.log(Function.__proto__ === Function.prototype); console.log(Object.__proto__ === Function.prototype); console.log(Object.prototype.__proto__ === null); console.log(Object.prototype.constructor === Object); console.log(Function.prototype.__proto__ === Person.prototype.__proto__); console.log(Function.prototype.__proto__ === Object.prototype); console.log(Person.prototype.__proto__ === Object.prototype); console.log(Function.prototype.__proto__); console.log(Person.prototype.__proto__);
运行结果截图.png
【JavaScript-属性注意点】
注意点: 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
function Person(myName, myAge) { this.name = myName; this.age = myAge; } Person.prototype = { constructor: Person, currentType: "人", say: function () { console.log("hello world"); } } let obj = new Person("lnj", 34); // console.log(obj.currentType); // "人" // console.log(obj.__proto__.currentType); // "人" // 注意点: 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性 obj.currentType = "新设置的值"; console.log(obj.currentType); // 新设置的值 console.log(obj.__proto__.currentType); // "人"
封装性
- 局部变量和局部函数
无论是ES6之前还是ES6, 只要定义一个函数就会开启一个新的作用域,只要在这个新的作用域中, 通过let/var定义的变量就是局部变量,只要在这个新的作用域中, 定义的函数就是局部函数 - 什么是对象的私有变量和函数
- 默认情况下对象中的属性和方法都是公有的, 只要拿到对象就能操作对象的属性和方法
外界不能直接访问的变量和函数就是私有变量和私有函数 - 构造函数的本质也是一个函数, 所以也会开启一个新的作用域, 所以在构造函数中定义的变量和函数就是私有和函数
- 什么是封装?
封装性就是隐藏实现细节,仅对外公开接口 - 为什么要封装?
4.1不封装的缺点:当一个类把自己的成员变量暴露给外部的时候,那么该类就失去对属性的管理权,别人可以任意的修改你的属性
4.2封装就是将数据隐藏起来,只能用此类的方法才可以读取或者设置数据,不可被外部任意修改. 封装是面向对象设计本质(将变化隔离)。这样降低了数据被误用的可能 (提高安全性和灵活性)
// 由于构造函数也是一个函数, 所以也会开启一个新的作用域
// 所以在构造函数中通过var/let定义的变量也是局部变量
// 所以在构造函数中定义的函数也是局部函数
// 结论: 默认情况下对象的属性和方法都是公开的, 只要拿到对象就可以操作对象的属性和方法
function Person() { this.name = "lnj"; // this.age = 34; let age = 34; this.setAge = function (myAge) { if(myAge >= 0){ age = myAge; } } this.getAge = function () { return age; } this.say = function () { console.log("hello world"); }
- 操作的是私有属性(局部变量)
- 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
obj.age = -3; console.log(obj.age);
- 由于私有属性的本质就是一个局部变量, 并不是真正的属性, 所以如果通过 对象.xxx的方式是找不到私有属性的, 所以会给当前对象新增一个不存在的属性
- 操作的是公有属性则直接修改
属性和方法分类
- 在JavaScript中属性和方法分类两类
- 1.1实例属性/实例方法
在企业开发中通过实例对象访问的属性, 我们就称之为实例属性
在企业开发中通过实例对象调用的方法, 我们就称之为实例方法 - 1.2静态属性/静态方法
在企业开发中通过构造函数访问的属性, 我们就称之为静态属性
在企业开发中通过构造函数调用的方法, 我们就称之为静态方法
在企业开发中如果构造函数和构造函数之间的关系是is a关系, 那么就可以使用继承来优化代码, 来减少代码的冗余度
bind-call-apply
2.这三个方法的作用是什么?
三个方法都是用于修改函数或者方法中的this的
2.1.bind方法作用
修改函数或者方法中的this为指定的对象, 并且会返回一个修改之后的新函数给我们
注意点: bind方法除了可以修改this以外, 还可以传递参数, 只不过参数必须写在this对象的后面,逗号隔开参数
let obj = { name: "shanjialan", age: 10 } function person(name, age) { console.log(age); console.log(name); console.log(this); } let fn3 = person.apply(obj, ["shanjialan", 10]); let fn2 = person.bind(obj, "shanjialan", 10); let fn1 = person.bind(obj, "shanjialan", 10); fn1(); fn2();
2.2.call方法作用
修改函数或者方法中的this为指定的对象, 并且会立即调用修改之后的函数
注意点: call方法除了可以修改this以外, 还可以传递参数, 只不过参数必须写在this对象的后面
2.3.apply方法作用
修改函数或者方法中的this为指定的对象, 并且会立即调用修改之后的函数
注意点: apply方法除了可以修改this以外, 还可以传递参数, 只不过参数必须通过数组的方式传递
JavaScript-基本数据类型和基本包装类型
- 有哪些基本数据类型?
字符串类型 / 数值类型 / 布尔类型 / 空类型 / 未定义类型 - 通过字面量创建的基本数据类型的数据都是常量
- 常量的特点和注意点
常量是不能被修改的
每次修改或者拼接都是生成一个新的 - 基本类型特点
没有属性和方法 - 对象类型的特点
有属性和方法 - 以前之所以能够访问基本数据类型的属性和方法, 是因为
在运行的时候系统自动将基本数据类型包装成了对象类型
String() / Number() / Boolean()
JavaScript-三大对象
JavaScript中提供三种自带的对象, 分别是"本地对象"/"内置对象"/"宿主对象"
什么是宿主?
宿主就是指JavaScript运行环境, js可以在浏览器中运行, 也可以在服务器上运行(nodejs);
- 本地对象
与宿主无关,无论在浏览器还是服务器中都有的对象
就是ECMAScript标准中定义的类(构造函数)。
在使用过程中需要我们手动new创建
例如:Boolean、Number、String、Array、Function、Object、Date、RegExp等。 - 内置对象
与宿主无关,无论在浏览器还是服务器中都有的对象
ECMAScript已经帮我们创建好的对象。
在使用过程中无需我们手动new创建
例如:Global、Math、JSON - 宿主对象
对于嵌入到网页中的JS来说,其宿主对象就是浏览器, 所以宿主对象就是浏览器提供的对象
包含: Window和Document等。
所有的DOM和BOM对象都属于宿主对象。
Math内置对象
- Math.floor() 向下取整
- Math.ceil() 向上取整
- Math.round() 四舍五入
- Math.abs() 绝对值
- Math.random() 生成随机数
需求: 要求生成一个1~10的随机数
function getRandomIntInclusive(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值 }
DOM
- 什么是window?
window:是一个全局对象, 代表浏览器中一个打开的窗口, 每个窗口都是一个window对象 - 什么是document?
document是window的一个属性, 这个属性是一个对象
document: 代表当前窗口中的整个网页,
document对象保存了网页上所有的内容, 通过document对象就可以操作网页上的内容 - 什么是DOM?
DOM 定义了访问和操作 HTML文档(网页)的标准方法
DOM全称: Document Object Model, 即文档模型对象
所以学习DOM就是学习如何通过document对象操作网页上的内容
BOM
- 什么是BOM?
DOM就是一套操作HTML标签的API(接口/方法/属性)
BOM就是一套操作浏览器的API(接口/方法/属性) - BOM中常见的对象
window
: 代表整个浏览器窗口
注意: window是BOM中的一个对象, 并且是一个顶级的对象(全局)Navigator
: 代表当前浏览器的信息, 通过Navigator我们就能判断用户当前是什么浏览器Location
: 代表浏览器地址栏的信息, 通过Location我们就能设置或者获取当前地址信息History
: 代表浏览器的历史信息, 通过History来实现刷新/上一步/下一步
注意点: 出于隐私考虑, 我们并不能拿到用户所有的历史记录, 只能拿到当前的历史记录Screen
: 代表用户的屏幕信息
Navigator
Navigator: 代表当前浏览器的信息, 通过Navigator我们就能判断用户当前是什么浏览器
// console.log(window.navigator); var agent = window.navigator.userAgent; if(/chrome/i.test(agent)){ alert("当前是谷歌浏览器"); }else if(/firefox/i.test(agent)){ alert("当前是火狐浏览器"); }else if(/msie/i.test(agent)){ alert("当前是低级IE浏览器"); }else if("ActiveXObject" in window){ alert("当前是高级IE浏览器"); }
Location
Location: 代表浏览器地址栏的信息, 通过Location我们就能设置或者获取当前地址信息
let oBtn1 = document.querySelector("#btn1"); let oBtn2 = document.querySelector("#btn2"); let oBtn3 = document.querySelector("#btn3"); let oBtn4 = document.querySelector("#btn4"); // 获取当前地址栏的地址 oBtn1.onclick = function(){ console.log(window.location.href); } // 设置当前地址栏的地址 oBtn2.onclick = function(){ window.location.href = "http://www.it666.com"; } // 重新加载界面 oBtn3.onclick = function(){ window.location.reload(); } oBtn4.onclick = function(){ window.location.reload(true); }
History
History: 代表浏览器的历史信息, 通过History来实现刷新/前进/后退
注意点: 出于隐私考虑, 我们并不能拿到用户所有的历史记录, 只能拿到当前的历史记录
let oBtn1 = document.querySelector("#btn1"); let oBtn2 = document.querySelector("#btn2"); // 前进 注意点: 只有当前访问过其它的界面, 才能通过forward/go方法前进 如果给go方法传递1, 就代表前进1个界面, 传递2就代表进行2个界面 oBtn1.onclick = function () { window.history.forward(); window.history.go(1); } // 刷新 // 如果给go方法传递0, 就代表刷新 oBtn2.onclick = function () { window.history.go(0); } // 后退 /* 注意点: 只有当前访问过其它的界面, back/go方法后退 如果给go方法传递-1, 就代表后退1个界面, 传递-2就代表后退2个界面 */ oBtn1.onclick = function () { // window.history.back(); window.history.go(-1); }