javascript笔记

学习一门编程语言的基本步骤
(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
NaN
NaN 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二阶段
准备:

  1. 书: 犀牛书 JavaScript权威指南
  2. 微信公众号: 前端大全
  3. 上一届的笔记: 打印出来
    概述:
    JavaScript核心编程: 闭包,面向对象,ES5,ES6,异步编程
    DOM: 为网页添加交互行为的一套程序——所有场合使用!
    jQuery: DOM的简化版——PC
    Vue: 自动化的框架
    React:
    正课:
  4. 正则表达式
  5. String中的正则API:
  6. 正则表达式:

有明确数量边界的数量词
\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”等情况,并防止中间加空格——作业

  1. String中的正则表达式:
    查找敏感词: 4种:

  2. 查找一个固定的敏感词出现的位置
    var i=str.indexOf(“敏感词”,starti)
    在str中,从starti位置开始,找下一个敏感词出现的位置
    返回值: 如果找到,返回敏感词第一个字的下标
    如果没找到,返回 -1
    问题: 只能查找一个固定的敏感词,不支持正则!
    var msg=prompt(“请输入消息内容”);
    //查找消息中的敏感词: 我草
    var i=msg.indexOf(“我草”/,0/);
    if(i!==-1){//如果找到
    //输出:包含敏感词,禁止发送
    alert(“包含敏感词!禁止发送!”);
    }else{//否则,将消息发到页面上
    //当前网页 写
    document.write(李然说:${msg});
    }

  3. 模糊查找一个敏感词的位置:
    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});
    }

  4. 查找敏感词内容 2种

  5. 只查找一个敏感词的内容:
    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});
    }

  6. 查找所有敏感词的内容:
    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
案例复习:
每个案例三遍:

  1. 代码+注释抄一遍
  2. 保留注释,删除代码,自己试着把代码填回来
  3. 都删掉,尝试将注释和代码一起写回来——自己会了
    注释最重要!

正课:

  1. String正则API

  2. RegExp对象

  3. ***Function对象

  4. String正则API
    查找:4种场景

  5. 查找一个固定的关键词出现的位置:
    var i=str.indexOf(“敏感词”,starti)

  6. 用正则表达式模糊查找关键词的位置:
    var i=str.search(/正则/i)

  7. 查找关键词的内容:

  8. 只查找第一个关键词的内容:
    var arr=str.match(/正则/i)
    arr:[“0”: 关键词内容, “index”: 下标i ]
    问题: 只能找一个敏感词

  9. 查找所有敏感词的内容:
    var arr=str.match(/正则/ig) global
    arr:[ 敏感词1, 敏感词2, … … ]
    返回所有找到的敏感词内容的列表
    如果找不到返回null
    强调: 凡是返回null的函数,都要先判断不是null,再使用。

  10. 既查找所有关键词的内容,又能获得每个关键词的位置

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 } 处);

  1. 高级替换: 根据本次找到的敏感词的不同,动态选择不同的新值替换
    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.切割
切割: 将字符串按指定的分隔符,分割为多段子字符串

  1. 简单切割: 分隔符是固定不变的
    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

  1. 复杂切割: 分隔符不固定
    var arr=str.split(/正则/)
  • 78
  • 82
  • 65
  • 90
  1. RegExp对象:
    什么是: 专门保存一条正则表达式,并提供用正则表达式执行查找和验证操作的方法。
    何时: 只要在js中使用正则表达式,都要创建正则表达式对象
    如何: 创建
  2. 用/方式:
    var reg=/正则/ig;
    问题: 不能在程序运行时,动态拼接正则,只能预先写死正则。
  3. 用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});}

功能:

  1. 验证:
    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(“密码格式不正确!”);
    }

  2. 查找: 查找所有敏感词的内容和位置
    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”

  1. Function对象:
    什么是: 保存一段可重用的代码段的对象,再起一个名字
    为什么: 重用一段代码
    何时: 只要发现一段代码需要反复使用时,都要定义为一个函数。
    如何:
  2. 声明方式:
    function 函数名(形参列表){
    函数体;
    return 返回值;
    }
    var 返回值=函数名(实参列表)
    问题: 会被声明提前:
  3. 用赋值方式:
    var函数名=function (形参列表){
    函数体;
    return 返回值;
    }

=============================================================================================day03

  1. Function对象
    创建: 3种:
  2. 声明方式:
    function 函数名(形参列表){
    函数体;
    return 返回值
    }
    会被声明提前
  3. 赋值方式(直接量方式):
    var函数名=function (形参列表){
    函数体;
    return 返回值
    }
    不会被声明提前
    揭示: 函数其实也是一个对象
    函数名其实只是一个简单的变量
  4. 用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.length
