学习API可以简化我们的底层操作:*string、number、bollean *regexp *array *function math data error *object globle 难点:面向对象
一、 String 对象
多个字符组成的【只读】字符【数组】
只读:所有的字符串的API都不会修改原字符串,只会返回的新的结果
和数组有相同的地方:
1、字符串中的个数:str.length
2、获取字符串中某个字符:str[i]
3、遍历字符串
4、所有数组不修改原数组的API,字符串也可以使用(concat、slice),其他不可以
和数组也有很多不同的地方:
所有的数组直接修改原数组的API,字符串都不可以使用,比如排序只有数组可以使用,但是字符串也有很多很多属于自己的API
引用/对象类型:11个
String Number Boolean -> 包装类型
Array Function Date(日期) Math(数学) RegExp(正则:验证)
Error(错误)
Object(面向对象开发方式)
Global(全局对象) - 在浏览器中被window全局对象(保存全局变量和全局函数)代替了,只不过window可以省略不写
包装类型:专门将原始类型的值封装为一个引用类型的对象
为什么:原始类型的值原本是没有任何属性和方法,意味着原始类型本身是不支持.操作
但是前辈们发现字符串经常会被我们程序员所操作
为了方便我们程序员为这三个人提供了包装类型(提供属性和方法)
何时使用:只要试图用原始类型的变量调用属性或者方法时,自动包装
何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又变回了原始类型)
扩展:周期定时器:每过一段时间就会执行一次
//开启:
timer=setInterval(function(){
操作;
},间隔毫秒数)
//停止:
clearInterval(timer)
二、String API
1、转义字符:\
作用:1、将字符串中和程序冲突的字符转为原文
2、包含特殊功能的符号——换行:\n
*3、输出unicode编码的字符
\uXXXX :第一个汉字:\u4e00 ascii:19968
最后一个汉字:\u9fa5 ascii:40869
2、*大小写转换:str.toUpperCase()
将字符串中的每个英文字母统一的转为大写 或 小写。
何时:只要程序不区分大小写,就要【先统一】转为大写 或 小写,再比较(验证码)
如何: 大写:var upper=str.toUpperCase();
小写:var lower=str.toLowerCase();
3、获取字符串中指定位置的字符:var newStr=str.charAt(i); === str[i]
4、获取字符串中指定位置的字符的ASCII码:
var ascii=str.charCodeAt(i);
根据ascii码转回原文:
var 原文=String.fromCharCode(ascii);
5、***检索字符串:检查索引:获取关键字的下标
var i=str/arr.indexOf("关键字",starti);
从starti位置开始,查找右侧【第一个关键字】的位置
starti可以省略,默认从0位置开始查找
返回值:找到,返回的是第一个关键字的第一个字符的下标
*没找到,返回-1,我们不关心下标为多少,只关心为不为-1
作用:判断有没有
强调:数组也能用此方法,后期才添加上的
笔试题:默认只能获取到第一个关键字的下标,如何才能获取所有关键字的下标?
var str="no zuo no die no can no bibi";
var index=-1;
while((index=str.indexOf("no",index+1))!=-1){
console.log(index);
}
6、*截取字符串:
1. var subStr=*str/arr.slice(starti,endi+1);//和数组用法一样,支持负数参数,-n代表倒数第n个
2. str.substring(starti,endi+1);//用法几乎和slice相同,不支持负数参数
3.*str.substr(starti,n);//支持负数参数,n代表截取的个数,不必考虑含头不含尾
7、拼接字符串:var newStr=str.concat(str1,...);//还不如+运算
8、*替换字符串:
var newStr=str.replace("关键字"/正则,"新内容");
9、*****切割/分割字符串:
作用:将字符串转为数组
var arr=str.split("自定义切割符");
特殊:1、切割后,切割符就不存在
2、切割符"",切散每一个字符
扩展:js创建自定义元素
1、JS创建空元素:
var elem=document.createElement("标签名");
2、为此空标签添加必要的属性或事件
elem.innerHTML="内容"
elem.属性名="属性值"
elem.on事件名=function(){}
3、渲染到DOM树上:
爸爸.appendChild(新元素)
三、正则表达式
1、最简单的正则:
写关键字原文 "no" -> /no/后缀
替换:str.replace(“no”,”yes”) -> str.replace(/no/gi,”yes”)
后缀: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]+)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
/(?![0-9a-z]+)(?![A-Z0-9]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,三者必须都有
四、支持正则表达式字符串的API:
切割:var arr=str.split(reg)
***替换:
1、基本替换法:缺陷:替换的新东西是固定的
var newStr=str.replace(/正则表达式/后缀,"新内容");
//replace支持正则,并且搭配上后缀就可以找到全部
2、高级替换法:
var newStr=str.replace(/正则表达式/后缀,function(a){
//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){
//在replace的时候,正则出现了分组:我们会得到更多的形参
//在形参a的后面就会出现n个形参,就看你有多少个分组
//第一个分组获得的内容会保存在第二个形参中
//第二个分组获得的内容会保存在第三个形参中
return b+"年"+c+"月"+d+"日";
})
console.log(id);
五、正则对象:
创建:
直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀");
API:var bool=reg.test(用户输入的内容);
true->验证成功 false->验证失败
六、Math对象:专门提供了数学计算的API,不需要创建
1、取整:3种
1、上取整:超过一点点,就取下一个整数
var num=Math.ceil(num);//小数位数不能超过15位
2、下取整:无论超过多少,都会省略掉小数部分
var num=Math.floor(num);
3、四舍五入取整:
var num=Math.round(num);
//以上三个都不用:都是在取整,只能取整
//取整的方式:以上三个 + *parseInt(str去掉单位) + *num.toFixed(d);
//个人最推荐*num.toFixed(d):优点:可以四舍五入保留指定的小数位数
缺点:结果是一个字符串,建议搭配上一个parseFloat
//*笔试题:不允许用toFixed的情况下,自己封装一个函数,实现toFixed的功能并且最后返回的是一个数字:
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);//1
5、***随机数:
Math.random():在[0-1)之间取出一个随机小数 - (取不到最大值)
只要以后网站中某一块既有随机的功能一定需要用到随机数方法
取min-max之间的随机整数公式:parseInt(Math.random()*(max-min+1)+min);
七、Date:日期对象:
1、创建: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);
2、操作:
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方法
八、Error对象 - 错误
1、浏览器自带4种错误类型:可以快速找到自己的错误**
语法错误:SyntaxError - 多半都是你的语法符号写错了
引用错误:ReferenceError - 没有创建就直接使用了
类型错误:TypeError - 不是你的方法,你却去使用了
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d取值范围的:0~100之间
2、只要发生错误,就会报错,会导致后续代码终止,我们不希望
错误处理——对用户:就算发生错误,我们也不希望报错,而是给一个错误的提示,后续代码可以继续执行
try{
只放入你可能出错的代码
}catch(err){
发生错误后才会执行
console.log(err);//err就是我们的错误提示,英文
console.log("中文错误提示");
}
try...catch...的性能非常差,几乎里面的代码效率会被降到最低
*可以用一个技术代替:if...else...
*开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就不会出现错误(!isNaN,比较运算,正则)
3、抛出自定义错误
throw new Error("自定义错误信息") - 只要是报错都会卡主后续代码
九、Function对象:
函数的执行原理:
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自动释放,局部变量也就自动释放了
5、***重载:
相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
为什么:减轻程序员的负担!
问题:js的语法不支持重载!
(js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有)
解决:在【函数中】有一个对象 - arguments对象
什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)
**作用:*可以接收所有传入的实参,组成一个类数组
只有3个点和数组相同:
1、使用下标
2、使用length
3、遍历
***arguments可以做的事:
1、实现重载:通过在函数内部判断arguments,执行不同的操作(比如判断length)
2、以后有没有形参都无所谓
3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化
6、匿名函数:没有名字的函数
1、匿名函数自调:
节约内存,因为匿名函数,没有变量引用着,用完,就会立刻释放变量
(function(){
//以后可以代替全局代码写法,尽量不要在外部再去书写JS(不用担心事件会被释放掉,只会释放掉变量)
})();
2、匿名函数回调:将函数作为实参,传递给其他函数调用(都是前辈们创建好的)
1.*学习回调的目的:让你们知道哪些叫做回调:只要不是自调,就是回调
arr.sort(function(){})
str.replace(reg,function(){})
btn.onclick=function(){}
2.以后ES6技术:箭头函数:简化一切的回调函数
3.了解一下回调函数的原理,都会有一个形参接住并调用
7、*****闭包
希望保护一个可以【反复使用的局部变量】的一种词法结构 - 其实还是一个函数,写法和以前不同
如何使用:
1、两个函数进行嵌套
2、外层函数创建出受保护的变量
3、外层函数return出内层函数
4、内层函数要去操作受保护的变量
强调:
1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远都不会被释放,使用过多,内存泄漏 - 不可多用
问题:应该在哪里用呢——三个事件需要防抖节流
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变就会触发
3、window.onresize - 每次窗口的大小发生变化就会触发
防抖节流公式:
elem.on需要防抖节流的事件=function(){
fdjl();//内层函数只要一旦移动就会触发
}
function f1(){
var timer=null;//3
return function(){//
if(timer){clearTimeout(timer);timer=null;}
timer=setTimeout(function(){
//操作
},1000)
}
}
var fdjl=f1();
十、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
5、构造函数中,this->正在创建的对象
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的指向非常的恶心
3、*****继承:父对象的成员(属性和方法),子对象可以直接使用
为什么:代码重用!节约内存空间!提升网站性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中
1、如何找到父对象(原型对象):保存一类子对象共有属性和共**有方法的父对象
1.对象名.proto;//必须先有一个对象
2.构造函数名.prototype;//构造函数名,几乎人人都有,除了Math
2、面试题:两链一包:作用域链、原型链、闭包
每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形参的一条链式结构,就称之为叫做原型链
可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的
最顶层是Object的原型,上面放着我们一个眼熟的toString,怪不得人人都可以用到toString();
JS万物皆对象
3、有了原型对象,设置共有的属性和方法:
1、原型对象.属性名=属性值;//添加了一个共有属性
2、原型对象.方法名=function(){};//添加了一个共有方法
***继承非常多的笔试题:
1、判断是自有还是共有:
1.判断自有:obj.hasOwnProperty("属性名");
如果结果为true,说明是自有属性,如果结果为false,有两种可能,共有或没有
2.判断共有:
if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){//in 会自动在obj的原型上进行查找
共有
}else{
没有
}
// 公式:
if(obj.hasOwnProperty("属性名")){//false
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){//老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是不是一个正则:
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=父对象
时机:应该在开始创建对象之前设置好继承关系