学习一门编程语言的基本步骤
(1) 了解背景知识:历史、现状、特点、应用场景
(2) 搭建开发环境,编写hello world
(3) 常量和变量
(4) 数据类型
(5) 运算符
(6) 逻辑结构
(7) 通用小程序
(8) 函数和对象
(9) 第三方框架、库
(10) 实用的项目
程序员必做50题
===============================================================================day01
一、了解背景知识:历史、现状、特点、应用场景
1.JS概述
(1)历史
1995年,JS最早出现在Netscape 的浏览器中
1996年,IE3中也出现JS,成为JScript
1997年,ECMA组织,指定标准 ECMAScript
2009年,遵循CommonJS 规范,开始向服务器端发展
(2)现状
既可以运行在客户端浏览器,也可以运行在服务器端
(3)特点
跨平台,支持所有的操作系统
解释型语言,编译一行执行一行
基于对象
弱类型语言
(4)应用场景
制作浏览器端的交互效果
创建web服务器,操作数据库等
二、搭建开发环境,编写hello world
2.JS的开发环境
(1)浏览器端自带的JS 解释器
(2)服务器端NodeJS 解释器
https//nodejs.ong nodejs下载地址
node -v 查看当前安装的nodejs版本
(3)执行JS代码
浏览器
创建01.js和01.html两个文件
在01.html中引入01.js文件
DodeJS
node C/xzmpp/…/01.js 回车
3.JS语法规范
(1)区分大小写
(2)每行代码结尾的分号可加可不加,建议都加
(3)分为单行注释(//…)和多行注释(/…/)
(4)由表达式,关键字,运算符组成
(5)所有字符串,必须放在引号中,单双引号均可。如果字符串内容中由包含引号,将内外单双引号区分开。
(6)语句中所有的符号都是英文
三、常量和变量
4.变量——一个允许被反复使用,并且可以变化的数据,
用于存储数据的容器
x=1
(1)声明变量
var x=1; variable
使用关键字var声明了一个变量,名称叫x,存储的值时1
(2)变量的命名规则
变量名称可以使用字母、数字、下划线、美元符号,其中不能以数字开 头。
不能使用关键字和保留字作为变量名称,例如:var、if、break…
user_name userPwd isLogin cityName
(3)变量的注意
var z
变量只是声明未赋值,此时的值是undefined
可以为变量多次赋值,并且赋不同类型的值
(4)一次性声明多个变量
var a=1,b=2,c;
多个变量之间用逗号隔开
练习:声明语文、数学、和总成绩三个变量;总成绩为空,然然后把语文和数学的和赋值给总成绩;打印三个变量
5.常量constant
一旦声明不能再重新赋值
const PI=3.14;
四、数据类型
6.数据类型
分为原始类型和引用类型
原始类型分为数值型、字符串型、布尔型、未定义型(undefined)、空(null)
(1)数值型
分为整型和浮点型
整型在内存中占4个字节,浮点型占8个字节
整型
8进制0 1 … 7 0,以0开头,例如010->8
16进制0 1 …9 a b c d e f 0,
af代表1015,以0x开头,例如0xc->12,字母不区分大小写
0xff->255
浮点型
分为普通小数和指数型小数
3.14 3.14e3 ->3140 3.14e-2->0.0314
(2)字符串型
任意的数据被引号包含就是字符串型,不区分单双引号
typeof 数据 检测数据类型
查看任意一个字符的Unicode码
‘a’.charCodeAt() //97
练习:查看自己名字的Unicode码
(3)布尔型
在程序中表示真或者假
true/false
常用于存储一些是否的结果,例如是否注册,是否登录,是否为会员
ixLogin=true isVip=false
(4)未定义型
声明了变量未赋值,结果就是undefined
(5)空
只有一个值null,必须小写,应用于引用类型数据中
2.数据类型转换
(1)隐式转换
数字+字符串 数字被转换成了字符串类型
2+‘3’//‘23’
数字+布尔值 布尔值被转成了数字 true->1 false->0
2+true //3
2+false //2
字符串+布尔值 布尔值被转成字符串
‘b’+true //btrue
JS中加好(+)的作用
执行加法运算
执行字符串拼接
练习:查看一下程序的执行结果
var a=2,b=true,c=‘tedu’;
console.log(a+b+c); //‘3tedu’
console.log(b+c+a); //‘truetedu2’
console.log(c+a+b); //‘tedu2true’
使用- * /执行运算
将运算符两端的非数字转为数字,最终运算结果还是数字,在转数字的过程中自动调用Number。
(2)强制转换
① 将任意类型转为数值型
Number(数据)
如果要转的字符串中含有非数字,则返回NaN(Not a Number)
undefined ->NaN
null->0
②将任意数据转为整型
parseInt(数据)
parseInt(‘2.3c’) //2
如果字符串以非数字开头,则返回NaN,布尔型,ndefined,null返回NaN
③将任意数据转为浮点型
parseFloat(数据)
parseFloat(‘2.3c’) //2.3
和parseInt用法几乎一致,只是返回时浮点型
④将数值型转为字符串型
toString()
var num=12
num.toString() //‘12’
num.toString(16) //c
转换为字符串的同时,可以设置要转换的进制
五、运算符
3. 运算符
由运算符连接的操作数据,所组成的形式成为表达式
(1)算数运算符
+ - * / % ++ –
% 取余
++ 自增,在原来的基础之上加1
– 自减,在原来的基础之上减1
console.log(num++);先打印num的值,再执行自增
console.log++ (num);先执行自增,然后打印num的值
练习:
var a=2;
console.log(a++ + ++a); 2 4 =6
var b=7;
console.log(–b + b–); 6 6 =12
先取出a的值——2,然后再执行自增,内存存储的是3,。先让a的值执行自增,内存存的值是4,再取出a的值——4
(2)比较运算符
> < >= <= (等于) != =(全等于) !(全不等于)
返回一个布尔型的值
== 只是比较值是否相同
=== 不仅比较值,还会比较类型是否相同
3>‘10’ //false
数字和字符串比较,字符串会转成数字
‘3’>‘10’ //true
两个字符串比较,比较首字符的Unicode码
‘3’->51 ‘1’->49
3>‘10a’
'10a’隐式转换为NaN,NaN和任何值比较(> < == === >= <=)都返回false
NaNNaN false
(3)逻辑运算符
||或者 &&并且 !非
返回一个布尔型的结果
||关联的两个条件只需要满足其一,结果就是true,否则false
&& 关联的两个条件都满足,结果是true,否则false
!取反 !true->false !false->true
练习:声明两个变量保存用户名和密码,如果用户名是‘root’,并且密码是‘123456’,打印true,否则打印false
var uname=‘root’;
var upws=123456
console.log(uname==‘root’ && upws==123456);
练习:声明一个变量保存年龄,如果年龄大于90岁,或者小于3岁,打印true,否则打印false
var age=93;
console.log(age>=90 || age <=3);
逻辑短路
&&当第一个条件为false的时候,就不许哟啊再执行第二个条件
||当第一个条件为true的时候,就不需要再执行第二个条件
练习:以下程序运行是否报错
var num=3;
num>5 && console.log(a); //不报错
num<1 || console.log(a); // 报错
(4)位运算符
在执行位运算符的时候,会把数字转成二进制进行运算。
1 10 11 100 101 110 111 1000 1001 1010
2 22 22*2
85=64+16+4+1
1000000+10000+100+1
1010101
按位与(&) 上下两位都是1,结果是1,否则是0.
按位或(|) 上下两位含有1,结果是1,否则是0
按位异或(^)上下两位不同为1,相同是0
按位右移(>>)删除二进制的最后一位
按位左移(<<)
(5)赋值运算符
= += -= *= /= %= 在原来基础之上再操作
课后任务
(1)复习今天内容,整理思维导图
(2)声明一个变量保存年份,判断这个年份是否为闰年,如果是打印‘闰年’——逻辑短路
闰年:4年一闰(能被4整除),判断是否是闰年。条件为:能被4整除,且不能被100整除;或者能被400整除
var year=1996;
conlose.log(闰年)
(3)预习if ifeles、swith-case语句
============================================================================day03
1.三目运算符
由三组操作数据或表达式组成的形式
条件表达式 ? 表达式1 : 表达式2
如果条件表达式为true,执行表达式1
如果条件表达式为false,执行表达式2
练习:声明变量保存用户名和密码,如果用户名是root,并且密码是123456,打印登录成功,否则打印登录失败
六、逻辑结构
2.浏览器端函数
alert()弹出警示框
prompt()弹出提示框(输入框),需要使用变量保存输入的值;值的类型是字符串型
练习:两次弹出提示框,使用变量保存后,计算两个数字相加的和,并将使用警示框弹出。
3.流程控制
程序分为顺序执行、选择执行、循环执行
程序 = 数据 + 算法
(1)if语句
满30减15
if(条件表达式){
语句1;
}
语句2;
如果if后的大括号中只有一行语句,可以省略大括号。
(2)if-else 语句 (如果 否则)
if(条件表达式){
语句1; //条件表达为true
}else{
语句2;
}
练习:使用弹出提示框分别输入商品的单价和数量,如果商品的总价满500,则打八折;加入当前卡内的余额为600,如果足够支付总价,打印支付成功,否则打印余额不足。
(3)if-else嵌套
if(条件表达式1){
语句1;
}else if(条件表达式2){
语句2;
}else…if(条件表达式n){
语句n;
}elae{
语句n+1;//以上所有的条件表达式都是false
}
(4)switch-case语句
switch(表达式){
case 值1: //如果表达式的值为值1
语句1;
break;
case 值2:
语句2;
break;
…
case 值n:
语句n;
break;
default:
语句n+1; //以上所有的结果都是false
}
注意:在case中表达式和值在比较的时候使用的是全等于(=),要求值和类型都要满足结果才是true
break,跳出,结束思维语句
对比if=else嵌套和switch-case语句
相同点:两者都可以用于多项分支语句
不同点:if-else介意判断相等或者不等的情况,使用范围更广;
switch-case只能用于全等于(=)的情况,结构上更为清晰合理,执行效率更高•
4.循环执行
循环:就是一边又一遍执行相同或者相似的代码
循环的两个要素:
循环的条件:控制循环的次数
循环体:重复执行的相同或者相似的代码
(1) while循环
while(循环条件){ //结果是布尔型的值
循环体
}
课后任务 (1)复习今天内容,整理思维导图
(2)练习
使用switch-case来根据成绩判断标准
使用while循环打印11~20之间的所有整数 使用while循环打印1~100之间的所有奇数 使用while循环打印20 22 24 26 28 30
(3)预习do-while循环,for循环,循环嵌套
===================================================================================day04
1.break关键字
可以结束任何形式的循环
练习:使用变量保存一个数字,无限循环弹出提示框,获取输入的值,如果输入的值,用输入的值和保存的数字比较,如果猜大了,弹出警示框‘big’,如果猜小了,弹出警示框‘small’,否则弹出警示框right 03_break.js 03_break.html
2.do while
do{
循环体
}while(循环条件)
(3)for循环
for(表达式1;表达式2;表达式3){
循环体
}
表达式1:初始值
表达式2:循环条件
表达式3:循环的增量
(4)break和continue
break:结束循环,不会再执行循环体和增量等
continue:跳过本次循环体,还会继续执行增量以及循环条件
(5)循环嵌套
任意循环之间都可以相互嵌套
var i=0,sum=0;
==================================================================================day05
八、函数和对象
1.函数
parseInt()转成整型/parseFloat()…转成浮点型
分为系统函数和自定义函数
函数:function,是一个功能体,可以接受若干个数据,返回特定的结果。用于封装反复执行的代码——饺子机
(1)创建普通函数
function函数名称(){
函数体——封装的反复执行的代码
}
调用:函数名称()
练习:创建函数,封装10+20的计算结果并打印出来,调用3次
练习:创建函数,封装1-100之间所有整数的和并打印结果,调用3次
(2)创建带有参数的函数
function 函数名称(参数列表){ //形参->用于接收数据
函数体
}
调用:函数名称(参数列表) //实参->实际要传递的数据
参数列表:创建函数时的参数成为形参,调用函数时的参数成为实参,在调用的时候实参会赋值给形参, 多个参数之间用逗号隔开。如果实参的个数小于形参的个数,则未赋值的形参为undefined。
练习:创建函数getSum,传递1个参数,计算1~任意数字之间所有整数的和,调用3次
练习:创建函数getRun,传递2个参数,计算任意两个年份之间的闰年个数
(3)创建带有返回值的函数
function 函数名称(参数列表){
函数体
return 返回值; //函数的返回结果
}
调用:函数名称(参数列表)
注意事项:
如果没有return或者return后没有返回值,则返回undefined
return后的所有代码不会被执行
函数调用后会得到return的值
练习:创建函数getMax,传递2个参数,返回两个数字中的最大值
练习:创建函数getMax,传递3个参数,返回三个数字中的最大值
练习:创建函数getMax,传递1个参数,根据状态码返回对应的中文
1-等待付款 2-等待发货 3-运输中 4-已签收 5-已取消
练习:创建函数getMax,传递1个参数(年份),返回任意年份的天数。
2.变量的作用域
作用域:变量或者函数可访问的范围。
全局作用域:在全局作用域下声明的变量,可以在任意合法位置访问到。
函数作用域:在函数中使用var声明的变量,只能在函数内部访问到。
变量的提升:
JS 程序执行前,当前作用域下使用var声明的变量,会将声明提升到最前,
但是赋值还是在原来的位置。
3.函数的作用域
和变量作用域一样,也分为全局作用域和函数作用域,
函数声明提升
和变量提升一样,使用function关键字创建的函数,声明也会提升到所在作用域最前边。在任意合法都可以调用。
4.递归(掌握)
在函数的内部调用自身
递归的使用:
要有跳出的条件,结合着return来跳出。
练习:使用递归来计算1~任意数字之间所有整数的和
练习:使用递归来计算任意数字的阶乘!10=1098*…*1
练习:使用递归来计算任意数字的阶乘的和!
斐波那契数列
1 1 2 3 5 8 13 21 34 55 89…
课后任务
(1 )
(2)练习
使用递归和普通函数来计算斐波那契数列
(3)预习js中的自定义对象
====================================================================================day06
1.匿名函数
没有名称的函数 function(){ }
创建函数——函数声明
function 函数名称(){ }
(1)创建函数——函数表达式
var 函数名称=function(形参列表){
函数体;
return 返回值;
}
调用 函数名称()
对此函数声明和函数表达式的区别
函数声明存在函数提升,可以在任意位置创建,也可以在任意合法位置调用。
函数表达式不存在函数提升,只能先创建在调用。
(2)匿名函数自调用
创建一个独立的函数作用域,防止污染全局。
(function(形参列表){
函数体;//封装的代码
})(实参列表);
(3)回调函数
将匿名函数以参数传递
function fn(a){
//调用的时候,实参赋值给形参a
//a就是函数名称,如果执行匿名函数体,只需要调用a
a();
}
fn(function(){…});
2.全局函数
parseInt 将数据转为整型
parseFloat 将数据转为浮点型
isNaN 判断一个值是否为NaN,是->true 不是->false
isFinite 判断一个值是否为有限制,是 ->true,不是->flase
3/0 -> Infinity 无限值
eval 执行字符串中的表达式 eval(‘1+1’)->2
练习:使用弹出提示框输入一组JS表达式,使用eval来执行这组表达式,并打印出来
encodeURI 对URI进行编码
decodeURI 对已编码的URI进行解码
URL
3.对象
对象就是一组属性和方法(功能)的集合。
某个人的手机:属性有品牌、颜色、内存、CPU,功能有看视频,听音乐,玩游戏…
某一个饭盒:属性有大小、颜色、品牌,方法盛饭…
——万物皆对象
(1)JS中的对象
内置对象:JS提供的
宿主对象:根据JS不同的执行环境来划分的
自定义对象:自己创建的对象
(2)自定义对象
对象字面(直接)量
内置构造函数
自定义构造函数
(3)对象字面量
使用的括号{} 创建空对象
属性名和属性值用冒号隔开
多组属性之间用逗号隔开
属性名引号可加可不加,如果含有特殊字符,必须加
练习:创建一个商品对象,包含的属性有编号、标题、价格、上架时间、是否在售
(4)访问对象的属性
对象.属性名
对象[‘属性名’] 如果属性不加引号,会认为是变量
练习:创建手机对象,属性有编号,品牌,尺寸,颜色;修改尺寸和颜色,添加内存属性
(5)内置构造函数创建对象
new Object() //创建一个空对象
需要单独添加每一个属性
练习:创建一个汽车对象,含有属性型号、品牌、颜色、长度。
(6)检测是否含有属性
对象.属性名===undefined
true-> 不存在 false-> 存在
‘属性名’ in 对象
true-> 存在 false-> 不存在
对象.hasOwnProperty(‘属性名’)
true-> 存在 false-> 不存在
(7)遍历属性
访问对象中的每一个属性
for(var key in 对象){
key 对象中每个属性名
对象[key] 每个属性名对应的属性值
}
练习:创建对象,包含5组属性,每一组属性是一个价格,使用遍历属性,来获取价格的总和。
(8)对象中的方法
var person={
name:‘jerry’;
say:function(){ //say就是成员方法
this.name //this 值代当前的对象
}
}
person.say(); //调用成员方法
练习:创建手机对象,属性有品牌,颜色,尺寸,方法打电话,发短信,看视频
练习:创建一个圆对象,属性有半径,圆周率,方法有计算圆的面积和周长
创建一个长方形对象
=================================================================================day07
数组
数组是由多个元素组成的集合,每个元素就是一个数据
1.创建数组
(1)数组字面量
[元素1,元素2,元素3…]
练习:创建数组,保存若干个成绩
练习:创建数组,保存若干个大学名称
(2)访问数组中的元素
数组[下标]
下标是从0开始,第一个元素下标0,最后一个元素下标是长度减1.
练习:创建数组,保存若干个国家名称,单独添加2个,修改其中的元素,打印结果
(3)使用内置构造函数
new Array(元素1,元素2,元素3…)
new Array(3) 初始化元素个数为3,后期可以添加更多元素
练习:创建数组,保存若干个编程语言的名称
练习:创建数组,初始化长度为5,保存篮球厂商五个位置。
2.获取数组长度
数组.length 可以获取到数组元素个数
在数组的末尾添加元素
数组[数组.length]=值
3.数组的分类
数组分为索引数组和关联数组
索引数组:以数字作为下标
关联数组:以字符串作为下标,只能先创建数组,然后单独添加元素
练习:创建数组,添加员工的编号、姓名 性别 工资 生日 下标使用字符串
4.遍历数组
(1)for in
for(var key in 数组){
key 要遍历的数组元素的下标(数字也可以是字符串),
数组[key]获取下标对应的元素
}
练习:创建数组。包含多个分数,使用for-in 遍历该数组,获取总成绩
(2)循环
for(var i=0;i<数组.length;i++){
数组[] 代表下标对应的元素
}
循环只能遍历索引数组,无法遍历关联数组
练习:创建函数,传递1个参数(数组),返回平均工资。getAvg([5000,8000,…])
练习:创建函数,包含多个汽车品牌,把数组中品牌名为‘宝马’的元素修改为BMW
练习:创建函数,getCount。传递两个参数(数组、要查找的值),返回任意值在数组中出现的次数
练习:创建函数getIndext,传递2个参数(数组、要查找的值),返回任意值在数组中第一次出现的数组下标,如果找不到返回-1
练习:创建函数getMax,传递1个参数(数组),返回最大值。
5.数组中的方法(API)
API 应用程序编程接口、预定义好的一些方法和函数
toString() 将数组中的元素按照逗号分隔转化成字符串
join(’-’) 将数组中的元素按照指定的字符分隔转换为字符串,默认是逗号。
concat(arr1,arr2…) 拼接多个数组
slice(start,end) 截取数组中的元素,start开始的下标,end 结束的下标,不包含end本身;如果是负数表示倒数
练习:创建数组,包含ag,每个字母是一个元素;截取bc,e~g;拼接成一个新数组
splice(start,count,value1,value2…)删除数组中的元素,start开始的下标,count删除的长度,value表示删除后补充的元素
练习:创建数组,包含a~h,每个字母是一个元素;删除d、e,将f替换m,在下标为2的位置插入元素z。
课后任务
(1)复习今天内容,整理思维导图
(2)练习:
使用数组遍历翻转数组中的元素
a~h h~a
使用冒泡排序将一组数字进行从小到大的排序
使用数组遍历
(3)预习字符串对象及API
================================================================================day08
1.数组API
reverse() 翻转数组中的元素
sort() 对数组中的元素排序,默认是按照Unicode码从小到大
对数字排序
sort(function(a,b){
return a-b; //从小到大
//return b-a; //从大到小
})
push() 在数组的末尾添加元素,返回添加后的长度,原数组会发生变化
pop() 删除数组末尾的一个元素,返回删除后的元素,原数组会发生变化
unshift() 在数组的开头添加元素
shift() 在数组的开头删除一个元素
2.二维数组
数组中的每个元素也是数组
var arr=[[],[],[]…]
访问二维数组中的元素 arr[下标][下标]
3.字符串操作
包装对象:目的是让原始类型数据可以向引用类型数据,具有一组属性的方法
JS中提供了三种包装对象:String、Number、Boolean
将任意类型转为字符串
new String(数据) 强制转为字符串,返回对象
String(数据) 强制转为字符串,返回字符串
注意事项:包装对象和普通的字符串用法没有区别
(1)转义字符——
转换字符本身的意义
\n将普通字符n转移成换行符
'将有特殊意义的引号转成普通字符
\t将普通字体t转移成指标符(tab键效果)
…
练习:打印出现 welcome to chi\na
(2)字符串API
toUpperCase() 将英文字母转大写
toLowerCase() 将英文字母转小写
练习:初始化4个英文字母(有大小写)保存到变量中,循环弹出提示框,输入4个字符(不区分大小写),如果输入正确结束循环 06_exercise.html o6_exercise.js
length 获取字符串的长度
charAt() 获取下标对应的字符 字符串[下标]
charCodeAt() 获取某个字符的Unicode码
练习:遍历字符串‘javascript’,获取a字符出现的次数
indexOf(value,start) 查看某个字符的下标,value要查看的字符串,star开始查找的下标,找不到返回-1
lastIndexOf(value) 查看某个字符最后一次出现的下标
练习:声明变量保存邮箱,检测是否为邮箱格式,如果是打印true,否则打印false
slice(star,end) 截取字符串,start开始的下标,end结束的下标,不包含end本身,如果end为空截取到最后。
substr(star,count) 截取字符串,start开始的下标,count截取的长度,如果count为空截取到最后。
练习:使用变量保存身份证号,截取其中的出生年月日,性别。打印1997年10月26日 性别男 110326199710263579
练习:使用变量保存邮箱,分别截取邮箱的用户名和域名。
jerry1997@126.com
练习:将一个英文单词的首字母转大写,其他转小写 welCome ->Welcome
split() 按照指定的字符将字符串分隔为数组。
练习:使用split 获取邮箱中的用户名和域名 jerry@126.com
4.匹配模式
作用:用于查找、替换字符串
正则表达式tom tum toom tem
replace(valuel,value2) 查找并替换,value1是要查找的字符串,value2是要替换的字符串;
value1 可以使用正则表达式的写法 /china/ig 匹配模式
i->ignore 忽略大小写
g->global 全局查找
match(value) 用于查找匹配你的字符串,返回数组
search(value) 用于查找匹配的字符串,返回满足条件的第一个的下标,如果找不到返回-1;可以使用i,不能使用g
5.Math对象
Math对象不需要使用new创建,可以直接使用
PI 获取圆周率
abs() 获取绝对值
floor() 向下取整
ceil() 向上取整
round() 四舍五入取整
max() 获取一组数字最大值
min() 获取一组数字最小值
pow(x,y) 获取x的y次幂
random() 获取随机数 >=0 <1
练习:创建数组,包含多个姓名,每次运行,随机取一个元素
随机获取0~9其中一个下标
课后任务
(1)
(2)练习
创建变量保存一句英文,把每个单词的首字母转大写,其余转小写
‘how are you ’ How Are You
在09和az之间随机取4个值,不能重复,返回数组
=================================================================================day09
1.Date对象
(1)用于对日期时间进行存储和计算
new Date(‘2019/3/14’ 9:47:30)
new Date(2019,2,14,9,48,30); //第2个参数月份范围0~11
new Date() 存储当前的系统时间
new Date(10006060*24) 存储的是距离计算机元年的毫秒数所对应的日期时间
(2)获取Date对象中日期时间
getFullYear/getMonth(范围0~11
,/getDate/getHours/getMinutes/getSeconds/
getMinutes/getSeconds/getMilliseconds
getDat(星期0-6)/getTime(距离计算机元年毫秒数)
练习:创建对象保存‘2019-10-1 10:30:50’,打印
2019年10月01日10点30分50秒 星期二
练习:计算距离你2019年10月1日相差的 天 小时 分钟 秒钟
(3)转为本地字符串格式
toLocaleString() //年-月-日 时:分:秒
toLocaleDateString() //年-月-日
toLocaleTimeString() //时:分:秒
(4)设置日期时间
setFullYear/setMonth/setDate/setHours/setMinutes/setSeconds/
setMilliseconds/setTime
setTime 设置后,可能会影响到其它的日期时间
(5)复制Date对象
把已经创建的Date对象以参数形式传递给构造函数
var d1=new Date();
var d2=new Date(d1);//复制d1对象
练习:创建Date对象,保存员工的入职时间‘2019-3-15’;3年后合同到期,计算到期时间;合同到期前一个月续签合同,加入是周末,提前到周五,计算续签时间;
步骤:创建Date对象
复制入职时间作为到期时间,修改年份,获取当前 年份加3,把结果设置为新的年份
打印 入职时间…到期时间
2.Number对象
new Number (值) 将数据转为数值型,返回对象
Number (值) 将数据转为数值,返回数值
toFixed(n) 保留小数点后n位
toString(n) 将数值转为字符串,n表示进制,默认10
3.Boolean对象
new Boolean(值) 将数据转为布尔型,返回对象
Boolean(值) 将数据转为布尔型,返回布尔型
!!值 隐式将数据转为布尔型
toString() 将布尔型数据转为字符串
4.错误处理
SyntaxError:语法错误,错误的使用中文,缺少括号等;出现后所有的代码都不执行。
ReferenceError:引用错误,使用了未声明的变量,属于运行时的错误,影响后边代码的执行。
TypeError:类型错误,错误的使用了数据,例如变量当做函数来用;属于运行时的错误,影响后边代码的执行。
RangeError:范围错误,参数的使用超出了范围;属于运行时的错误 ,影响后边代码的执行。
try{
尝试执行的代码,可能出现错误
}catch(err){
err:捕获到的错误
具体处理错误的内容
}
练习:初始化一个变量add,尝试调用add函数,传递两个数字参数,如果执行错误,给add赋值一个匿名函数,然后再调用add
ECMAScript
5.ES6新特性
ECMAScript 6
ECMAScript 2015 2016 2017
《ES6入门》
http://es6.ruanyifeng.com
(1)块级作用域
使用let关键字声明变量,只能在块级作用域下使用,不能被外部访问,不存在变量提升。
块级作用域:{ }、for、while、do-while、if…
(2)箭头函数 =>
是回调函数的另一种写法,和匿名函数完全不一样
sort( (a,b)=>{
return a-b;
} )
如果箭头函数的函数体重只有一行代码,并且是return形式的,可以简化为
sort( (a,b)=>a-b )
练习:创建函数add,传递两个参数,每个参数都是回调函数,在回调函数中返回一个数字,在函数add中计算两个数字相加的和
课后任务
练习:计算2019-5-19 9:30:00 距离 2019-12-25 日相差的天,小时,分钟,秒钟。
(3)幻速参数的默认值
ES6 允许为形参设置默认值,如果没有传递实参,自动使用形参的默认值。
练习:创建函数getSex,传递一个参数(0或者1),根据值打印对应的性别;如果参数为空,默认为0.
(4)模板字符串
在此之前就是模板字符串 ${JS 表达式}
练习:创建一个图书的对象,包含编号,标题,价格,作者,是否在售(1/0);
使用模板字符串打印这些内容
编号:
标题:
…
是否在售:是
javascrip二阶段
准备:
- 书: 犀牛书 JavaScript权威指南
- 微信公众号: 前端大全
- 上一届的笔记: 打印出来
概述:
JavaScript核心编程: 闭包,面向对象,ES5,ES6,异步编程
DOM: 为网页添加交互行为的一套程序——所有场合使用!
jQuery: DOM的简化版——PC
Vue: 自动化的框架
React:
正课: - 正则表达式
- String中的正则API:
- 正则表达式:
有明确数量边界的数量词
\d{3} 3个数字
\d{3,5} 3-5个数字
\d{3,} 至少三个数字
没有明确数量边界的数量词
- 可有可无,多了不限 \s*
? 可有可无,最多一次 \s+
- 至少一次,多了不限 \s?
指定匹配位置
^ 表示字符串开头
^\s+ 仅匹配开头的空字符
$ 表示字符串结尾
\s+$ 仅匹配结尾的空字符
\b 表示单词边界,可匹配:空格,标点符号,字符串开头和结尾等可将一个单词与其它单词分割开的符号。
\b[a-z] 每个单词首字母
正则表达式中有些字符有特殊的语法含义,是不能直接使用的,必须使用、进行转义后才能使用
. 、 / * ? + [ ( ) ] { } ^ $ |
定义完整手机号规则:
((+86|0086)\s+)?1[3-8]\d{9}
定义完整身份证号规则:15位或者18位,最后一位数字或者X
\d{15}(\d\d[0-9x])?
匹配“微信”,“weixin”,“w x”等情况,并防止中间加空格——作业
-
String中的正则表达式:
查找敏感词: 4种: -
查找一个固定的敏感词出现的位置
var i=str.indexOf(“敏感词”,starti)
在str中,从starti位置开始,找下一个敏感词出现的位置
返回值: 如果找到,返回敏感词第一个字的下标
如果没找到,返回 -1
问题: 只能查找一个固定的敏感词,不支持正则!
var msg=prompt(“请输入消息内容”);
//查找消息中的敏感词: 我草
var i=msg.indexOf(“我草”/,0/);
if(i!==-1){//如果找到
//输出:包含敏感词,禁止发送
alert(“包含敏感词!禁止发送!”);
}else{//否则,将消息发到页面上
//当前网页 写
document.write(李然说:${msg}
);
} -
模糊查找一个敏感词的位置:
var i=str.search(/正则/i)
在str中,查找符合正则要求的第一个敏感词出现的位置
返回值: 如果找到,返回敏感词第一个字的下标
如果没找到,返回 -1
问题: 正则表达式默认区分大小写
解决: 在正则第二个/后加后缀i, 表示ignore
问题: 只能返回位置,不能返回内容
//请用户输入一条消息
var msg=prompt(“请输入消息内容”);
//查找消息中的敏感词: 我草
var i=msg.search(/([我卧]|wo)\s*([草艹槽]|cao)/i);
if(i!==-1){//如果找到
//输出:包含敏感词,禁止发送
alert(“包含敏感词!禁止发送!”);
}else{//否则,将消息发到页面上
//当前网页 写
document.write(李然说:${msg}
);
} -
查找敏感词内容 2种
-
只查找一个敏感词的内容:
var arr=str.match(/正则/i)
在str中查找第一个敏感词的[内容和位置]
返回值: 如果找到,返回一个数组: [“0”:敏感词内容, “index”: 下标i ]
如果没找到,返回null
var msg=prompt(“请输入消息内容”);
var arr=msg.match(/([我卧]|wo)\s*([草艹槽]|cao)/i);
//arr:[“0”:敏感词内容, “index”: 下标i ]
if(arr!==null){//如果找到
//输出:包含敏感词,禁止发送
alert(在位置 ${arr.index} 发现敏感词 ${arr[0]}
);
}else{//否则,将消息发到页面上
//当前网页写
document.write(李然说:${msg}
);
} -
查找所有敏感词的内容:
var arr=str.match(/正则/ig) global
arr:[ 敏感词1, 敏感词2, … … ]
返回所有找到的敏感词内容的列表
如果找不到返回null
强调: 凡是返回null的函数,都要先判断不是null,再使用。
var str=“老师:请用小红 我的 朋友造句。小亮:小红是我的朋友。小然:朋友!小红是我的!”;
//查找字符串中所有"小"字开头的人名
var arr=str.match(/小[\u4e00-\u9fa5]/g);
//null
console.log(arr);
//如果找到敏感词,返回敏感词的个数,否则,返回0个
console.log(共找到 ${ arr!==null?arr.length:0 } 处
);
======================================================================================day02
案例复习:
每个案例三遍:
- 代码+注释抄一遍
- 保留注释,删除代码,自己试着把代码填回来
- 都删掉,尝试将注释和代码一起写回来——自己会了
注释最重要!
正课:
-
String正则API
-
RegExp对象
-
***Function对象
-
String正则API
查找:4种场景 -
查找一个固定的关键词出现的位置:
var i=str.indexOf(“敏感词”,starti) -
用正则表达式模糊查找关键词的位置:
var i=str.search(/正则/i) -
查找关键词的内容:
-
只查找第一个关键词的内容:
var arr=str.match(/正则/i)
arr:[“0”: 关键词内容, “index”: 下标i ]
问题: 只能找一个敏感词 -
查找所有敏感词的内容:
var arr=str.match(/正则/ig) global
arr:[ 敏感词1, 敏感词2, … … ]
返回所有找到的敏感词内容的列表
如果找不到返回null
强调: 凡是返回null的函数,都要先判断不是null,再使用。 -
既查找所有关键词的内容,又能获得每个关键词的位置
reg.exec()
1.替换
1.简单替换
将所有敏感词都替换为一致的新值
str=str.replace(/正则/ig,“新值”)
var str=“老师:请用小红 我的 朋友造句。小亮:小红是我的朋友。小然:朋友!小红是我的!”;
var reg=/小[\u4e00-\u9fa5]/g;
var arr=str.match(reg);
str=str.replace(reg,"**");
console.log(str);
console.log(共替换 ${ arr!==null?arr.length:0 } 处
);
- 高级替换: 根据本次找到的敏感词的不同,动态选择不同的新值替换
str=str.replace(
/正则/ig,
//replace每找到一个敏感词,就自动调用一次回调函数
function(kword){ kword: y c y u
形参kword,自动获得本次找到的敏感词内容
return 返回的新值
kword.toUpperCase()
}// return Y C Y U
)
var str=“you can you up!”;
// ↑ ↑ ↑ ↑
//将每个单词的首字母替换为它的大写形式
str=str.replace(
/\b[a-z]/ig,
function(kw){ //kw: y c y u
console.log(回调函数自动被replace调用一次,传入参数kw:${kw},返回:${kw.toUpperCase()}
);
return kw.toUpperCase();
}//return Y C Y U
);
console.log(str);
//You Can You Up!
回调函数callback:咱们负责定义函数,然后将函数交给别的程序去自动执行。
特点: 1. 不一定立刻执行
2. 不止执行一次,可能反复执行
3. 自动传入参数
衍生: 删除敏感词: ——作业:
其实就是将敏感词替换为""
str=str.replace(/正则/ig,"")
2.切割
切割: 将字符串按指定的分隔符,分割为多段子字符串
- 简单切割: 分隔符是固定不变的
var arr=str.split(“分隔符”)
固定套路: 打散字符串为字符数组(按"“切割)
var arr=str.split(”")
var email=“zhangdong@tedu.cn”;
//按@切割
var arr=email.split("@");
//arr:[“zhangdong”,“tedu.cn”]
// [0] [1]
var uname=arr[0];
var domain=arr[1];
console.log(uname);
console.log(domain);
//鄙视: 翻转一个字符串
var str=“helloworld”;
var arr=str.split("");
console.log(String(arr));
arr.reverse();
console.log(String(arr));
str=arr.join("");
console.log(str);
//dlrowolleh
- 复杂切割: 分隔符不固定
var arr=str.split(/正则/)
- 78
- 82
- 65
- 90
- RegExp对象:
什么是: 专门保存一条正则表达式,并提供用正则表达式执行查找和验证操作的方法。
何时: 只要在js中使用正则表达式,都要创建正则表达式对象
如何: 创建 - 用/方式:
var reg=/正则/ig;
问题: 不能在程序运行时,动态拼接正则,只能预先写死正则。 - 用new:
var reg=new RegExp(“正则”,“ig”)
优势: 可在运行时动态拼接正则字符串
//ajax -> Server
// ↓
var arr=[“青天”,“白鹭”];//紫烟,明月。。。
//Browser
//var reg=/arr.join("|")/;
var reg=new RegExp(arr.join("|"));
// //青天|白鹭|紫烟
//请用户输入一条消息
var msg=prompt(“输入消息内容”);
//在消息中查找是否包含敏感词
var i=msg.search(reg);
//如果包含,就提示包含敏感词,不能发送
if(i!==-1){ alert(“包含敏感词,不能发送!”)}
//否则,将敏感词写到网页中
else{ document.write(李然说:${msg}
);}
功能:
-
验证:
var bool=reg.test(str)
验证str是否符合reg的格式要求
问题: 并不是完全匹配。只要在str中找到部分内容和reg匹配,就返回true!
解决: 要求从头到尾完整匹配
今后只要做验证,必须前加^,后加$
var pwd=prompt(“请输入密码”);
var reg=/^\d{6}$/;//定义规则:6位数字
//如果(用)规则验证用户输入的密码,通过
if(reg.test(pwd)==true){
//在页面上显示,验证通过
document.write(<h1 style="color:green">验证通过</h1>
)
}else{//否则,提示密码格式不正确
alert(“密码格式不正确!”);
} -
查找: 查找所有敏感词的内容和位置
var arr=reg.exec(str)
在str中找下一个符合reg要求的敏感词的内容和位置
返回值: 同match不加g时一样
arr:[ “0”: 敏感词内容, “index”: 下标i ]
var str=“老师:请用小红 我的 朋友造句。小亮:小红是我的朋友。小然:朋友!小红是我的!”;
var reg=/小[\u4e00-\u9fa5]/g;
do{//反复
//先查找一次敏感词
var arr=reg.exec(str);
//如果找到敏感词,就输出内容和位置
if(arr!=null){
console.log(在位置${arr.index}发现敏感词${arr[0]}
)
}else{//否则,就退出循环
break;
}
}while(true);
trim
//去掉str开头的空字符
function ltrim(str){
//将str开头的空格替换为空字符串
return str.replace(/^\s+/,"")
}
//去掉str结尾的空字符
function rtrim(str){
}
//去掉str开头和结尾的空字符
function trim(str){
}
//测试代码:
var str=" zhang dong “;
console.log(ltrim(str));
//“zhang dong "
console.log(rtrim(str));
//” zhang dong”
console.log(trim(str));
//“zhang dong”
- Function对象:
什么是: 保存一段可重用的代码段的对象,再起一个名字
为什么: 重用一段代码
何时: 只要发现一段代码需要反复使用时,都要定义为一个函数。
如何: - 声明方式:
function 函数名(形参列表){
函数体;
return 返回值;
}
var 返回值=函数名(实参列表)
问题: 会被声明提前: - 用赋值方式:
var函数名=function (形参列表){
函数体;
return 返回值;
}
=============================================================================================day03
- Function对象
创建: 3种: - 声明方式:
function 函数名(形参列表){
函数体;
return 返回值
}
会被声明提前 - 赋值方式(直接量方式):
var函数名=function (形参列表){
函数体;
return 返回值
}
不会被声明提前
揭示: 函数其实也是一个对象
函数名其实只是一个简单的变量 - 用new:
var 函数名=new Function(“形参1”,“形参2”,…,“函数体”)
重载overload:
什么是: 相同函数名,不同形参列表的多个函数,在调用时可根据传入参数的不同,自动选择匹配的函数执行。
为什么: 减少函数的个数,减轻调用者的负担
何时: 一件事,可能根据传入的参数不同,选择执行不同的操作时
如何:
问题: js语言默认不支持重载的语法
因为js不允许多个同名函数同时存在
如果同时存在,只有最有一个会覆盖之前所有
解决: 用arguments对象变通实现
arguments: 每个函数中自带的接收所有实参值的类数组对象
每个函数: 所有函数中都有arguments对象
自带: 不用自己创建,直接使用
作用: 接收所有实参值
数据结构: 类数组对象——长的像数组的对象
相同: 1. 下标 2. length 3. for循环遍历
不同: 类型不同——类数组对象是对象家孩子,无权使用数组家的API
function pay(){
//arguments:[].length
//如果用户没有传入实参,就手机支付
if(arguments.length0){
console.log(“手机支付…”);
}//否则如果用户传入一个实参,就现金支付
else if(arguments.length1){
console.log(现金结账:收您${arguments[0]}
)
}else{//否则,就刷卡支付
console.log(刷卡支付: 从您卡号${arguments[0]}扣款成功
)
}
}
pay();//手机支付
pay(100);//现金支付,收您100元
pay(“6553 1234”,“123456”);//刷卡支付,…
2.匿名函数:
什么是: 定义函数时,不起名的函数,称为匿名函数
为什么: 节约内存!
何时: 只要一个函数,只使用一次时,都要用匿名函数,而不是有名称的函数
如何: 2种:
- 回调函数: 将一个函数作为参数传入另一个函数内,被其他函数调用
比如: setInterval/setTimeout(function(){…},1000)
arr.sort(function(a,b){return a-b;})
ES6 arr.sort((a,b)=>a-b)
str.replace(/reg/g, function(kw,$1,$2,…){return 替换值})
ES6 str.replace(/reg/g, (kw,$1,$2,…)=>{return 替换值}) - 匿名函数自调: 也称为立即调用函数IIF,定义函数后自己调用自己,调用后,立刻释放
什么是: 定义一个匿名函数,然后立刻执行
为什么:定义一个临时作用域,减少使用全局变量, 避免全局污染
何时: 今后所有的js代码,都要放在一个巨大的匿名函数自调中。
(function(参数列表){函数体; return 返回值})(参数值列表)
+function(参数列表){函数体; return 返回值}(参数值列表)
3.作用域和作用域链
函数中,没有用任何对象/this就直接访问的变量,在作用域链中找 - 作用域(scope)
变量的可用范围
其实是一个对象
包括:
全局作用域对象:
window 保存全局变量
优点:可反复使用
缺点:随处可用,全局污染
建议:尽量少用全局变量
函数作用域对象:
AO 保存局部变量
包括:①参数变量 ②函数内var出的变量
优点:仅函数内可用,不会被污染
缺点:不可重用
2.函数的声明周期
开始执行程序前
创建ECS,专门保存正在调用的函数的执行环境的 数组
首先在ECS中添加浏览器主程序的执行环境main
创建全局作用域对象window,所有全局变量都是保存在window对象中
main执行环境引用window
定义函数时
用函数名声明全局变量
创建函数对象,封装函数定义
函数对象的scope属性,指回函数创建时的作用域
函数名变量引用函数对象
调用函数时
向ECS中压入本次函数调用的执行环境元素
创建本次函数调用时使用的函数作用域对象(AO)
在AO中创建所有局部变量
包括
形参变量
函数内用var声明的变量
设置AO的parent属性引用函数的scope属性指向的父级作用域对象
函数的执行环境引用AO
变量的使用顺序
先在AO中找局部变量
AO中没有才去父级作用域中找
调用后
函数的执行环境出栈
导致AO释放
导致AO中的局部变量一同被释放
- 作用域链(scope chain)
由各级作用域逐级引用,形成的链式结构
保存着所有的变量
控制着
变量的使用顺序
闭包
词法表示包括不必计算的变量的函数,也就是说,该函数能使用函数外定义的变量
3.闭包
什么是
即重用变量,又保护变量不被污染的机制
为什么
全局变量
优:可重用
缺:易被污染
局部变量
缺:不可重用
优:不会被污染
何时:
即重用变量,又保护变量不被污染
三特点(3步) - 外层函数
包裹受保护的变量和操作变量的内层函数 - 外层函数要返回内层函数的对象
3种- return function(){…}
- 直接给全局变量赋值一个内部function
- 将内部函数保存在一个对象的属性或数组元素中
return [function,function,function]
return { fun:function(){…} }
- 调用外层函数,用外部变量接住返回的内层函数对象
形成闭包
闭包如何形成
外层函数调用后,外层函数的作用域对象(AO),无法释放
被内层函数对象的scope引用着
缺点:
比普通函数占用更多内存
多的是外层函数的作用域对象(AO)始终存在
容易造成内存泄漏
解决:
如何释放闭包
将引用内层函数对象的外部变量置为null
导致内层函数对象被释放
导致外层函数的AO被释放
鄙视
画简图
2步- 找受保护的的变量,确定外层函数调用后,受保护变量的最终值
- 找操作受保护的变量的内层函数对象
结论:
同一次外层函数调用,返回的内层函数对象,共用同一个受保护的变量
作业:
- function fun(){
var n=999;
nAdd=function(){ n++ };
return function(){
console.log(n);
}
}
var getN=fun();
getN()//? 999
nAdd() nAdd is not a function
getN()//? 1000 - function fun(){
for(var i=0,arr=[]; i<3;i++){
arr[i]=function(){ console.log(i); }
}
return arr;
}
var funs=fun();
funs0;//? 3
funs1;//? 3
funs2;//? 3
local函数作用域对象
=====================================================================================day04
一.面向对象
什么是:程序中都是用对象结构来描述现实中的事物
为什么:便于大量数据的维护
何时:所有程序都是用面向对象的思想管理数据和功能
如何:面向对象三大特点:封装,继承,多态
- 封装
什么是:创建一个对象,集中存储一个事物的属性和功能
为什么:便于大量数据的维护
何时:今后所有数据都是先封装在对象中,再按需使用
如何:3种
1.1对象直接量
用{}创建一个对象:
var 对象名={
属性: 属性值,
… : … ,
功能: function(){
… this.属性名 …
}
}
访问对象的成员:成员=属性+方法
访问属性:对象名.属性
调用方法:对象名.方法
问题:对象自己的方法内,想访问自己的属性,也不能直接属性,报错: 属性 未定义
为什么:对象的属性没有直接保存在一个函数的作用域链中,而引擎不能擅自进入对象中获取数据。
解决一: 对象名.属性/方法
问题: 紧耦合: 对象名发生变化,必须同时改内部的对象名,一旦忘改,就出错!
解决二: 松耦合: 能不能自动获得对象名——this
什么是this: 正在调用当前函数的.前的对象
每个函数与生俱来的关键词,可自动获得.前的对象
鄙视时: this只和调用时.前的对象有关,和保存在哪儿毫无关系!
如果一个函数前没加任何.,默认是window.
总结: 何时: 只要对象自己的方法内,想使用对象自己的属性和其它方法时,都必须用this.
1.2用new: 2步: 同关联数组的创建过程
var obj=new Object(); //obj:{}
obj.属性名=值;
obj.方法名=function(){ … }
揭示了: js底层所有对象都是关联数组
vs 关联数组相比:
相同:
1. js中的对象,可随时添加新属性和新方法,而不会报错!
2. 访问对象中不存在的属性,不会报错,而是返回undefined
3. 访问对象中的成员,有两种方式:
简写: 对象名.属性名/方法名()
// ||等效
何时: 如果要访问的属性名是写死的
完整: 对象名[“属性名”]/[“方法名”] ()
何时: 如果要访问的属性只能在运行时动态获得
4. 对象也可以用for in遍历每个属性
缺点: 一次只能创建一个对象,如果反复创建多个相同结构的对象时,代码会很繁琐。
解决: 构造函数
1.3用构造函数反复创建多个相同结构的对象
什么是构造函数: 专门描述一类对象统一结构的函数
何时: 只要反复创建多个相同结构的对象
如何: 2步:- 定义构造函数:
function 类型名(形参1, 形参2, … ){
this.属性名=形参1;
… = …;
this.方法名=function(){ … this.属性名 …}
} - 用构造函数创建多个对象:
用new调用构造函数,创建出规定格式和功能的对象
var 对象名=new 类型名(属性值,属性值,…)
鄙视题: new的原理: 4件事: - 创建一片空的存储空间
- 用新的空对象作为主语调用构造函数
new Student(属性值,…)
=> new.Student(属性值,…) this->new{}
↓
=> this.属性名=形参
↓
=>new{}.属性名=属性值
在new{}中强行添加该新属性,并将属性值保存进对象中——野蛮的强行赋值方式
结果: new{}中就反复添加了规定的新属性和方法 - 返回新对象的结果
- 定义构造函数:
- 继承
父对象中的成员,子对象无需重复创建,就可直接使用 - 多态
同一事物,在不同情况下,表现出不同的状态
============================================================================================day05
1.OOP
封装:
用构造函数反复创建多个相同结构的对象
function Student(sname,sage){
this.sname=sname;
this.sage=sage;
this.intr=function(){
console.log(I'm ${this.sname},I'm ${this.sage}
);
}
}
//用new创建:
var lilei=new Student(“Li Lei”,11);
var hmm=new Student(“Han Meimei”,12);
console.log(lilei);
console.log(hmm);
lilei.intr();
hmm.intr();
问题:将方法的定义放在构造函数内,会重复创建多个相同函数的副本,浪费内存。且,不便于维护。
解决:1.构造函数中不要包含方法定义
2.将方法定义放在共同的爹中,让所有孩子共用!
继承:
什么是继承:父对象中的成员,子对象无需重复创建,就可直接使用
为什么继承:代码重用,节约内存
何时继承:多个子对象,拥有相同的属性值或方法时,就要用继承,将方法保存在爹中个,所有孩子自动共用。
如何实现继承:js中的继承都是继承原型对象——原型继承
原型对象:
什么是:在一个类型的大家庭中,集中保存所有孩子对象共用的成员的父对象。
如何使用原型对象:
创建:不用自己创建:买一赠一
当创建构造函数(妈妈)时,自动附赠了一个原型对象(爹)
构造函数.prototype指向原型对象
原型对象.constructor指回构造函数
继承:不用自己继承,new的第二步:
每创建一个新子对象,都会自动设置子对象的__proto__继承构造函数的原型对象(prototype)
向原型对象中添加共有成员:需要自己做
构造函数.prototype.成员=值
如何使用原型对象中的公有成员:
var lilei=new Student(…,…);
lilei.intr()
//先在lilei里找,如果找到,就先用自己的
//如果没找到,才延_ proto _属性先父对象中查找,只要找到,可直接使用,无需重复创建。
自有属性和共有属性
自有属性:直接保存在子对象本地,仅当前子对象自己所有的属性
共有属性:保存在原型对象中,所有子对象共有的属性
操作
读取:子对象.属性 lilei.dname lilei.className
都可以用子对象来读取
修改:自有属性,可用子对象修改 子对象.属性名=值
lilei.sage++; lilei.sname=“Li Lielie”;
共有属性,必须用 原型对象,不能用子对象修改共有属性
构造函数.prototype.属性名=值
如果强行用子对象修改共有属性,结果:会为当前子对象个人添加一个同名的自有属性 。从此,该子对象与其他子对象分道扬镳——错误的效果!
应该:只要修改共有属性,必须用原型对象亲自改。
Student.prototype.className=“初三2班”;
lilei.proto.intr() I’m undefined, I’m undefined
lilei.intr() I’m Li Lei, I’m 11
判断
自有: var bool=obj.hasOwnProperty(“属性名”)
共有:不是自有&&
obj.属性名!undefined
属性名 in obj,判断属性名是否在obj的原型链中
否则,就是没有
内置对象的原型对象
其实内置对象类型Array,Date…都是构造函数
每种类型都有自己的原型对象:Array.prototype, Date.prototype, …
内置对象的原型对象中保存了所有该类型的子对象共用的API
固定套路:解决旧浏览器无法使用新API的问题
1.判断
if(typeof 内置类型.prototype.API !“function”)
if(!(“API” in 内置类型.prototype))
2.添加
内置类型.prototype.方法=funciton(参数){
… this //获得将来调用方法的.前的对象 }
原型链(prototype chain)
什么是:由多级原型(父)对象,逐级继承形成的链式结构
2个作用:
1. 保存了: 所有对象的属性,存储着一个对象可用的所有属性和方法
2.控制着对象成员的使用顺序:先自有,再共有
1.先用自有属性
2.自己没有,才延原型链向父级找
3.原型链上没有,返回undefined
vs 作用域链
1.保存了: 局部和全局变量,存储着所有不用.就可访问的变量
控制着变量的使用顺序:1.先用局部变量
2.局部没有,才去作用域链上找
3.找不到,报错
总结:1.所有不用.的变量都在作用域链上找,作用域链中的变量不用.,可直接访问
2.所有对象.访问的属性都保存在原型链上,原型链上的属性必须用“对象.”才能访问
js内置类型:11种:
Number String Boolean
Array Date RegExp Math
Error
Function Object
global(在浏览器中被更名为window)
可以new的都是构造函数
判断对象的类型
0. typeof
只能区分基础类型和function
不能进一步区分对象的类型
- var bool=类型.prototype.isPrototypeOf(child)
不仅检查直接父对象,而且检查整个原型链 - var bool= child instanceof 构造函数
不仅检查直接父对象,而且检查整个原型链
问题:检查整个原型链
解决: 3. 检查内置class属性
Object.prototype.toString.call(obj)==“[object 类型名]”
obj.toString()
更严格:class属性直接保存在对象本地,只在创建对象时确定类型,对象创建后,不随继承关系的改变而改变
如果检查数组类型: 4. var bool=Array.isArray(obj)
ES5 IE9+
原理和Object.prototype.toString.call一样
鄙视:方法定义在原型对象中,还是定义在构造函数对象上
答:如果方法仅限当前类型的子对象可用,其他类型的对象不可用,就定义在原型对象中,必须当前类型的子对象才能调用
如果方法不确定将来调用它的对象类型,就定义在构造函数对象上,不需要任何对象实例,即可用构造函数名直接调用
自定义继承
- 仅修改两个对象间的继承关系:(仅修改一个对象的父对象)
child.proto=father
设置child的爹为father
设置子对象继承指定父对象:Object.setPrototypeOf(child,father)
获得子对象的父对象:var father=Object.getPrototypeOf(child) - 修改构造函数原型对象,来修改所有子对象的父对象(同时更换多个子对象的爹)
更换妈妈的老公
Student.prototype=father,构造函数.prototype=father
讲究时机:必须 紧跟在构造函数定义之后
开始创建子对象之前 - 仅基于现有父对象,创建子对象,并扩展自有属性: Object.create()
创建新子对象,继承父对象,扩展子对象自有属性
var child=Object.create(father,{ 属性名:{四大特性}, … : … })
鄙视: 模拟实现Object.create()
Object.create=function(father,props){ //var child=new Object(); //Object.setPrototypeOf(child,father); var Fun=function(){}; Fun.prototype=father; var child=new Fun(); //Object.defineProperties(child,props); if(props!==undefined){ for(var key in props){ child[key]=props[key].value; } } return child; } - 两种类型间的继承
何时:如果发现多个类型拥有部分相同的属性结构和方法定义,都要抽象父类型
如何:2步: - 定义抽象父类型
相同的属性结构定义在父类型的构造函数中
相同的方法定义在父类型的原型对象中 - 让子类型继承父类型
- 在子类型构造函数中借用父类型构造
extends
让父类型构造函数帮助添加相同部分的属性定义
子类型构造函数仅负责添加独有的属性定义即可
错误:
直接调用父类型构造函数
this->window
父类型中的属性都泄露到全局
正确
父类型构造.call(this, 参数1,参数2,…)
简写:
父类型构造.apply(this, arguments)
鄙视:
call vs apply
相同:
都是强行借用一个本来无法调用的函数,并临时替换函数中this为指定对象
不同:
call
传入借用函数的参数,必须单独传入,逗号分隔
apply
传入借用函数的参数,放在一个数组中整体传入
可自动打散数组类型参数 - 让子类型原型对象继承父类型原型对象
inherits
Object.setPrototypeOf( 子类型构造.prototype, 父类型构造.prototype )
多态:
什么是:一个函数在不同情况下表现出不同的状态
包括2中情况:
1.重载
2.重写(override):如果子对象觉得从父对象继承的成员不好用,可在子对象本地创建同名成员,覆盖父对象中的成员,使用时,只要自己有,就先用自己的。
3. 觉得从父对象继承的成员不好用
如何:只要在对象本地定义与父对象中同名的成员,即可。
- ES5:
ECMAScript 第5个版本 - 严格模式:
什么是: 比普通js运行机制,要求更严格的模式
为什么: js语言本身具有很多广受诟病的缺陷
何时: 今后所有的js程序,必须运行在严格模式下!
如何:
在当前作用域的顶部写:“use strict”;——启用严格模式
规定: - 禁止给未声明变量赋值:
- 静默失败升级为错误:
什么是静默失败: 执行不成功,但还不报错。
ES5中规定,所有静默失败,都升级为报错! - 匿名函数自调和普通函数调用中的this,不再指向window!而是this=undefined。
说说严格模式的限制
1.变量必须先声明在使用
2.不允许重复声明变量
3.禁止使用arguments
4.eval不会再他的外层作用域引入变量
5.禁止this指向全局
6.函数参数不能有同名,否则报错
7.不能对只读属性赋值,否则报错
8.不能使用with语句
9.不能使用前缀0表示八进制数,否则报错
=================================================================================================day06
ES5
严格模式
-
禁止给未声明的变量赋值
-
静默失败升级为错误
-
匿名函数自调和普通函数调用中的this,不再指向window!而是this=undefined。
4.禁止使用arguments.callee
arguments.callee是在函数内自动获得当前函数自身。
问题:递归的效率极低
递归重复量极大!
解决:绝大多数的递归,都可用循环代替!
保护对象
问题:js中对象底层其实就是一个关联数组,随时可以修改属性,随之可篡改属性结构。
解决:保护对象
对象的属性
命名属性
可直接用.访问到的属性, 最需要保护的
ES5中分为
数据属性
什么是:直接保存在属性本地的属性
如何保护:
ES5中,一个属性,不再只是一个普通的变量,而是一个缩微的小对象。每个属性都有一个value特性存储属性值,还有三个开关,实现初步的自保能力:修改开关: 不能用., 必须使用defineProperty: HTML中: Attribute 定义 属性 属性 Object.defineProperty(对象,"属性名",{ 开关: 开true/关false }) 比如: 让eric的eid属性只读: Object.defineProperty(eric,"eid",{ writable: false })
问题: 咱们能关,别人就能打开!
解决: 只要修改开关,都要双保险:
都要关上: configurable: false
配置 |可以
问题: 用enumerable:false隐藏的属性,其实用.依然可以访问。 枚举/遍历|可
问题: 一个defineProperty只能修改一个属性
解决: 用defineProperties
Object.defineProperties(对象,{
属性名:{
开关: true/false,
… : …
},
属性名:{
开关: true/false,
… : …
},
})
比如:
//员工编号只读:
//姓名禁止删除:
//salary禁止被遍历:
Object.defineProperties(eric,{
eid:{
writable:false,//管value
configurable:false//管writable
},
ename:{ configurable:false },
salary:{
enumerable:false,
configurable:false
}
});
问题: 只能用固定的规则保护属性,无法自定义规则
四大特性
{ value: 属性值,
writable: true/false, //控制是否可修改
enumerable: true/false, //控制是否可被for in遍历
configurable:true/fslse,//1. 控制是否可删除 //2. 控制是否可修改前两个特性
}
强调: configurable一旦改为false,不可逆
问题:
问题:
解决:
Object.defineProperties(eric,{
eid:{
writable:false,
configurable:false
},
ename:{
configurable:false
},
})
访问器属性
2. 访问器属性: 保镖
什么是: 不实际存储属性值,仅提供对其它数据属性保护业务的 特殊的属性
何时: 只要用自定义规则保护属性时,只能用访问器属性。
如何: 2步:
1. 先定义一个隐姓埋名的半隐藏的数据属性,实际存储属性值
Object.defineProperty(eric,"_eage",{
value:25,
writable:true,
enumerable:false,
configurable:false,
})
2. 添加正式属性名称的访问器属性,来保护半隐藏的数据属性
必须用defineProperty添加,不能用{ . }
//定义保镖用正式的属性名代替受保护的属性抛头露面
Object.defineProperty(eric,“eage”,{
//第一个保镖: 帮用户去属性中取值
get:function(){ return this._eage },
//第二个保镖: 帮用户将新值送入属性中,但是会先验证新值是否符合规定。如果新值不符合规定,则不保存直接报错。
set:function(value){//value自动接到新值
if(value>=18&&value<=65){
this._eage=value
}else{
throw Error(“年龄超范围!”)
}
},
enumerable:true,//代替受保护的属性抛头露面
configurable:false//不能删除保镖
})
保护对象结构: 防止别人对对象添加新属性或删除现有属性
3个层次:
1. 防扩展: 禁止向对象中添加新属性
Object.preventExtensions(obj);
阻止 扩展
禁止对obj添加新属性
原理: 其实每个对象中都有一个隐藏的内部属性:
extensible: true 所有对象默认都是可扩展的
Object.preventExtensions(obj); 将extensible改为false
2. 密封: 在兼具防扩展同时,进一步禁止删除所有属性
属性值依然可以改
Object.seal(obj) 禁止删除obj中任何属性
密封
原理: 1. preventExtensions
2. 自动将所有属性的configurable改为false!
获取一个属性的四大特性
var attrs=Object.getOwnPropertyDescriptor(obj,“属性名”)
attrs: {四大特性}
修改四大特性
只改一个属性的四大特性
Object.defineProperty(obj,“属性名”,{ 特性名:值, … : … })
同时修改多个属性的四大特性
Object.defineProperties(obj,{ 属性名1: { 特性名:值, … : … }, 属性名2: {四大特性} })
强调:双保险,修改writable或enumerable时,最好同时定义configurable为false,禁止反向修改
要修改的属性不存在,会自动添加该属性
特性的默认值
用.添加的新属性
特性的默认值为true
defineProperty/defineProperties添加的新属性
特性的默认值为false
内部属性
对象中有,但不能用.访问到的属性, 不用保护
比如:每个对象中,都藏着一个class属性,保存着对象的类型名,不能用。class直接访问。
proto
Object.getPrototypeOf(obj)
Object.setPrototypeOf(child,father)
class
Object.prototype.toString.call(obj)
extensible:true
var bool=Object.isExtensible(obj)
Object.preventExtensions(obj)
防篡改
- 防扩展
禁止添加新属性
相当于
将对象的extensible:false
判断是否已禁止扩展
Object.isExtensible(obj)
设置防扩展
Object.preventExtensions(obj) - 密封
在防扩展同时,禁止删除现有属性
相当于
将每个属性的configurable:false
其他属性在修改特性时,不必反复修改configurable:false
判断是否已密封
Object.isSealed(obj)
密封对象
Object.seal(obj)
最像Java的构造函数
function Emp(id,ename,salary,age){ this.id=id; this.ename=ename; this.salary=salary; Object.defineProperties(this,{ id:{writable:false}, salary:{enumerable:false}, _age:{ writable:true, enumerable:false }, age:{ get:function(){return this._age;}, set:function(val){ if(val<18||val>65) throw new Error(…) this._age=val; }, enumerable:true } }); this.age=age; //防篡改: Object.seal(this);//密封 }
鄙视:
实现一个js类型,包含public属性和private属性 - 冻结
Object.freeze(obj) 禁止修改obj中的所有属性的值
原理: 1. 自动执行seal()
2. 自动修改所有属性的writable特性为false
Object.create(): 如果没有构造函数,也想创建子对象继承父对象时
- 创建新对象
- 继承父对象
- 为新对象添加新属性
如何:
var child=Object.create(father,{
//为child添加自有属性
属性:{value:值, 开关…},
… : …
})
比如:
var hmm=Object.create(father,{
//defineProperties
//属性名:{ value:, 开关… }
bao:{
value:“LV”,
writable:true,
enumerable:true,
configurable:false
},
phone:{
value:“iPhoneXXX”,
writable:true,
enumerable:true,
configurable:false
}
});
vs new:
相同: 前三步做的事儿都是相同的
不同: new必须构造函数才能执行
create只要有father就能执行
call apply bind
替换函数中不想要的this!
何时: 只要函数执行时,其中的this不是想要的,都要用这三个去换
如何:
- call/apply
调用 用
何时: 在本次调用函数时,临时替换一次this!
如何:
任意函数.call(任意对象,实参值列表…)
比如: calc.call(lilei,10000,2000,3000);
调用全局函数calc时,临时将其中的this替换为lilei
this.ename,就变为lilei.ename。
calc.call(hmm,10000,1000,2000);
调用全局函数calc时,临时将其中的this替换为hmm, this.ename就变为hmm.ename;
call/apply/bind()
什么是: 替换函数中不想要的this为指定的对象
何时: 如果函数执行时,其中的this不是想要的,都要替换this为想要的对象
如何:
-
call/apply
在本次调用函数时,临时替换函数中的this为指定对象
本次调用后,替换失效。
call: 任意函数.call(任意对象, 实参值列表…)
让任何一个对象,可临时调用任何一个不相干的函数,并传入实参值列表。
比如: calc.call(lilei,10000,2000,3000);
用lilei调用calc函数,并传入10000,2000,3000三个参数值。
apply: 如果实参值列表是以数组形式给的,则必须将call
换成apply
如何: var arr=[10000,2000,3000] //实参值列表
calc.apply(lilei, arr)
用lilei调用calc,但是会打散数组参数为单个值
比如: arr=[10000,2000,3000]
calc.apply(lilei, arr )
打散数组 ↓ ↓ ↓
等效于: calc.call(lilei, 10000,2000,3000)
结果: this base bonus1 bonus2 -
bind:
什么是: 基于原函数,创建一个新函数,并永久绑定this
何时: 如果函数中的this,需要反复被替换,就用bind创建一个新的专属的函数。永久绑定this。
如何: var 新函数=原函数.bind(对象, 要绑死的实参值,…)
比如: var calc_l=calc.bind(lilei, 10000)
创建一个和calc一模一样的新函数calc_l,并永久替换calc_l中的this为lilei对象,并永久替换第一个参数base为10000。
好处: 今后反复使用calc_l不用再反复传入lilei和10000.
强调: bind仅创建新函数,不执行函数
要执行函数: calc_l(1000,2000)
//bonus1, bonus2
强调: 用bind创建的新函数中的this,不可能再被别人用call替换。因为bind后的函数中的this已经被替换成固定的对象了。所以再用call/apply已无this可换
总结:
当函数中的this不是想要的,想换成想要的
就用call/apply/bind
- 当仅临时替换本次调用函数中的this时——call
当实参值列表是数组形式时,必须将call换成apply,自动打散数组为单个值 - 如果一个函数需要反复替换this为一个指定的对象时,就用bind创建一个专属的新函数,永久绑定this为指定的对象,同时可绑定部分固定的参数值。
数组API
1.数组中也有indexOf方法,查找一个指定元素的位置
用法同字符串.indexOf完全一样:
var i=arr.indexOf(元素,starti)
在arr数组中,从starti开始查找一个指定元素的位置。
找到了,返回元素下标,找不到,返回-1.
2.every(fun)判断数组中每个元素是否都满足fun函数定义的条件。只有都满足才返回true, 否则返回false
判断:arr.every(条件)中每个元素是否都符合条件
arr.every(function(elem,i,arr){
//elem: 当前元素值
//i : 当前位置
//arr : 当前数组
return 判断条件;
//如果当前元素值elem,符合判断条件要求,返回true
//如果当前元素值elem,不符合判断条件要求,返回false
})
执行原理:every会自动遍历arr中每个元素,每遍历一个元素,就用回调函数在当前元素上指向一次。每次调用时都会自动给回调函数传入当前元素值,当前位置和当前数组对象。
中途every
some(fun)判断数组中是否包含满足fun函数定义的条件元素。只要包含就返回true,否则 返回false。
判断arr.some中是否包含符合条件的元素
arr.some(function(val,i,arr){ return 判断条件; })
3.遍历
对数组中每个元素执行相同的操作
- arr.forEach():
- arr.map():
直接修改原数组
arr.forEach(function(val,i,arr){ arr[i]=新值; })
IE8
Array.prototype.forEach=function(callback){
for(var i=0;i<this.length;i++){
if(this[i]!==undefined)//防稀疏数组
callback(this[i],i,this);
} }
不修改原数组,返回新数组
var newArr=arr.map(function(val,i,arr){
return 修改后的新值 })
IE8
Array.prototype.map=function(callback){ for(var i=0,result=[];i<this.length;i++){ if(this[i]!==undefined)//防稀疏数组 result[i]=callback(this[i],i,this); } return result; }
其实map也可像forEach一样使用
回调函数中的this,指window
过滤和汇总
过滤filter
选取arr中符合条件的元素组成新的子数组
var subArr=arr.filter(function(val,i,arr){ return 判断条件 })
汇总
将数组中每个元素值汇总出一个结果
var r=arr.reduce(function(prev,val,i,arr){ return prev+val; }, 初始值)
=========================================================================================day08
1.ES6:
ECMAScript的第6个版本: 在不改变原理的基础上,尽量简化了代码。
let:
什么是: 专门代替var,用于声明变量用的关键词
何时: 今后所有的var,都用let代替
为什么:var的两个缺点:
-
声明提前: 打乱了程序正常执行的顺序
-
没有块级作用域: 块内的变量很有可能提前到块外部,影响原本正确的外部的程序。
块: if(){ … }else if(){ … }else{ … }
for(){ … }
while(){ … } do{ … }while();
都是块,但都不是作用域
其内部的变量,都会被提前到块外对外部造成不可预期的影响。
比如:
var t=0;
function fun(){
t+=10;//原本就是想对全局t+10
}
fun();
console.log(t); //10
过了一段时间/其它人给函数中添加了一段不会执行的代码:
var t=0;
function fun(){
var t//undefined;t+=10;//原本就是想对全局t+10 //因为有了局部变量t,就无法修改全局了 var err=false; if(err){ //if根本没执行 //if不是作用域,拦不住内部的t被提前到外部函数的顶部 var t=new Date(); ... ... }
}
fun();
console.log(t); //0
结论: 今后所有的var都用let代替
let的两个作用: -
阻止声明提前: 在let a之前,不允许使用变量a
在程序中,相同范围内,不允许重复声明两个let a; -
让if while for do while块都变成作用域,块内部的变量无法提前到外部,也就不会影响外部。
let的原理: 其实let底层就是一个匿名函数自调
let相当于做了两件事: -
用匿名函数自调包裹当前范围内的剩余代码
-
将let后的变量a,更名为_a
比如:
再比如:
for+let: 会形成闭包
for(let i=0;i<3;i++){
arr[i]=function(){console.log(i)}
//(function(_i){
//arr[_i]=function(){console.log(_i)}
//})(i)
}
箭头函数:
对所有函数定义的简写:
function 函数名(形参列表){ 函数体; return 返回值 }
var 函数名= function (形参列表){ 函数体; return 返回值 }
3句话:
-
去function,在()和{}之间加=>
(形参列表)=>{ 函数体; return 返回值 } -
如果形参列表中只有一个形参,可省略()
形参1=>{ 函数体; return 返回值 } -
如果函数体只有一句话,可省略{}
形参1=>一句话
如果仅剩的一句话,还是return,必须去return
省略{}后,结尾的分号,必须省略
箭头函数的双刃剑: 箭头函数中的this,是内外通用的
比如:
var lilei={
sname:“Li Lei”,
friends:[“tom”,“jerry”,“jack”,“rose”],
intr:function(){
//this->lilei
this.friends.forEach(
function(name){//回调函数
//不加bind,回调函数中的this->window
console.log(${this.sname} 认识 ${name}
);
}.bind(this) //加上bind才能将函数内部错误的this换成外部正确的this
)
}
}
如果觉得bind不直观,可改为箭头函数:
var lilei={//对象的{不是作用域!}
sname:“Li Lei”,
friends:[“tom”,“jerry”,“jack”,“rose”],
intr:function(){//这里的function不能改箭头,一旦改为箭头,this->window
//this->lilei
this.friends.forEach(
//内部的function可改为箭头函数,因为希望和外部的this通用!this->lilei
name=>console.log(${this.sname} 认识 ${name}
)
)
}
}
总结:
如果希望内外this一致时,就可改为箭头函数,代替bind
如果不希望内外this相同时,就不可改为箭头函数。比如: 对象中的方法不能改箭头;事件处理函数不能改箭头
for of:
快速遍历一个数组的内容的最简化写法
总结: 遍历一个数组: -
for循环
for(var i=0;i<arr.length;i++){
arr[i] //获得当前数组元素
}
最灵活的遍历,即可控制遍历的顺序,又可控制遍历的步调 -
forEach简化:
arr.forEach((elem,i)=>{
elem //当前数组元素
})
问题: 只能从头到尾依次遍历每个元素,不能控制顺序(从后向前或从前向后)和步调(2,4,6,… 5,10,15,… )
何时: 如果既关系内容,又关心位置,但不会改变遍历的顺序或步调时 -
for of更简化:
for(var elem of arr){
//of会依次取出索引数组中的每个元素值
elem //当前数组元素
}
问题:只能获得元素内容,无法获得元素位置
所以: 仅关心元素内容时,才用for of
vs for in- 使用场合: for of专门遍历索引数组
for in专门遍历关联数组 - of vs in:
of只取元素值
in 只取属性
比如: var names=[“亮亮”,“然然”,“东东”]
for(var name of names){
//of依次取出names数组中每个人名保存到前边的变量name中
console.log(name + " - 到!");
}
参数增强: - 使用场合: for of专门遍历索引数组
-
默认值:
ES6中可以对形参列表中的最后一个形参设置默认值
何时: 如果只有最后一个形参不确定时
如何:
//定义函数find,在arr中查找指定元素出现的位置
//如果不给第三个参数,则默认从0位置开始找
//如果给第三个参数,则从给定的位置向后找
function find(arr,elem,starti){
/方法一: if判断
if(starti===undefined){
starti=0;
}/
//方法二: 短路
//starti=starti||0;//0是starti的备胎
//||会自动将前后转为boolean
//如果starti转换后不是false,就首选starti使用。如果starti转换后为false,就用0代替starti;
console.log(在arr中,从${starti}位置开始,找下一个${elem}出现的位置...
);
}
//方法三: ES6的默认值
function find(arr,elem,starti=0){
//如果没有给starti传参,则自动用0代替!
console.log(在arr中,从${starti}位置开始,找下一个${elem}出现的位置...
);
}
var arr=[];
find(arr,3);//从0位置开始
find(arr,3,5);//从5位置开始找
2. 剩余参数: 代替arguments
什么是: 让函数可以自动收集结尾多个不确定的参数值
何时: 如果在函数形参的结尾,有不确定个数的参数值传入,则可用剩余参数方式收集
为什么:
arguments: 2大缺点:
1. 不是纯正的数组类型对象,数组家的API一个都不能用
2. 只能收集所有参数值,无法有选择的收集部分参数值
如何: 2步:
1. 在定义函数时,在可以确定的形参后添加"…数组名"
2. 在函数内,"数组名"数组中,就自动收集了除已经确定的形参之外的剩余实参值。
比如:
定义函数时:
function calc(ename,…arr){
调用时:
calc(“Li lei”,10000,2000,3000);
//…arr[10000,2000,3000]
calc(“Han Meimei”,5000,1000,2000,3000);
//…arr[5000,1000,2000,3000]
优点: 1. 可有选择的获得部分实参值
2. arr是纯正的数组,数组家的API可以随便用!
3. 打散数组: 代替apply,专门用于打散数组类型的参数
何时: 只要函数需要多个零散的参数值,但给定的参数值却是放在一个数组中传入的,发生不一致,就要打散数组
apply一定有问题:
apply的主要目的是替换this,顺便打散数组
如果只希望打散数组参数,和this无关时,用apply就会很别扭.
比如: Math.max(arr)不支持数组中查找最大的元素
传统: 可用: apply打散数组类型参数为单个值
console.log(Math.max.apply(null,nums));
console.log(Math.max.apply(Math,nums));
console.log(Math.max.apply(nums,nums));
apply的第一个参数即没用,又必须写
解决: 不用apply,用…
如何: console.log(Math.max(…nums))
// 拆
…先将nums数组打散为单个值,再陆续传给max
总结:
-
如果在定义函数时,形参列表中…arr,是收集剩余参数保存到数组的意思
-
如果在调用函数时,在实参列表中…arr,作用是相反的,是打散数组为单个值,再传入。
解构:
什么是: 仅提取出一个大对象中的部分成员单独使用
何时: 如果只使用一个大对象中的一小部分成员时
如何: 3种情况: -
数组解构: 下标对下标:
var date=[2019,5,5];
/var y=data[0]
var m=data[1]
var d=data[2]/
var [y,m,d]=date;
y m d -
对象解构: 属性对属性:
比如:
var user={
uid:1001,
uname:“dingding”,
set:1,
signin:function(){
console.log(“登录…”);
},
signout:function(){
console.log(“注销…”);
},
signup:function(){
console.log(“注册…”);
}
}
//var {uid:uid,signup:signup}=user;
var {uid,signup}=user;
console.log(uid);
signup();
原理: -
参数解构:
什么是: 在向函数中传参时,将一个大的对象,打散后,传递给对应的形参变量.
何时: 多个形参不确定时,又要求按顺序传入时、
为什么:- 默认值: 只能应对结尾一个参数不确定时
- 剩余参数: 虽然可应对多个参数不确定的情况
但无法规定传入参数的顺序
比如:
function calc(ename, base,饭补, 高温,房补)
calc(lilei, 10000, , 200, 600)
calc(hmm, 10000, , , 200)
calc(jack, 10000, , 300, 500)
总结: 当多个形参都不确定时,且每个实参值必须对位传给指定的形参变量时,单靠调整形参的个数和顺序,无法满足所有调用的情况
解决: 参数解构:
如何: 2步: - 定义形参时,所有的形参变量都要定义在一个对象结构中
比如:
//1. 定义形参列表时,就用对象结构定义
function ajax({
//与顺序无关
url:url,
type:type,
data:data,//不确定
dataType:dataType//不确定
}){
console.log(向${url}发送${type}请求
);
if(data!=undefined&&type==“get”){
console.log(在url结尾拼接参数?${data}
)
}
if(data!=undefined&&type==“post”){
console.log(xhr.send(${data})
);
}
if(dataType==“json”){
console.log(JSON.parse(返回结果)
);
}
} - 调用函数传参时,所有实参值,都要放在一个对象结构中整体传入。
比如:
ajax({
url:“http://localhost:3000/products/getProductsByKwords”,
type:“get”,
data:“kw=macbook i5”,
dataType:“json”
});
总结: 参数解构,其实就是对象解构在传参时的应用而已
======================================================================================day09
1.ES6
OOP:
- 对对象直接量的简写: 2处:
- 如果对象的成员值来自于外部的变量,且属性名和变量名相同时,可只写一个:
比如: user.js
var signin=function(){ 登录… }
var signup=function(){ 注册… }
var signout=function(){ 注销… }
var obj={ signin:signin, signup:signup, signout:signout };
{ signin, signup, signout };
//将三个函数放在一个对象中导出export
module.exports=obj - 对象直接量中方法的定义不需要加":function"
为什么: 对象中的方法,不能用箭头函数简写。一旦简写,方法中的this不再指向当前对象,很可能指向全局的window。
比如:
var lilei={
sname:“Li Lei”,
intr:function(){//不能改成箭头函数
… this.sname …
}
}
解决:
var lilei={
sname:“Li Lei”,
intr (){//不要加箭头
//等效于intr:function(),可保持this不变!
… this.sname …
}
} - class
什么是class:
用途: 描述一类事物统一属性结构和功能的程序结构
本质: 其实就是以前的构造函数+原型对象的整体
为什么: 因为构造函数和原型对象分开的写法不符合封装的含义。
如何: 3句口诀
//1. 用class{}包裹构造函数和原型对象方法
class Student{
//2. 构造函数名提升成类型名,所有构造函数更名为constructor
constructor(sname,sage){
this.sname=sname;
this.sage=sage;
}
//3. 所有方法不再需要prototype前缀,不再需要=function
intr(){
console.log(I'm ${this.sname}, I'm ${this.sage}
);
}
}
class的用法和存储结构跟ES5中构造函数创建的对象用法和存储结构是完全相同的 - class之间的继承:
问题: 两种类型之间存在部分相同的属性结构和方法定义,代码可能出现重复编写。
解决: 定义抽象父类型: 2步:- 定义抽象父类型集中定义多个子类型共有的属性和方法
1.1 在父类型的构造函数中仅定义相同部分的属性
1.2 在父类型原型对象中仅定义相同部分的方法 - 让子类型继承父类型:
2.1 子类型使用extends继承父类型
2.2 子类型构造函数中先用super调用父类型构造函数
比如:
//定义敌机类型
//所有敌机都有名称,速度和分值三个属性
//所有敌机都可以飞行,击落敌机可以得分
//定义蜜蜂类型
//所有蜜蜂都有名称,速度和分值三个属性
//所有蜜蜂都可以飞行,击落蜜蜂可以的奖励
//但是,因为敌机和蜜蜂两种类型拥有部分相同的属性和方法,导致代码重复,不便于日后维护
//所以,应该先定义抽象父类型"敌人类型",集中保存两种子类型部分相同的属性和方法。
class Enemy{
//抽象父类型构造函数中集中保存所有子类型共有的属性部分
constructor(fname,speed){
this.fname=fname;
this.speed=speed;
}
//抽象父类型原型对象中集中保存所有子类型共有的方法
fly(){
console.log(${this.fname}以时速${this.speed}飞行
);
}
}
//子类型必须继承(extends)父类型,才能获得父类型中的方法
class Plane extends Enemy{
//子类型构造函数中:
constructor(fname,speed,score){
//先借用父类型构造函数(super)帮忙添加共有的属性部分
super(fname,speed);//Enemy中的constructor()
//再自己添加本类型自有的独特的属性
this.score=score;
}
//Prototype中:只定义自己类型特有的方法即可,不必定义相同部分的方法。
getScore(){
console.log(击落${this.fname}得${this.score}分
);
}
}
class Bee extends Enemy{
constructor(fname,speed,award){
super(fname,speed);
this.award=award;
}
getAward(){
console.log(击落${this.fname}获得${this.award}奖励
);
}
}
var f22=new Plane(“F22”,1000,5);
//表面上f22是Plane一个妈妈创建的,但是其实f22中三个属性:fname和speed是奶奶给的,只有score才是妈妈给的。是两个人合作创建的f22对象。
console.log(f22);
f22.fly();
f22.getScore();
- 定义抽象父类型集中定义多个子类型共有的属性和方法
var bee=new Bee(“小蜜蜂”,50,“1 life”);
console.log(bee);
bee.fly();
bee.getAward();
Promise:
什么是: 专门实现异步函数,顺序执行的一种机制
为什么: 避免回调地狱
何时: 多个异步函数,必须顺序执行时
如何: 2步:
- 定义异步函数内:
function liang(){
return new Promise(function(open){
亮要执行的所有代码(包含异步代码)
//当亮的任务执行完,自动打开open()
//当open(),自动执行下一个then中的函数
})
} - 多个支持promise的异步函数,用.then串联起来
liang().then(ran).then(dong).then(function(){结束})
DOM
============================================================================================day01
- 什么是DOM:
- DOM Tree:
- 查找元素:
- 什么是DOM: document object model
什么是: 专门操作网页内容的一套对象和函数的集合
DOM其实是一个标准: W3C制定的,规定了所有浏览器操作网页内容的API统一标准。
为什么: 1. DOM是操作网页内容的唯一办法!
2. DOM标准几乎所有浏览器100%兼容.(ie8以下除过)
何时: 今后只要操作网页的内容,只能用DOM
包括:
查找元素,修改元素,添加元素,删除元素,事件绑定 - DOM Tree:
什么是: 内存中保存网页中所有内容的树形结构
网页中的一切内容,在内存中,都是保存在一棵DOM树中
为什么: 网页中的内容是父子级嵌套的包含关系
树形结构是最直观的描述上下级包含关系。
如何: - 当浏览器读取到一个html内容时
先在内存中建立一个树根节点: document
document就代表整个网页
document.write(“xxx”) 向网页中写入一句话 - 浏览器一边扫描HTML内容,一边在DOM树上创建节点对象
网页中每项内容都是DOM树上的一个节点对象 - 查找元素: 4种:
- 不需要查找,就可直接访问的元素:
document
document.documentElement ->
document.head ->
document.body ->
提示: 在控制台中输出元素节点,仅输出DOM树结构,而不是输出节点对象
如果确实需要查看一个元素对象的属性: console.dir(元素) - 按节点间关系查找:
节点树: 包含所有网页内容的完整树结构
2大类关系: - 父子: 4种:
元素.parnetNode 获得元素的父节点
父元素.childNodes 获得父元素下的所有直接子节点的集合
返回: 多个平级子元素组成的类数组对象
父元素.firstChild 获得父元素下的直接子节点中第一个节点
父元素.lastChild 获得父元素下的直接子节点中的最后一个节点 - 兄弟: 2种:
元素.previousSibling 获得元素的前一个平级的兄弟节点, 前一个兄弟
元素.nextSibling 获得元素的下一个平级的兄弟节点,下一个兄弟
问题: 网页中一切内容都是节点,包括看不见的空格,换行都是节点对象,极大的干扰查找!
解决: DOM出了一种新的树:
元素树: 仅包含元素节点的树结构
优点: 不会受看不见的空字符干扰查找!
2大类关系: - 父子: 4种:
元素.parnetElement 获得元素的父元素
和parentNode几乎通用
因为能当爹的一定是元素
父元素.children 获得父元素下的所有直接子元素的集合
返回: 多个直接子元素组成的类数组对象
父元素.firstElementChild 获得父元素下的直接子元素中第一个元素
父元素.lastElementChild 获得父元素下的直接子元素中的最后一个元素 - 兄弟: 2种:
元素.previousElementSibling 获得元素的前一个平级的兄弟元素
元素.nextElementSibling 获得元素的下一个平级的兄弟元素
何时使用按节点间关系查找:如果已经站在DOM树的某个节点上了,想找周围附近的节点时,首选按节点间关系查找,且必须用元素树。不要用节点树。
===============================================================================================day02
正课:
-
查找
-
修改
-
查找: 4种:
-
不需要查找就可直接获得的元素: 4个
document 根节点对象
document.documentElement 根元素对象
document.head 元素对象
document.body 元素对象 -
按节点间关系查找: 2大类关系,6个属性
何时: 已经站在树上的一个节点上了。 -
父子关系:
元素.parentElement 获得元素的父元素
.parentNode
元素.children 获得元素的所有直接子元素
返回: 所有直接子元素对象组成的类数组对象
元素.firstElementChild 获得元素的第一个直接子元素
元素.lastElementChild 获得元素的最后一个直接子元素 -
兄弟关系:
元素.previousElementSibling 获得元素的前一个相邻的平级兄弟元素
元素.nextElementSibling 获得元素的后一个相邻的平级兄弟元素
问题: 不可能所有的查找都从body为起点,一旦元素藏的很深,代码会很繁琐 -
按HTML特征查找:
何时: 首次查找元素时
包括: 4个函数: -
按id查找:
var elem=document.getElementById(“id”)
获得 元素 按 id
强调: 1. .前的主语必须是document
2. 仅返回一个匹配的元素对象
如果找不到: 返回null -
按HTML标签名查找:
var elems=任意父元素.getElementsByTagName(“标签名”)
获得 元素们 按标签 名
<标签>
强调: -
.前的主语可以是任意父元素
比如: table.getElementsByTagName(“tr”)
只查找当前table下的所有tr元素对象
ul.getElementsByTagName(“li”)
只查找当前ul下的所有li元素对象 -
返回当前父元素下所有符合条件的元素的类数组对象。如果找不到,返回空的类数组对象([].length=0)
-
不仅查找直接子元素,且在所有后代中查找
比如:
-
ul.getElementsByTagName(“li”) 8个
-
ul.getElementsByTagName(“li”) 3个
-
ul.getElementsByTagName(“li”) 3个
-
ul.getElementsByTagName(“li”) 8个
-
按name属性查找:
只有表单中要将值回发给服务器端的元素,才有name属性。name属性会成为回发服务端时的变量名
比如:
提交时: http://xxxx/接口?uname=xxx&upwd=xxx
何时: 几乎专门用于查找表单中的表单元素
如何:
var elems=document.getElementsByName(“name”);
强调: 1. .前的主语必须是document
2. 返回多个元素组成的类数组对象
如果找不到,返回[].length=0
比如:
男
女
var elems=document.getElementsByName(“sex”);
elems:[ , ]
坑:
var txtName=document.getElementsByName(“uname”)
希望只获得姓名文本框对象
但无论在任何情况getElementsByName都返回一个类数组对象,即使只找到一个元素对象,也必须放在类数组对象中返回。
var elems=document.getElementsByName(“uname”);
elems: 类数组对象 [ ] != 对象
[0]
如果想获得input姓名文本框对象,需要再多写一步下标
var txtName=elems[0]; -
按class属性查找:
var elems=任意父元素.getElementsByClassName(“class名”)
强调: -
.前可以是任意父元素
-
返回所有符合条件的后代元素的集合
如果找不到返回空集合:[].length=0 -
不仅查找直接子元素,且在所有后代中查找符合条件的。
比如:- ul.getElementsByClassName("child") 6个
-
- ul.getElementsByClassName("child") 3个
-
-
如果一个元素被多个class修饰,只使用其中一个class就可以找到元素
var elems=父元素.getElementsByClassName(“alert”)
父元素.getElementsByClassName(“alert-danger”)
都返回: 类数组对象: [对象 ].length=1
[0]
如果只想获得唯一的div元素对象:
必须再多写一步下标:
var div=elems[0]
总结: 4种查找方式:
-
按id查找:
var elem=document.getElementById(“id”) -
按标签名查找:
var elems=父元素.getElementsByTagName(“标签名”) -
按name属性查找:
var elems=document.getElementsByName(“name”) -
按class属性查找:
var elems=父元素.getElementsByClassName(“class”) -
其实js中也可以使用选择器查找元素: 2个函数:
-
如果确定只找一个元素:
var elem=任意父元素.querySelector(“任意复杂选择器”)
var img=document.querySelector("#ctrl img") -
如果查找多个元素:
var elems=任意父元素.querySelectorAll(“任意复杂选择器”)
按选择器查找,效率不如按HTML特征查找
总结:- 如果仅靠一个条件就可查找到想要的元素:
首选按HTML查找——快 - 如果查找条件复杂
首选按选择器查找——简单
- 如果仅靠一个条件就可查找到想要的元素:
-
修改
内容: -
获取或修改元素开始标签到结束标签之间的原始HTML片段: 元素.innerHTML
-
获取或修改元素开始标签到结束标签之间的纯文本内容: 元素.textContent
vs 元素.innerHTML 多干两件事:
1. 去掉所有内嵌标签
2. 将特殊符号翻译为正文 -
所有表单元素的值,都必须用.value才能获得
样式: -
如果修改内联样式:
元素.style.css属性=“属性值”
比如: div.style.display=“none”; 隐藏
相当于:
div.style.display=“block”; 显示
相当于:
div.style.width=“400px”; 必须加单位
相当于:
特例: 如果css属性名包含-(-杠),则必须去横线,变驼峰
首字母小写,之后每个单词首字母大写
修改元素的背景图片:
div.style.backgroundImage=“url(…)”
相当于:
属性: 3种: -
HTML标准属性:
HTML标准中规定的,所HTML元素都具有的属性: 比如: title, class, id, value, name … …
2种方式修改:- 最早的一批核心DOM函数: 4个
- 获取一个元素指定属性的值:
元素.getAttribute(“属性名”)
比如:
a.getAttribute(“href”)
-> “http://tmooc.cn” - 修改一个元素指定属性的值:
元素.setAttribute(“属性名”,“新值”)
比如: a.setAttribute(“target”,"_self")
结果: - 判断是否包含指定属性:
var bool=元素.hasAttribute(“属性名”)
比如: a.hasAttribute(“target”) => true
a.hasAttribute(“title”) => false - 移除属性:
元素.removeAttribute(“属性名”)
比如: a.removeAttribute(“target”)
结果:
问题: 太长! - 使用HTML DOM提供的属性:
HTML DOM在核心DOM基础上,对常用的属性和元素对象提供了部分简写的代码
福利: HTML DOM已经将所有标准属性提前定义在了内存中的元素对象上!可直接用元素.属性名获取或修改。
比如:
其实: a在内存中:
a:{
id:"",
href:" http://tmooc.cn “,
target:” _blank “,
title:”",
name:""
}
所以: 获取a的href: a.href
修改a的target: a.target="_self"
判断a是否包含title: a.title!=""
移除a的target: a.target="";
特例: class属性
ECMAScript中规定: 所有js对象内部都有一个隐藏的class属性,记录当前对象的类型名: “[object Object]”
class
如果ECMAScript标准中已经提前规定过了class属性另有用途。则DOM标准中不能重复定义class属性
所以DOM标准规定class属性一律更名为className
访问元素的className属性,其实就是在访问<标签 class="">
-
四大状态属性:
-
自定义扩展属性:
==========================================================================================day03
正课:
-
修改:
-
添加/删除元素:
-
HTML DOM常用对象:
-
修改:
内容: .innerHTML .textContent .value
属性: -
标准属性: 2种:
-
核心DOM: 4个函数:
.getAttribute() .setAttribute() .hasAttribute()
.removeAttribute();
坑: 核心DOM 仅对字符串类型的属性起作用 -
HTML DOM: 元素.属性名
-
三大状态属性: disabled selected checked
值都是bool类型,不能用核心DOM函数
只能用.操作:
比如: //想启用按钮
btn.setAttribute(“disabled”,false) 无效
应该: btn.disabled=false;
再比如: //想判断一个checkbox复选框是否选中:
应该: if(chb.checked==true) 可简写为 if(chb.checked)
补充: CSS中有三大状态伪类:
:checked :selected :disabled
专门选择处于某种状态的元素
3. 自定义扩展属性:
什么是: 开发人员自行添加在元素开始标签中的,HTML标准中没有的属性。
何时: 2个典型的场景
1. 代替id, class和元素选择器,用于选择触发事件的元素
2. 在网页中临时缓存业务数据
如何使用自定义扩展属性:
1. 在html开始标签中手动添加定义扩展属性:
<ANY data-属性名=“属性值”
比如: <a href="#" data-trigger=“tab”
2. 用js为元素添加自定义扩展属性:
不能用.添加: 因为自定义扩展属性不是标准属性,内存中的元素对象上没有!
只能用核心DOM函数添加
a.setAttribute(“data-trigger”,“tab”)
等效于: <a href="#" data-trigger=“tab”
3. 读取自定义扩展属性的值:
不能用.来读取自定义扩展属性
因为自定义扩展属性不是标准属性,没有提前定义在内存中的元素对象上
只能用核心DOM的函数来获取自定义属性的值
a.getAttribute(“data-自定义属性”)
样式:
- 修改内联样式: 元素.style.css属性名=“属性值”
强调: 1. css属性名中如果包含-,必须去横线变驼峰
修改div的z-index为11
div.style.z-index=11;
应该: div.style.zIndex=11;
2. 如果css属性值包含单位,必须添加单位
修改: ul的高:
ul.style.height=“106px”
问题: 如果同时修改一个元素的多个css样式属性,代码会很繁琐
解决: 如果批量修改一个元素的多个css样式时,都是先将样式定义在一个css的class中,再在js中只修改class属性即可.
总结:
内容: .innerHTML .textContent .value
属性: - 标准属性: 核心DOM四个函数 或 . 都行
- 状态属性: disabled selected checked
只能用.改 - 自定义扩展属性:
只能用.getAttribute(“data-属性名”)
.setAttribute(“data-属性名”,值)
样式: - 修改内联样式: 元素.style.css属性名=“值”
- 如果批量修改样式,最好用 .className=“xxx”
- 添加/删除
- 添加新元素: 3步
- 创建新的空元素:
var a=document.createElement(“a”)
结果: - 为新元素添加必要的属性
a.innerHTML=“go to tmooc”;
a.href=“http://tmooc.cn”
结果: go to tmooc - 将新元素挂载到DOM树的指定父元素下:
为什么: 浏览器显示网页内容是根据DOM树上的节点对象画出来的。如果DOM树上的节点不变化,则浏览器不回重绘页面。
要想让浏览器将新元素画出来,只能新元素添加到DOM树的指定位置,改变DOM树,才会触发浏览器的重绘操作。
如何: 3个函数:- 追加到指定父元素下的末尾
父元素.appendChild(a) - 插入到指定元素之前
父元素.insertBefore(a,child)
将a插入到父元素下的child元素之前 - 替换现有元素
父元素.replaceChild(a, child)
用a替换父元素下的child元素
优化: 尽量减少操作DOM树的次数,避免重绘
为什么: 每修改一次DOM树,页面会重绘一次
解决: 2种情况: - 如果同时添加父元素和子元素到DOM树
应该先在内存中将子元素全部添加到父元素中
最后将整个父元素一次性添加到DOM树上
只会触发一次重绘!
- 追加到指定父元素下的末尾
=================================================================================================day04
正课:
- 添加/删除
- HTML DOM常用对象
- BOM
- ***事件
- 添加/删除:
优化: 尽量减少操作DOM树的次数 - 如果同时添加父元素和子元素时
应先将子元素都添加到父元素中
最后,再一次性将整个父元素加入DOM树
结果: 浏览器只重绘一次 - 如果父元素已经在页面上了,要添加多个平级子元素
应先将平级子元素添加到文档片段对象中
最后,再一次性将文档片段对象添加到DOM树
结果: 浏览器也只重绘一次
什么是文档片段: 内存中临时保存多个子元素的虚拟父元素对象
何时使用: 只要向页面中添加多个平级子元素时
如何: 3步: - 创建文档片段:
var frag=document.createDocumentFragment();
创建 文档 片段 - 将子元素添加到文档片段中
frag.appendChild(child) - 将整个文档片段添加到DOM树
父元素.appendChild(frag)
删除元素: 父元素.removeChild(child)
2. HTML DOM常用对象
Image Select/Option Table/… Form/表单元素
Image对象: 就代表页面上一个元素
唯一的简化:
创建一个元素: var img=new Image();
等效于: var img=document.createElement(“img”)
结果:
Select对象: 代表页面上一个元素
属性:
select.selectedIndex : 获得当前选中项的下标
select.value: 获得select中当前选中项的内容/值
如果选中的option有value,则优先返回value,如果选中的option没有value,则返回innerHTML内容代替
select.options: 获得select下所有的option元素对象组成的集合。
select.options.length 获得select下所有option元素对象的个数
select.length: 等效于select.options.length
比如: 强行select.length=0; 会清空select
Option:代表select元素下的每个option元素对象
简化: 创建:
var opt=new Option(innerHTML, value)
Table对象: 代表页面上一个table元素
Table是个大家族,采用逐级管理的方式:
Table管着行分组:
Table可添加行分组: var thead=table.createTHead();
等效于: var thead=document.createElement(“thead”)
table.appendChild(thead);
var tbody=table.createTBody()
var tfoot=table.createTFoot();
删除行分组: table.deleteTHead();
table.deleteTFoot();
获取行分组: table.tHead
table.tFoot
table.tBodies[i]
行分组管着行:
行分组可添加行: var tr=行分组.insertRow(i)
在行分组内i位置,添加并插入一个新行
原位置的行不是替换,而是向后顺移一位
固定套路:
1. 末尾追加一个新行
var tr=行分组.insertRow()
2. 在开头插入一个新行:
var tr=行分组.insertRow(0)
行分组可删除行: 行分组.deleteRow(i)
坑: i要求是行在行分组内的相对下标位置
问题: 行在行分组内的相对下标位置无法自动获得
解决: 今后删除行不要用行分组
应该直接用table对象.deleteRow(i)
因为i要求是行在整个表中的下标位置
又因为行在整个表中的下标位置可用tr.rowIndex自动获得,所以,今后删除行的标准写法:
table.deleteRow(tr.rowIndex);
行分组可获取行: 行分组.rows[i]
行管着格:
行可以添加格: var td=tr.insertCell(i)
固定套路: 在行末尾追加新格:
var td=tr.insertCell()
问题: insertCell只能创建td,不能创建th
行可以删除格: tr.deleteCell(i)
行可以获取格: tr.cells[i]
补充: DOM中三大对话框:
alert confirm prompt
警告 确认 输入
Form对象: 代表页面上一个元素
获取: 也不需要查找,就可直接获得:
浏览器已经将网页中所有form元素保存在了document对象的forms集合中: var form=document.forms[i]
如果网页中只有一个表单,则document.forms[0]
属性: form.elements 获得表单中所有表单元素的集合
form.elements.length 获得表单中表单元素的个数
form.length 等效于 form.elements.length
获得表单中倒数第二个表单元素——提交按钮:
var btnSubmit=form.elements[form.length-2]
表单元素对象:
获取: 如果要获取的表单元素有name属性
可用: form.name名
3. BOM: Browser Object Model
什么是: 专门操作浏览器窗口或软件的一套对象和函数
何时: 如果想操作浏览器窗口,或访问浏览器软件相关的配置信息
如何:
包括:
window: 2个角色:
1. 代替ES标准中的global充当全局作用域对象
2. 保存所有除ES外的DOM和BOM的对象和函数库
包括:
全局的BOM函数和属性:
alert() confirm() prompt() window.open() window.close() … …
掌控特定功能的对象:
location对象 专门掌控地址栏
history对象 掌控着前进/后退/刷新
navigator对象 掌控着浏览器的配置信息
document对象 掌控着网页的所有内容
screen 对象 掌控着和显示屏有关的信息
event对象 掌控着浏览器中发生的所有事件
window中常用的全局函数或属性:
-
对话框: alert() confirm() prompt()
-
窗口大小:
2组:- 完整窗口大小: window.outerWidth/outerHeight
- 文档显示区: window.innerWidth/innerHeight
文档显示区: 浏览器窗口内专门用于显示网页内容有限的白色区域。
-
打开和关闭窗口:
打开窗口: window.open() 和的用法几乎完全一样
总结: 用程序打开新链接有几种效果:- 在当前窗口打开,可后退:
html:
js: open(“url” , “_self”) - 在当前窗口打开,禁止后退:
js: location.replace(“新url”)
用新url代替history中当前旧的url,保持history中只有一条记录。从而禁止后退。 - 在新窗口打开,可同时打开多个:
html:
js: open(“url” , “_blank”) - 在新窗口打开,只能打开一个:
html:
js: open(“url” , “自定义窗口名”)
在浏览器中,每个窗口都有内置的name属性来唯一标识一个窗口
浏览器规定,相同name名的窗口只能打开一个。
其实target属性,是在给新窗口起名
如果target的名字是自定义的名称,则新打开的窗口,只能开一个。不能重复开。
但是,target有两个内置的名称:
_self: 用当前窗口自己的名字,给新窗口,导致新窗口覆盖现有窗口
_blank: 不给新窗口指定任何名称,浏览器会自动随机分配新窗口的名称。导致新窗口的名称不会重复
关闭窗口: window.close()
- 在当前窗口打开,可后退:
history: 保存当前窗口打开后,成功访问过的url的历史记录的集合。
何时: 用程序执行前进,后退,刷新
如何: history.go(1) 前进一步
history.go(0) 刷新
history.go(-1) 后退一步
location:
什么是location: 专门保存当前正在打开的url的信息的对象
何时: 1. 获得url地址栏中各个部分的参数时
2. 通过操作地址栏中的地址实现跳转和刷新时
如何:
-
获得url中的各个部分:
http://localhost:3000/public/products.html#top?kwords=macbook
协议 主机 端口 相对路径 锚点 查询字符串
location.href 完整url
location.protocol 协议部分
location.host 主机名+端口号: localhost:3000
location.hostname 主机名 localhost
location.port 端口号 3000
location.pathname 相对路径 public/products.html
location.hash 锚点地址 #top
location.search 查询字符串 ?kwords=macbook
想进一步获得变量值: location.search.split("=")[1] -
通过操作地址栏实现跳转和刷新:
- 在当前窗口打开新链接,可后退:
location.href=“新url” <==> open(“新url”,"_self") - 在当前窗口打开新链接,禁止后退:
location.replace(“新url”) - 刷新当前页面:
location.reload(); <==> history.go(0)
- 在当前窗口打开新链接,可后退:
navigator:
什么是: 保存浏览器配置信息的对象
何时: 判断浏览器的名称和版本号
如何:
- navigator的userAgent属性包含了浏览器的名称和版本号——字符串——没有标准
测试: window 10的 Edge浏览器
========================================================================================day05
- 事件:
什么是: 浏览器自动触发的或用户手动触发的,页面中元素状态的改变。
事件发生时如何执行一项任务: 绑定事件处理函数
什么是: 当事件发生时自动调用的一个函数
本质: 是元素对象上一个事件名属性
比如: btn.onclick sel.onchange txt.onfocus
何时: 只要希望发生事件时自动执行一项任务
如何:
在事件发生前就提前绑定事件处理函数到元素的事件属性上。
如何绑定: 3种:- 在html中绑定:
html中:
js中: function fun(){ … }
问题: 不符合内容与行为分离的原则,不便于维护 - 在js中用赋值方式绑定:
js中: 元素对象.οnclick=function(){
this->当前触发事件的元素对象本身
}
问题: 一个事件属性,只能绑定一个处理函数 - 在js中通过添加事件监听对象的方式: ——最灵活
元素对象.addEventListener(“事件名”,处理函数)
添加 事件 监听对象
强调: 事件名本来是没有on: click, change, focus
只不过在用.时,为了和其它普通属性区分,才加了on前缀。
原理:
什么是事件监听对象: 一个保存元素+事件名+处理函数一套组合的对象:
比如: btnShoot.addEventListener(“click”,fun1);
先创建一个事件监听对象:
{
元素: btnShoot,
事件: click,
处理函数: fun1
}
然后,addEventListener会将监听对象添加到浏览器中一个隐藏的事件队列中。
当事件发生时,浏览器会遍历事件队列中的每个监听对象,选择匹配的监听对象中的处理函数执行。
如何从元素上移除一个处理函数:
元素对象.removeEventListener(“click”,处理函数)
处理函数决不能用匿名函数
强调: 如果一个处理函数有可能被移除,则绑定时,就不能用匿名函数。必须用有名称的函数。移除时,必须使用函数名,找到原函数对象,才能移除。
- 在html中绑定:
事件模型: 从点击元素触发事件开始,到事件处理函数执行完,期间所发生的一切事情。
DOM标准规定: 从点击元素触发事件开始,到事件处理函数执行完,期间共经历三个阶段:
- 捕获阶段: 由外向内,记录各级父元素上的处理函数
- 目标触发: 先触发最初发生事件的元素上的处理函数
目标元素(target): 最初发生事件的实际想点击的元素 - 冒泡阶段: 由内向外,依次执行捕获阶段记录的父元素上的时间处理函数。
事件对象:
什么是: 当事件发生时,浏览器自动创建的,封装事件信息和操作事件的API 的对象
何时: 1. 想改变事件默认的执行行为时
2. 获得事件信息时
如何:
创建: 不用自己创建,直接使用
在事件发生时,自动获取事件对象:
事件对象总是作为处理函数的第一个参数传入处理函数中。——信任
如何获取事件对象:
btn.οnclick=function(e){ //event的缩写
//当事件发生时,会自动调用处理函数,并自动传入事件对象给形参e
}
事件对象能干什么?
1. 停止冒泡(Propagation): e.stopPropagation()
2. 利用冒泡(事件委托/事件代理)
优化: 尽量减少事件监听对象的个数
因为浏览器触发事件,是在监听队列中遍历查找符合条件的监听对象来触发。
所以,监听对象越多,事件响应就越慢
如何: 当多个平级子元素都需要绑定相同的事件时,只要在父元素上绑定一次事件处理函数。所有孩子就可冒泡共用。
2个难题:
1. this已经不指子元素了,而是指父元素:
因为处理函数是定义在父元素上,且是等到事件冒泡到父元素时才触发。
如何获得实际单击的子元素:
用e.target代替this
目标
e.target可自动获得实际点击的目标子元素
且不随冒泡而改变
总结: this->不求天长地久,只愿曾经拥有
e.target->一旦拥有别无所求
2. 有可能点在正确的子元素上,也有可能点偏,点在不想要的元素上。
解决: 在正式执行操作前,必须先验证e.target是不是想要的。
比如: 验证标签名, 验证class, … …
补: 如何获取元素的标签名: 元素.nodeName
但nodeName返回全大写的标签名:
BUTTON A IMG LI … …
阻止默认行为:
问题: 有些浏览器的某些元素拥有默认的行为,而默认行为不是我们想要的。
比如: 默认行为会在url地址栏结尾添加#,擅自篡改地址栏。
如何: e.preventDefault();
阻止 默认
预告: HTML5拖拽API中,几乎所有函数:
一上来先是两句话:
e.stopPropagation()
e.preventDefault();
获得鼠标的坐标:
事件对象中包含3组坐标:
1. 相对于整个屏幕左上角的坐标:
e.screenX e.screenY
2. 相对于文档显示区左上角的坐标:
e.clientX e.clientY
3. 相对于当前所在元素左上角的坐标:
e.offsetX e.offsetY