1){
console.log(现金结账:收您${arguments[0]})
}else{//否则,就刷卡支付
console.log(刷卡支付: 从您卡号${arguments[0]}扣款成功)
}
}
pay();//手机支付
pay(100);//现金支付,收您100元
pay(“6553 1234”,“123456”);//刷卡支付,…

2.匿名函数:
什么是: 定义函数时,不起名的函数,称为匿名函数
为什么: 节约内存!
何时: 只要一个函数,只使用一次时,都要用匿名函数,而不是有名称的函数
如何: 2种:

  1. 回调函数: 将一个函数作为参数传入另一个函数内,被其他函数调用
    比如: 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 替换值})
  2. 匿名函数自调: 也称为立即调用函数IIF,定义函数后自己调用自己,调用后,立刻释放
    什么是: 定义一个匿名函数,然后立刻执行
    为什么:定义一个临时作用域,减少使用全局变量, 避免全局污染
    何时: 今后所有的js代码,都要放在一个巨大的匿名函数自调中。
    (function(参数列表){函数体; return 返回值})(参数值列表)
    +function(参数列表){函数体; return 返回值}(参数值列表)
    3.作用域和作用域链
    函数中,没有用任何对象/this就直接访问的变量,在作用域链中找
  3. 作用域(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中的局部变量一同被释放

  1. 作用域链(scope chain)
    由各级作用域逐级引用,形成的链式结构
    保存着所有的变量
    控制着
    变量的使用顺序
    闭包
    词法表示包括不必计算的变量的函数,也就是说,该函数能使用函数外定义的变量
    3.闭包
    什么是
    即重用变量,又保护变量不被污染的机制
    为什么
    全局变量
    优:可重用
    缺:易被污染
    局部变量
    缺:不可重用
    优:不会被污染
    何时:
    即重用变量,又保护变量不被污染
    三特点(3步)
  2. 外层函数
    包裹受保护的变量和操作变量的内层函数
  3. 外层函数要返回内层函数的对象
    3种
    1. return function(){…}
    2. 直接给全局变量赋值一个内部function
    3. 将内部函数保存在一个对象的属性或数组元素中
      return [function,function,function]
      return { fun:function(){…} }
  4. 调用外层函数,用外部变量接住返回的内层函数对象
    形成闭包
    闭包如何形成
    外层函数调用后,外层函数的作用域对象(AO),无法释放
    被内层函数对象的scope引用着
    缺点:
    比普通函数占用更多内存
    多的是外层函数的作用域对象(AO)始终存在
    容易造成内存泄漏
    解决:
    如何释放闭包
    将引用内层函数对象的外部变量置为null
    导致内层函数对象被释放
    导致外层函数的AO被释放
    鄙视
    画简图
    2步
    1. 找受保护的的变量,确定外层函数调用后,受保护变量的最终值
    2. 找操作受保护的变量的内层函数对象
      结论:
      同一次外层函数调用,返回的内层函数对象,共用同一个受保护的变量

作业:

  1. 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
  2. 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
一.面向对象
什么是:程序中都是用对象结构来描述现实中的事物
为什么:便于大量数据的维护
何时:所有程序都是用面向对象的思想管理数据和功能
如何:面向对象三大特点:封装,继承,多态

  1. 封装
    什么是:创建一个对象,集中存储一个事物的属性和功能
    为什么:便于大量数据的维护
    何时:今后所有数据都是先封装在对象中,再按需使用
    如何: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步:
    1. 定义构造函数:
      function 类型名(形参1, 形参2, … ){
      this.属性名=形参1;
      … = …;
      this.方法名=function(){ … this.属性名 …}
      }
    2. 用构造函数创建多个对象:
      用new调用构造函数,创建出规定格式和功能的对象
      var 对象名=new 类型名(属性值,属性值,…)
      鄙视题: new的原理: 4件事:
    3. 创建一片空的存储空间
    4. 用新的空对象作为主语调用构造函数
      new Student(属性值,…)
      => new.Student(属性值,…) this->new{}

      => this.属性名=形参

      =>new{}.属性名=属性值
      在new{}中强行添加该新属性,并将属性值保存进对象中——野蛮的强行赋值方式
      结果: new{}中就反复添加了规定的新属性和方法
    5. 返回新对象的结果
  2. 继承
    父对象中的成员,子对象无需重复创建,就可直接使用
  3. 多态
    同一事物,在不同情况下,表现出不同的状态

============================================================================================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
不能进一步区分对象的类型

  1. var bool=类型.prototype.isPrototypeOf(child)
    不仅检查直接父对象,而且检查整个原型链
  2. 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一样
    鄙视:方法定义在原型对象中,还是定义在构造函数对象上
    答:如果方法仅限当前类型的子对象可用,其他类型的对象不可用,就定义在原型对象中,必须当前类型的子对象才能调用
    如果方法不确定将来调用它的对象类型,就定义在构造函数对象上,不需要任何对象实例,即可用构造函数名直接调用

自定义继承

  1. 仅修改两个对象间的继承关系:(仅修改一个对象的父对象)
    child.proto=father
    设置child的爹为father
    设置子对象继承指定父对象:Object.setPrototypeOf(child,father)
    获得子对象的父对象:var father=Object.getPrototypeOf(child)
  2. 修改构造函数原型对象,来修改所有子对象的父对象(同时更换多个子对象的爹)
    更换妈妈的老公
    Student.prototype=father,构造函数.prototype=father
    讲究时机:必须 紧跟在构造函数定义之后
    开始创建子对象之前
  3. 仅基于现有父对象,创建子对象,并扩展自有属性: 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; }
  4. 两种类型间的继承
    何时:如果发现多个类型拥有部分相同的属性结构和方法定义,都要抽象父类型
    如何:2步:
  5. 定义抽象父类型
    相同的属性结构定义在父类型的构造函数中
    相同的方法定义在父类型的原型对象中
  6. 让子类型继承父类型
  7. 在子类型构造函数中借用父类型构造
    extends
    让父类型构造函数帮助添加相同部分的属性定义
    子类型构造函数仅负责添加独有的属性定义即可
    错误:
    直接调用父类型构造函数
    this->window
    父类型中的属性都泄露到全局
    正确
    父类型构造.call(this, 参数1,参数2,…)
    简写:
    父类型构造.apply(this, arguments)
    鄙视:
    call vs apply
    相同:
    都是强行借用一个本来无法调用的函数,并临时替换函数中this为指定对象
    不同:
    call
    传入借用函数的参数,必须单独传入,逗号分隔
    apply
    传入借用函数的参数,放在一个数组中整体传入
    可自动打散数组类型参数
  8. 让子类型原型对象继承父类型原型对象
    inherits
    Object.setPrototypeOf( 子类型构造.prototype, 父类型构造.prototype )

