一、
1、*****正则表达式:
什么是:定义字符串中字符出现规则的表达式
何时:切割 替换 【验证】
如何:语法:/正则表达式/
1、最简单的正则:
关键字原文 "no" -> /no/后缀
后缀:g:global找全部 i:忽略大小写
2、备选字符集:
/^[备选字符集]$/
强调:1、一个中括号,只管一位字符
2、正则表达式默认只要满足就不管后续,我们希望从头到尾完全匹配,解决:前加^后加$:/^[备选字符集]$/
特殊:如果备选字符集中的ascii码是连续的,name可以用-省略掉中间的部分
比如:一位数字:[0-9]
一位字母:[A-Za-z]
一位字母、数字、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了xxx之外的:[^0-9] - 很少使用,范围太广了
3、预定义字符集:
前辈们提前定义了一些字符集,方便我们程序员 - 简化备选字符集
一位数字:\d === [0-9]
一位字母、数字、下划线:\w === [0-9A-Za-z_]
一位空白字符:\s:包括:空格、制表符、换行
一位除了换行外的任意字符:. - 很少使用,范围太广了
优先使用预定义字符集,预定义满足不了在备选字符集自定义补充
问题:不管是预定义字符集还是备选字符集,一个都只管一位
4、量词:
规定一个字符集出现的次数
1、有明确数量:
字符集{n,m}:前边相邻的字符集,至少n个,最多m个
字符集{n,}:前边相邻的字符集,至少n个,多了不限
字符集{n}:前边相邻的字符集,必须n个
2、没有明确数量
?:前边相邻的字符集,可有可无,最多1个
*:前边相邻的字符集,可有可无,多了不限
+:前边相邻的字符集,至少1个,多了不限
5、选择和分组:
选择:在两个规则中选一个
规则1|规则2
分组:将多个字符集临时组成一组子规则
(规则1|规则2)
6、指定匹配位置:
^:开头
$:结尾
特殊:两者同时使用:前加^后加$:表示从头到尾要求完全匹配 - 只要你做【验证】!
7、密码强度:
4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
/^[0-9A-Za-z]{4}$/
预判公式:(?![0-9]+$) -> 不能全由数字组成
(?![a-z]+$) -> 不能全由小写字母组成
(?![0-9a-z]+$) -> 不能全由数字、不能全由小写字母、不能只有他俩的组合组成
/(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
/(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,三者必须都有
2、*****支持正则表达式字符串的API:
1、切割:var arr=str.split(reg)
2、*****替换:很有可能出现在鄙视中
1、基本替换法:缺陷:替换的新东西是固定的
var newStr=str.replace(/正则表达式/后缀,"新内容");
2、高级替换法:
var newStr=str.replace(/正则表达式/后缀,function(a){
return a.length==2?"**":"***";
});
3、格式化:
var id="500103198602215933";
var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
id=id.replace(reg,function(a,b,c,d){
return b+"年"+c+"月"+d+"日";
})
console.log(id);
更多详细案例,见08替换字符串.html
总结:何时前加^后加$,何时又该加后缀gi?
1、前加^后加$ - 验证
2、替换,你希望替换到所有 - 必须加g
3、正则对象:
创建:
直接量方式:var reg=/正则表达式/后缀
构造函数方式:var reg=new RegExp("正则表达式","后缀")
API:var bool=reg.test(用户输入的内容)
true->验证成功 false->验证失败
1、Math对象:专门提供了数学计算的API
强调:不需要创建,直接使用
属性:Math.PI === 3.1415926我们不需要自己创建保存这个数字,浏览器自带
API:
1、取整:3种
1、上取整:超过一点点,就取下一个整数
var num=Math.ceil(num)
2、下取整:无论超过多少,都会省略掉小数部分
var num=Math.floor(num)
3、四舍五入取整:
var num=Math.round(num)
都是在取整,只能取整
取整的方式:以上三个 + *parseInt(str去掉单位) + *num.toFixed(d)
个人最推荐*num.toFixed(d):优点:可以四舍五入保留指定的小数位数
缺点:结果是一个字符串,建议搭配上一个parseFloat
不允许用toFixed的情况下,自己封装一个函数,实现toFixed的功能并且最后返回的是一个数字:
看用户要保留几位小数,我们就拿着那个数字*10的几次方,使用Math.round四舍五入取整,最后/10的几次方
function sswr(num,d){
num*=Math.pow(10,d)
num=Math.round(num)
num/=Math.pow(10,d)
return num.toString()
}
2、乘方和开方
*乘方:Math.pow(底数,幂)
开方:Math.sqrt(num)
3、*最大值和最小值:
var 最大/最小的=Math.max/min(a,b,c,d,e,....)
问题:本身不支持数组参数
解决:固定用法:var 最大/最小的=Math.max/min.apply(Math,arr)
apply其中一个作用是可以将数组打散为单个元素
4、绝对值:把负数转为正数
Math.abs(-1)
5、***随机数:Math.random()
可能取到0,但是不可能取到1,意味着取不到最大值
公式:parseInt(Math.random()*(max-min+1)+min)
甚至还提供了三角函数,但是我们不会使用到
2、Date:日期对象:
创建:4种
1、*创建一个当前日期时间
var now=new Date();
2、*创建一个自定义时间:
var birth=new Date("yyyy/MM/dd hh:mm:ss");
3、创建一个自定义时间:
var birth=new Date(yyyy,MM-1,dd,hh,mm,ss);//修改月份,从0开始的,0->1月
4、*复制一个日期:
为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期
所以,在执行API之前先进行赋值,然后再操作复制后的日期
var end=new Date(start);
操作:
1、两个日期对象之间可以相减,得到一个毫秒差,换算出自己想要的某一部分 - 日期的本质其实就是保存的一个毫秒数
创建的最后一个方式:var date=new Date(毫秒数);
2、API:
分量:时间的单位
年日月星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一堆getXXX/setXXX
其中getXXX负责获取某一个分量的值
其中setXXX负责设置某一个分量的值
特殊:
1、取值范围:
FullYear:当前年份的数字
*Month:0~11
Date:1~31
*Day:0~6,外国人的眼里星期天才是一周的第一天
Hours:0~23
Minutes,Seconds:0~59
2、Day,没有set方法
3、如果希望对某个分量进行加减计算
date.setXXX(date.getXXX()+/-n)
4、格式化日期为字符串:
用了此方法会失去一些东西:日期会自动进制,但是字符串不会,但是你却具有了字符串的API
date.toLocaleString();//本地日期格式 :具有兼容性问题,解决:自定义format方法
1、Error对象 - 错误
以后工作/学习中目的:
1、快速找错
2、记得要防用户
1、***浏览器自带4种错误类型:可以快速找到自己的错误
语法错误:SyntaxError - 多半都是你的语法符号写错了
引用错误:ReferenceError - 没有创建就直接使用了
类型错误:TypeError - 不是你的方法,你却去使用了
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);
2、只要发生错误,就会报错,会导致后续代码终止,我们不希望
错误处理:就算发生错误,我们也不希望报错,而是给一个错误的提示,后续代码可以继续执行
语法:
try{
只放入你可能出错的代码
}catch(err){
发生错误后才会执行
console.log(err);
console.log("中文错误提示");
}
try...catch...的性能非常差,几乎里面的代码效率会被降到最低
*可以用一个技术代替:if...else...
*开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就不会出现错误(!isNaN,比较运算,正则)
3、抛出自定义错误
throw new Error("自定义错误信息") - 只要是报错都会卡主后续代码
2、*****Function对象:
1、创建:3种
1、*声明:function 函数名(形参,...){函数体;return 返回值;}
2、直接量:var 函数名=function(形参,...){函数体;return 返回值;}
3、*构造函数:var 函数名=new Function("形参1","形参2",...,"函数体;return 返回值;");
何时:如果你的函数体不是固定的而是动态拼接的一个字符串
比如:以下案例,我们让用户来参与到底是顺序还是降序排列
var arr=[321,5,43,65,8,213,43,765,12];
var user=prompt("排序数组,如果您输入a-b则为升序排列,如果您输入b-a则为降序排列");
var compare=new Function("a","b","return "+user);
arr.sort(compare);
console.log(arr);
2、调用时,
如果有return,记得接住
var result=函数名(实参,...)
3、考点:
1、***创建
2、***作用域:变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
3、***声明提前:
4、***按值传递:
5、***重载:相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
为什么:减轻程序员的负担!
问题:js的语法不支持重载!
js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有
解决:在【函数中】有一个对象 - arguments对象
什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)
作用:***可以接收所有传入的实参
argument对象是一个类数组对象:长得像一个数组,但是,不是数组!
只有3个点和数组相同:
1、使用下标
2、使用length
3、遍历
***arguments可以做的事:
1、实现重载:通过在函数内部判断arguments,执行不同的操作
2、以后有没有形参都无所谓
3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化
6、***匿名函数:没有名字的函数
1、匿名函数自调:
为什么:节约内存,因为匿名函数,没有变量引用着,用完,就会立刻释放变量
语法:
(function(){
})();
2、匿名函数回调:
将函数作为实参,传递给其他函数调用
1、*学习回调的目的:让你们知道哪些叫做回调:只要不是自调,就是回调
arr.sort(function(){})
str.replace(reg,function(){})
btn.onclick=function(){}
2、以后ES6技术:箭头函数:简化一切的回调函数
3、了解一下回调函数的原理
1、*****Function
作用域:2种
1、全局:随处可用,可以反复使用,缺点:容易被污染
2、函数:只能在函数内部使用,不会被污染,缺点:一次性的,是会自动释放的
***函数的执行原理:
1、程序加载时
创建执行环境栈(ECS):保存函数调用顺序的数组
首先压入全局执行环境(全局EC)
全局EC引用着全局对象window
window中保存着全局变量
2、定义函数时
创建函数对象:封装代码段
在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
全局函数的scope都是window
3、调用前
在执行环境栈(ECS)压入新的EC(函数的EC)
创建活动对象(AO):保存着本次函数调用时用到的局部变量
在函数的EC中有一个scope chain(作用域链)属性引用AO
AO有一个parent属性是函数的scope引用着的对象
4、调用时:正是因为前面三步,我们才有了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
5、调用完:函数的EC会出栈,AO自动释放,局部变量也就自动释放了
***两链一包:
作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一个链式结构
作用:查找变量,带来变量的使用规则
1、*****闭包:
希望保护一个可以【反复使用的局部变量】的一种词法结构 - 其实还是一个函数,写法和以前不同
何时使用:希望保护一个可以【反复使用的局部变量】的时候
如何使用:
1、两个函数进行嵌套
2、外层函数创建出受保护的变量
3、外层函数return出内层函数
4、内层函数要去操作受保护的变量
强调:
1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远都不会被释放,使用过多,内存泄漏 - 不可多用
问题:应该在哪里用呢?
1、三个事件需要防抖节流
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变就会触发
3、window.onresize - 每次窗口的大小发生变化就会触发
防抖节流公式:
elem.on需要防抖节流的事件=function(){
fdjl();
}
function f1(){
var timer=null;
return function(){
if(timer){clearTimeout(timer);timer=null;}
timer=setTimeout(function(){
},1000)
}
}
var fdjl=f1();
2、*****Object:
对象 - Array/String/RegExp...对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象
面向对象:三大特点:封装、继承、多态
1、***开发方式:
面向对象 和 面向过程?
面向过程:经过 - 开始->结束,我们一直的开发方式都是面向过程,先干什么在干什么最后干什么
面向对象:对象(属性和方法),js有一句话万物皆对象
为什么要面向对象:现实中所有的数据都必须包含在一个事物中才有意义
何时使用面向对象:以后做任何操作都要封装在一个对象中
2、封装:
创建自定义对象:3种方法
1、直接量方式:var obj={
"属性名":属性值,
...
"方法名":function(){},
...
}
强调:1、其实属性名和方法名的""可以不加 - 暂时建议你加上
2、访问对象的属性和方法
*obj.属性名 === obj["属性名"]
*obj.方法名() === obj["方法名"]();
建议使用.去访问属性和方法,更简单
***JS中一切都是对象,一切对象的底层都是hash数组
3、访问到不存在的属性,返回undefined
4、可以随时随地的添加新属性和新方法
5、如果我希望遍历出对象所有的东西,使用for in,obj[i]才能拿到,不要用.会出问题
6、***如果你希望在对象的方法里使用对象自己的属性:写为this.属性名
*****难点:this的指向:
1、单个元素绑定事件:this->单个元素
2、多个元素绑定事件:this->当前触发的元素
3、函数中使用this,this->谁在调用此方法,this指的就是谁
4、定时器中this->window
2、预定义构造函数方式:var obj=new Object();
obj.属性名=属性值
obj.方法名=function(){}
以上两个方法都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候(第一种方法),第二种方法永远不远
3、自定义构造函数方法:2步
1、创建自定义构造函数
function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
2、调用构造函数创建出对象
var obj=new 类名(实参1,...)
面向对象:
优点:1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义
2、每个功能特地分开写 - 便于维护
3、铁索连舟 - 一个方法触发多个方法联动
缺点:对新手不友好,this的指向非常的恶心
1、*****Object:
1、*****继承:
父对象的成员(属性和方法),子对象可以直接使用
为什么:代码重用!节约内存空间!提升网站性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中
2、***如何找到父对象(原型对象):
保存一类子对象共有属性和共有方法的父对象
1、对象名.__proto__;
2、构造函数名.prototype;
3、*面试题:
两链一包:作用域链、原型链、闭包
每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形参的一条链式结构,就称之为叫做原型链
可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的
最顶层是Object的原型,上面放着我们一个眼熟的toString,怪不得人人都可以用到toString();
JS万物皆对象
4、有了原型对象,设置共有的属性和方法:
1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){};
1、判断是自有还是共有:
1、判断自有:obj.hasOwnProperty("属性名");
如果结果为true,说明是自有属性,如果结果为false,有两种可能,共有或没有
2、判断共有:
if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){
共有
}else{
没有
}
公式:
if(obj.hasOwnProperty("属性名")){
console.log("自有")
}else{
if("属性名" in obj){
console.log("共有")
}else{
console.log("没有")
}
}
2、删除自有和共有
自有:修改:obj.属性名=新值
删除:delete obj.属性名
共有:修改:obj.共有属性名=新值
删除:delete obj.属性名
找到原型,修改/删除原型
3、如何为老IE的数组添加indexOf
原理:
if(Array.prototype.indexOf===undefined){
Array.prototype.indexOf=function(key,starti){
starti==undefined&&(starti=0);
for(var i=starti;i<this.length;i++){
if(this[i]==key){
return i;
}
}
return -1;
}
}
4、如何判断x是不是一个正则:
4种方法:千万别用typeof,只能检查原始类型,不能检查引用类型
1、判断x是否继承自Array.prototype
Array.prototype.isPrototypeOf(x);
2、判断x是不是由Array这个构造函数创建
x instanceof Array;
3、Array.isArray(x); - ES5提供的,只是ES5+,老IE不支持,此方法只有数组
4、输出【对象的字符串】形式
在Object的原型上保存着最原始的toString方法
原始的toString输出形式:[object 构造函数名]
***多态:子对象觉得父对象的成员不好,在本地定义了同名成员,覆盖了父对象的成员
固定套路:借用:Object.prototype.toString.apply(x);
if(Object.prototype.toString.apply(arr)=="[object Array]"){
console.log("是数组")
}else{
console.log("不是数组")
}
5、实现自定义继承:
1、两个对象之间设置继承:
子对象.__proto__=父对象
2、多个对象之间设置继承:
构造函数名.prototype=父对象
时机:应该在开始创建对象之前设置好继承关系