admin管理员组文章数量:1574605
1. String的正则函数:
1. 查找敏感词: 4种:
1. 查找一个固定的敏感词出现的位置
var i=str.indexOf("敏感词",fromi);
位置<-的<-"敏感词"
在str中查找fromi位置之后的下一个"敏感词"的位置。
如果没有给fromi参数值,则默认从开头(0位置)开始找。
返回值: 如果找到敏感词,返回敏感词第一个字在字符串中的下标位置.
如果没找到,返回-1
问题: 只能查找一个固定的敏感词,如果换成同音字或拼音,就找不到了!
2. 用正则表达式模糊查找一个敏感词的位置:
var i=str.search(/正则/i)
在str中查找第一个符合正则表达式要求的敏感词的位置。
强调: search不能指定开始查找的位置,只能从头(0位置)开始找。
返回值: 同indexOf
问题: 所有正则默认区分大小写
解决: 在第二个/后加i
问题: 只能返回敏感词的位置,不能返回敏感词的内容。
3. 获取敏感词的内容:2种:
(1) 只获取一个敏感词的内容和位置:
var arr=str.match(/正则/i)
匹配
在str中,查找第一个符合正则要求的敏感词的内容和位置
返回值: 如果找到敏感词,返回的是一个数组:
如果想获得本地找到的敏感词的内容:
arr[0]
如果想获得本次找到的敏感词的位置:
arr["index"]
强调: "index"不能改名,因为这个数组时match内部建立好,返回给咱们的。人家建立数组时,用的什么门牌号,咱们访问数组内容时,就要遵循人家match的要求!
如果没找到,返回null
强调: 如果一个函数有可能返回null!则使用前必须先验证不是null,才能使用。因为null之后,什么都不能加!null()报错,null.报错!
问题: 正则表达式默认只能匹配一个敏感词。匹配到之后,就下班了!
解决: 只要在/后加g,global全部
(2) 查找所有敏感词的内容:
var arr=str.match(/正则/ig)
在str中查找所有符合正则要求的敏感词的内容。
问题: 如果match加了g,就只能返回敏感词的内容,无法返回位置了!
返回值: 如果找到多个敏感词:
arr: ["敏感词1", "敏感词2",... ...]
如果没找到,返回null
今后,只要只关心所有敏感词的内容,而不关心敏感词的位置时,就用match+g。
4. 即查找每个关键词的内容,又查找每个关键词的位置。
RegExp对象的exec()函数解决
2. 替换:2种:
(1) 简单替换:
将所有敏感词都替换为统一的新词
str=str.replace(/正则/ig, "新词")
将str中所有符合正则要求的敏感词,都替换为指定的统一的新词。
坑: 替换后,不报错,也替换不成功!
因为: 字符串都是不可变类型!
无法对原字符串直接修改。
所有字符串函数,都只能返回修改后的新字符串。而保持原字符串不变。
解决: 要想获得新值,必须用=接住新值,并保存到变量中。
(2) 高级替换:
根据每次找到的敏感词不同,动态选择不同的新词替换!
str=str.replace(
/正则/ig,
//回调函数callback:
//replace会自动在每个找到的关键词上调用一次这个回调函数。找到几个词,就反复调用几次!每次调用时,会自动将本次找到的关键词传给函数的参数keyword
//2个要求:
funtion(keyword){//1. 必须有一个参数接住本次找到的一个关键词
//2. 必须返回处理后的新词
return keyword.toUpperCase();
}
//replace会将回调函数返回的新词,替换到本次找到的关键词位置!
)
强调: 所有形参,都可以自己定义变量名。
补: 回调函数: 我们自己定义的,但是不是由我们自己调用执行。而是交给其它对象或函数去执行。
衍生: 删除敏感词,其实就是将敏感词替换为""
3. 切割:
什么是: 将一个字符串,根据指定的切割符,切割成多段子字符串。
(1) 简单切割: 切割符是固定的
var arr=str.split("切割符")
将字符串str,按指定的"切割符",切割成多段子字符串保存在数组中
(2)复杂切割: 切割符是变化的,但是有规律的。
var arr=str.split(/正则/)
将字符串str,按符合正则的切割符,切割成多段子字符串。
衍生: 打散字符串为字符数组
总结:
查找敏感词
1. 查找一个固定的敏感词: indexOf
2. 用正则模糊查找一个敏感词的位置: search
3. 用正则查找敏感词的内容
1. 只查找一个敏感词的内容和位置match
2. 查找所有敏感词的内容,但是无法获得位置
match+g
4. 即查找每个敏感词的内容,又查找每个敏感词的位置。——暂缺
替换敏感词 replace
删除 replace为""
切割 split
打散字符串为字符数组 split("")
验证格式——暂缺
2. RegExp对象:
什么是: 专门保存一条正则表达式,并提供用正则执行查找和验证方法的 对象
何时:
1. 验证
2. 高级查找
如何:
1. 创建对象: 2种:
1. 用//简化版创建:
var reg=/\b[a-z]/ig;
何时: 如果正则表达式是固定不变的!
问题: 不支持动态生成正则表达式
因为: //之间是正则表达式的地盘!写js!人家不认识!
2. 用new创建
var reg=new RegExp("\b[a-z]","ig")
何时: 如果正则表达式需要根据变量或数组内容,动态生成!
因为: 这种方法用js认识的""代替了//。而在js中,有很多种办法。灵活的拼出想要的字符串
2. RegExp提供的函数:
1. 验证格式:
var bool=reg.test(str)
用reg规则,检测str是否符合规则要求。
返回值: true/false
坑: test默认只要在str中找到部分和reg匹配,就返回true!而不要求从头到尾完全匹配!
解决: 今后凡是验证,都要求从头到尾必须完整匹配:前加^,同时后加$,意为从头到尾!
2. 即查找每个关键词的内容,又查找每个关键词的位置
var arr=reg.exec(str);
在str中查找下一个关键词的内容和位置
返回值: 和match不加g的时候是一样的!
如果找到: arr: [ 0: 关键词的内容, index: 位置]
如果没找到返回null
问题: 每次只能找一个关键词的内容和位置,无法找所有。
解决: 正则表达式后+g
并且用循环反复执行exec()。每次执行exec(),exec()都非常聪明的自动找下一个。
但是,因为exec()返回的数组中,一次只能放一个敏感词的内容和位置。所以,第二次找到的只能覆盖第一次找到的。
2. Function
1. 以声明方式创建:
function 函数名(形参列表){
函数体
return 返回值
}
形参:
什么是: 专门接收从函数外部传入函数内数据的变量
为什么: 有些函数执行时,需要动态获得必须的数据,才能正常执行。
何时: 只要一个函数,必须某些数据才能正常执行时
返回值:
什么是: 一个函数的执行结果
为什么: 外部调用者可能需要获得函数的执行结果
何时: 只要外部调用者,需要获得函数的执行结果时。
问题: 会被声明提前(hoist)
什么是: 在程序开始执行前!程序会先将所有var声明的变量和function声明的函数,提前到当前作用域的顶部集中创建。而赋值(=)留在原地。
所以声明提前是js中广受诟病的缺陷。打乱了程序正常的执行顺序。
2. 赋值方式创建:
var函数名=function (形参列表){
函数体
return 返回值
}
说明: 赋值方式创建的函数,和声明方式创建的函数在使用时,是完全一样的!
只不过,在程序开始执行前,赋值方式可避免函数被声明提前。保持了程序原有的执行顺序。
揭示: js中其实函数也是一个普通的对象而已。函数名仅仅是一个普通的变量。函数名变量通过对象地址引用着函数对象。每次调用函数名时,都是通过地址,找到函数对象,再执行其中的内容。
3. 调用函数:
var 变量=函数名(实参值列表)
调用函数名,等于调用函数中的函数体
实参值以赋值方式传递给形参变量
如果函数有返回值,则用变量接住。
强调: 如果一个函数,只是定义,而没有调用,其内部的代码是不执行的!即使写错!也不会发现,也不报错!!
1. 闭包:
1. 受保护的变量是谁?
2. 外层函数共生了几个内层函数孩子
规律: 外层函数是妈妈,外层函数返回到外部的内层函数时生的孩子。
1. 每调用一次妈妈,就包一个红包给孩子
2. 调用一次妈妈生的多个孩子,共用同一个红包。
2. 面向对象:
什么是对象:
用途: 对象是描述现实中一个具体事物的属性和功能的程序结构。
本质: 程序中集中存储一个事物的属性和功能的一块存储空间,再起一个名字
什么是面向对象编程: 程序都是先将数据封装在对象中,然后再按需使用对象中的成员。这样的编程方式,就称为面向对象编程。
为什么: 便于大量数据的维护和使用
何时: 今后所有程序都是用面向对象方式实现的
如何: 面向对象三步/三大特点: 封装,继承,多态
1. 封装:
什么是: 创建一个对象,集中保存一个事物的属性和功能。
为什么: 便于大量数据的维护和使用
何时: 只要使用面向对象方式编程,都要先将数据和功能封装在对象中,然后再按需使用。
如何: 3种:
1. 用{}创建一个对象:
var 对象名={
属性名: 值,
... : ... ,
方法: function(){
}
}
如何访问对象的成员:
访问对象的属性: 对象名.属性名
其实属性就是保存在对象中的变量而已。只不过,要找到属性,得先找到对象,再用.操作符,进入对象中,访问属性。
访问对象的方法: 对象名.方法()
其实方法就是保存在对象中的函数而已。只不过,要先找到对象,然后用.操作符进入对象中,才能找到内部的方法加()调用。
问题: 在对象自己的方法,直接使用属性名却无法访问到自己的属性值。报错: 属性名未定义
原因: 所有不带.的变量,默认只能在作用域链中(临时函数作用域和window)查找。但是对象的{}又不是作用域,所以对象是不包含在作用域链中的。所以,直接使用属性名,无法找到藏在对象内的属性的。
不好的解决办法: 在属性名前加"对象.",勾引着引擎进入对象中查找属性。
问题: 紧耦合: 如果外部修改,内部被迫跟着修改。
好的解决: 松耦合: 外部改变,内部代码不用变,也能自动适应!——this.属性名
什么是this: 在调用函数时,临时指向正在调用函数的.前的对象的关键词
总结: 只要对象自己的方法,想访问自己的属性,都必须加"this."
错误: 函数定义在哪个对象中,this就指哪个对象
正确: this与函数定义在哪儿无关!只与函数调用这一瞬间,.前的对象有关!
2. 用new创建: 2步:
1. 先创建空对象: var obj=new Object()
2. 强行向空对象中添加新属性:
obj.属性=值
obj.方法=function(){ ... }
揭示: 所有js对象底层,其实都是关联数组。
对象 vs 关联数组:
相同: 4个
1. 都可用["成员名"]或.成员名两种方式,访问自己的成员。.成员名 其实就是 ["成员名"] 的简写。也就说 .成员名 到底层会被自动翻译为 ["成员名"]
坑: 如果成员名来自于一个变量!则不能用".变量"方式访问成员。因为".变量",会被翻译为["变量"],出错了!因为变量不能放在""里!
解决: 今后只要成员名不是写死的,而是来自于一个变量,只能用[变量] 方式访问,不能带引号。
2. 都可被for in循环遍历每个成员。
3. 随时可以给数组或对象在任何位置添加新成员,而不会报错!而是自动创建该成员。
所以,如果想给一个已经创建完的对象,添加新属性,只有一个野蛮的办法:强行赋值!
4. 访问数组或对象中不存在的位置,不会报错!而是返回undefined!
不同: 类型:
关联数组: 是数组家孩子
对象: 是Object家孩子
问题: 只能创建一个对象。如果反复创建多个相同结构的对象时。代码很冗余,不便于维护。
解决:
3. 用构造函数:
什么是: 专门描述一类对象统一结构的函数。
为什么: 重用对象的结构定义。
何时: 只要程序中,需要反复创建同一类型的多个对象时。
如何: 2步:
1. 定义构造函数来描述一类对象的统一结构
function 类型名(形参1,...){
this.属性名=形参1;
... = ... ;
this.方法名=function(){
}
}
2. 调用构造函数按照统一结构创建对象
var obj=new 类型名(属性值1,...);
new的原理: 4件事:
1. 新建一个空对象
2. ?
3. 用new调用构造函数。将构造函数中的this,都吸引到new上!
然后通过强行赋值的方式!给新对象new,添加规定好的属性!
凡是构造函数中规定的属性和方法,最终都会添加到孩子身体里!
4. 返回新对象的地址给变量保存起来。
问题: 构造函数虽然实现了代码重用,但是浪费了内存!
2. 继承:
什么是: 爹的成员,孩子无需重复创建,就可直接使用。
为什么: 代码重用,节约内存
何时: 只要多个孩子都需要共用的成员,都要通过继承使用!
如何:
1. 不用自己设置继承关系!其实,js中已经帮我们设置好了继承关系。咱们直接使用即可!
js实现继承,都是通过继承原型对象来实现的。
原型对象: 专门集中保存一类子对象的共有成员的父对象。
原型对象是在定义构造函数时,附赠的。只不过,这个原型对象暂时是空的。
如何找到原型对象: 每个构造函数都有一个属性叫prototype,指向自己附赠的这个原型对象。
其实原型对象中,也有一个constructor属性,指回构造函数。
子对象何时如何继承的父对象: new的第2步: 让子对象的_ _proto_ _属性指向妈妈的原型对象。凡是从_ _proto_ _属性指出的关系,都叫继承关系。
结果: 原型对象中的成员,子对象无需创建,可直接使用!
2. 如何向原型对象中添加共有成员:
强行赋值:
妈妈.prototype.成员名=值
比如: 向所有学生的共同的爸爸里强行添加一个公用的方法intrSelf()。
Student.prototype.intrSelf=function(){
... ...
}
结果: 凡是Student生的孩子,都能.intrSelf()来直接调用爹里的函数。
比如: lilei.intrSelf()
hmm.intrSelf()
自有属性和共有属性:
自有属性: 保存在子对象中,归对象个人所有的属性
共有属性: 保存在原型对象中,归多个子对象共有的属性
获取属性值: 二者没有任何差别!
子对象.属性名
修改属性值:
自有属性,可直接用子对象修改
比如: lilei.sage++
共有属性,只能用原型对象修改
如果强行用子对象.共有属性修改
会给这个子对象添加一个同名的自有属性。从此,这个子对象和父对象的属性,无关了。
内置对象的原型对象:
任何一种类型,其实都是由构造函数和原型对象组成。内置类型也是这样。
js包含哪些内置类型:
String Number Boolean
Array Date Math RegExp
Error
Function Object
global
所有内置类型也包含构造函数和原型对象:
构造函数: 创建该类型的子对象
原型对象: 保存该类型的所有子对象共有的方法和属性。
比如: Array类型就包含2部分:
1. 数组类型构造函数:
function Array(){ ... }
所以,创建数组可以使用new Array()
2. 数组类型也有一个原型对象,包含所有数组对象共用的函数:
为一个类型添加一个共有的自定义函数:
何时: 只要项目中,经常对一个类型的对象做一种操作,而这个类型的原型对象中又不包含这种函数。
如何: 比如: 为所有数组添加求和函数sum()
1. 在数组类型的原型对象中添加sum函数
2. 用数组类型的子对象,调用这个sum函数。
原型链:
什么是: 由多级父对象,逐级继承,形成的链式结果。
作用:
1. 保存了一个对象可用的所有成员
将来判断一个对象可以使用哪些成员,就看它的原型链上有什么成员
如果一个成员在子对象的原型链的某个父级对象上,就可以: 子对象.成员 访问
如果一个成员不包含在子对象的原型链的任意一个对象上,就不能用 子对象.成员访问
2. 控制着成员的使用顺序:
先自有,再共有!
多态:
什么是: 一个函数,在不同情况下表现出不同的状态
包括:
1. 重载:
2. 重写:
什么是: 在子对象中定义和父对象中成员完全相同的成员。来覆盖父对象中的成员。
为什么: 因为从爹继承来的成员不一定都是好用!
何时: 只要子对象觉得从爹继承来的成员不好用,就要重写!
如何: 只要在子对象中定义和父对象同名的成员
自定义继承:
何时: 只要觉得现在的爹不好用!就可以换。
如何:
1. 只更换一个自对象的爹:
子对象._ _proto_ _=新爹
问题: _ _proto_ _浏览器不推荐使用
解决: setPrototypeOf()代替__proto__
Object.setPrototypeOf(子对象, 新爹)
set of Prototype
设置 子对象 的 原型对象 为 新爹
2. 同时换多个对象的爹:
构造函数.prototype=新老公
时机: 在定义完构造函数后,立刻更换!
2. ES5
什么是: ECMAScript标准的第五个升级版本
为什么: 因为js这门语言,有很多广受诟病的缺陷
包括哪些新的规定:
1. 严格模式:
什么是: 比普通js运行要求更严格的机制
为什么: 因为js这门语言,有很多广受诟病的缺陷
何时: 今后所有js程序都要运行在严格模式下!
如何: 在这段程序的顶部,写 "use strict";
严格模式的新要求:
1. 禁止给未声明的变量赋值:
旧js中: 强行给未声明的变量赋值
结果: 会自动在全局创建
全局污染,内存泄漏
严格模式中: 强行给未声明的变量赋值
结果: 报错: xxx is not defined!
避免内存泄漏和全局污染!
2. 静默失败升级为错误
静默失败: 执行不成功,也不报错!
极其不便于调试
严格模式中: 静默失败,也会报错!
便于调试!
3. 普通函数调用和匿名函数自调中的this不再默认指向window。而是指向undefined
防止内存泄漏和全局污染。
4. 禁止使用arguments.callee
什么是arguments.callee: 是在函数运行时,专门获得当前正在执行的函数本身。
何时: 递归!
为什么: 避免紧耦合,避免在函数内写死函数名。
问题: 递归的效率极低——重复计算量太大!
所以: ES5严格模式,出于性能考虑,禁用了arguments.callee,就暗示着不推荐使用递归算法。
解决: 绝大多数递归,都可用循环代替
2. 保护对象:
什么是: 控制对对象的属性值或对象结构进行的随意的篡改。
为什么: 在旧的js中,对象的属性和结构毫无自保能力。任何人可在任何时候修改属性值为任何值,也可以随意添加属性和删除属性。
何时: 需要控制对对象的访问时
如何: 2个层面:
1. 保护对象的属性
ES5对对象的属性进行了重新的分类:
命名属性: 可用.访问到的属性
又分为:
数据属性: 实际存储属性值的属性。
访问器属性: 不实际存储属性值,仅提供对另一个数据属性的保护。
内部属性: 无法用.访问到的属性
如何保护数据属性:
旧js中: 数据属性其实就是一个普通的变量。毫无自保能力。
ES5标准中: 每个数据属性都是一个缩微的小对象。每个小对象中,保存一个属性值和三个开关!
比如: eric={ eid: 1001 }
eid不再是一个变量而是:
如何修改属性小对象内的开关属性来保护一个属性呢?
问题: 不能用.直接修改属性对象内部的开关属性!
解决: 必须用专门的函数;
Object.defineProperty(
对象名,
"属性名",
{
开关: true/false
}
)
问题: writable和enumerable两个开关,任何人都可以随意开关。所以,仅设置这两个属性,起不到保护的作用。
解决: 只要设置writable和enumerable,都要同时关闭configurable,目的是禁止修改前两个开关。且configurable一旦被改为false,不可逆!
问题: enumerable只能禁止for in遍历这个属性。无法阻止用.直接访问这个属性。——只是半隐藏
解决: 无法解决
问题: Object.defineProperty()一次只能修改对象中的一个属性。如果修改多个属性,就要重复写多遍。
解决: Object.defineProperties(
对象名,
{
属性名:{
开关:true/false
},
属性名:{
开关:true/false
},
}
)
问题: 无法使用自定义规则保护属性
解决:使用访问器属性保护数据属性
什么是访问器属性: 自己不保存数据,专门提供对其它数据属性的保护。
何时: 只要使用灵活的自定义规则保护数据属性时,就要请保镖!
如何:
1. 先将要保护的数据属性,隐形埋名,并且半隐藏!
2. 为对象添加访问器属性,保护数据属性
访问器属性要冒名顶替使用原属性名。
访问器属性中包含get和set两个函数
get中负责读取受保护的属性值
set中负责先验证再修改受保护的值。
访问器属性不再需要value和writable。但是,需要设置enumerable和configurable
访问器属性如何发挥作用:
因为访问器属性是冒名顶替的,所以,外人访问访问器属性时,和访问普通属性是一样的
只不过访问器属性执行时,会自动根据外人做到操作,来选择get或set来调用。
如果外人试图获取属性值时,自动调用get函数
如果外人试图修改属性值时,自动调用set函数
2. 保护对象的结构
保护对象的属性:
保护对象的结构: 3个级别:
1. 防扩展: 禁止添加新属性:
Object.preventExtensions(obj)
阻止对obj对象添加任何扩展属性
原理: 每个对象内部都有一个隐藏的extensible的属性,默认值为true。
preventExtensions(obj)其实就是将这个内部的隐藏属性改为false!
2. 密封: 在兼具防扩展同时,进一步禁止删除现有属性
Object.seal(obj)
2件事: 1. 禁止添加新属性
obj的extensible=false
2. 禁止删除现有属性:
自动将所有属性的configurable都改为false!从此defineProperties中不需要再重复写configurable:false!
其实,今后绝大多数对象,都要密封!
强调: 虽然密封禁止修改结构,但是属性值随便改!
3. 冻结: 在兼具密封的基础上,进一步禁止修改属性的值!
Object.freeze(obj)
3件事: 1. 禁止添加新属性
obj的extensible=false
2. 禁止删除现有属性:
自动将所有属性的configurable都改为false!
3. 自动将所有属性的writable都改为false。
Object.create()函数: 没有构造函数的情况下,也想创建一个子对象。
3件事: 1. 创建一个新对象
2. 继承一个父对象
3. 为新对象添加自有属性
var 子对象=Object.create(父对象,{
//defineProperties
属性名: {
value: 属性值,
writable: true/false,
enumerable:true/false,
configurable:true/false
},
... ...
})
call, apply, bind: 替换this
何时: 只要函数执行时,内部的this不是我们想要的,就可用这三个函数,将不想要的this换成任意一个想要的对象。
如何:
1. call, apply:
在这一次调用函数时,临时替换一次this为任意指定的对象!
要调用的函数.call(替换this的对象, ...)
强调: 实参值列表必须从第二个位置开始。因为第一个实参的位置让给了替换this的对象!
原理:
call的更大的作用: 可让任何对象去调用原本没有任何关系的一个函数。——抢!
apply vs call:
apply和call用法几乎完全一样。只不过,要求所有实参值都要放在一个数组中整体传入。
执行过程:
1. 先用第一个参数值对象替换函数中的不想要的this。
2. 打散数组为多个单独的元素值,分别传给对应的每个形参变量。
总结: 通常情况,要替换函数中的this,用call就够了!只有实参值列表是放在一个数组中的时候,才需要apply,先打散数组,再传入函数。
问题: 只能一次性临时替换this。如果反复调用,反复替换,代码会很繁琐!
2. bind:
不调用函数,而是基于原函数,创建一个新函数副本。并永久替换新函数中的this为指定的对象。
何时: 今后,如果一个函数需要反复调用,又反复替换其中的this时,都用bind()来创建副本,并永久绑定this
如何:
var 新函数=旧函数.bind(
替换this的对象,
要绑定的实参值,
... ...
)
结果: 1. 新函数的功能和旧函数是完全一样的。
2. 但是新函数中的this和开头部分形参,已被永久替换为固定的对象和固定的参数值。
3. 今后调用新函数时,只要传入可能变化的剩余几个实参值即可!
数组函数:
1. 在数组中查找一个元素的位置: indexOf
数组的indexOf和字符串的indexOf完全一样
var i=arr.indexOf("元素", fromi)
在arr数组内,从fromi位置开始,找下一个和指定"元素"相同的元素所在的位置i
返回值: 如果找到,返回i
如果没找到,返回-1
2. 判断:
1. 判断数组中是否所有元素都符合要求
var bool=arr.every(function(value, i, arr){
//value: 当前元素值
//i: 当前下标位置
//arr: 当前数组对象
return 判断条件
})
原理: every会自动遍历arr中每个元素。每遍历一个元素,就自动调用一次回调函数function。每次调用function时,自动传入当前正在遍历的元素值和下标位置,以及当前数组对象。每次调用function,都会用函数内的条件检查当前元素是否符合要求。只有当前元素符合要求,才继续遍历下一个元素。只要碰上一个不符合要求的元素,就立刻退出遍历。并返回false。意思是不是所有元素都满足要求。如果遍历结束,都还没有退出,说明整个数组中所有元素都符合要求。整个every(),就返回true。
2. 判断数组中是否包含符合要求的元素
var bool=arr.some(function(value,i,arr){
return 判断条件
})
用法和every完全相同
只不过执行时,只要碰到一个元素判断为true,就不再遍历,直接返回true。只有遍历结束,还没有找到判断为true的元素,才返回false!
3. 遍历:
1. 普通遍历: 代替for循环
for(var i=0;i<arr.length;i++){
//i 当前位置
//arr[i] 当前元素值
}
arr.forEach(function(value, i, arr){
//对当前元素执行的操作!
})
forEach中自动遍历arr中每个元素,每遍历一个元素,就在这个元素上自动调用一次回调函数function。自动传入当前元素值给value, 当前位置给i, 当前数组对象给arr。
2. 遍历并复制原数组为新数组:
var 新数组=原数组.map(
//var newArr=[]; //自动
//for(var i=0;i<arr.length;i++){ //自动
// arr[i] i this
// ↓ ↓ ↓
function(value, i, arr){
return 要放入新数组中的元素值
}
//}
//return newArr; //自动
)
原理:
1. map内先自动创建一个新数组
2. 然后才是带着回调函数一起遍历原数组。自动将回调函数在每个元素上执行一次。将回调函数return的结果,放到新数组中相同的位置上
3. 遍历结束,返回新数组
总结:
1. 仅单纯代替for循环遍历数组,首选forEach。
2. 除非想保护原数组,返回新数组时,采用map.
4. 过滤和汇总:
1. 过滤: 复制出原数组中符合条件的元素组成新数组返回。原数组保持不变!
var 新数组=原数组.filter(
function(value, i, arr){
return 判断条件
}
)
原理:
1. 先自动创建一个空数组
2. 再自动遍历原数组中每个元素。每遍历一个元素,就自动调用一次回调函数。如果当前元素经过回调函数验证,返回true,则加入新数组中。如果当前元素经过回调函数验证,返回false,则不加入新数组。
3. 遍历结束,返回新数组!
2. 汇总: 对数组中的元素内容进行统计,得出最终结论。
var sum=arr.reduce(
function(prev, elem,i,arr){
//prev 保存截止到当前元素的临时汇总值。
return 将当前元素elem,累加到prev中,得到的新的汇总值。
},
起始值
)
1. ES6
什么是: ECMAScript 第6个版本
优点: 在不改变原理的基础上,简化了js代码。
问题: 兼容性
包括:
1. let:
什么是: 专门代替var来声明变量用的
为什么: var的问题:
1. 声明提前
2. 没有块级作用域
何时: 只要声明变量都用let
优点: 1. 阻止了声明提前
2. 添加了块级作用域
原理: let其实就是一个匿名函数自调!
且let为了双保险,其实在底层悄悄给变量改了名字
let的小脾气:
1. 在相同作用域/块内,禁止同时let两个同名的变量
2. 在let a之前到当前作用域的顶部之间,不允许提前使用a变量
2. 箭头函数:
什么是: 对普通函数声明的简写
何时: 今后绝大多数匿名函数的创建,都用箭头函数。
如何: 3件事:
1. 去掉function 在()和{}之间加=>
2. 如果形参列表只有一个变量,可省略()
3. 如果函数体只有一句话,可省略{}
但是,如果仅有的这句话,还是return,则必须省略return
去掉{}时,还要注意去掉语句结尾的";"
双刃剑: 让函数内的this,与函数外的this连通,保持一致。都指向函数外的this。
结论: 如果希望函数内的this和函数外的this,不一致时!就不能用箭头函数!
比如: 对象的方法不能用箭头函数简写。
比如: DOM中的事件处理函数也不能用箭头函数简写。
问题: 一旦改为箭头函数,则不能再使用arguments
解决: 参数增强中的rest语法
3. for of
什么是: 在特定情况下专门简化for循环
总结: 遍历一个数组:
1. for(var i=0;i<arr.length;i++){
//当前元素arr[i]
}
优点: 即可控制遍历的方向,又可控制循环的步调。
2. arr.forEach((elem,i,arr)=>{
//当前元素elem
})
局限: 不能控制遍历的方向,也不能控制遍历的步调。
3. for of
如何: for(var elem of 数组){
//of会依次取出数组中每个元素值,保存在of前的变量elem中
}
局限: 只能获得元素值,无法获得当前元素的位置。
其实, for of不但可以用于数组,还可以用于字符串,类数组对象。
总结:
for of可以遍历数字下标的数组,类数组对象,字符串中的每个字符。
for in 专门遍历自定义下标的关联数组和对象。
4. 参数增强:
什么是: ES6中对函数的参数,提供了更强大,更方便的功能
包括:
1. 参数默认值(default):
function fun(形参1, ..., 形参n=默认值){
//将来,如果调用fun函数时,传入了最后一个参数,则用用户传入的。如果调用fun函数时,没有传入最后一个参数,则使用默认值作为备胎。
}
ES5的代替方式:
形参n=形参n||默认值
//如果"形参n"是有意义的值(不是false,"",undefined,null, NaN),就用"形参n"的值继续后续执行。
//如果"形参n"是没有意义的值(没给值),就用"默认值"作为备胎。
强调: 局限: 只能解决最后一个形参不确定的情况。
2. 剩余参数(rest):
什么是: 专门代替arugments
为什么: arguments的问题:
1. 不是纯正的数组类型,用不了数组家的函数。
2. 在箭头函数中不支持arguments了。
3. 只能获得全部实参值。不能有选择的获得部分实参值。
何时: 今后只要使用arguments的地方,都可用rest语法代替
如何:定义函数时:
function fun(形参1, 形参2,...数组名){
//...后的数组中会接住除之前形参外的其它所有剩余实参值。
//数组是纯正的数组类型,可随意使用数组家的函数
//即使将fun改为箭头函数,依然可以使用...数组名的rest语法
}
3. 打散数组(spead):
什么是: 将一个数组或对象打散成单个的变量值
为什么: 代替apply。
apply的问题是: 本职工作不是打散数组,而是替换this,顺便打散数组。
何时: 今后只要希望将一个数组整体,打散为单个值。
如何: 传递参数或赋值时
fun(...arr)
先将arr中的元素值打散为单个值,再分别传给fun()。
项目中:
1. 拼接两个数组:
var arr1=[1,2,3], arr2=[4,5,6];
arr=[...arr1,...arr2]
arr : [ 1,2,3,4,5,6 ]
2. 合并两个对象:
var obj1={x:1, y:2},
obj2={a:1,b:2};
var obj={...obj1, ...obj2}
obj:{ x:1, y:2, a:1,b:2 }
5. 解构:
什么是: 将一个大的对象或数组中的个别成员提取出来,单独使用。
何时: 如果给的是一个巨大的对象,而我们这个功能,只需要其中个别函数或属性时,就要将所需的成员解构出来,单独使用。
如何:
1. 数组解构:
什么是: 将大的数组中的个别元素提取出来单独使用。
如何: 下标对下标
[变量1,变量2,...]=数组
0 1 ...
结果: 变量1=数组[0]
变量2=数组[1]
2. 对象解构:
什么是: 将大的对象中的个别成员提取出来单独使用。
如何: 属性名对属性名
{属性名1:变量1, 属性名2:变量2, ... }=对象
结果: 变量1=对象.属性名1
变量2=对象.属性名2
简写: 如果新变量的名字延用成员在原对象中的属性名,则可以简写:
比如: 想从user对象中,解构出登陆方法和注册方法单独使用:
var {signin:signin,signup:signup}=user;
可简写为:
var {signin,signup}=user;
调用时:
signin();
signup();
3. 参数解构:
什么是: 在定义函数和调用函数时,采用对象结构传递参数。
何时: 多个形参值不确定有没有,而且又要求实参值与形参值之间必须对应。
为什么:
默认值: 局限: 只有最后一个形参不确定时,才能用默认值。
...rest和arguments: 局限:无法让形参值与实参值一一对应。
用对象结构传参: 问题: 定义函数时,只有一个对象形参,将来调用者不知道具体形参的个数和名称!
如何: 2步:
1. 定义函数时,形参就写成对象语法!
2. 调用函数时,所有实参必须放在一个和形参对象结构相同的对象中传入
比如:
//定义制作西红柿鸡蛋面的函数
/*function cook({
//配对 变量
dan:dan,
cai:cai,
mian:mian
}){*/
function cook({dan="鸡蛋",cai="西红柿",mian="面"}){
//如果一个形参变量没有传值,则给一个默认值顶替。
//dan=dan||"鸡蛋";
//cai=cai||"西红柿";
//mian=mian||"面";
console.log(`炒${dan}`)
console.log(`炒${cai}`)
console.log(`炒${cai}和${dan}`)
console.log(`煮${mian}`)
console.log(`把${mian}和${cai}${dan}拌在一起`)
return `热腾腾的${cai}${dan}${mian}`;
}
//周一:
var wan=cook({});
console.log(`获得一碗:${wan}`);
//周二:
wan=cook({
dan:"辣条",
cai:"黄瓜",
mian:"米线"
});
console.log(`获得一碗:${wan}`);
//周三:
wan=cook({
dan:"火腿肠"
})
console.log(wan);
//周四:
wan=cook({
cai:"黄瓜"
})
console.log(wan);
//周五:
wan=cook({
cai:"榴莲",
mian:"米饭"
})
console.log(wan);
1. 面向对象上的简化:
1. 对单个对象提供了2处简化:
1. 如果对象的属性值来自于对象外的变量,且变量名刚好和属性名相同。则不用写两遍相同的名字,只要写一遍即可。
2. 所有对象的方法,不再需要写":function"
强调: 对象中的方法去掉:function,就不等效于箭头函数。突出的特点就是this保持原样不变!
所以,去掉:function,只是单纯的简写,没有任何原理的改变!
2. class:
什么是: 集中定义一种类型的所有对象统一属性结构和方法的程序结构
为什么: 每一种类型: Array Student Date都有两部分组成: 构造函数+原型对象
构造函数: 负责定义所有子对象统一的属性结构,并且负责创建子对象
原型对象: 负责保存所有子对象共有的属性值和方法
但是,在ES5中,构造函数和原型对象是分开定义的。不符合"封装"的要求
何时: 今后只要希望创建一种自定义类型时,都用class
如何: 3件事
1. 用class{}包裹构造函数和原型对象方法
2. 构造函数名提升为class名,所有构造函数,从此统一更名为constructor
3. 所有放在class中的函数,不需要加类型名.prototype前缀,自动就是保存在构造函数的原型对象中。
如何使用class: 用法和从前的构造函数完全一样!原型对象的原理依然保持不变。
两种类型间的继承:
问题: 两种类型之间包含部分相同的属性结构和方法定义。
解决: 定义一个上层的父类型,集中保存两种子类型相同的部分
如何:
1. 定义抽象父类型:
父类型构造函数中集中定义多个子类型都有的相同的属性结构
父类型class中集中保存多个子类型共用的方法
2. 让子类型继承父类型:
class 子类型 extends 父类型{
}
其中: extends等效于Object.setPrototypeOf(子类型.prototype, 父类型.prototype)。
但是,继承父类型不但要继承父类型的方法,还要继承父类型构造函数中的属性结构。所以,需要在子类型构造函数中先调用父类型构造函数。请父类型构造函数帮忙创建相同部分的属性结构。然后子类型构造函数,再添加自有的个性化的属性结构。最后,创建的子对象的属性结构来自于父类型构造函数和子类型构造函数共同执行的结果。
问题: 父类型构造函数没有名字!
解决: 只要class 子类型 extends 父类型。就可以在子类型构造函数中调用super()关键词。
super是子类型中,专门指向父类型构造函数的方法。
强调:
1. 只要class 子类型 extends 父类型。则子类型构造函数中必须调用super(),否则会报错。
2. super()必须放在子类型构造函数开头,先调用。因为这样可以始终保证万一子类型构造函数中规定的属性和父类型构造函数中规定的属性重名时,子类型构造函数的属性可始终覆盖父类型构造函数中的属性。
2. Promise:
什么是: 专门保证多个异步函数,可以顺序执行的机制。而且还防止了回调地狱问题。
何时: 多个异步调用的函数,要求必须顺序执行!
为什么: 其实用回调函数,也可以实现多个异步函数,顺序执行。但是,使用回调函数,会有回调地狱问题!
问题1: 多个异步函数要求顺序执行
错误的解决: 仅按顺序调用
结果: 无法保证顺序执行
因为: 多个异步函数,相当于百米赛跑,每人各占一条跑道,互相之间不会等待。
传统的解决: 使用回调函数:
1. 在定义函数时,定义一个callback形参变量
在函数内部,最后一句话执行之后,自动调用callback()
function dong(callback){//异步!
console.log("东起跑...");
setTimeout(function(){//异步!
console.log("东到达终点!")
//等东的最后一句话执行完,自动调用提前托付的任务callback
callback();
},4000)
}
2. 在调用函数时,传入一个函数,函数中包含下一步要执行的操作。——提前托付
dong(
//callback
function(){
console.log("比赛结束!")
}
//当东最后一句话执行完,自动执行callback(),等效于执行console.log("比赛结束")
)
效果: 当前一个函数调用时,暂时不执行传入的小函数。只有前一个函数最后一句话执行完,才自动调用传入的小函数。一次保证小函数一定会在异步函数调用完才执行。
问题: 当连续执行的异步函数非常多时,就会形成回调地狱!
解决: ES6 Promise
前提: 不要用回调函数参数了!
因为回调函数嵌套传参的写法是造成回调地狱的根源
第一步: 在异步函数内部,用new Promise(function(door){ ... })来包裹原函数中所有代码。
其中door是用来打开通向下一项任务的大门的要是。
第二步: 在异步任务执行完最后一步之后,调用附赠的开关door(),开门,通知下一项任务可以开始执行。
问题: 不能在函数内写死.then(下一个函数),应该让当前函数可以和之后任意函数自由组合。
第三步: 将整个new Promise()对象返回到函数外部,再用.then()接下一项任务函数
调用支持promise的函数:
前一个异步函数().then(下一个异步函数)
强调: .then()中"下一个异步函数"后不要加(),因为下一个异步函数不是立刻调用,而是在这里等待前一个函数开门。
信任: 前一个函数内调用door()后,就等于通知了.then()中的下一个函数可以开始执行。于是.then()就自动执行下一个函数。
多个异步函数顺序执行:
.then()能否接下一个.then(),取决于前一个.then()中的函数是否也支持promise。如果前一个.then()中的函数支持promise,则.then()可以继续.then()。如果前一个.then()中的函数不支持promise,则不能继续.then()
前后两个函数间传参:
2步:
1. 上一个函数中door(参数值)
2. 下一个函数定义时就要定义一个形参准备接
原理: 当上一个函数调用door(参数值)时,参数值,会顺着.then()交给.then()中的下一个函数的形参变量。在下一个中就可通过自己的形参变量获得上一步传下来的参数值。
局限: door()中只能传一个变量
如果必须穿多个值,则可以将多个值放在数组或对象中整体传入。
错误处理:
如何: 任何一个支持promise的函数中都有另一扇门。如果当前异步任务执行过程中发生错误,就可从另一扇门出来。一旦从报错这扇门出来后,后续.then()都不再执行。
其实new Promise()除了then外,还有另一个方法.catch()。凡是从出错的门出来的代码都进入.catch()中执行错误处理操作。
等待多个异步任务完成才执行:
Promise.all([
多个支持promise的函数调用(),
...,
...,
]).then(function(){ 后续操作...})
问题:如果每个异步任务都返回一个接力棒,则如何获得所有接力棒呢?
解决: .then(function(arr){ ... })
其中: arr数组中保存了Promise.all中所有异步函数通过door()返回的执行结果。
强调: arr中返回值存储的顺序和异步函数执行完成的顺序无关。只和调用的顺序有关!
Promise的问题: 并没有彻底消灭嵌套:
比如: .then(dong)
.then(function(){
console.log("比赛结束")
}).catch(function(err){
错误处理
})
解决: ES7: async await 可按照传统的同步指定的代码一样,编写异步代码顺序执行
只要多个异步任务需要顺序执行:
(async function(){
同步代码;
var 返回值=await 异步函数()
同步代码;
})();
其中: await可让整段匿名函数自调暂时挂起,等待当前异步函数执行完,在执行后续代码!
强调: es7的async和await仅仅简化的是promise函数调用的部分。而并没有简化Promise函数的定义。且,如果想用await,则异步函数必须定义为支持promise的样式。
错误处理: 如果await修饰的异步函数中调用了err()方法,打开了错误的门,则await会认为是程序错误。
应该用try{}catch(err){}来解决
强调: 只有async下的try catch才能捕获异步任务中的错误。没在async下的js基础中所学的try catch是不能捕获异步任务的!因为普通js基础中的try catch属于主程序,不会等待异步任务执行,就已经结束了。即使异步任务出错,try catch因为早就结束了,所以根本捕获不到。
为什么await配合try catch就可以捕获异步任务中的错误呢?
因为await可留住当前程序中的一切代码,等待异步函数执行完。try catch就有可能捕获到异步任务中的错误。
本文标签: JavaScript
版权声明:本文标题:前端 JavaScript 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1727771404a1128602.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论