多态:
什么是:一个函数在不同情况下表现出不同的状态
包括2中情况:
1.重载
2.重写(override):如果子对象觉得从父对象继承的成员不好用,可在子对象本地创建同名成员,覆盖父对象中的成员,使用时,只要自己有,就先用自己的。
3. 觉得从父对象继承的成员不好用
如何:只要在对象本地定义与父对象中同名的成员,即可。

  1. ES5:
    ECMAScript 第5个版本
  2. 严格模式:
    什么是: 比普通js运行机制,要求更严格的模式
    为什么: js语言本身具有很多广受诟病的缺陷
    何时: 今后所有的js程序,必须运行在严格模式下!
    如何:
    在当前作用域的顶部写:“use strict”;——启用严格模式
    规定:
  3. 禁止给未声明变量赋值:
  4. 静默失败升级为错误:
    什么是静默失败: 执行不成功,但还不报错。
    ES5中规定,所有静默失败,都升级为报错!
  5. 匿名函数自调和普通函数调用中的this,不再指向window!而是this=undefined。

说说严格模式的限制
1.变量必须先声明在使用
2.不允许重复声明变量
3.禁止使用arguments
4.eval不会再他的外层作用域引入变量
5.禁止this指向全局
6.函数参数不能有同名,否则报错
7.不能对只读属性赋值,否则报错
8.不能使用with语句
9.不能使用前缀0表示八进制数,否则报错
=================================================================================================day06
ES5
严格模式

  1. 禁止给未声明的变量赋值

  2. 静默失败升级为错误

  3. 匿名函数自调和普通函数调用中的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)
防篡改

  1. 防扩展
    禁止添加新属性
    相当于
    将对象的extensible:false
    判断是否已禁止扩展
    Object.isExtensible(obj)
    设置防扩展
    Object.preventExtensions(obj)
  2. 密封
    在防扩展同时,禁止删除现有属性
    相当于
    将每个属性的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属性
  3. 冻结
    Object.freeze(obj) 禁止修改obj中的所有属性的值
    原理: 1. 自动执行seal()
    2. 自动修改所有属性的writable特性为false

