1、****正则表达式: 学习中什么东西都很难,因为每天都在接触新东西 - 只能猛肝 工作中其实更轻松
什么是:定义字符串中字符出现规则的表达式 何时使用:分割、替换、【验证】 如何:语法:/正则表达式/; 1、最简单的正则:/关键字原文/ "no" -> /no/后缀 后缀:g:全部 i:忽略大小写
2、备选字符集:/[备选字符集]/; 强调:1、一个中括号,只管一位字符 2、正则表达式默认只要满足了就不管后续了,我们希望从头到尾完全匹配,解决:前加^后加/; 特殊:如果备选字符集中ascii码是连续的,那么中间部分可用-省略 比如:一位数字:[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、无明确数量: 字符集?:前边相邻的字符集,可有可无,最多一次 字符集*:前边相邻的字符集,可有可无,多了不限 字符集+:前边相邻的字符集,至少一次,多了不限
5、选择和分组: 选择:再两个规则中选一个 规则1|规则2 分组:将多个字符集临时组成一组子规则 (规则1|规则2)
6、指定匹配位置: 开头:^ 结尾:,表示从头到尾要求完全匹配 - 只要你做【验证】必须加上
7、预判:密码强度验证:4位密码,数字和字母都可以,至少要有一位大写和一位数字。 /^(?![0-9]+/; - 前面的预判意思是:不能纯由数字组成,可能由大写、小写 /^(?![A-Za-z]+/; - 前面的预判意思是:不能纯由大写字母组成,也不能纯由小写字母组成,也不能纯由大写和小写字母组成
/^(?![0-9a-z]+)[0-9A-Za-z]{4})(?![A-Za-z]+)[0-9A-Za-z]{4})[0-9A-Za-z\u4e00-\u9fa5]{4}$/ - //至少要有一位汉字
2、****String支持正则的API: 1、分割:var arr=str.split(str/reg);
2、*****替换: 1、基本替换法: var newStr=str.replace(str/reg,"新内容"); //replace支持正则,搭配上后缀就可以替换所有的关键字,而且替换往往都要加后缀 //问题:只能替换为固定的内容
2、高级替换法: var newStr=str.replace(/[卧我握窝沃][槽操草糙艹屮肏你]+/g,function(a,b,c){ // console.log(a);//要替换的关键字 - 正则匹配到的关键字 // console.log(b);//要替换的关键字的下标 - 正则匹配到的关键字的下标 // console.log(c);//原文本身 return a.length==2?"":a.length==3?"":"***"; });
3、格式化: var idCard="500103198602215933"; var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/; idCard=idCard.replace(reg,function(a,b,c,d){ //再replace的时候,如果正则中出现了分组,那么我们会得到更多的形参 //再形参a的后就会再出现n哥形参,具体多少个看你有多少个分组 //第一个分组获取到的内容会保存在第二个形参之中 //第二个分组获取到的内容会保存在第三个形参之中 //... //倒数两个形参分别是下标和原文 return b+"年"+c+"月"+d+"日"; })
3、****正则对象 创建:2种 1、直接量方式:var reg=/正则表达式/后缀; 2、构造函数方式:var reg=new RegExp("正则表达式","后缀");
方法:1个 验证:var bool=reg.test(用户输入的); true->说明用户输入正确 false->说明用户输入错误 Math数学对象:专门提供了数学计算的API 强调:不需要创建,直接使用的,由浏览器的JS的解释器自动创建
属性:Math.PI === 3.1415926不需要我们创建的,Math中有它
方法: 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几次方(保留几位小数)
2、乘方:Math.pow(底数,幂) === 底数**幂 开平方:Math.sqrt(num)
3、绝对值:把负数转为正数 Math.abs(num);
4、最大值和最小值: var max/min=Math.max/min(a,b,c,d,e,f,g,....); 问题:本身不支持数组参数的 *解决:固定用法: var max/min=Math.max/min.apply(Math,arr); apply其中一个功能是可以将数组打散为单个元素
5、**随机整数:Math.random() - 在0~1之间取出一个随机的小数,可能取到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,dd,hh,mm,ss); //修正月份:从0开始的,0->11月
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、在设置时间的时候,如果超过了范围,会自动进制 3、Day是不允许设置的
3、如果希望对某个分量进行加减操作,推荐一句话完成 date.setXXX(date.getXXX()+/-num)
4、格式化日期为字符串: 作用:1、date->string:stringAPI更多更实用 2、默认的标准国际日期,不是所有人都认识 date.toLocaleString();//转本地字符串 问题:存在兼容性问题 解决:自定义日期格式化format Error错误对象: 以后工作/学习中目的: 1、快速找错 2、记得有的时候可能要防一手用户
1、***浏览器自带4种错误类型,可以快速找到自己的错误: 语法错误:SyntaxError - 多半都是语法/符号错了 引用错误:ReferenceError - 没有创建过就去使用了 类型错误:TypeError - 不是你的方法/属性,你却使用了 范围错误:RangeError - 只有一个API:num.toFixed(d);//d取值范围的,0~100之间
2、只要发生错误,就会报错,会导致后续代码终止执行了(后面代码没效果、如果你做的是APP、软件发生报错,则会闪退,体验感差),我们不希望 错误处理:就算发现错误,我们也不希望报错,而是给一个错误提示,后续代码依然可以继续执行 语法: try{ 只放可能出错的代码 }catch(err){ 只有发生错误后才会执行 }
try...catch...的性能非常差,几乎是所有代码中效率最差的 *可用一个技术代替:if...else... *开发经验:提前预判用户:记住一切的客户端输入/用户输入都是坏人 - 你也不必担心,只要做好你该做的防护即可(!isNaN、正则);
3、抛出自定义错误: throw new Error("自定义错误描述");
2、*****Function: 1、***创建:3种 *声明方式:function 函数名(形参列表){函数体 return 返回值} - 具有完整的声明提前 直接量方式:var 函数名=function(形参列表){函数体 return 返回值} - 只提前函数名部分,函数名其实就是一个变量名 *构造函数:var 函数名=new Function("形参1","形参2",.....,"函数体"); 何时:如果你的函数体不是固定的,而是动态拼接的(函数体现在毕竟是一个字符串)
2、调用:var 接住结果=函数名(实参列表);
3、考点: 1、***如何创建 2、***什么是作用域 3、***什么是声明提前 4、***什么是按值传递 5、***什么是重载:相同的函数名,传入不同的实参,可以自动选择对应的函数执行 为什么:减轻程序员的压力 问题:JS的语法不支持重载的! JS不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前所有的 解决:在【函数中】有一个对象 - arguments对象 什么是arguments:自动创建的,是一个类数组对象,不是数组!作用:接受住所有的实参, 以后有没有形参无所谓了,变相的实现了重载,可以在函数内部判断arguments的不同,执行不同的操作 只有3个点和数组相同: 1、都有下标 2、都有length 3、都可以遍历
正式开发中,开发结束后,需要做代码的优化,可能就会将多个函数整合为一个函数
6、***匿名函数:没有名字的函数,理论上来说我们无法调用 1、自调:函数自己调用自己 语法: (function(){ //代替全局代码 })();
特点:自调的代码只能执行一次 === 全局代码 为什么:节约内存,因为匿名函数,没有变量引用着,用完后,就会立刻释放
2、回调:将函数作为了实参,传递给其他函数使用 1、学习回调的目的:让你们知道哪些叫回调,匿名函数,只要不是自调,就是回调 arr.sort(function(a,b){return a-b}) str.replace(reg,function(){}) btn.onclick=function(){}
2、以后ES6有一个技术:箭头函数:简化一切的【回调函数】
3、了解了以下回调函数的原理 - 其实不是没调,只是我们没调用,前辈们早就帮我们调用好了 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无人引用,垃圾回收器登场,AO自动释放,局部变量也就自动释放
*****两链一包: 作用域链:以函数的EC为起点,经过AO,【逐级引用】,形成的一条链式结构我们就称之为叫做作用域链 作用:查找变量,带来了变量的使用规则 原型链:今天不管
1、*****闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是是一个函数 何时:保护一个可以【反复使用的局部变量】 如何: 1、两个函数进行嵌套 2、外层函数创建受保护的变量 3、外层函数return出内层函数 4、内层函数要去操作受保护的变量
强调: 1、判断是不是闭包,有没有两层函数嵌套,返回内层函数,内层函数在操作外层受保护的变量 2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本 3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺陷:受保护的变量,永远不会被释放,使用过多,会导致内存泄漏 问题:应该在哪里使用?使用场景:防抖节流 三个事件需要防抖节流: 1、elem.onmousemove - 频繁的修改DOM树,影响性能 2、input.oninput - 每次input的内容修改都会触发,性能不好 3、window.onresize
xxx.以上三个事件=function(){ inner(); }
公式: function fdjl(){ var timer=null;//局部的timer准备保存定时器,定时器的序号 return function(){ if(timer!==null){clearTimeout(timer)}//停止一次性定时器 timer=setTimeout(function(){ 操作即可! },1000) } } var inner=fdjl();
2、*****Object:对象 - Array/String/Function/RegExp... 都是前辈们提前创建好的,我们现在要学习的是自定义对象 面向对象:三大特点:封装、继承、多态 1、***鄙视题:简单的说一说 面向对象 和 面向过程 开发方式的区别? 面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话 属性:身高、体重、姓名、性别、年龄、智商、情商、发色... 方法:吃饭、睡觉、拉屎、跑步、喝水、唱歌... 所有的东西都要放在一个对象之中,才显得更有意义 面向过程:经过:开始 -> 结束,我们一直的开发方式都是面向过程:先干什么再干什么最后干什么
为什么要面向对象:现实生活中所有的数据都必须包含在一个事物中才有意义 何时要使用面向对象开发方式:以后做任何操作都要封装在一个对象中 - 非常的不适合初学者
2、封装:创建自定义对象:3种方式: 1、*直接量: var obj={ "属性名":属性值, ... "方法名":function(){操作}, ... };
特殊: 1、其实属性名和方法名的""可以不加的,暂时不推荐,因为以后我们要学习一种数据格式JSON,要求属性名和方法名必须是一个"",单引号都不可以,为了习惯,我们就一直加上使用 2、访问对象的属性和方法 *obj.属性名 === obj["属性名"] *obj.方法名() === obj"方法名"; ***js有一句话万物皆对象,一切对象的底层都是hash数组 3、访问到不存在的属性,返回undefined 4、可以随时随地的添加新属性和方法 5、希望遍历出对象所有的东西,使用for in循环 6、***如果你希望再对象的方法里使用对象自己的属性:this.属性名
*****难点:this的指向 1、单个元素绑定事件:this->这个元素 2、多个元素绑定事件:this->当前元素 3、函数中使用this:this->当前调用此函数的对象,看看函数前面是谁 4、定时器中this->window 5、箭头函数this->外部对象 6、构造函数中的this->当前正在创建的对象
2、预定义构造函数方式:var obj=new Object();//空对象 //需要自己后续慢慢添加 obj.属性名=属性值; obj.方法名=function(){}
以上两个方法都有一个缺陷:一次只能创建一个对象
- 3、批量创建对象:【自定义】构造函数方式 1、创建自定义构造函数 function 类名(name,age,hobby){ this.name=name; this.age=age; this.hobby=hobby }
2、调用构造函数创建出对象 var obj=new 类名(实参列表);
面向对象: 优点: 1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实生活 2、每个功能特地分开写 - 便于后期维护,哪怕没有注释,我也能一眼看懂代码 3、铁锁链舟 - 一个方法触发多个方法联动
缺点:对新手不友好,this的指向变来变去的
JavaScript是基于原型的面向对象语言
1、*****Object继承:父对象的成员(属性和方法),子对象可以直接使用
为什么:【代码重用】!节约内存控件,提升网站的性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义再父对象之中,尤其是方法,方法/函数也是一个对象,创建的函数越多性能越差
2、如何去找到父对象(原型对象):保存一类子对象共有属性和共有方法的 1、对象名.proto;//必须先有一个对象 2、构造函数名.prototype;//构造函数名:String、Function、Object、RegExp、Array、Date、h52205...,几乎人人都有,除了undefined,null,Math
3、面试题:两链一包:作用域链、闭包、原型链 每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形成的一条链式结构,我们就称之为叫做原型链 可以找到所有父对象的成员(属性和方法),作用:【查找】共有属性和共有方法 最顶层是Object的原型,上面放着我们眼熟的toString,怪不得人人都可以toString(); JS万物皆对象
4、有了原型对象,设置共有属性和共有方法: 1、原型对象.属性名=属性值; 2、原型对象.方法名=function(){}
非常多的鄙视题: 1、判断自有还是共有 1、判断自有:obj.hasOwnProperty("属性名"); 如果结果为true,说明是自有属性,如果结果为false,有两种可能:共有或没有 2、判断共有: if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in自动去整条原型链上进行查找 console.log("共有") }else{ console.log("没有") }
公式: if(obj.hasOwnProperty("属性名")){ console.log("自有") }else{ if("属性名" in obj){ console.log("共有") }else{ console.log("没有") } }
2、修改/删除 自有和共有 自有:修改:obj.属性名=新属性值; 删除:delete obj.属性名;
共有:修改:原型对象.属性名=新属性值; - 千万不要对着对象直接修改共有属性,这样会导致再本地添加一个同名属性 删除:delete 原型对象.属性名;
3、如何为老IE的数组添加indexOf - 不是固定的,但是是一种类型题,为一类人添加方法 if(Array.prototype.indexOf===undefined){//说明你打开了老IE 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是不是一个数组:千万别用typeof,typeof只能判断原始类型,引用类型显示的都是一个object,这道题也不是固定的 4种: 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) === "[object Array]"; true->数组 false->不是数组
5、实现自定义继承: 1、实现两个对象之间继承: 子对象.proto=父对象;
2、实现批量对象继承 构造函数名.prototype=父对象; 但是要小心时机,必须在创建对象之前,确定关系