正课:
Js1k:用1k的代码做出炫目的效果。代码扩写
犀牛书
微信公众号:前端大全
1. ***变量
2. ***数据类型
一. ***变量:内存中存储*一个*数据的存储空间,再起一个名字
何时使用:程序中反复使用的数据,都要先保存在变量中,再参与运算
如何使用:声明 赋值 取值
1. 声明:在内存中创建一个新变量
如何声明:var 变量名;
仅声明,但暂未赋值,默认值为undefined
变量命名:1. 不能以数字开头,仅能包含字母,数字和下划线
2. 不能用保留字:js中已经预先使用的关键字
比如:name X
3. 见名知意
4. 驼峰命名:第一个单词首字母小写,
之后每个单词首字母大写:
比如:backgroundColor,listStyleType
2. 赋值:将等号*右边*的数据,保存到等号*左边*的变量中
如何赋值:变量名=值;
*对已经包含值的变量赋新值,新值会替换旧值*
建议:1. 在声明同时,初始化变量的值
如何在声明同时初始化:var 变量名=值;
3. 取值:只要使用变量名,在运行时,会被自动替换为变量中的值
特殊情况:3种:
1. 只声明,但未赋值的变量,可任意使用
比如:var hb; console.log(hb); //undefined
2. 从未声明的变量,不能使用(取值)!
报错:ReferenceError: XXX is not defined
3. 未声明的变量,却可赋值!——其实和var不一样
强烈建议:必须用var声明变量
***声明提前:在程序正式*执行前*,都会将var声明的变量提前到*当前脚本块*的顶部集中声 明。再开始执行程序。***赋值留在原地***
常量:一旦初始化,值不能改变的量
何时使用:程序中,一旦定义不可擅自改变的值,都要用常量保存
如何创建:const 常量名=值;
用作常量的名称,都要大写字母
强行修改常量的值,不会报错,但也无法修改成功!
二. ***数据类型:数据在内存中的存储形式
1.为什么要有不同数据类型:现实中,不同类型的数据,都有专门的用途
2.数据类型:2大类:*原始类型*的值和*引用类型*的对象
原始类型:值直接保存在变量本地的类型(值为一个,而且不能特别复杂)
原始类型5类:Number String Boolean undefined null
引用类型:值没有直接保存在变量本地的类型
变量中只保存指向实际数据所在位置的地址值
1. Number:表示所有数字,即可表示整数,又可表示浮点数(小数)
如何定义:凡是不加引号的数字,都是Number类型数据
何时使用:只要做计算或比较的数字,都不加引号
比如:年龄 价格
所占空间:所有数字都是用64位二进制数(8字节)存储
(个别)整数采用32位二进制(4字节)存储
1byte=8Bit
字节 位
结论:数值大小和所占空间,无关。
十进制转2进制:var i=500; console.log(i.toString(2));
舍入误差:计算机中也有无法精确计算的数字而导致的计算结果进度偏差。—不可避免
解决:按指定小数位数四舍五入
n.toFixed(2)-->将计算结果按2位小数四舍五入
2.String:字符串,表示所有文字信息
如何定义:凡是用作显示的信息都要加引号,单双引号不分
何时使用:凡是用作显示的信息都要加引号
比如:手机号 身份证号 QQ号
js中没有字符类型,所以一个字也要加引号。
所占空间:每个英文字母或数字,1字节
每个汉字,2字节
js的内存中都是用字符的unicode号存储字符
unicode:人为给主要语言中的每个字符编一个号
***字符串内容一旦创建,值不可改变,非要改变,只能用新字符串替换旧字符串。
特殊:+运算中,如果参与运算的都是字符串,则加法变为字符串拼接
var str="Hello";
str=str+"World"
console.log(str); //过程中共创建了几个字符串//3个
字符串不能修改是。
Day 02
回顾:
1. js数据类型:2大类:
原始类型: 5个Number String Boolean undefined null
引用类型
Boolean类型:只有两个值:true , false
何时使用:主要保存对/错,真/假, 成立/不成立,都用boolean类型
如何定义:不带引号的true和false就是boolean类型
正课:
1. ***数据类型转换
2. ***运算符和表达式
1. ***数据类型转换:
***js是弱类型编程语言:3点:
1. 声明变量时,不用考虑变量中将要存储的数据类型
2. 赋值时,动态判断数据的类型
因为是动态判断同一个变量,先后可保存不同类型的数据。
3. 运算时,js会根据自己的需要,动态转换数据类型
2大类:
1. 隐式转换:无需程序员干预,而自动完成的转换
先讨论算数运算中的隐式转换:
规律:1). 默认一切转数字类型,做算数计算
bool类型:true-->1 false-->0
2). +法中,只要有一个是字符串, 都转为字符串,+变为字符串拼接
其他类型转字符串: 在字面量前后直接加引号
(转字符串:true-->”true”)
表达式:由数据,变量和运算符组成的公式
*默认,从左向右,两两计算
NaN(Not a Number):“不是一个数字”的“数字(类型:指的是数字类型)”
何时遇到:当无法转为数字时,或无法正常计算时,结果都为NaN
特点:和任何数据做计算,结果永远为NaN
加法计算中,碰上字符串,也会转为"NaN"
typeof(x): 返回x的数据类型名称: Number String Boolean undefined
typeof(x): 可以测任意一个数据的数据类型(原始类型)
2. 强制转换:程序员主动调用专门函数完成的转换
1. 任意 to String: 2种:
1) var str=x.toString();
2) var str=String(x);-->相当于隐式转换
***String(x):万能
x.toString(): null和undefined转不了
何时使用:通常都是js在需要时,自动调用
原理:function String(x){
If(x===undefined){
return “undefined”;
}else if(x==null){
Return “null”;
}else{
Return x.toString();
}
}
2. ***任意 to Number:3种:
Number有自动去除空格的功能
1. 任意 to Number:var n=Number(x);-->隐式
何时使用:将非字符串类型转为数字时使用
将纯数字组成的字符串转为数字类型时
特例:Number("")-->0
Number(null)-->0
Number([])-->0
Number(undefined)-->NaN(not a number 参与任何算术计算,结果永远为NaN; 不大于不小于 不等于任何值[包括自己] NaN!=NaN。出现nan说明转不了,要返回检查。)
2. String to Number:
1. to 整数:var n=parseInt(str);默认情况下放字符串
何时使用:将*字符串*转为整数时使用
如果传入的不是字符串,则先隐式转为String
原理:从头开始读取字符串中每个字符
跳过*开头的*空字符 只读取数字字符
只要碰到第一个不是数字的字符,就退出 不认识小数点
Number( x) vs parseInt(str):
Number只认识纯数字组成的字符串
Number却可转bool类型
parseInt(str)认识包含非数字字符的字符串开头位置的数字
parseInt转不了bool
2. String to 浮点数:var n=parseFloat(str);
何时使用:只要将字符串转为小数时
用法和parseInt用法完全一样。
只认第一个碰到的小数点。
3. 任意类型 to Boolean: var bool=Boolean(x);
"" 0 NaN undefined null -->false
其余都是true
课堂练习:获取用户输入,保存在变量input中
var input=[window.]prompt("输入提示");
***凡是从页面上进来的,都是字符串!***
2. 运算符和表达式:
程序:*人的想法*在计算机中的执行步骤
运算符:程序中模拟人的思维运算或判断的符号
表达式:由数据,变量和运算符组成的一个公式
任何表达式都有返回值。
算数运算符:+ - * / % ++ --
返回值:只能是number,如果运算无效,返回NaN
算式运算中:隐式转换:默认转Number,再进行转换。
特殊情况:+法中,碰到字符串,都转字符串,再拼接
%模运算(取余数):m%n : m/n,取除不尽的余数部分
何时:1. 判断能否整除:
比如:判断奇偶数:
m%2等于0,说明m是偶数
m%2不等于,说明m是奇数
2. 限制计算结果,永远小于除数:
***递增,递减运算:++ --,也是赋值运算。
n++: n=n+1; 将n中的值+1,后再保存回n中
2种情况:都将n中的值+1
++n: 前++, 返回*新*值
n++: 后++, 返回*旧*值
前++,后++,变量值都会增加1,但是返回值不一样
例子:var my=10;
var lp=my++;
console.log(lp);//10
console.log(my);//11
var my=10;
var lp=++my;
console.log(lp);//11
console.log(my);//11
++n或n++单独使用,无影响;
如果参与其它表达式中时,会影响执行结果。
关系运算:做比较,比大小
返回值:true、false
隐式转换:默认都转为数字再比较
特殊情况:
1. 如果无法转为数字:转为NaN
NaN不等于,不大于,不小于任何值,甚至自己
NaN!=x-->true
isNaN(num): 专门判断一个num数值是否为NaN
如果是NaN,就返回true,否则返回false
何时使用:只要判断一个数字无效,就用isNaN
开发中何时使用:*反着用*
用isNaN判断一个num是不是数字,或是否能被隐式转为数字使用。
!isNaN(num): 返回true-->num是数字
返回false-->num不是数字
2. 如果两个都是字符串,则按位pk每个字符的unicode号
*************Number隐式转换会把空字符串转为0;****************
回顾:
关系运算:做比较,做判断,只可能返回true/false
> < >= <= != == -->都带隐式转换
|-------11-----| |--10--|
隐式转换:默认都转为number
特殊:3种
1. 如果无法隐式转为Number,会转为NaN
NaN不等于,不大于,不小于任何值
NaN!=x 永远返回true
isNaN(num): 如果num是数字,返回false
不是数字,返回true
何时使用:判断num是不是数字,或能否被隐式转为数字
!isNaN(num): 如果num是数字,返回true 不是数字,返回false
正课:
1. ***运算符和表达式:
***关系运算
***逻辑运算:
位运算:
扩展赋值运算:
***三目运算:
***关系运算:
特殊情况:
2. undefined vs null
undefined:所有变量的默认初始值,自动赋值
null:主动清空一个变量,释放一个对象
typeof的检测结果:
typeof(undefined)-->undefined
typeof(null)-->object
===: 全等 首先类型必须相同,其次值相同
10 全等不带隐式转换
何时使用:要求类型相同,且值相等时
3. 如果参与比较的两个值都是字符串类型
不再转数字,而是pk 每个字符的unicode号
比如:"jerry"<"scott"<"smith"
"jerry"<"scott"
"j"106<"s"115
"scott" "smith"
"s" "s"
"c"99 <"m"109
总结:凡是条件:一般都是关系运算或逻辑运算(关系运算和逻辑运算天生就是当条件的)
***逻辑运算:多个条件(关系运算),综合得出最后的结论,只可能返回true/false
隐式转换:默认转换为bool类型:Boolean(x)
何时使用:只要综合多个条件,得出最终结论
如何使用:3种:
&&: 读作且,程序中称为"逻辑与"
条件1&&条件2: 必须条件1和条件2都为true,结果为true
只要任意一个为false,结果为false
||: 读作或,程序中称为"逻辑或"
条件1||条件2:只要任意一个条件为true,结果为true
只有所有条件都为false,结果才为false
!: 读作不,程序中称为"非"
!条件:颠倒条件的true/false结果
运算符优先级:
() ++ -- ! */% + - <>>=<= != == ===
19 前15 15 14 13 11 10
后16
&& || 三目 赋值
6 5 4 3
***短路逻辑:逻辑运算中,如果前一个条件已经可以得出最终结论,则后续所有条件不再执行!
PPT: 短路逻辑:鄙视题
***利用短路逻辑实现按条件执行语句
条件&&操作:如果满足条件,才执行操作, 否则,什么也不做
何时使用:1个条件,1件事,满足就做,不满足就不做
利用逻辑或实现默认值效果或二选一取值
值1||值2:如果值1有效,就返回值1,否则返回值2
比如可以代替if语句,优先级的关系,若是&&后面是赋值的运算,最好加上括号,因为赋值的等号的优先级特别低。
例子:var price=prompt(“输入金额”);
If (price>=100){price=price*0.8}等同于(price>=100)&&(price=price*0.8)若用扩展简写,price*=0.8
例题:主人很懒
鄙视题:4&&5 4||5 0&&5 0||5 输出结果?
alert(4&&5);//5 alert(boolean())
alert(4||5);//4
alert(0&&5);//0
alert(0||5);//5
/*在逻辑运算中都自动转为boolean类型进行运算,由true或者false来看要不要进一步判断,逻辑运算的返回值不一定是布尔值
在利用短路逻辑时候,不再返回布尔值,若参与运算的都是值将在两个值之间选择一个返回
位运算:直接由二进制进行的运算,将数值的二进制左移或右移n位
左移n位:*2的n次方。1<<3,即1左移3位,是8。
右移n位:/2的n次方。n<<m,n乘以2的m次方。
固定套路:取整数:n>>>0 n^0 n|0
var n=34.56;
undefined
n>>>0
34
n^0
34
n|0
34
这三种方法都可以取整,是一个固定用法。
*赋值运算:基本赋值运算:=,将等号的右边的值保存在左边的变量中。赋值表达式也有返回值,会返回将要保存回变量的新值。
所有表达式都会返回一个结果,即都有返回值。
***赋值运算的结果,是等号右侧表达式的值!
chrome的控制台中:输入一个表达式,直接按回车,控制台默认输出表达式的结果
扩展赋值运算:对变量内部的值进行修改的简写方法
比如:total=total*0.8; -->total*=0.8; a=a+5-->a+=5表示累加;a/=5,a%=5;
何时使用扩展赋值运算:需要修改变量中的值,再存回变量中
包括:+= -= *= /= %= 用法完全一样
***三目运算(条件运算 三元):根据不同条件,判断执行不同操作,或返回不同的值
何时使用:多个条件,多件事,必须多选一执行!
如何使用:
条件1?操作1:
条件2?操作2:
...?... :
默认操作;
特点:1. 如果前一个条件已经满足,则不再向下判断
保证每次只可能有一个操作被执行
2. *默认操作不能省略!*
隐式转换:是暂时性的转换。
Day 03
正课:
1. ***函数
2. 分支结构
- ***函数:封装一项任务的步骤清单的代码段,再起一个任务名
作用:代码重用
函数是一个引用类型的对象
对象:内存中同时存储多个值得空间。——了解
何时使用:发现一项任务,被反复调用,要先将任务步骤封装为一个函数,再反复调用。
如何声明函数:
function 任务名([参数变量列表]){
步骤清单代码段
[return 返回值]
}
如何调用函数:函数名([参数值列表]);
强调:1. 函数只有被调用时,才会执行!
2. 函数可被反复调用!——代码重用!
参数变量:专门接收方法执行所必须的数据的变量
何时使用:如果一个函数,必须提供指定数据,才能正常执行时
需要提供几个数据,就定义几个参数接收
如何定义参数:不需要var,直接在函数名后的括号中定义参数名
多个参数,每个参数名之间用逗号分隔
何时,如何传入参数值:调用时,按照参数定义的个数和顺序传入
为什么使用参数:参数可让方法变的更灵活
***内存中函数生命周期:
1. 定义时:将函数直接封装在一个对象中保存——函数对象
***函数名,其实是指向函数对象的一个*变量*
***定义时,不会读取函数的内容
2. 调用时:在执行环境中增加一个当前函数的执行环境对象
才会逐行读取并执行函数的内容
3. 调用后:函数的执行环境弹出,活动对象被释放。
***作用域:一个变量的可用范围。scope
本质其实是一个存储多个变量的对象
Js中有2种作用域:
1. 全局作用域:专门保存全局变量的对象
2. 函数作用域:专门保存函数内部的局部变量的对象
——活动对象(专门保存函数的局部变量的地方)
局部变量包含:2类:1. 参数变量 2. 在函数内声明的
局部变量:只在函数调用时的函数内部才可使用!
全局变量:在任何时候,任何位置都可被访问
鄙视题:
返回值:函数调用的执行结果(不是所有函数都有)
何时使用:只要函数的执行,需要有明确的返回结果时
定义返回值是要看调用者是否需要获得执行结果
如何定义返回值:在函数定义结尾:return 返回值
返回值只能返回一个值,不能返回多个
何时如何获得返回值:调用时:
var 变量=函数名(xxx);
***按值传递:两变量间赋值或向函数中传递参数时
都是将原变量中的值复制一个副本给对方
修改一方,另一方不受影响
var a=10;//按值传递:原始类型的值
var b=a;//变量中的值复制给对方
a++;
console.log(b);
var my=["包","包","包","包","包"];//按值传递:引用类型的对象
var lp=my;//按值传递,将变量中的地址复制给对方;
lp[0]="";
console.log(String(my));
my[1]="";
console.log(String(lp));
例题:
***函数声明提前hoist:在正式执行程序前,都会先预读所有var声明的变量和function声明的函数, 集中到当前作用域的顶部集中声明。
***赋值留在原地***
例题:
定义函数的第二种方法:
var 函数名=function(参数){函数体;return 返回值}
vs 声明函数
第二种方法,函数定义不会被提前,仅函数名提前 0
声明函数,整体(函数名+函数定义)提前
问题一:如果一个表达式或函数有结果,就可直接当一个值用
问题二:如果传入参数个数不符,不会报错:
个数超了,多出的没用,个数不够,未接到值得参数变量,默认值undefined
问题三:在函数内,为没有声明过的变量赋值, 变量会被自动创建在全局-->危险
强烈建议:所有变量使用前,必须用var声明
问题四:return特点:2个:
1. return与返回值之间不能加回车
2. return不能放在其他表达式中间使用
例题:判断是否为闰年:
作业:100题中的2,4,5(不用第三个变量,交换两变量的数值)
回顾:(鄙视题)
var n=10;
function fun(n){
console.log(m);
var m=n;
n++;
console.log(m);
}
fun(n); //undefined 10
console.log(n); //10
console.log(m); //报错
//第二种
正课:
1. 全局函数:(基本考哪些是全局函数哪些不是)
2. *分支结构:
1. 全局函数:ES标准中规定的,浏览器厂商已经实现的
不需要任何对象即可直接调用的函数
手册中:课程表->JavaScript->JavaScript对象->JS Functions
干扰项:BOM,凡是在BOM阶段讲的都不是全局函数,alert不是全局函数,不是ES标准。
编码解码:
encodeURI: 对包含多字节字符的url字符串编码为单字节字符组成
decodeURI: 将encodeURI编码后的字符串,解码为原文
为什么:url不允许包含多字节字符:比如汉字
www.baidu.com/s?kw=张东
%E5%BC% A0%E4%B8%9C,三个百分号是一个字,是汉字的utf-8编码格式,在这种格式中一个汉字占3个字节。
问题:url还不允许包含保留的特殊符号:比如 : /
解决:encodeURIComponent/decodeURIComponent
冒号在url中区分协议和主机名 ,而斜杠用来表示路径上下级的区分。
Encode和decode只能编码解码多字节,保留字不可。
eval: 专门执行字符串格式(string格式)的js语句
还可计算字符串格式的表达式的值
普通模式下,eval中代码的作用域就是eval所在的作用域,在严格模式下,eval自己一个作用域,eval中声明的变量或者函数在eval外不能用。
除数为0:js中除数可以为0,结果为: Infinity
Infinity,可参与关系运算
isFinite(num): 专门判断一个数字是否在有效范围内
是 有限的
Num在有效范围内返回true;超出最大数字范围为false。
全局函数要了解。
2. *分支结构:重点
程序3种结构:顺序 分支 循环
顺序:除去声明提前,其余代码默认都从上向下顺序执行
分支:根据不同的条件,执行不同的代码
循环:让程序反复执行一段相同代码。
*分支:3种:
1. 一个条件,一件事,满足条件就做(,不满足就不做)
如果操作简单:利用短路:条件&&(操作1,操作2,...);操作可以用逗号分开写多个。
如果操作复杂:if( 条件 ) { 操作代码段 }
//如果满足条件,就执行操作
2. 一个条件,两件事,满足条件做第一件,不满足做另一件
如果操作简单:三目:(条件,三元)运算。
条件?操作1:操作2;
满足条件吗? : 否则
如果操作复杂:if(条件){
满足才执行的代码
*如果前一个条件满足,则不再继续判断*
}else{
不满足才执行的代码
}
完整的分支结构,都是必须且只能多选一执行。
3. 多个条件,多件事,多选一执行,可一件不执行
如果操作简单:三目:条件1?操作1: 条件1?值1:
条件2?操作2:
...?... :
默认操作; //不能省略
例子:function getLevel(score){
alert(score<0||score>100?"无效成绩":
score>=90?"A":
score>=80?"B":
score>=60?"C":
"D"
)
}
getLevel(prompt("输入成绩")); “D”那里不能加分号,还没有结束。
如果操作复杂:if(条件1){
操作1;
}else if(条件2){
操作2;
}else if(...){
...;
}else{
以上条件都不满足,则执行的默认操作
}
强调:2点:
1. 最后的else可省略,一旦所有条件都不满足,则什么都不做。
2. 条件可以是任意关系运算或逻辑运算
只要返回true和false的函数或表达式,都可当条件用
例题 :
Day 04
正课:
1. *分支结构:switch case
2. ***循环结构:
1. *分支结构:
1. 一个条件,一件事:
2. 一个条件,两件事:
3. 多个条件,多件事:
任意条件:
专门判断等值比较的条件:switch case
何时使用:如果所有条件都是全等比较时
switch case因为不带隐式类型转换,效率稍微高一些。
语法:switch(表达式){
case 值1:
操作1;
表达式的结果===值1,才执行的代码段;
case 值2:
表达式的结果===值2,才执行的代码段;
case ...:
...;
[default:
如果表达式的结果和之前所有case值都不相同
才执行默认代码;]
}//default可省略的
switch:入口:进入第一个匹配的case中,开始依次向下执行
break: 中止当前结构的继续执行
break只能自己一句话。
何时不加break:最后一个不用加break. 相邻两个case执行相同操作时,中间不加break,这样会减少代码量
等值比较一般用switch,比起ifelse它的效率更高。
三目运算中语法规定最后一个默认规定默认操作不能省略。
而ifelse中最后一个else可以省略。
2. ***循环结构:让程序反复执行一段相同代码
循环三要素:
1. 循环条件:保证循环可以*继续*执行的判断条件
*不是退出的条件*
2. 循环变量:循环条件中,用作判断的变量
从几开始,每次变化多少?
保证循环不变为死循环,循环变量都要向着不满足循环条件的趋势发生变化
3. 循环体:循环反复执行的代码段2
循环体中都要迭代变化循环变量的值。
while循环:(凡是遇到循环变量没有规律的)
语法:var 循环变量=初始值;//声明并初始化循环变量
while(循环条件){//当满足循环条件时,才进入循环执行
循环体;
迭代(有规律的反复)变化循环变量;
}
何时使用:循环变量的变化没有规律时
例题
break: 中止当前结构的执行
退出循环:2种方式:
1. 优雅: 完全使用循环条件控制循环退出——难度高
2. 野蛮: 在循环体中,任意位置强行break——随意
do-while:
语法:var 循环变量=初始值;
do{
循环体;
迭代变化循环变量;
}while(循环条件);
变化:while后的循环体,剪切到while前,开头加do,结尾加;
do-while vs while
只看开始循环前,条件是否满足:
开始循环前,条件都满足:则两者完全一样
开始循环前,条件不满足:do while至少可执行一次循环体
while一次都无法执行
何时使用do-while:如果第一次条件都不满足,也至少希望执行一次时
for: for循环完全等效于while循环
何时使用:只要循环变量变化有规律!就用for循环
语法:
for(var 循环变量=初始值 ; 循环条件 ; 迭代变化循环变量){
循环体;
}
特殊写法:
1. for中第一部分,可同时声明并初始化多个变量
2. for中第三部分,可同时执行多个小操作,但要注意执行顺序
3. for循环条件可省略,省略后是死循环
比如:for(;;)代替while(true)
其实,如果if结构或循环结构下只有一句话,可省略大括号
强烈建议,if结构和循环结构,都要严格加{}
作业:打印三种* 100题:1,9,11
Day 05
回顾:
1. 循环:
三要素:
1. 条件: 继续执行的条件
2. 变量: 从几开始,每次如何变化
迭代变化循环变量
3. 循环体:
while: var 循环变量=初始值;
while(循环条件){
循环体
迭代变化循环变量
}
do-while: var 循环变量=初始值;
do{
循环体
迭代变化循环变量
}while(循环条件);
while vs do-while:
第一次条件就满足:两者完全一样
第一次条件不满足:do-while,至少执行一次
while, 一次都不执行
for:
for(var 循环变量=初始值 ; -> 循环条件; <- 迭代变化循环变量){
↓ ↑
循环体-------------|
}
for的特殊情况:
1. 第一部分可同时声明并初始化多个循环变量,用逗号分隔
2. 第三部分可同时执行多个小操作,用逗号分隔
3. 死循环:for(;;)代替while(true)
***作用域:vs Java
js: 2级作用域:全局,函数
Java: 3级:全局,函数,块({})级
补:continue: 中止本轮循环,继续下一轮
颠倒执行continue的条件,可省略continue,实现跳过本轮效果
例题
正课:
1. ***数组:多个变量的集合,起一个统一的名字——用法
连续保存多个数据的存储空间,再起一个统一的名字——存储
为什么使用数组:
程序=数据结构+算法
好的数据结构可以极大提高程序的执行效率!
何时(为什么)使用:程序中都是用数组集中管理多个数据
如何使用:创建 赋值 取值
1. 创建:4种
1. 创建空数组:var 数组名=[]; -->js中见到[ ],就是数组
2. 创建数组同时,初始化数组中的每个元素:
var 数组名=[值1,值2,值3,...];
内存中的数组:引用类型的对象
每个值相当于一个元素, 每个元素都有一个下标
下标从0开始,每次增1,到个数-1结束
数组对象的个数: arr.length(自动保存的)
arr.length永远等于最大一个下标+1
Arr[arr.lenth-1]访问数组最后一个元素,arr[arr.length]在数组末尾追加新元素。
(数组可以直接输出)
3. 创建空数组:var 数组名=new Array(n);
人话:创建一个数组类型的对象
new 创建一个新对象
Array 数组类型(类型决定了数组如何存储,下标如何分配)
n表示初始元素的个数,省略则创建空数组,不省略创建
4. 创建数组同时初始化数组元素:
var 数组名=new Array(值1,值2,...);
new Array的歧义: new Array(7)
如何使用数组中每个元素:数组名[i]
数组名[i]的用法和普通的变量完全相同! 特殊:越界都不会报错,在取值的时候会返回undefined,赋值时候会在指定位置添加新元素并自动修改length属性。
下标不连续的叫稀疏数组。
面试:数组是引用类型的对象:垃圾回收-释放不再被引用的对象。
垃圾回收器:在后台自动执行的。回收不再使用的对象的程序,一个对象每被一个变量引用,引用计数器都会加1.
何时回收:只有一个对象,不再被任何变量引用(引用计数器为0时),垃圾回收期就会自动释放对象的空间。
问题是无法实时回收。建议:使用完较大的对象后,主动释放总是好的习惯,如何释放:变量名=null
2. 赋值:数组名[i]=值 将“值”存入数组的下标为i的元素中
2不限制:和其他语言最大的不同
1. 不限制元素个数:没有数组越界错误,
length自动增长为最大下标+1
2. 不限制元素的类型:(原理:js中的每个元素都是变量,变量是不限制类型的)
3. 取值 : 任何位置使用数组名[i],等效于直接使用数组名[i]中的值
数组是引用类型对象:按值传递
原始类型的值,赋值后,相互不影响
引用类型的对象,赋值后,依然引用同一个对象
任何修改,都会影响对方。
null:主动释放一个对象或者初始化一个变量
垃圾回收器: 内存中的垃圾回收器,专门释放内存中不再被引用的对象的小程序
伴随主程序执行
每个对象都有一个引用计数器,增加引用+1,减少引用-1
一个对象,引用计数器为0,不再被任何变量引用,被回收
建议:使用完较大的对象后,都要主动释放
例题:结果:[0,2,3]
数组的length属性固定套路:2个
1. 获得最后一个元素值: arr[arr.length-1]
2. 向数组末尾追加一个新元素:arr[arr.length]=新值;
例题:
遍历数组: 依次对数组中每个元素执行相同操作
//三要素:
1. 条件: 下标i<数组长度
2. 变量: 下标i 从0开始,每次++
for(var i=0;i<arr.length;i++){
当前元素:arr[i]
}
Chrome中的console.log有时是异步执行,伴随主程序同时执行
zhangdong@tedu.cn
正课:
1. ***数组:
关联(hash)数组
数组API
有些数组必须指明意义。
关联(hash)数组:可自定义下标的数组(hash算的是字符串的code)
姓名 数学 语文 英语
小笼包 81 61 89
索引数组:自动分配下标的数组
问题:2个
1. 下标无法表示元素的意义
2. 无法精确查找指定元素
解决:自定义下标的关联数组
如何创建:1种:
1. 先创建空数组: var arr=[];
2. 再向数组中添加元素: arr[key]=value;
强调:key必须是字符串
***length属性无效!***永远为0
使用:arr[key]
关联数组中的key不能重复
***所有数组如果访问不存在的下标位置,不会报错!
会返回undefined
关联数组优点:快速精确查找!不需要遍历!和数据量无关!相当于把下标名字符串给Hash算法,很快查找到。
Hash算法:内存中的一个小程序,专门接收一个字符串,并计算出一个尽量不重复的编号,相同的字符串计算出的号一定相同。不同的字符串计算的号一般不同。添加元素时候,
如何遍历hash数组:for(var key in arr)结构
for(var key in hashArr){//反复取出每个key放入变量key中,key中获取的仅是元素的下标名称
当前元素的值: hashArr[key]此处不加双引号,本来key就是字符串类型,加了双引号反而把该变量写死了。
}其实也可以遍历索引数组,但无法控制开始和结束
今后:只要遍历索引数组,选普通for循环
只要遍历关联数组,只能用for in循环
鄙视题:一次性获得关联数组中所有的下标号
例题一:返回数组中最大值
例题二:删除数组中的重复的元素
第二种:
例题三:
2. 数组API:浏览器厂商已经实现的方法,开发人员直接调用,不需要了解具体实现。
将数组转化为字符串:2个,hash数组不是正经的数组。
1). var str=arr.toString(); 返回数组中元素的内容,用逗号分隔
(除了null和undefined都有toString,返回数组中元素的内容,用逗号分隔)
2). var str=arr.join("连接符"); ***可自定义连接符***
固定套路:
1. 无缝拼接数组每个元素:var str=arr.join("")
如果不加"",分隔符为空,等效于toString,默认按逗号分割
2. 将单词拼接为句子: var str=arr.join(" ");,与上一个的区别是双引号之间有空格。
3. 将数组拼接为html元素:“<开始标签>”+
Arr.join(“</结束标签></开始标签>”)+”<结束标签>”
例题:
优化:数组的拼接比字符串拼接效率更高!(因为字符串创建了不能更改)
建议:今后凡是频繁的字符串拼接,都要2步:
1. 先将要拼接的子字符串放入一个数组中
2. 调用数组的join方法,一次性生成结果字符串
拼接数组:var newArr=arr1.concat(arr2,值1,值2,......),此例中arr1不发生改变。会自动打散参数中的数组,以单个元素形式拼接到新数组中。而且打散的方式平级加上去。
强调:concat不修改原数组,总是返回一个新数组
获取子数组slice:var subArr=arr.slice(starti,endi+1); 获取arr中starti位置到endi位置的元素组成新子数组。
特殊:1.含头不含尾的原则,不包含结束的位置。
2.第二个参数可省略,表示一直获得到结尾。
3.支持负数参数:-n表示倒数第n个元素。
本质:相当于arr.Length-n
Arr.slice(3,5)获取的是数组中的3和4两个下标的元素。
starti: 开始获取的下标位置
endi:表示获取到的下标位置
***含头不含尾
slice方法支持倒数参数:其实负数下标不存在
假定的负数下标从-1开始 slice(6,-4)
splice: 删除 插入 替换 --> 直接修改原数组对象
1. 删除: arr.splice(starti,n) var deletes=arr.splice(starti.n)
n表示个数。
从starti位置开始,直接删除数组n个元素。
删除arr中starti位置开始后的n个元素。
强调:返回被删除的元素组成的临时数组
但是会返回删除的元素组成的新数组,若是删掉的元素不要,不用赋值。
2. 插入: arr.splice(starti,0,新值1,新值2,......)
在arr中starti位置插入新元素
在starti位置插入新值1,新值2,......
原starti位置及其之后的元素,被向后顺移
强调:不支持打散参数中的数组
3. 替换: arr.splice(starti,n,新值1,新值2,......)
删除arr中starti位置开始的n个元素,再在starti位置插入新元素。
删除的元素个数和插入的新元素个数不必相等
新元素的个数和n不一定相等
数组会自动调整元素的位置和长度
对二维数组来说,splice不会自动打散参数中的数组。
颠倒数组中所有元素的位置:arr.reverse(); 直接改变原数组
作业:100题:14,16, 18
Day 06
回顾:
- 手动排序:冒泡排序:
公式:x+ =y;
y= x-y;
x- = y;
正课:
- ****数组的sort方法:arr.sort();直接修改原数组
特点:默认按升序排列
默认一切都转为字符串,再按字符串比大小,比较的是unicode号。只有在排序字符串类型的元素时候用。如果元素不是字符串,则隐式转换为字符串。
自定义排序规则:2步:
1. 自定义比较器函数:专门比较任意两值大小的函数
规定:两个参数(a ,b),
必须返回数字:如果a>b,就要返回正数
如果a<b,就要返回负数
如果a=b,就要返回0
例:①function cmp(a,b){return a-b;}负责比较大小
②arr.sort(cmp);负责交换
2. 将比较器函数对象作为参数传入sort方法中函数也可以当做参数
arr.sort(比较器函数名); //函数名不加圆括号,作为参数
(sort的核心原理)
强调:将函数作为参数传入另一个函数时不加()。
其他时候:如果另一个函数,需要补充一段逻辑才能正常执行时,就要将逻辑封装在一个函数中,在作为参数传入。
函数 vs 方法:(都是function对象,函数和方法本质一样的)
函数:不属于任何对象的叫函数(不需要通过.访问)
方法:属于特定对象的函数叫方法(需要通过对象.才可访问)
***函数也是引用类型的对象
例题:
总结:函数里如fun():如果是fun(),就是调用函数
如果传入的是fun:叫参数
(人话:把函数给别人,别人用,传的是函数,不是结果,所以只传函数名,不加括号)
***升序改降序: 只要给比较器的逻辑 —
降序排列:
1.自定义比较器:只要颠倒比较器函数的比较逻辑
2.默认字符串比较:先sort升序,在reverse反转
①function cmp(a,b){return -(a-b);}
②arr.sort(cmp);
正课:
1. ***数组API:
*栈和队列
2. ***二维数组:
3. ***String对象
创建一个函数:2种
1.声明方式:可被声明提前
function 函数名(参数列表){函数体;return返回值}
2.以函数直接量方式创建:不会被提前
var 函数名=function(参数列表){函数体;return返回值}
说明:函数名其实就是一个变量
函数其实就是一个对象
函数名变量报错了函数对象的地址,是引用的关系。
作用域:js中:只有2级作用域:全局作用域和函数作用域,没有块作用域
Java:有3级作用域:全局作用域 ,函数作用域,块作用域{}
1. ***数组API:
*栈stack和队列queue:其实都是数组,只不过使用了不同的方法
栈:一端封闭,只能从另一端进出的*数组*
何时使用栈:只能从数组一端进出(如果希望总是使用最新进入数值的元素时)
特点: LIFO
如何使用:结尾出入栈:
入栈: arr.push(新值); (将值压入数组结尾) ==>arr[arr.length]=新值;
出栈: var last=arr.pop();(弹出数组最后一个元素)
优点:无论出入栈都不影响已有元素的位置——效率高
从开头出入栈:
入栈: arr.unshift(新值); (将值压入数组的开头),用右向左读取数值。
出栈: var first=arr.shift( ) (取出数组第一个元素)
缺点: 每次出入栈都会影响所有剩余元素的位置发生顺移1——低
队列:只能从一端进入,必须从另一端出,结尾进入开头出的数组。希望按照先来后到的顺序依次使用元素的时候使用。
何时使用:只要先到的先得
FIFO:
结尾入队列:arr.push(新值);push与concat,concat返回的是新值。一次压入多个值用push。
开头出队列:var first=arr.shift();
- ***二维数组:数组的元素又引用了另一个子数组,多个数据需要分类存储时候。
何时使用:专门保存横行竖列的二维数据
将一个大数组的元素,在进行分类时(公司有很多部门,部门有很多员工)
如何创建:2步:
1. 创建一个空数组:var data=[];
2. 设置数组中每个元素再引用另一个小数组
data[0]=[0,0,0,0];
访问二维数组中任意位置的元素:data[r][c],获得r行c列的元素。
找不到数组都会显示undifined,
对二维数组,若是行r越界,则会报错。若是r不越界c越界,会显示undefined.
例题:生成4*4的二维数组,随机生成2;
***二维数组行下标r不能越界,越界就报错
(行越界会返回undefined,而[undefined]是非法的,)
遍历二维数组:固定套路:
外层循环遍历行r,内层循环遍历*当前行的*中的列c
for(var r=0;r<data.length;r++){
for(var c=0;c<data[r].length;c++){
当前元素:data[r][c]
}
}
补:函数 vs 方法
相同:本质都是function
差别:函数不属于任何对象
方法指的定对象内的函数
Vs数组:1.下标访问每个字符
2.都有length属性记录字符个数
3.不直接修改原数组的API都可用
concat slice
3.***字符串对象:多个字符组成的只读*数组*,一旦创建不能修改,所以数组的sort方法不能用,因为sort方法会修改数组。
相同:用[i]方式访问每个字符。
和数组相同的API:
访问字符串中任意一个字符: str[i]
字符串的长度:str.length
凡是不直接修改原数组的API,字符串都能用:
比如: var substr=str.slice(starti,endi+1);
var newstr=str.concat(otherstr);
不能用:reverse() push() str[i]='字符'
内置对象:ES标准已经规定好,由浏览器厂商已经实现的对象
11个(就是API):
开发人员直接使用,不必关心具体实现。
String Number Boolean --包装类型,和小写的不同。
Array Date RegExp(正则表达式) Math
Error
Function(所有函数的祖宗) Object(所有对象的祖宗)
Global(全局)-->window,在ECMA标准中没有window这个词。
(包装类型是面试题)
***包装类型:专门封装原始类型的数据,并提供对原始类型数据操作方法的对象。原始类型的值不能拥有属性和方法。但是实际开发中需要对原始类型的值进行相同操作。
(所有对象的功能,第一个都是封装数据,第二个就是提供操作数据的方法)
何时使用包装类型:不用主动使用
生命周期:试图用原始类型的值调用方法时,自动创建包装类型的对象。封装原始类型的值。
调用的方法,其实是包装类型对象的
当调用完方法后,包装类型对象用完自动释放!
具体创建何种包装类型,要看值得类型
调用的是包装类型对象的函数,调用完自动释放,并且把值返回。
为什么有包装类型:原始类型的值没有任何方法!(eg:var price=1.6;中的1.6是没有任何方法的)
为什么null和underfined不能调用任何方法:因为没有对应的包装类型。
***new String(xxx) vs String(xxx)
String(xxx):隐式类型转换为字符串,返回原始类型的字符串值
new String(xxx): 创建一个String包装类型的对象,
返回String包装类型的对象。
转义字符:如果字符串内包含与语法冲突的特殊符号,
可用转义字符将特殊符号标记为普通字符,比如双引号的冲突。
比如:"\" '\' \变为\\
如果在字符串中包含特殊功能
比如:\n 换行 \t 一个tab键 \u 一个16进制unicode号
作业:day01素材->preview-->homeword_string_mario
String API: *所有API都无法直接修改原字符串,必定返回新字符串*,要转变需要接一下,比如str=。。
1. 大小写转换:
var newStr=str.toLowerCase(); //全部转小写
var newStr=str.toUpperCase(); //全部转大写
何时使用:只要不区分大小写!
2. 获取指定位置的字符或unicode:
var char=str[i]或str.charAt(i);-->(模仿java,为了保证语法的一致)
var unicode=str.charCodeAt(i);(i是指字符串中的下标);返回
作业: 请用户输入任意一句话,保存在msg中
定义一个编码函数encode,接收一个字符串参数msg
遍历字符串中每个字符
将每个字符转为unicode,重新拼接到新字符串上
返回新字符串
测试代码:var msg=prompt("输入一句话");
alert(encode(msg));//xxxxxxxxxxxxxxxxxx
3. 查找关键字的位置:2个API , 返回的都是关键字的位置下标(找到的都是关键字第一个字的位置)
找下一个匹配的关键字位置:var i=str.indexOf("kword",starti)
如果省略starti,从0开始
找前一个匹配的关键子位置:
ex:
ex2:
var i=str.lastIndexOf("kword",starti);
如果省略starti,从最后一位开始
starti:开始查找的位置
两种方法找到的都是同一个值,只是查找的方向不一样,如果没找到,都返回-1
Day 07
回顾:
1. String API:
var unicode=str.charCodeAt(i);
number
var i=str.indexOf("kword"[,starti]); //找右侧下一个,默认从头
str.lastIndexOf("kword"[,starti]);//找左侧下一个,默认结尾
lastIndexOf只要关键子第一字符<=当前位置
循环中会用到if(i==0){break;}来防止刚好零位置就是关键词时候死循环。
笔试题:
正课:
1. String API:
获取子字符串
2. ****正则表达式
3. ****js中支持正则的API
1. String API:
str.charAt(i) => str[i];
1、获取指定位置字符的unicode号
str.charCodeAt( I );
2、获取子字符串:3种:
var sub=str.slice(starti,endi+1);
str.substring(starti,endi+1); //不支持负数参数,但是可以用str.length-1代替。原生字符串自带的方法
str.substr(starti,n);//n表示要截取的字符个数,不考虑含头不含尾。
例题:
2. ****正则表达式(Regular Expression):
概念:专门规定字符串中字符串的规律,是一个格式的定义。
何时使用:1.用规格,模糊匹配多种敏感词时
2.用规则验证字符串的格式:密码强度,邮箱格式,日期…….
比如:验证字符串格式、 模糊查找、替换/格式化字符串、切割字符串
语法:
1. 最简单的正则:就是关键字原文
2. 备选字符集:规定了*某一位*可选的字符列表
必须且只能多选一匹配
语法: [备选字符列表]
[^]是除了...的意思,但必须放在[]开头位置
例题:
2.1如果备选字符中部分unicode连续,可用”-”省略中间的字符
常用省略:一位数组:[0-9] 、一位小写字母: [a-z]、一位字母: [a-zA-Z]
正课:
1. ****正则表达式
2. ****String支持正则的API
1. ****正则表达式:
1. 最简单的正则:关键词原文
2. 备选字符集:某一位可用的备选字符列表
语法:[备选字符列表]
强调:一个中括号,必须且只能匹配一位。两个中括号,则后面匹配两个
2.1 连续备选字符: -
[^47]除了4 7
3. 预定义备选字符集: 对常用字符集的简写
\d -->1位数字 [0-9]
\w -->1位字母,数字或_ [0-9a-zA-Z_]
\s -->1位空字符,比如:空格 制表符 换行
\大写 -->\小写 的反义:
比如:\D 表示非\d 除了数字
‘.’ -->除了换行和回车后的任意字符
一个任意字*符:. 一个点
4. 量词:规定相邻的前一个字符集出现的次数,重要
如何使用:紧跟在一个字符集之后
数值数量:{min,max}(最少,最大) eg:\d{6,8} -->数字6-8位
{min,}(几位以上)
{n}(必须n个) --->\d{6}------>6位数字
不确定数量:
?: 可有可无,最多1个,比如0086?,问号之修饰6,若想修饰整个0086,需要加个括号。
*: 可有可无,数量不限
+: 至少1个,多了不限
5. 选择和分组: 选择:或 | 分组: ( ),或在正则中的优先级特别的低
比如:手机号:
+86或0086
至少一个空字符 \s+
地区代码和空字符整体 可有可无,最多1次
1
在34578中选其一 [34578]
9位数字 \d{9}
((\+86|0086)\s+)?1[34578]\d{9}
身份证号:前15位数字
再两位数字
一位数字或Xx
后三位,整体可有可无,最多1次=********`1*
\d{15}(\d{2}\d[0-9Xx])?
7. 匹配指定位置:
^ 匹配开始位置的xxx, eg: ^\s+匹配字符串开头的空字符
$ 匹配结束位置的xxx, eg:\s+$ 匹配字符串结尾的空字符
匹配开头或结尾的空字符: ^\s+|\s+$
前加^后加$,且中间没有|:^xxxx$必须和正则完全匹配
***今后:做验证:都要前加^,后加$,即在验证中需要完全匹配。
\b:表示单词边界:eg:\bno\b,只找单词no .如果要匹配独立单词,要两边同时加\b.
匹配首字母和结尾字母相同的单词:首字母:\b[a-zA-Z]
中间字母:[a-zA-Z]+
结尾字母:1\b\
\b 的范围比较广,空格什么的也都算在内。
8. 预判:(?):在正式匹配前,先大概预判整个字符串的规则
多用于排除法 比如:密码强度:8位字母,数字字符,
必须包含一个大写和一个数字
所有字符:4大类:数字,小写字母,大写字母,特殊符号
1. 排除:不全由数字和小写字母组成:
结论:可能包含大写字母或特殊符号 (?![0-9a-z]+$)
解读:(?![0-9a-z]+$) 指预判从头到尾是否只有...组成,
“+”如果不加只表示结尾是否只有...组成
2. 排除:不全由大写字母和小写字母组成:
结论:可能包含数字或特殊符号 (?![a-zA-Z]+$)
3. 只能由字母或数字组成——排除了特殊符号 \w{6,8}
最终:(?!^[a-zA-Z]+$)(?^[0-9a-z]+$)[0-9a-zA-Z]{8}
或者^(?![a-zA-Z]+$)(?[0-9a-z]+$)[0-9a-zA-Z]{8}$
9:汉字备选字符集:[\u4e00-\u9fa5] eg:[\u4e00-\u9fa5]{3,5}-->3-5个汉字
jQuery中封装了大量正则表达式,比如电子邮件,html标签,url地址
2. ****js中String支持正则的API:
reg: 正则表达式对象:
创建正则表达式:var reg=/正则表达式/属性后缀
属性后缀(是js里面4500的):i 忽略大小写!Insentitive的开头。
g 查找或替换全部匹配内容
查找:2种:
1. 只判断是否包含关键字:不考虑具体内容和个数
var i=str.search(reg); 只能返回第一个找到的关键字的位置 找不到返回-1
2. 获取所有关键字的内容:不考虑位置,不返回位置。正常情况下正则表达式是区分大小写的。
Step1:创建 正则表达式加后缀g
var kwords=str.match(reg); 返回所有关键词的数组,保存在kwords数组中
***找不到,返回null,都要先判断不为null,再使用
kwords.length,如果kwords为null,则报错!
数组是个对象,没有找到返回的是null ,不能加点要先判断再使用。
不加g的话也可以使用但是找到的是子字符串。
例题:
替换或删除子字符串:
var newStr=str.replace(reg,"替换字符");
格式化字符串:
正则表达式中的每个(),都是一个子表达式
每个子表达式都会自动获得一个从1开始的编号
替换时,可用$n,指代本次匹配中第n个子表达式的对应内容
分隔字符串:切割字符串。
var subs=str.split(reg[,count]); (count表示切后要几个,subs为数组)
100题:17 19 20
Day 08
正课:
1. ***RegExp对象
2. *Math对象
3. *Date对象
对象:存储:内存中存储多个数据的存储空间,相对于变量而言
用途:封装数据,并提供对数据的操作方法
1. ***RegExp对象:
何时使用:只要在js中使用正则表达式,都要先创建RegExp对象
如何创建:2种:
1. 直接量:var reg=/正则表达式/ig;-->(js中只要见到//都是正则表达式),ig可加可不加看需求。
何时使用:使用固定的正则表达式时--->不灵活
强调:"正则表达式"中的/都要变为\/,
正则表达式"中的/都要变为\/ 转义。
2. 使用new:var reg=new RegExp("正则表达式"[,"ig"]);--->凡是类型名首 ,对象的类型名一般都要大写开头。 何时使用:如果正则表达式需要动态拼接
var kwords=["明明","静静","日日"];
var reg=new RegExp(kwords.join("|"),"g");
var msg="明明喜欢我,却不告诉我\n"+
"\t别理我,我想静静"+
"静静是谁?\n"+
"日日思君不见君\n"+
"\t日日是谁\n"+
"床前明月光\n"+
"\t明月是谁";
var arr=msg.match(reg);
document.write(String(arr));
动态拼接。
正则API:2个:
查找:4种:
1. 查找固定关键字,仅返回位置,可指定开始位置:-->不支持正则
var i=str.indexOf("kword"[,starti]);
str.lastIndexOf("kword"[,starti]);
何时使用:关键字确定 通过循环,反复获得每个关键字的位置
2. 只判断有没有:var i=str.search(reg);
返回值:找到返回i,没找到返回-1
3. 获取*所有*关键字的内容:var kwords=str.match(reg);
返回值:找到返回所有关键字内容的数组
没找到返回null,必须先判断不是null,再使用
4. 既获得每个关键字的内容,又获得每个关键字的位置:
var arr=reg.exec(str)
特点:1. Arr为数组,每次只返回一个关键字的内容,新的关
键字替换上一个,存在arr的[0], 如果找不到了,返回null
2. 将本次找到的关键字的位置保存在arr.index
3. 自动调整reg.lastIndex属性为下次开始的位置(默认0开始)
所以每个敏感词都是arr[0];因为会替换。
例题:
(对比)
课堂练习:网络爬虫
1. 正则:<a[^>]*href=['"][^'"]*['"][^>]*>.*</a>
修改为懒惰模式:<a([^>]*?)href=['"]([^'"]*?)['"]([^>]*?)>(.*?)<\/a>
默认贪婪模式:尽量匹配最长的符合条件的子内容
懒惰模式:只匹配最短的符合条件的子内容
贪婪 改 懒惰:.* --> (.*?) (?这里的表示阻止)
.*最容易引起贪婪模式
2. RegExp.$n: 获得本次匹配中第n个子表达式匹配的内容
3. 替换: str=str.replace(reg,"替换值")
如果reg中没有g,只替换第一个匹配的
例题:网络爬虫
验证:var bool=reg.test(str);用的非常多。
如果str符合reg的要求,返回true,说明验证通过
否则返回false,说明验证未通过
***强调:凡是验证,前加^后加$
如果不加,只要局部匹配,就通过。
页面中找表单:var form=document.forms["id"|i]
form是封装了表单所有内容的对象 提供了提交表单的方法!
form.submit(); 手动提交表单
在表单中找元素:var input=form.elements["name"]
input是封装了一个input元素所有属性的对象
input.value属性,获得用户输入的内容字符串
其实.elements可省略
正课:
1. *Math
2. *Date
1. *Math:封装了数学计算的常量值和方法
***没有构造函数,不能实例化对象
实例化:用new创建一个指定类型的具体对象——实例
instance
Math常用API:
取整:3种:
上取整 下取整 四舍五入取整
Math.ceil(n) Math.floor(n) Math.round(n)
n.toFixed(d) vs Math.round(n)
n.toFixed(d): 按指定d位小数四舍五入,*返回字符串*
Math.round(n): 不能指定小数位数,只能取整
*返回number*
乘方和开平方:
乘方: Math.pow(底数,幂);
开平方: Math.sqrt(n);
最大值和最小值:默认不支持查找数组中的最大值,但可以设置。
最大值: var max=Math.max(值1,值2,值3,......);
//默认不支持数组
固定套路:获取数组中的最大值/最小值
var max=Math.max.apply(Math,[值1,值2,...]); apply相当于把数组中的数字打散交给max。
min min
随机数:Math.random() 0<=r<1,取到0的几率非常低可以认为取不到。
公式:min~max取随机整数
Math.floor(Math.random()*(max-min+1)+min)
parseInt()
简化版的公式在0到任意值
2. *Date: 封装了1970年1月1日0点至今的毫秒数,只要在程序中保存日期或者时间就需要用到Date对象。
创建:4种:
1. 创建日期对象,同时获得客户端*当前时间*
var now=new Date();
2. 自定义任意时间点:年 月 日 时 分 秒
var date=new Date("xxxx/xx/xx[ xx:xx:xx]");
//00:00:00
3. 自定义任意时间点:
var date=new Date(xxxx,xx,xx[,xx,xx,xx]);
月,都要-1修正,计算机中的月份从0开始计算,这种定义需要修正 月份,所以自定义时间一般用上面那个方法。
4. 复制一个日期对象:
何时使用:日期API都直接修改原日期对象
如果希望留住旧日期对象,就需要先复制,再修改副本
如何复制:var old=new Date("xxx");
var target=new Date(old.getTime());
其中:old.getTime() 返回旧时间中的毫秒数
Var d1=
Var d2=new Date(d1)
Date对象API:
分量:年 月 日 星期
FullYear Month Date Day
时 分 秒 毫秒
Hours Minutes Seconds Milliseconds
同JAVA一样,用从1970年一月一日零点开始计算的毫秒数,比如1000*3600*24就是1970年的一月二日的零点。
console.log(Number.MAX_VALUE);可以计算计算机能存储的最大数值。
1. 每个分量都有一对儿get/set方法
var n=date.getXXX()获得分量的值
date.setXXX(n)设置分量的值,Day没有set,只能读不能改。直接修改原对象方法
2. 命名:年月日星期,没有s结尾
时分秒毫秒,都有s结尾
3. 返回值或者说取值范围。:Date从1开始到31结束
除Date外,其余都是从0开始到 进制-1结束
强调:只有month需要修正,其余都正常
一月 二月 三月 。。。。。。十一月 十二月
0 1 2 10 11
月中的日:
4. 日期转字符串:
Date.
var str=date.toLocaleString(); //日期+时间
var str=date.toLocaleDateString(); //日期
var str=date.toLocaleTimeString(); //时间
Date的计算:2种:
1. 两日期对象可相减,结果是毫秒差
2. 对日期任意分量做加减:3步:取分量,做加减,set回去
set方法自动调整进制
固定套路:date.setXXX(date.getXXX()+/-n);
Day 09
回顾:
1. Date对象:
封装:1970年元旦至今的毫秒数
API:1. 每个分量都有一对儿get/set方法
星期Day,没有set方法
2. 年月日星期,不带s;时分秒毫秒,带s结尾
3. 返回值:只有Date从1开始,到31结束
其余都从0开始,到进制-1结束
只有Month需要修正
计算:2种:
1. 两日期相减: 结果毫秒差
2. 对任意分量做加减:
固定套路:date.setXXX(date.getXXX()+/-n)
强调:1. set方法直接修改原日期对象:
如果保留旧日期对象:先复制,再计算
var old=new Date();
var target=new Date(old.getTime());
2. set方法自动调整进制
正课:
1. *错误处理:
2. *****函数对象:
1. *错误处理:
错误:程序执行过程中,发生的导致程序无法继续执行的状态
错误处理:即使程序出错,也要保证程序不退出的机制。
Error对象:在错误发生时,*自动创建*的封装错误信息的对象
属性:err.name: 错误类型:6种
SyntaxError:语法错误
ReferenceError:引用错误,找不到对象或变量时。
TypeError:类型错误,错误的使用了对象的*方法*
RangeError: 范围错误,参数超范围
EvalError: Eval错误,错误的使用了eval函数
URIError: URI错误
如何错误处理:
语法:try{
可能出错的正常代码。;
}catch(err){//一旦出错,err中自动获得Error对象,,catch中的代码只有在try中代码出错的时候才执行。
只有出错,才执行的错误处理代码;
}[finally{
无论是否出错,都要执行的代码;即无论正常或者错误执行都要执行的代码。释放资源。
}]
强调:1. 被try包裹的代码,执行效率会降低。
所以,try应尽量少的包裹代码
2. err不能去掉,err.message可以看到详细的错误信息。
建议:能用if屏蔽的问题,优先选择if
大多数try catch都可提前预防
***鄙视题:js中如何解决浏览器兼容性问题:3种
1. 不兼容的类型或方法:2种:
if else
try catch
2. 不兼容的值:短路逻辑中的||
值1||值2
IETester问题:
1. 只支持document.write输出
2. 只要出错,当前窗口不能继续使用,必须打开新窗口
3. 将网页文件拖拽到窗口内,自动加载。
***错误处理中的return:
1. finally中的return,会替换try catch中的return
2. 即使finally中没有return,finally中的代码一定会在return前执行。但无法影响已确定的return结果
抛出:自定义错误:
何时使用:方法定义者向方法调用者抛出使用错误
如何抛出:throw new Error("错误消息")
正课:
1. *****函数对象
***重载
****匿名函数
****作用域和作用域链
*****闭包
1. *****函数对象:
执行环境栈:ECS(Execute Contect Stack),保存全局以及每个函数的执行环境的栈结构
执行环境:EC,调用函数时,创建的引用函数资源的对象
窗口一打开,默认ECS中压入一个全局EC
全局EC引用了window对象 VO window对象中的变量都是全局变量
变量对象:VO,专门存储变量的对象
函数生命周期:
定义时:仅创建一个函数对象,封装了函数的定义。 不会读取函数的内容。
调用时:创建函数的EC对象压入ECS中
函数的EC对象引用了,当前函数的活动对象AO
活动对象:AO:专门保存本次函数调用时的局部变量
AO中有一个属性始终指向window对象
变量使用规则:优先在AO中找,
找不到,才去window对象找
调用后:函数的EC对象出栈,AO对象失去引用,被回收
AO对象中的局部变量一起被释放
***重载: overload
相同名称,不同参数列表的多个函数
在调用时,可根据传入参数的不同,动态选择对应的函数执行
现实中不同操作步骤的相关任务通常都使用相同的任务名。
虽然js语法默认不支持重载,JS不支持同名函数,会覆盖。下面的会覆盖上面的。但可用arguments对象模拟重载效果
arguments对象:调用时,自动创建并接收所有传入的参数值
类(like)数组(的)对象(人话:像数组的对象,本质是个对象,用法和数组很像。)
2点像数组:1). arguments[i] 2). arguments.length
何时使用:今后只要多个操作,共用同一个函数名时
***类数组对象 vs 数组对象(鄙视题):
相同:都可以下标访问每个元素。都有类似属性。但是类数组对象不鞥呢使用数组的任何API。Arguments在调用函数时会在函数函数内部自动创建,
类型不同:类数组对象的父类型是Object 数组对象的父类型是Array
导致类数组对象无法使用数组类型的方法
类数组对象 to Array:固定套路:
var arr=Array.prototype.slice.apply/call(arguments);
为什么还要定义参数:作用:2个:
1. 提示调用者,该如何传入参数
2. 简化函数定义者,在函数内使用参数值
结论:今后大部分方法依然要定义参数
只有不确定传入参数的个数时,才省略参数,用arguments
****匿名函数:定义时,不用任何变量引用的函数
何时使用:2种情况:
1. 如果一个函数只执行一次——匿名函数自调
语法: (function(参数变量){函数体;return 返回值})(参数值);(可以写参数值)这个圆括号可以写在里面或者外面。
比如(function(){alert(“开始加载”)})()
2. 如果将一个函数作为对象或者说参数交给其他函数使用时——回调
调用的时机和参数和函数定义者无关。
凡是只用一次的函数都用匿名函数,不用变量,比如arr.sort(function(a,b){return a-b})
创建一个比较器函数(鄙视题):3种(都不是匿名):
1. 声明方式定义函数(不是匿名):function compare(a,b){return a-b}
***只有声明方式定义的函数才能被声明提前
***以下两种方式定义的函数,不能被提前
2. 函数直接量方式定义:var compare=function(a,b){return a-b}
3. 使用new关键字: var compare=new Function("a","b","return a-b");-->必须加引号
鄙视题:js中有几种方式创建函数:
匿名函数优点:节约内存空间--> 调用前和调用后,内存中不创建任何函数对象
总结:回调:arr.sort(function(a,b){return a-b});
自调:(function(a,b){return a-b})();
课后练习:
****作用域与作用域链:
作用域:用法:一个变量的可用范围,其实就是变量的实际存储位置
本质:EC对象的一个scope属性,引用了window或AO(activedObject)对象(scope叫作用域),函数对象中有个scope属性记录了函数从哪里来。
只可能是window中或AO中
window:全局作用域——全局变量
AO:函数作用域——局部变量,AO中有个parent属性。
作用域链:以当前EC的scope属性为起点依次引用每个AO,直到window结束,形成的。从当前函数的AO到
多级引用关系,scopechain
只要在作用域链上存在的变量,当前函数都可使用。
*****闭包:
问题:全局变量 vs 局部变量
全局变量:优:反复使用,且共享使用
缺:可能随时在任意位置被篡改——全局污染
建议:尽量避免使用全局变量
局部变量:不可反复使用!方法调用完自动释放
解决:闭包:反复使用一个局部变量,且不会被污染
何时使用:想反复使用一个局部变量,且不希望被污染时,就要用闭包结构保护局部变量
如何使用:3步:
1. 定义外层函数(工厂函数)
特点:2个:
1. 定义了受保护的局部变量
2. 返回一个专门操作局部变量的内部函数对象
2. 调用外层函数(工厂函数),获得返回的内部函数对象
3. 使用获得的内部函数对象,操作受保护的局部变量——唯一途径
例题:取号机
判断闭包:3特点:
1. 内外层函数嵌套
2. 内层函数必须使用了外层函数的局部变量
3. 外层函数将内层函数返回到外部,可在外部调用
判断闭包结果:2句话:
1. 外层函数调用了几次,就有几个受保护的局部变量副本
2. 同一次外层函数调用返回的内部函数对象,操作同一个变量
解析:
Day 10
回顾:
1. 闭包:反复使用局部变量,且保证局部变量不被污染的结构
何时使用:希望反复使用局部变量,且保证不被污染时
缺点:比普通函数占用更多的内存空间(外层函数的AO)
如何创建:3步:
1. 外层函数封装受保护的局部变量
2. 外层函数,返回一个操作受保护的局部变量的内层函数对象
3. 全局由变量调用并保存了外层函数返回的内层函数对象
结果:只有内层函数有权操作受保护的局部变量
判断闭包输出结果:
1. 找首保护的局部变量
2. 外层函数被调用几次,就创建了几个受保护的变量副本。
3. 同一次外层函数调用返回的内层函数,总使用统一个局部变量
例题:
正课:
****面向对象 object oriented program
1. 什么是对象和面相对象:
对象:程序中描述现实中一个具体事物的属性和方法的结构。
本质:内存中保存多个属性和方法的一块存储空间,再起一个名字。--->相对于变量
面向对象:OOP(面向对象程序设计):程序中,描述一具体事物都要用对象来封装事
物的属性和功能。
写任何程序,都要先找对象。 再识别对象的属性和功能
封装:将描述一个事物的属性和方法集中定义在一个对象中。
为什么面向对象:现实中一个属性或一个功能都必须依附在一个具体的事物上才有意义,不同的事物即使是同一功能,它的定义也不一样。
- 创建对象:3种:属性和方法统称为对象的成员。事物中的属性会成为对象的属性。
1. 仅创建一个对象:2种:大括号创建新对象的意思,逗号分隔每个属性。
1. var obj={"属性名":值,"属性名":值,“方法名”:function(...)... ...};方法本身就是一个函数
对象 vs 关联数组:
***js中的一切都是对象,每一个对象底层都是数组js中一切对象底层都是关联数组: 每个属性可用["属性名"]访问,每个成员名都是字符串“”,所以在写的时候双引号可以省略。
遍历一个对象中所有属性,也用for in,
也可以在任何时候动态添加新属性,在数组中不会越界。
强行访问没有的属性返回undefined
方法其实就是属性引用了一个函数对象而已。
2. var obj=new Object(); //创建一个空对象
约等于{}
obj.属性名=值; <==> obj["属性名"]=值;
... ...
(可以访问:js中默认的一切都是关联数组)*
*****this关键字:存在EC中,保存正在调用当前方法的对象,所有的EC中都有this
默认this-->window
对象中的方法,要使用对象内部的属性时,必须用this引用
不用this:默认在作用域链(AO和window)中找
加this.:在this引用的当前对象中找
This:是指自己的方法调用自己的属性时,就要用this.,
*****this和定义在哪儿无关,只和调用时使用的对象有关!
没有用任何对象,就直接调用的函数,this默认值window
2. 反复创建多个相同结构的对象时:2步:
1. 创建构造函数:规定一类对象,统一属性的函数
function 类型名(属性参数列表){
this.属性名=参数值;
this.属性名=参数值;
... ...
this.方法名=function(){
}
}
2. 用new调用构造函数,装饰一个新创建的对象
var obj=new 类型名(属性值,... ...);
new: 1. 先创建空对象,相当于{}
2. 用空对象调用构造函数,this-->正在创建的空对象
按照构造函数的定义,为空对象添加属性和方法
3. 将新创建对象的__proto__属性指向构造函数的prototype对象。
4. 将新创建对象的地址,保存到等号左边的变量中
(人话:new是创建一个空对象,空对象赋值任何属性,都会给该属性自动赋值,new相当于一个空房子,构造函数相当于设计图,参数就是空房子里对应的相对的物品)
例题:
基于例题:定义共有的toString方法:
(alert是默认调用toString()方法的)
鄙视题:
构造函数的执行:
(第二个console里是赋值表达式,等号右边的值付给等号左边,左边得到一个function对象的地址,
逗号表达式返回的是逗号右边的值)
结果:9/10/10/10
3. ***原型和原型链:
原型:prototype:保存所有子对象共有属性值的父对象
每个(构造)函数都自带一个prototype对象
所有被构造函数创建的子对象都有一个__proto__属性指向构造函数的prototype对象。
什么放在构造函数的prototype中?所有子对象共用的属性值和方法——只在读取时共享 特殊情况:修改prototype中共有属性,只能用prototype对象,不能用子对象。
如果用子对象修改共有属性,会在子对象创建共有属性副本
原型链:由各级子对象的__proto__属性连续引用形成的结构
所有对象都有__proto__,所有对象原型链的顶部都是Object.prototype
.运算符:访问一个对象的属性,只要在原型链上有的,都可以访问!
重写:override 如果子对象觉得父对象的方法不好用,可在子对象定义同名方法,就会覆盖父对象中的同名方法。
原型相关API:
1. 判断对象的属性是自有,还是共有:
自有属性:在当前对象本地保存的属性
共有属性:从原型链中继承来的属性
1). 如何判断自有:obj.hasOwnProperty("属性名")
如果返回false,不一定说明共有,也可能没有!
2). 如何判断在原型链上有没有:2种
1. “属性名” in 对象
2. if(obj["属性名"]) 如果有效
可能被干扰:如果属性值刚好为0,NaN,null,""
3). 如何判断共有:固定套路:
if(obj.hasOwnProperty("属性名")){
console.log("是自有属性");
}else if("属性名" in obj){
console.log("是共有属性");
}else{
console.log("没有该属性");
}
2. 获得任意子对象的原型(父对象):
obj.__proto__ (个别浏览器禁用)
var prototype=Object.getPrototypeOf(子对象)
何时使用:无法获得构造函数时,又希望设置子对象的共有属性
3. 判断父对象是否在子对象的原型链上:
父级对象.isPrototypeOf(子对象)
强调:isPrototypeOf不仅找直接父级,而是找整个原型链
/*****鄙视题:*****/
typeof失效:只能判断原始类型的值,不能判断引用类型的值, 所以对Array无效
如何判断一个对象是数组类型:
1. if(Array.prototype.isPrototypeOf(obj)){
obj是数组类型
}
2. 其实构造函数的prototype指向原型对象
同时,原型对象有constructor指回构造函数对象
constructor只在原型对象上有
if(obj.constructor==Array)
3. 判断子对象是否是指定构造函数的实例:
if(子对象 instanceof 构造函数名)
instanceof也是查找整个原型链
4. if(Object.getPrototypeOf(obj)==Array.prototype)
5. call和apply:(可以返回任何对象的构造函数)
if(Object.prototype.toString.call(obj)=="[object Array]")
执行的一刹那,相当于arr.toString();
鄙视题:call和applay:
***call vs apply: 都是:在调用方法时,修改当前调用方法的对象
差别:传入参数的形式:
xxx.call(obj,值1,值2,值3....)
xxx.apply(obj,[值1,值2,值3....])
例题:call和apply:
6. ES5中新函数:Array.isArray(obj)
4. ****继承:父对象中的属性和方法,子对象可直接使用!
为什么继承:代码重用!节约内存空间!便于维护!
js中的继承都是用__proto__
总结:面向对象:封装、继承、多态(重写和重载)
多态:同一个东西在不同情况下,表现不同的状态。
Day 11
回顾:
1. 原型和原型链
2. 面向对象三大特点:
封装:为什么:1. 单独的属性和方法是没有意义的,只有放在对象上才有意义。
2. 便于反复调用——代码重用!便于维护!
多态:重写和重载
重载:为什么:便于调用!
重写:为什么:为了实现子对象与父对象的差异
继承:父对象的属性值和方法,子对象可直接使用
js中所有的继承都是通过原型(__proto__)实现的
为什么:代码重用!便于维护!节约内存空间!
例题1:
例题2:
例题3:判断浏览器是否支持isArray方法,如果不支持就自定义添加:
(.isArray不是一个共有的方法,其实就是一个Array的一个属性)
3. *****修改继承:4种:
1. 仅修改一个对象的父对象:
子对象.__proto__=父对象;
Object.setPrototypeOf(子对象,父对象);(只适合修改一个子对象修改父对象)
2. 替换构造函数的原型对象(prototype),为新父对象:
结果:将来再创建的子对象,所有新创建的子对象都继承新父对象
时机:在刚刚定义完构造函数后,立刻修改!
在修改原prototype对象和创建新子对象之前
步骤:1. 构造函数.prototype=新父对象
2. 构造函数.prototype.constructor=构造函数对象
3. 用一个已有的父对象作为参照,创建一个新子对象,同时,扩展子对象自有属性。
(ES5中最新提出的,本身不是继承的方法,是创建对象的方法)
var son=Object.create(父对象);
这句话做了2件事: 1. 创建空对象son
2. 设置son的__proto__指向父对象
var son=Object.create(父对象,{
扩展属性名1:{
writable:true,//是否可修改,可以省略
value:属性值,
configurable:true//可以设置,可以省略
},
扩展属性名2:{...}
});
何时使用:在创建一个子对象时,希望随意扩展子对象的自有属性时
例题:
4. *****既继承结构,又继承原型:——推荐的继承方式
何时使用:两种类型间的继承
如何继承:2处:
1. 子类型构造函数开始位置,借用父类型构造函数:
父类型构造函数.call(this,参数列表)
apply(this,[参数列表])
强调:仅完成第一步,只是借用构造函数中的语句而已
没有实现任何对象间的继承
2. 定义构造函数后,设置子类型构造函数的prototype继承父类型的prototype:
Object.setPrototypeOf(子类型.prototype,父类型.prototype)
数组的API:
1).数组转字符串:arr.toString()
arr.join(“连接符”)
2).连接数组:var newArr=arr.concat(值1,值2......)
3)获取子数组:var subArr=arr.slice(starti,endI);(支持负数下标)
4)Splice方法:删除:[var deletes]=arr.splice(starti,[n])(直接删除原数组的参数,被删除的可以不接)
插入:arr.Splice(starti,0,值1,值2)
替换:arr.Splice(starti,n,值1,值2)
5).颠倒数组元素:arr.reverse();
6)栈:结尾:arr.push(值1,值2)(支持多值传入,但不支持打散的数组)
var last=arr.pop();
开头:arr.unshift(值);
var first=Arr.shift();
7)队:
字符串的API:
1).和数组相同的Str.slice()和var newStr=str.concat(值...)可用
2)大小写转换:str=toUpperCase()/toLowerCase()
3)获取指定位置的字符:str.charAt(i)
4)获取指定字符的unicode:str.charCodeAt(i)/unicode反向转回字符:string.fromCharCode(unicode)
5)查找字符串API:indexOf/lastIndexOf(“敏感词”,[fromi])(不支持正则的,仅仅提供位置,indexOf 默认从头开始找下一个,lastindexOf如果传入负数开始位置,会自动转为0,如果第 一个字符就是关键字,会死循环
match:var arr=str.match(/正则/)(支持正则,可获得关键字的内容,无法获得位置)
Var arr=reg.exec(str)也要加g,否则只找1个
6).获取子字符串:var sunStr=str.slice(starti,endi+1);(支持负数参数)
Var substr=str.substring(starti,endi+1)(不支持负数,用length-n变通)
Var subStr=str.substr(starti,n);
7)替换:var newStr=str.replace(/正则/g,”替换为”)
8)切割:var subStrs=str.split(/正则/)(返回一个子字符串)
正课: DOM
1. 什么是DOM
2. DOM树
3. 选取元素
1. 什么是DOM: Document Object Model
专门操作网页内容的API
js=ECMAScript(核心语法)+DOM+BOM(Browser Object Model)
DHTML: 一切实现网页动态效果的技术的统称:
DHTML=HTML+CSS+JS
鄙视题: 区分: DHTML HTML XHTML XML
DHTML模型:
BOM: window 全局对象(2天)
history: 封装历史记录栈(和前进,后退,刷新有关)
navigator: 封装浏览器的配置信息
location: 封装当前网页的url信息(地址栏)
screen: 封装桌面分辨率信息——判断设备种类
event: 封装事件信息,并监听事件触发
DOM:
DOM标准分为:
核心DOM: 本来是希望操作一切结构化文档(HTML XML)
优: 万能 缺: API繁琐
HTML DOM: 对核心DOM中部分常用API提供的简化版本,
专门用于操作HTML的内容
优: 简洁 缺: 无法实现所有功能,更侧重修改
实际开发中: 优先使用HTML DOM,如果无法实现的功能,用核心DOM补充。
2. ***DOM Tree:
什么是: 浏览器为页面中每个内容分别创建节点对象
所有节点都是按上下级包含关系在内存中以树形结构存储
什么是节点: 封装网页中任意一项内容的属性的对象
包括:
document对象:根节点
当浏览器获得一个网页时,会自动创建document对象
所有网页内容中的对象,都是document的子节点
包括: 查找 创建
元素节点: 专门封装页面中的一个元素
文本节点: 专门封装页面中的一段连续的文本
属性节点: 专门封装页面中元素的一个属性
注释节点: 专门封装页面中一段注释
所有节点的父类型都是:Node
节点对象的公共属性: 3个
nodeType: 描述节点对象的类型,值是一个数字
ELEMENT_NODE: 1
ATTRIBUTE_NODE: 2
TEXT_NODE: 3
DOCUMENT_NODE: 9
nodeName: 获取节点名称
元素节点: 更精确的获得元素的标签名
属性节点: 属性名
文本节点: #text
document: #document
nodeValue: 节点的值
元素节点: null
属性节点: 属性值
文本节点: 文本内容
document: null
节点间关系: 节点树: 包含所有节点对象的树结构
2类:
1. 父子关系: parentNode childNodes firstChild lastChild
childNodes: 返回所有直接子节点的集合——类数组对象
遍历: for(var i=0,len=parent.childNodes.length;i<len;i++){
}
childNodes返回动态集合。
动态集合: 不返回完整节点对象,仅返回对节点对象的快速扫描结果。
优点: 查询速度快 缺点: 每次查询都要重新访问DOM树
问题: 如果反复直接和childNodes.length比较,会造成反复查找
解决: 今后,凡是遍历动态集合,都要提前存储集合的length
再和存储的length比较
2. 兄弟关系: previousSibling nextSibling
课堂练习: 递归遍历节点树(手写)
递归: 函数内部又调用函数自己
何时使用: 希望对子内容执行和父内容相同的操作时
如何使用: 2步:
1. 定义对父元素及其直接子节点的操作方法:
2. 遍历父元素下的直接子元素,对每个子元素在调用和父元素相同的方法
算法: 深度优先: 优先遍历一个节点的下级节点
只有下级节点遍历完,才遍历兄弟节点
元素树: 仅包含元素节点的树结构
优: 不会受到空字符的干扰
缺: 无法获得文本节点
6个关系:
1.父子关系: parentElementNode
children IE8兼容
firstElementChild
lastElementChild
2. 兄弟关系: previousElementSibling,nextElementSibling
1. 递归API:
1. NodeIterator: 按照深度优先的顺序,依次遍历每个节点对象
如何使用: 2步:
1. 创建NodeIterator对象:
var iterator=document.createNodeIterator(
parent, NodeFilter.SHOW_ALL, null, false
SHOW_ELEMENT
);
iterator就站在开始父节点上
2. 循环调用iterator,遍历每个节点对象
var currNode=iterator.nextNode(): 1. 返回当前节点
2. 然后跳到下一个节点
var prevNode=iterator.previousNode():1. 倒退一步
2. 返回当前节点
循环条用: while((node=iterator.nextNode())!=null){
node//当前节点
}
2. TreeWalker: 可选择跳转方向的迭代器
基本功能和NodeIterator完全一样
相同: 开始时都站在开始父元素上
不同: nextNode()返回的是下一个节点对象
previousNode()返回的是上一个节点对象
API: parentNode(): 跳到父节点
firstChild(): 跳到第一个子节点
lastChild(): 跳到最后一个子节点
previousSibling(): 跳到前一个兄弟节点
nextSibling(): 跳到后一个兄弟节点
2. ***选取元素:
1. 按HTML属性选取:
1. 按id查找: var elem=document.getElementByid("id");
返回一个元素对象。如果未找到,返回null
2. 按标签名查找:
var elems=parent.getElementsByTagName("tagName");
不但找直接子元素,而且还找所有子元素
3. 按name属性查找:
var elems=parent.getElementsByName("name");
4. 按class属性查找:
var elems=parent.getElementsByClassName("class")
返回值: 返回动态集合(live collection)(类数组对象)
优: 查询效率高
遍历时都要先缓存length属性到变量,在和变量比较
如果没找到,返回空数组[]
2. 按选择器查找:
1. 只查找一个符合条件的元素:
var elem=docuemnt.querySelector("selector");
返回一个元素对象,如果没找到,返回null
2. 查找所有符合条件的元素
var elems=document.querySelectorAll("selector");
返回静态集合(static collection)
缺点: 查询效率低
包含完整的节点对象及其属性
DOM树的更改,不会影响当前结果
如果没找到,返回空集合[]
getXXXByXXX vs querySelector
1. getXXXByXXX的效率,比querySelector高的多
2. querySelector比getXXXByXXX 使用更简洁
如果通过一个属性就可获得结果时,首选getXXX
必须通过复杂的查询才可获得结果时,首选querySelector
DOM TREE
实现 隔行变色
实现伸缩二级菜单
实现购物车相关功能
实现单选复选框
Day02
1.DOM可以对页面的 任意内容 执行操作
2.今天主要讲 DOM的修改
3.改内容,改属性,改样式
- select option 不直观 选择着容易,选项多的时候用select,不用想checkbox,虽然checkbox直观,选项少的时候可以用。
5.选项多的时候,只有一个可选,就是select option
6.所有的用户,用最少的鼠标和键盘,完成相同的一项任务,例子为验证码有的会给输入好,这就是所谓的新领域UE :user expression;
7.多个数据是建数组
8.用匿名函数留住this 留住this用闭包
9.凡是遇到this中发生冲突时,就用this既为属性,有为对象,则用this指定对象,再用me留住this
10.先排序,后更新
11.凡是出现有闭包的东西,在函数的开始都要写var me=this
12.清空,用[],null,””,length=0
13.将来遍历当中,要删除一些元素,要从,后面开始遍历(即从后往前遍历),可以避免由于删除带来的后面下标的变化********
14.Splice 删除 瑞士军刀,返回的都是数组
15.Concat 返回的是新数组,旧数组扔到内存中,丢弃旧对象,一般不好。用Array.prototype.push.apply(me.unselCts,me.selCts)
16.一个元素用push
17.如果一个值天生是布尔值,就不写
18.开发当中批量生成元素,用数组API
19.批量删除 批量生成 用数组API
20.批量操作元素的内容用innerHTML
21.node 节点“对象”
22.很少将属性当节点看,
23.Node.value=”值” 值是字符串
24.HTML DOM只能访问标准属性,不能访问自定义属性,访问自定义属性,就用核心DOM,用核心DOM中的attribute
25.在控制台里能展开的都是对象
26.document.body.getAttribute(“class”)
27.关于class属性:核心DOM API 中,写“class”
在HTML DOM 中, 写为”className”
28.一般做底层框架,用attribute,用变量动态传入。
29.Push不可以
30.要想打散数组,一共就有两个方法,concat 和apply,concat的不好的地方就是,返回新数组,旧数组扔到内存中,有点浪费内存。
正课
1. 修改
改内容
改属性: 标准属性,自定义属性
改样式: 内联样式,样式表(外部/内部)
1. 获取和修改元素的内容:
1. 获取和修改原始的html内容:
elem.innerHTML: 指代元素开始标签到结束标签之间的一切html原文。
2. 获取和修改纯文本的内容:
(不包含子元素的标签,自动转换特殊字符)
elem.textContent: 指代元素开始标签到结束标签之间的文本正文。(IE8不兼容)
IE8 : elem.innerText
2. 获取和修改元素的属性:
1. 标准属性:
核心DOM: 万能
获取属性:
1. 获取所有属性的集合: elem.attributes
2. 获取属性节点对象: (了解)
var node=elem.attributes[i/"属性名"]
var node=elem.getAttributeNode("属性名")
3. 获得属性值:
elem.attributes[i/"属性名"].value
elem.getAttributeNode("属性名").value
var value=elem.getAttribute("属性名")
设置属性:
node.value="值"; 了解
elem.setAttribute("属性名","值")
HTML DOM: 仅能访问标准属性
elem.属性名="值";
class属性: 核心DOM API中,写"class"
HTML DOM中,写为.className
正课:
1. 修改:
修改属性: 标准属性,自定义属性
修改样式: 内联样式,样式表
1. 修改属性:
1. 标准属性: HTML标准中已经规定好的元素属性
核心DOM:
elem.attributes 封装了所有属性节点的集合
elem.getAttribute("属性名") 获得指定属性的值
elem.setAttribute("属性名","属性值") 设置指定属性的值
elem.removeAttribute("属性名") 移除属性
elem.hasAttribute("属性名") 判断元素是否包含指定属性
HTML DOM:
elem.属性名
如果不包含指定属性,则返回""
如果给一个属性赋值为"",相当于移除了
2. 自定义属性: 在元素开始标签中定义的自定义属性名的属性
只能使用核心DOM访问
特性attribute vs 属性property
attribute: 指出现在html元素开始标签中的标准属性
property: 内存中,对象上的保存一个数据的属性变量
HTML DOM将标准attribute,同样也封装为了内存中元素对象的property
HTML5中: 规定:
html中如何定义自定义属性: data-属性名="值"
js中如何定义自定义属性: elem.dataset.属性名="值"
如何访问: elem.dataset.属性名
2. 获取或修改元素的样式:
HTML DOM:
内联样式:
获取: elem.style.属性名
style: 封装所有CSS属性的CSSStyleDeclaration对象
js中CSS属性名都变为: 去横线改驼峰
问题: 只能获得内联样式
解决: 获得计算后的样式: 一个元素最终应用的所有样式
即包含内联样式,又包含样式表中的样式和浏览器的默认样式
var style=getComputedStyle(elem)
IE8: elem.currentStyle
var value=style.属性名
样式表: 危险,了解
找到要修改的属性所在的位置:
1. 找到样式表: var sheet=document.styleSheets[i]
2. 获得样式表中要修改的CSSRule对象:
var rule=sheet.cssRules[i]
3. 获得CSSRule对象中的一个属性:
var value=rule.style.属性名
特殊: 动画的关键帧规则中:
rule.cssRules[i]获取每一帧的CSSRule对象
读取并修改元素的内容
读取并修改元素的内容
<!--1. 使用自定义属性实现摇号排名-->
实现带样式的表单验证
实现多标签页效果
实现表盘效果
Day03
1.看updateView 是怎么实现的
2.表单中的name尽量不用name="name"
3.onfocus="getFocus(this)"
4. onfocus="getFocus(this)"获取焦点onblur="valiName(this)"失去焦点
5.重构:排除程序中的错误,简化程序中的步骤,简单点就是考试中老师让检查。如果发现程序中的代码,有重复的,要重构!
6.给穿衣服,就修改className属性
脱衣服,就用将属性className为空,“”
txt.className="txt_focus";//穿上衣服
txt.className="";//脱掉衣服
7.if(reg.test(txt.value)){//用value,而不用innerHTML
8.用自定义属性, 在当今用户体验的今天,常用自定义属性,不用访问服务器就可以修改,但是访问自定义属性必须用核心DOM访问,
例子,age
9, 能看见的,
在内存中,看不见,
能访问
用核心DOM设置的 setAttribut
凡是标准属性:即是property,还是attribute,标准属性:src ,id,class,....
Property:只能用 . 访问 ,在能存中的看不见的
Attribute: 只能用核心DOM访问,是反应到页面中的,能看见的
10.以后html添加自定义属性都要加data- 用elem.dataset.访问
11.Js中添加自定义属性,用dataset
12.Dom树在页面加载完只生成一次,
13.lis[i].setAttribute("data-num",r) 为当前li添加num属性,值为r
14.写比较器函数,就是arr.sort(function(a,b){
});
15, 按节点排序:
/ /将节点按顺序排列
var lis=Array.prototype.slice.call(lis);
lis.sort(function(a,b){
return a.getAttribute("data-num")
-b.getAttribute("data-num");
});
摇号:var nums=[];
window.onload=function(){
var lis=queue.getElementsByTagName("li");
for(var i=0,len=lis.length;i<len;i++){
while(true){
var r=parseInt(Math.random()*(lis.length)+1);
if(nums.indexOf(r)==-1){
nums.push(r);
lis[i].setAttribute("data-num",r)
break;
}
}
}
console.log(String(nums));
//将节点按顺序排列
var lis=Array.prototype.slice.call(lis);
lis.sort(function(a,b){
return a.getAttribute("data-num")
-b.getAttribute("data-num");
});
}
16.最终应用到该元素的所有样式在浏览器f12中的computed中
17.往后获得样式要用var style=getComputedStyle(elem) //这是拿到的样式最全的
var value=style.属性名
而不用elem.style.属性名。因为这个访问的只是内联样式,而不能访问外部样式表和浏览器默认样式表
18.document.styleSheets查找所有的表
19.用下标的方式获得样式表
Var sheet=document.styleSheets[i]
20.每个选择器加一个大括号,就是一个cssRule
一个大括号,一个选择器就是一个cssRule
21,今后喜欢集合,所有集合都可以用下标访问
22.一切属性的值都是字符串
23.设置秒针的开始和结束
var sheet=document.styleSheets[1];
var rule=sheet.cssRules[4];
var startR=rule.cssRules[0];
var endR=rule.cssRules[1];
startR.style.transform="rotate("+90+"deg)";
endR.style.tansform="rotate("+270+"deg)";
23.h>12&&(h-=12);把时间改成12小时制
正课:
1. 创建和删除
2. HTML DOM常用对象: Select Table Form
1. 创建和删除:
1. 创建: 3步:
Step1. 创建空元素对象:
var elem=document.createElement("标签名")
相当于: <标签名></标签名>
Step2. 为空元素添加关键属性
elem.属性名="值"
elem.innerHTML="内容"
相当于: <标签名 属性名="值">内容</标签名>
Step3. 将新元素挂到DOM树中指定父元素下
追加: parent.appendChild(elem)
插入: parent.insertBefore(elem,现有元素)
替换: parent.replaceChild(elem,现有元素)
2. 删除: parent.removeChild(elem)
网页加载过程:
获得HTML内容->构建DOM Tree
|
将CSSRules附加到DOM Tree-*layout*-paint
|
获得CSS内容->构建COM(CSSRules)
layout: 重新计算布局——效率低
只要改变DOM树上的元素,都会重新layout——效率更低
如何减少layout的次数:先在内存中拼凑要添加的DOM子树
然后一次性挂到页面
只会触发一次layout——效率提高
课堂练习:
select: 事件: onchange, 当选中项发生改变时触发
属性: selectedIndex:获得当前选中项的下标
3. 文档片段: 内存中临时存储一个DOM子树的临时父节点
用法和普通父节点完全一样
可将DOM子树整体挂到DOM树上,但自己不出现在DOM中
何时使用: 同时添加多个平级元素时,都要先放在文档片段中,再将文档片段整体挂到页面
如何使用: 3步:
Step1: 创建文档片段对象:
var frag=document.createDocumentFragment();
Step2: 将平级子元素,追加到文档片段中
frag.appendChild(elem)
Step3: 将文档片段挂到DOM树上指定父节点下
parent.appendChild(frag);
2. HTML DOM常用对象:
Select对象: 指代一个select元素
属性: options: 包含一个select下所有option元素对象的集合
selectedIndex: 获得当前选中项的下标
方法: add(opt): 将opt元素对象追加到当前sel下
remove(i): 移除sel下下标为i的option元素对象
事件: onchange:
Option对象: 指代select元素下一个option元素
创建Option: var opt=new Option(innerHTML,value)
相当于核心DOM:
var opt=document.createElement("option");
opt.innerHTML=innerHTML;
opt.value=value;
简写: 创建,设置,并追加option:
sel.add(new Option(innerHTML,value));
属性: index: 当前option元素在select中的下标位置
selected: 返回当前option是否被选中,返回bool值
Table对象: 指代一个table元素
创建: createTHead/TBody/TFoot
返回刚创建的对象,用于继续添加子内容
删除: deleteTHead/TFoot
获取: .tHead/tFoot .tBodies[i]
tHead
添加: .insertRow(i),如果省略i,表示在最后一行追加
如果i在中间,就在i位置插入,原i位置的行向后顺移
返回刚创建的对象,用于继续添加子内容
删除: .deleteRow(i), 如果省略i,表示删除第一行
获取: .rows[i]
tr
添加: .insertCell(i),省略i表示末尾追加
删除: .deleteCell(i),省略i删除第一个
获取: .cells[i]
td
tBodies
tBody
tr
td
tFoot
tr
td
table.insertRow()
看最后一个tr所在的行分组是哪个,就将新tr追加到哪个行分组内
table.deleteRow(),永远删除全表的第一行
table.deleteRow(i),删除相对于全表的下标为i的行
tBody.deleteRow(i),删除当前tBody内下标为i的行
tr.rowIndex: 表示tr相对于整个表的下标位置
几乎专用于deleteRow
3. Form对象: 专门指代一个form元素
获取form对象: var form=document.forms[i/name];
获取form元素下的表单控件:
var elem=form.elements[i/name]
其中: elements仅包含所有输入控件和按钮
其实可以更简单: form.name
方法: form.submit();//手动提交表单
何时使用: 不希望自动提交时,可使用自定义按钮调用form的submit方法,手动提交
elem.focus();//让当前elem元素获得焦点
elem.blur();//让elem失去焦点,但很少主动使用
动态生成表格
二级菜单联动
动态操作表格
联动菜单
表单提交验证
正课:
1. BOM:
window: 打开,关闭,定位,大小
*****定时器
1. window对象:
2个角色: 1. 代替ES标准中的Global,充当全局对象
2. 封装了浏览器软件以及窗口的信息
打开窗口: window.open("url","name")
其中: name属性是内存中窗口的名称
在打开窗口时,才赋值
name的默认值: _self _top _blank
也可自定义name属性
规定: 相同name属性的窗口只能打开一个
后打开的会替代先打开的
打开链接:
1. 替换当前窗口内容,可以后退
html:<a href="url" [target="_self"]>
js:window.open("url","_self");
2. 替换当前窗口内容,禁止后退
js:location.replace("url")
3. 在新窗口打开,可重复打开多个
html:<a href="url" target="_blank">
js:window.open("url"/*,"_blank"*/);
4. 在新窗口打开,只能打开一个
html:<a href="url" target="自定义name属性">
js:window.open("url","自定义name属性")
窗口大小和窗口位置:
窗口大小:
完整窗口大小: window.outerHeight/outerWidth
文档显示区大小: window.innerHeight/innerWidth
调整窗口大小:
1. 在打开窗口时:
var config="top=?,left=?,height=?,width=?";
window.open("url","name","config")
2. 在窗口打开后,调整大小:
window.resizeTo(宽,高)
window.resizeBy(宽的增量,高的增量)
窗口定位:
窗口左上角相对于屏幕左上角的坐标:
top: window.screenTop|screenY
left: window.screenLeft|screenX
移动窗口位置:
window.moveTo(x,y): 将窗口左上角移动到x,y的位置
window.moveBy(x的增量,y的增量)
屏幕的宽和高:
1. 完整分辨率的宽高: screen.width/height
2. 去掉任务栏后的可用宽高: screen.availWidth/availHeight
在事件发生时,获得鼠标位置:
e.screenX/screenY
正课:
上午:
1.只有父元素才有资格去添加新元素
2.一般不把文本当节点看,用innerHTML
3.Parent.insertBefore(elem,现有元素) 在
4.所有的标准属性都可以用点访问,如
A.href=”http://tmooc.cn”
var a=document.creatElement("a");
a href="http://tmooc.cn";
a.innerHTML="go to tmooc";
document.body.appendChild(a);
Varbtn=document.creatElement("button"); button.onclick=function(alert("before"));
button.innerHTML="点击我";
document.body.insertBefore(btn,a);
5.Json
JavaScript Object Notation
是字符串
服务器和客户端只能发字符串
{“属性名”:值,“属性名”:值}
服务器端专门接受字符串,服务器用eval解码,
好处:
比XHTML短,
不用解析,
但是:属性名必须加引号,就着一点和js不一样,其他都一样
Json 是今后当中服务器和客户端唯一的数据交换方式。
6.建table时,一定要建全了,即<thead><tbody><tfoot>写全了。开发习惯!
- Var emps=eval(json);将json字符串转为内存中的对象
8.二维表格:一行即为一个对象
一列即为一个属性,有几个属性就建几个td
9.创建元素:var elem=document.createElement(“标签名”);
相当于<标签名><>
10.追加:parent.appendChild(elem)
插入: parent.insertBefore(elem,现有元素)
替换:Parent.replace(elem,现有元素)
11.网页加载过程*****
获得HTML页面->构建DOM Tree->
|
将CSSRules追加到DOM Tree-layout(重新计算布局,定位元素,这个地方最消耗性能的,只要你修改页面样式的时候,layout都要重新做,就像你搬家完重新布置东西)-paint
|
获得CSS内容->构建COM(CSSRules)
Css object modle
Layout什么时候执行,新元素挂一次,执行一次。
12.select:事件,onchange ,当选中项发生改变时触发
属性,selectedIndex返回选项的下标
13.文档片段,(像托盘):内存中 临时存储 一个 DOM子树的 临时父节点
用法和普通父节点完全一样
可将DOM子树整体挂到DOM树上,但自己不出现在DOM中
何时使用,同时添加多个平级元素时,都要先放在文档片段中,再将文档片段整体挂到页面
如何使用:3步:
Step1:创建文档片段对象:
var frag=document.createDocument();
2.HTML DOM,
只有image和option可以new
只有普通数组和类数组对象用for遍历,类数组对象只能用for不能用for in,
因为类数组对象只有数字下标
只有关联数组和对象用for in遍历
只要有数字下标时,就用for,没有数字下标时就用for in
依次取出当前对象的属性名,一切皆哈希。里面是属性,没有数字下标,就用for in
1. *****定时器:
周期性定时器
一次性定时器
1. 周期性定时器: 让程序每隔一段时间间隔,反复执行一项任务
何时使用: 只要连续,有规律的持续播放的动画
如何使用: 3件事:
1. ***任务函数: 定时器每次执行的任务
function task(){...}
任务函数通常要自己考虑:*何时停止*定时器的临界值
2. 将任务函数放入定时器,定时执行:
timer=setInterval(task,interval)
其中: interval 是间隔的毫秒数
timer往往是一个全局变量: 用来保存当前定时器的序号
序号是内存中唯一标识一项任务的编号
原理:首先将task函数对象和interval时间间隔记录在定时器对象中,再返回当前任务的序号
然后,定时器会按照指定时间间隔,将task函数定时加入回调队列(callback queue)中。
只有ECS中没有正在执行的函数时,才将callbackqueue中的函数加入ECS中执行
3. 停止定时器:
clearInterval(timer);
2. 一次性定时器: 先等待一段时间,再自动执行一次任务
何时使用: 1. 如果任务只执行一次时
2. 如果动画的启动,停止,变化规律都很随意
如何使用: 3件事:
1. 任务函数:
function task(){...}
任务函数的结尾要考虑: *何时启动下一次*
2. 启动一次性定时器
timer=setTimeout(task,wait)
其中: wait是等待的毫秒数
3. 停止一次性定时器: 其实是停止等待,不再执行任务
clearTimeout(timer);
timer=null;
其实: 一次性定时器连续启动,也可实现周期性的效果
区别: 任务函数的结尾:
周期性定时器任务: 判断何时停止定时器
一次性定时器任务: 判断何时启动定时器