Object.create(): 如果没有构造函数,也想创建子对象继承父对象时

  1. 创建新对象
  2. 继承父对象
  3. 为新对象添加新属性
    如何:
    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不是想要的,都要用这三个去换
如何:

  1. 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为想要的对象
如何:

  1. 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

  2. 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

  1. 当仅临时替换本次调用函数中的this时——call
    当实参值列表是数组形式时,必须将call换成apply,自动打散数组为单个值
  2. 如果一个函数需要反复替换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.遍历
对数组中每个元素执行相同的操作

  1. arr.forEach():
  2. 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的两个缺点:

  1. 声明提前: 打乱了程序正常执行的顺序

  2. 没有块级作用域: 块内的变量很有可能提前到块外部,影响原本正确的外部的程序。
    块: 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的两个作用:

  3. 阻止声明提前: 在let a之前,不允许使用变量a
    在程序中,相同范围内,不允许重复声明两个let a;

  4. 让if while for do while块都变成作用域,块内部的变量无法提前到外部,也就不会影响外部。
    let的原理: 其实let底层就是一个匿名函数自调
    let相当于做了两件事:

  5. 用匿名函数自调包裹当前范围内的剩余代码

  6. 将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句话:

  1. 去function,在()和{}之间加=>
    (形参列表)=>{ 函数体; return 返回值 }

  2. 如果形参列表中只有一个形参,可省略()
    形参1=>{ 函数体; return 返回值 }

  3. 如果函数体只有一句话,可省略{}
    形参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:
    快速遍历一个数组的内容的最简化写法
    总结: 遍历一个数组:

  4. for循环
    for(var i=0;i<arr.length;i++){
    arr[i] //获得当前数组元素
    }
    最灵活的遍历,即可控制遍历的顺序,又可控制遍历的步调

  5. forEach简化:
    arr.forEach((elem,i)=>{
    elem //当前数组元素
    })
    问题: 只能从头到尾依次遍历每个元素,不能控制顺序(从后向前或从前向后)和步调(2,4,6,… 5,10,15,… )
    何时: 如果既关系内容,又关心位置,但不会改变遍历的顺序或步调时

  6. for of更简化:
    for(var elem of arr){
    //of会依次取出索引数组中的每个元素值
    elem //当前数组元素
    }
    问题:只能获得元素内容,无法获得元素位置
    所以: 仅关心元素内容时,才用for of
    vs for in

    1. 使用场合: for of专门遍历索引数组
      for in专门遍历关联数组
    2. of vs in:
      of只取元素值
      in 只取属性

    比如: var names=[“亮亮”,“然然”,“东东”]
    for(var name of names){
    //of依次取出names数组中每个人名保存到前边的变量name中
    console.log(name + " - 到!");
    }
    参数增强:

  7. 默认值:
    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

总结:

  1. 如果在定义函数时,形参列表中…arr,是收集剩余参数保存到数组的意思

  2. 如果在调用函数时,在实参列表中…arr,作用是相反的,是打散数组为单个值,再传入。
    解构:
    什么是: 仅提取出一个大对象中的部分成员单独使用
    何时: 如果只使用一个大对象中的一小部分成员时
    如何: 3种情况:

  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

  4. 对象解构: 属性对属性:
    比如:
    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();
    原理:

  5. 参数解构:
    什么是: 在向函数中传参时,将一个大的对象,打散后,传递给对应的形参变量.
    何时: 多个形参不确定时,又要求按顺序传入时、
    为什么:

    1. 默认值: 只能应对结尾一个参数不确定时
    2. 剩余参数: 虽然可应对多个参数不确定的情况
      但无法规定传入参数的顺序
      比如:
      function calc(ename, base,饭补, 高温,房补)
      calc(lilei, 10000, , 200, 600)
      calc(hmm, 10000, , , 200)
      calc(jack, 10000, , 300, 500)
      总结: 当多个形参都不确定时,且每个实参值必须对位传给指定的形参变量时,单靠调整形参的个数和顺序,无法满足所有调用的情况
      解决: 参数解构:
      如何: 2步:
    3. 定义形参时,所有的形参变量都要定义在一个对象结构中
      比如:
      //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(返回结果));
      }
      }
    4. 调用函数传参时,所有实参值,都要放在一个对象结构中整体传入。
      比如:
      ajax({
      url:“http://localhost:3000/products/getProductsByKwords”,
      type:“get”,
      data:“kw=macbook i5”,
      dataType:“json”
      });
      总结: 参数解构,其实就是对象解构在传参时的应用而已

======================================================================================day09
1.ES6
OOP:

  1. 对对象直接量的简写: 2处:
  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
  3. 对象直接量中方法的定义不需要加":function"
    为什么: 对象中的方法,不能用箭头函数简写。一旦简写,方法中的this不再指向当前对象,很可能指向全局的window。
    比如:
    var lilei={
    sname:“Li Lei”,
    intr:function(){//不能改成箭头函数
    … this.sname …
    }
    }
    解决:
    var lilei={
    sname:“Li Lei”,
    intr (){//不要加箭头
    //等效于intr:function(),可保持this不变!
    … this.sname …
    }
    }
  4. 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中构造函数创建的对象用法和存储结构是完全相同的
  5. class之间的继承:
    问题: 两种类型之间存在部分相同的属性结构和方法定义,代码可能出现重复编写。
    解决: 定义抽象父类型: 2步:
    1. 定义抽象父类型集中定义多个子类型共有的属性和方法
      1.1 在父类型的构造函数中仅定义相同部分的属性
      1.2 在父类型原型对象中仅定义相同部分的方法
    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步:

  1. 定义异步函数内:
    function liang(){
    return new Promise(function(open){
    亮要执行的所有代码(包含异步代码)
    //当亮的任务执行完,自动打开open()
    //当open(),自动执行下一个then中的函数
    })
    }
  2. 多个支持promise的异步函数,用.then串联起来
    liang().then(ran).then(dong).then(function(){结束})

DOM
============================================================================================day01

  1. 什么是DOM:
  2. DOM Tree:
  3. 查找元素:
  4. 什么是DOM: document object model
    什么是: 专门操作网页内容的一套对象和函数的集合
    DOM其实是一个标准: W3C制定的,规定了所有浏览器操作网页内容的API统一标准。
    为什么: 1. DOM是操作网页内容的唯一办法!
    2. DOM标准几乎所有浏览器100%兼容.(ie8以下除过)
    何时: 今后只要操作网页的内容,只能用DOM
    包括:
    查找元素,修改元素,添加元素,删除元素,事件绑定
  5. DOM Tree:
    什么是: 内存中保存网页中所有内容的树形结构
    网页中的一切内容,在内存中,都是保存在一棵DOM树中
    为什么: 网页中的内容是父子级嵌套的包含关系
    树形结构是最直观的描述上下级包含关系。
    如何:
  6. 当浏览器读取到一个html内容时
    先在内存中建立一个树根节点: document
    document就代表整个网页
    document.write(“xxx”) 向网页中写入一句话
  7. 浏览器一边扫描HTML内容,一边在DOM树上创建节点对象
    网页中每项内容都是DOM树上的一个节点对象
  8. 查找元素: 4种:
  9. 不需要查找,就可直接访问的元素:
    document
    document.documentElement ->
    document.head ->
    document.body ->
    提示: 在控制台中输出元素节点,仅输出DOM树结构,而不是输出节点对象
    如果确实需要查看一个元素对象的属性: console.dir(元素)
  10. 按节点间关系查找:
    节点树: 包含所有网页内容的完整树结构
    2大类关系:
  11. 父子: 4种:
    元素.parnetNode 获得元素的父节点
    父元素.childNodes 获得父元素下的所有直接子节点的集合
    返回: 多个平级子元素组成的类数组对象
    父元素.firstChild 获得父元素下的直接子节点中第一个节点
    父元素.lastChild 获得父元素下的直接子节点中的最后一个节点
  12. 兄弟: 2种:
    元素.previousSibling 获得元素的前一个平级的兄弟节点, 前一个兄弟
    元素.nextSibling 获得元素的下一个平级的兄弟节点,下一个兄弟
    问题: 网页中一切内容都是节点,包括看不见的空格,换行都是节点对象,极大的干扰查找!
    解决: DOM出了一种新的树:
    元素树: 仅包含元素节点的树结构
    优点: 不会受看不见的空字符干扰查找!
    2大类关系:
  13. 父子: 4种:
    元素.parnetElement 获得元素的父元素
    和parentNode几乎通用
    因为能当爹的一定是元素
    父元素.children 获得父元素下的所有直接子元素的集合
    返回: 多个直接子元素组成的类数组对象
    父元素.firstElementChild 获得父元素下的直接子元素中第一个元素
    父元素.lastElementChild 获得父元素下的直接子元素中的最后一个元素
  14. 兄弟: 2种:
    元素.previousElementSibling 获得元素的前一个平级的兄弟元素
    元素.nextElementSibling 获得元素的下一个平级的兄弟元素
    何时使用按节点间关系查找:如果已经站在DOM树的某个节点上了,想找周围附近的节点时,首选按节点间关系查找,且必须用元素树。不要用节点树。

===============================================================================================day02
正课:

  1. 查找

  2. 修改

  3. 查找: 4种:

  4. 不需要查找就可直接获得的元素: 4个
    document 根节点对象
    document.documentElement 根元素对象
    document.head 元素对象
    document.body 元素对象

  5. 按节点间关系查找: 2大类关系,6个属性
    何时: 已经站在树上的一个节点上了。

  6. 父子关系:
    元素.parentElement 获得元素的父元素
    .parentNode
    元素.children 获得元素的所有直接子元素
    返回: 所有直接子元素对象组成的类数组对象
    元素.firstElementChild 获得元素的第一个直接子元素
    元素.lastElementChild 获得元素的最后一个直接子元素

  7. 兄弟关系:
    元素.previousElementSibling 获得元素的前一个相邻的平级兄弟元素
    元素.nextElementSibling 获得元素的后一个相邻的平级兄弟元素
    问题: 不可能所有的查找都从body为起点,一旦元素藏的很深,代码会很繁琐

  8. 按HTML特征查找:
    何时: 首次查找元素时
    包括: 4个函数:

  9. 按id查找:
    var elem=document.getElementById(“id”)
    获得 元素 按 id
    强调: 1. .前的主语必须是document
    2. 仅返回一个匹配的元素对象
    如果找不到: 返回null

  10. 按HTML标签名查找:
    var elems=任意父元素.getElementsByTagName(“标签名”)
    获得 元素们 按标签 名
    <标签>
    强调:

  11. .前的主语可以是任意父元素
    比如: table.getElementsByTagName(“tr”)
    只查找当前table下的所有tr元素对象
    ul.getElementsByTagName(“li”)
    只查找当前ul下的所有li元素对象

  12. 返回当前父元素下所有符合条件的元素的类数组对象。如果找不到,返回空的类数组对象([].length=0)

  13. 不仅查找直接子元素,且在所有后代中查找
    比如:

    • ul.getElementsByTagName(“li”) 8个

      • ul.getElementsByTagName(“li”) 3个











  14. 按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];

  15. 按class属性查找:
    var elems=任意父元素.getElementsByClassName(“class名”)
    强调:

  16. .前可以是任意父元素

  17. 返回所有符合条件的后代元素的集合
    如果找不到返回空集合:[].length=0

  18. 不仅查找直接子元素,且在所有后代中查找符合条件的。
    比如:

    • ul.getElementsByClassName("child") 6个
      • ul.getElementsByClassName("child") 3个
  19. 如果一个元素被多个class修饰,只使用其中一个class就可以找到元素


    var elems=父元素.getElementsByClassName(“alert”)
    父元素.getElementsByClassName(“alert-danger”)
    都返回: 类数组对象: [
    对象 ].length=1
    [0]
    如果只想获得唯一的div元素对象:
    必须再多写一步下标:
    var div=elems[0]

总结: 4种查找方式:

  1. 按id查找:
    var elem=document.getElementById(“id”)

  2. 按标签名查找:
    var elems=父元素.getElementsByTagName(“标签名”)

  3. 按name属性查找:
    var elems=document.getElementsByName(“name”)

  4. 按class属性查找:
    var elems=父元素.getElementsByClassName(“class”)

  5. 其实js中也可以使用选择器查找元素: 2个函数:

  6. 如果确定只找一个元素:
    var elem=任意父元素.querySelector(“任意复杂选择器”)
    var img=document.querySelector("#ctrl img")

  7. 如果查找多个元素:
    var elems=任意父元素.querySelectorAll(“任意复杂选择器”)
    按选择器查找,效率不如按HTML特征查找
    总结:

    1. 如果仅靠一个条件就可查找到想要的元素:
      首选按HTML查找——快
    2. 如果查找条件复杂
      首选按选择器查找——简单
  8. 修改
    内容:

  9. 获取或修改元素开始标签到结束标签之间的原始HTML片段: 元素.innerHTML

  10. 获取或修改元素开始标签到结束标签之间的纯文本内容: 元素.textContent
    vs 元素.innerHTML 多干两件事:
    1. 去掉所有内嵌标签
    2. 将特殊符号翻译为正文

  11. 所有表单元素的值,都必须用.value才能获得
    样式:

  12. 如果修改内联样式:
    元素.style.css属性=“属性值”
    比如: div.style.display=“none”; 隐藏
    相当于:


    div.style.display=“block”; 显示
    相当于:

    div.style.width=“400px”; 必须加单位
    相当于:

    特例: 如果css属性名包含-(-杠),则必须去横线,变驼峰
    首字母小写,之后每个单词首字母大写
    修改元素的背景图片:
    div.style.backgroundImage=“url(…)”
    相当于:

    属性: 3种:

  13. HTML标准属性:
    HTML标准中规定的,所HTML元素都具有的属性: 比如: title, class, id, value, name … …
    2种方式修改:

    1. 最早的一批核心DOM函数: 4个
    2. 获取一个元素指定属性的值:
      元素.getAttribute(“属性名”)
      比如:
      a.getAttribute(“href”)
      -> “http://tmooc.cn”
    3. 修改一个元素指定属性的值:
      元素.setAttribute(“属性名”,“新值”)
      比如: a.setAttribute(“target”,"_self")
      结果:
    4. 判断是否包含指定属性:
      var bool=元素.hasAttribute(“属性名”)
      比如: a.hasAttribute(“target”) => true
      a.hasAttribute(“title”) => false
    5. 移除属性:
      元素.removeAttribute(“属性名”)
      比如: a.removeAttribute(“target”)
      结果:
      问题: 太长!
    6. 使用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="">
  14. 四大状态属性:

  15. 自定义扩展属性:

==========================================================================================day03
正课:

  1. 修改:

  2. 添加/删除元素:

  3. HTML DOM常用对象:

  4. 修改:
    内容: .innerHTML .textContent .value
    属性:

  5. 标准属性: 2种:

  6. 核心DOM: 4个函数:
    .getAttribute() .setAttribute() .hasAttribute()
    .removeAttribute();
    坑: 核心DOM 仅对字符串类型的属性起作用

  7. HTML DOM: 元素.属性名

  8. 三大状态属性: 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-自定义属性”)
样式:

  1. 修改内联样式: 元素.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
    属性:
  2. 标准属性: 核心DOM四个函数 或 . 都行
  3. 状态属性: disabled selected checked
    只能用.改
  4. 自定义扩展属性:
    只能用.getAttribute(“data-属性名”)
    .setAttribute(“data-属性名”,值)
    样式:
  5. 修改内联样式: 元素.style.css属性名=“值”
  6. 如果批量修改样式,最好用 .className=“xxx”
  7. 添加/删除
  8. 添加新元素: 3步
  9. 创建新的空元素:
    var a=document.createElement(“a”)
    结果:
  10. 为新元素添加必要的属性
    a.innerHTML=“go to tmooc”;
    a.href=“http://tmooc.cn”
    结果: go to tmooc
  11. 将新元素挂载到DOM树的指定父元素下:
    为什么: 浏览器显示网页内容是根据DOM树上的节点对象画出来的。如果DOM树上的节点不变化,则浏览器不回重绘页面。
    要想让浏览器将新元素画出来,只能新元素添加到DOM树的指定位置,改变DOM树,才会触发浏览器的重绘操作。
    如何: 3个函数:
    1. 追加到指定父元素下的末尾
      父元素.appendChild(a)
    2. 插入到指定元素之前
      父元素.insertBefore(a,child)
      将a插入到父元素下的child元素之前
    3. 替换现有元素
      父元素.replaceChild(a, child)
      用a替换父元素下的child元素
      优化: 尽量减少操作DOM树的次数,避免重绘
      为什么: 每修改一次DOM树,页面会重绘一次
      解决: 2种情况:
    4. 如果同时添加父元素和子元素到DOM树
      应该先在内存中将子元素全部添加到父元素中
      最后将整个父元素一次性添加到DOM树上
      只会触发一次重绘!

=================================================================================================day04
正课:

  1. 添加/删除
  2. HTML DOM常用对象
  3. BOM
  4. ***事件
  5. 添加/删除:
    优化: 尽量减少操作DOM树的次数
  6. 如果同时添加父元素和子元素时
    应先将子元素都添加到父元素中
    最后,再一次性将整个父元素加入DOM树
    结果: 浏览器只重绘一次
  7. 如果父元素已经在页面上了,要添加多个平级子元素
    应先将平级子元素添加到文档片段对象中
    最后,再一次性将文档片段对象添加到DOM树
    结果: 浏览器也只重绘一次
    什么是文档片段: 内存中临时保存多个子元素的虚拟父元素对象
    何时使用: 只要向页面中添加多个平级子元素时
    如何: 3步:
  8. 创建文档片段:
    var frag=document.createDocumentFragment();
    创建 文档 片段
  9. 将子元素添加到文档片段中
    frag.appendChild(child)
  10. 将整个文档片段添加到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中常用的全局函数或属性:

  1. 对话框: alert() confirm() prompt()

  2. 窗口大小:
    2组:

    1. 完整窗口大小: window.outerWidth/outerHeight
    2. 文档显示区: window.innerWidth/innerHeight
      文档显示区: 浏览器窗口内专门用于显示网页内容有限的白色区域。
  3. 打开和关闭窗口:
    打开窗口: window.open() 和的用法几乎完全一样
    总结: 用程序打开新链接有几种效果:

    1. 在当前窗口打开,可后退:
      html:
      js: open(“url” , “_self”)
    2. 在当前窗口打开,禁止后退:
      js: location.replace(“新url”)
      用新url代替history中当前旧的url,保持history中只有一条记录。从而禁止后退。
    3. 在新窗口打开,可同时打开多个:
      html:
      js: open(“url” , “_blank”)
    4. 在新窗口打开,只能打开一个:
      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. 通过操作地址栏中的地址实现跳转和刷新时
如何:

  1. 获得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]

  2. 通过操作地址栏实现跳转和刷新:

    1. 在当前窗口打开新链接,可后退:
      location.href=“新url” <==> open(“新url”,"_self")
    2. 在当前窗口打开新链接,禁止后退:
      location.replace(“新url”)
    3. 刷新当前页面:
      location.reload(); <==> history.go(0)

navigator:
什么是: 保存浏览器配置信息的对象
何时: 判断浏览器的名称和版本号
如何:

  1. navigator的userAgent属性包含了浏览器的名称和版本号——字符串——没有标准
    测试: window 10的 Edge浏览器

========================================================================================day05

  1. 事件:
    什么是: 浏览器自动触发的或用户手动触发的,页面中元素状态的改变。
    事件发生时如何执行一项任务: 绑定事件处理函数
    什么是: 当事件发生时自动调用的一个函数
    本质: 是元素对象上一个事件名属性
    比如: btn.onclick sel.onchange txt.onfocus
    何时: 只要希望发生事件时自动执行一项任务
    如何:
    在事件发生前就提前绑定事件处理函数到元素的事件属性上。
    如何绑定: 3种:
    1. 在html中绑定:
      html中:
      js中: function fun(){ … }
      问题: 不符合内容与行为分离的原则,不便于维护
    2. 在js中用赋值方式绑定:
      js中: 元素对象.οnclick=function(){
      this->当前触发事件的元素对象本身
      }
      问题: 一个事件属性,只能绑定一个处理函数
    3. 在js中通过添加事件监听对象的方式: ——最灵活
      元素对象.addEventListener(“事件名”,处理函数)
      添加 事件 监听对象
      强调: 事件名本来是没有on: click, change, focus
      只不过在用.时,为了和其它普通属性区分,才加了on前缀。
      原理:
      什么是事件监听对象: 一个保存元素+事件名+处理函数一套组合的对象:
      比如: btnShoot.addEventListener(“click”,fun1);
      先创建一个事件监听对象:
      {
      元素: btnShoot,
      事件: click,
      处理函数: fun1
      }
      然后,addEventListener会将监听对象添加到浏览器中一个隐藏的事件队列中。
      当事件发生时,浏览器会遍历事件队列中的每个监听对象,选择匹配的监听对象中的处理函数执行。
      如何从元素上移除一个处理函数:
      元素对象.removeEventListener(“click”,处理函数)
      处理函数决不能用匿名函数
      强调: 如果一个处理函数有可能被移除,则绑定时,就不能用匿名函数。必须用有名称的函数。移除时,必须使用函数名,找到原函数对象,才能移除。

事件模型: 从点击元素触发事件开始,到事件处理函数执行完,期间所发生的一切事情。
DOM标准规定: 从点击元素触发事件开始,到事件处理函数执行完,期间共经历三个阶段:

  1. 捕获阶段: 由外向内,记录各级父元素上的处理函数
  2. 目标触发: 先触发最初发生事件的元素上的处理函数
    目标元素(target): 最初发生事件的实际想点击的元素
  3. 冒泡阶段: 由内向外,依次执行捕获阶段记录的父元素上的时间处理函数。

事件对象:
什么是: 当事件发生时,浏览器自动创建的,封装事件信息和操作事件的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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值