(1). 只查找一个固定的敏感词出现的位置:
示例: 用indexOf查找敏感词
(2). 用正则查找多种敏感词出现的位置:
示例: 使用search代替indexOf查找敏感词
(3). 查找敏感词的内容: 2种:
示例: 使用match查找敏感词的内容和位置:
示例: 使用match查找所有敏感词的内容:
(4). 既查找每个关键词的内容,又查找每个关键词的位置:
String类型没有提供这种函数 RegExp类型提供了一个reg.exec()函数
总结: 查找方法的返回值规律
- 如果原函数返回的是下标位置,如果找不到,都返回-1
- 如果原函数返回的是一个数组或一个对象,如果找不到,都返回null 强调: 如果一个函数有可能返回null!一定先判断是不是null,再使用。并且为null的情况提供备选方案!
- (未完…待续…)
(1). 简单替换: 将所有敏感词都替换为统一的新值
示例: 使用replace替换所有人名为**
(2). 高级替换: 根据每次找到的敏感词不同,动态替换成不同的新值
示例: 鄙视题: 将字符串中每个单词首字母改为大写:
(3). 衍生操作: 删除敏感词: 其实就是将敏感词替换为空字符串
例: 鄙视题,定义三个函数,分别去掉字符串开头的空字符,去掉结尾的空字符,以及同时去掉字符串开头和结尾的空字符:
(1). 简单切割: 切割符是固定的
(2). 复杂切割: 切割符不是固定的,但是却能找到规律!
(3). 示例: 分别使用固定切割符和正则表达式切割字符串:
(4). 固定套路:
1.字符串其实是一种不可变类型!
- 不可变类型: 一旦创建值不可改变,只能整体替换! 直接后果: 所有字符串家的函数都无权直接修改原字符串。只能创建新字符串保存加工后的结果,并返回新字符串。原字符串始终保持不变! 强调: 所有字符串家的函数执行后的结果,必须用变量接住,才能保存下来!
- 可变类型: 创建后,还可改变其内容。比如: 数组 比如: 数组排序arr.sort(), 向数组中追加一个新元素arr.push(xxx), 都直接修改原数组,无需用变量=接住
2. 数组本质:
内存中专门保存一条正则表达式,并提供用正则表达式执行查找和验证的函数 的对象
因为正则表达式的语法不是js语法。js不认识正则表达式。所以,需要一种专门的对象让js认出正则表达式来。
今后只要在js中使用正则表达式都需要先创建正则表达式对象
1. 什么是函数:
程序中专门保存一段可重用的代码片段的程序结构,再起一个名字。
2. 为什么: 重用代码
3. 何时:
只要一段代码可能被反复使用,都要先将这段代码保存在函数中。然后再反复使用函数
4. 创建函数:
5. 调用函数: var 变量=函数名(实参值列表)
6. 形参变量:
(1). 什么是: 专门接收函数执行时必需的数据的变量(虽然没有var) (2). 为什么: 有些函数执行时,必须传入必要的数据,才能正常执行! (3). 何时: 今后,只要一个函数执行时,必须外界传入某些数据才能正常执行时,都要定义形参变量
7. 返回值:
(1). 什么是: 一个函数的执行结果,被返回到函数外部 (2). 何时: 今后,只要函数的调用者需要获得函数的执行结果时,都要定义函数的返回值。
8. 示例: 定义一个会做饭的函数,可以接收各种食材,做饭
1. 用声明方式创建:
2. 用赋值方式创建:
示例: 鄙视题: 判断声明提前后,程序的输出结果:
3. 用new 创建:
1. 问题:
一件事,根据传入实参值的不同,动态执行不同的逻辑。
2. 不好的做法:
为同一件事,每种不同的逻辑,单独定义一个函数,起一个专门的函数名 原因: 增加了函数名和单词的数量,不好记忆,也不便于使用!
3. 好的解决方法——重载:
相同函数名,不同参数列表的多个函数,在调用时,可根据传入的实参值不同,自动选择匹配的一个函数执行!
4. 何时:
今后只要根据传入的参数值不同,自动执行不同的逻辑时,都要用重载
5. 优点:
减少了函数名和单词的个数,便于记忆和使用!
6. 如何:
7. 问题:
既然js中一个函数不定义形参,照样可以传实参值,那么今后是不是都不用定义形参了? 答: 不是! 自定义形参变量和arguments[i] 自定义形参变量: 简单, 见名知义,可以告诉调用者如何正确使用函数正确传参 ——今后依然首选自定义形参变量 只有参数个数不确定时,才被迫使用arguments代替!
8. 示例:
使用重载实现一个pay函数,支持三种支付方式
1. 什么是:
定义函数时,不指定函数名的函数
2. 为什么:
节约内存(用完就被立刻释放了,因为该函数没有变量引用,所以将被当做垃圾回收),划分临时作用域
3. 何时: 2种情况:
4. 如何: 2种情况:
示例: 使用匿名函数自调禁止产生全局变量
1. 作用域(scope):
(1). 什么是作用域:
(2). 为什么: 防止不同范围之间的变量互相污染
(3). 在js中共包括2级作用域:
( js中是没有块/局部作用域的:js中if ,else if, else, while, do while, switch case, for等的{}都不是作用域,这些{}中的变 量,出了{},依然可以使用)
a. 全局作用域:
b. 函数作用域:
c. 总结: 如果函数内自己创建了所需的局部变量,则优先使用局部变量 如果函数内自己没创建所需的局部变量,则去全局找需要的变量使用! ——先局部,后全局!
d. 示例: 判断以下三个程序的输出结果:
2. 作用域链(scope chain):
(1). 什么是作用域链:
函数对象中保存函数调用时所有可用的作用域对象的链式结构
(2). 其实就是上图中的“好友列表”,学名就叫作用域链!
(3). 作用域链保存着: 一个函数可用的所有变量(局部变量和全局变量)
(4). 作用域链控制着: 变量的使用顺序(先局部后全局!)
1. 什么是闭包:
(1). 用法:既重用一个变量,又保护变量不被篡改的一种编程方法 (2). 本质: ?
2. 为什么使用闭包: 全局变量和局部变量都有不可兼得的优缺点
(1). 全局变量:优: 可重用,缺: 随处可以极易被污染 (2). 局部变量:优: 仅函数内可用,不会被污染,缺: 不可重用!
3. 何时:
如果希望让一个函数即可重用一个变量,又保护这个变量不会被篡改,都用闭包!
4. 如何使用: 3步: (不要问什么)
5. 示例: 使用闭包保护钱数,并重用钱数
6. 原理:
7. 总结:
(1). 到底什么是闭包: 外层函数的作用域对象,因为受到内层函数的引用和保护,所以称为闭包对象。 (2). 鄙视题: 闭包形成的原因: 外层函数调用后,因为内层函数引用着外层函数的作用域对象,导致外层函数的作用域对象无法释放,形成了闭包!
8. 简化: 理解闭包,只要找3样东西:
9. 问题:
问:多次调用外层函数妈妈,两次生出的孩子,是共用一个受保护的变量?还是各自有独立的受保护的变量?互补干扰? 答: 多次调用妈妈,生出的多个内层函数,各自有独立的闭包变量,互不影响。
10. 总结:
(1). 妈妈一次调用生出的1个孩子,多次调用该孩子,这多次调用过程,共用同一个红包 (2). 妈妈多次调用生出的多个孩子,各自有独立的红包,互不干扰!
11. 闭包的缺点:
比普通的函数多占用一块内存空间——外层函数的函数作用域对象 解决: 一旦一个闭包不再使用,应该尽快释放! 如何: 孩子=null
1. 什么是对象:
2.什么是面向对象编程:
程序都是先用对象结构集中存储一个事物的属性和功能,然后再按需调用对象中的属性和功能
3. 为什么:
便于大量数据的管理和维护
4. 何时:
今后,几乎所有的项目,都用面向对象方式开发!
5. 如何:
面向对象三步/三大特点: 封装 继承 多态
1. 什么是:
创建一个对象集中保存一个事物的属性和功能
2. 为什么:
'便于大量数据的管理和维护
3. 何时:
今后,只要使用面向对象方式编程,都要先创建对象。
4. 如何: 3种:
(1). 用{}创建一个对象: {}是new Object()的简写
(2). 用new Object()创建: ——很少用
5. 如何访问对象中的成员:
(1). 访问对象中的属性: 对象名.属性名 (2). 调用对象中的方法: 对象名.方法名()
6. this:
自动获得正在调用当前函数的点.前的对象 的js关键词(自动存在函数作用域对象中) (1). 需求: 对象自己的方法中的内容,需要随对象自己的属性值动态变化 (2). 错误的解决: 直接在方法中写属性名
(3). 正确的但是不好的解决: 在对象的方法中写死"对象名.属性名" 原理: 让程序先找到指定名称的对象,.运算符可让程序进入对象中,读取对象的属性 问题: 紧耦合,外部的对象名改变,也必须手动修改内部写死的对象名。一旦忘记修改内部的写死的对象名,程序立刻就出错!——不好!不便于维护!
7. 示例: 使用{}创建一个对象,并在对象方法中使用this获得对象自己的属性
8. 示例: 验证对象底层就是关联数组:
9. 示例: 克隆一个对象:
10.封装: 3种方式
1. 用{}创建一个对象:
2.用new Object()创建一个对象:
3. 用构造函数反复创建多个相同结构的对象:
(1). 问题: 如果需要反复创建多个相同结构的对象时,用{}代码就很冗余——重复。也不便于维护! (2). 何时: 今后只要反复创建多个相同结构的对象时,都用构造函数来创建对象 (3). 如何: 2步
(4).示例: 定义学生类型的构造函数,反复创建两个学生对象:
(5). 构造函数的优点是: 重用对象结构代码 (6). 问题: 如果在构造函数中包含方法定义,则每创建一个对象,都会反复创建完全相同的函数对象的副本,而不是只创建一个所有对象共用!——浪费了内存! 但是确实所有对象都需要相同的方法!怎么办? (7). 总结: 今后构造函数中就不应该包含方法定义
1. 什么是继承:
父对象中的成员,子对象无需重复创建,就可直接使用!
2. 何时:
同一类型的所有子对象,都需要相同的方法定义或属性值时,都会用继承的方式来实现。
3. 如何:
(1). (自动,不需要你手写!) 其实当我们定义一个构造函数时,js程序都会自动赠送我们一个原型对象(prototype),也称为该类型下所有孩子的父对象。 (2). 什么是原型对象(prototype):替所有子对象保存共有方法和属性值的父对象 (3). (自动,不需要你手写!) 当new一个新对象时,new的第二步是自动让新子对象继承构造函数的原型对象。自动设置子对象的_ proto 属性指向构造函数的原型对象。 新子对象.proto = 构造函数.prototype (4). 结果,将来用子对象试图访问一个子对象没有的共有方法或属性时,js程序先在子对象自身内部查找。如果子对象内部没有,js程序会自动延 proto _向父对象(原型对象)中查找是否包含要用的方法或属性。只要在父对象(原型对象)中找到想要的方法,依然可以用"子对象.共有方法名()"调用。看起来就像子对象在用自己的方法一样! (5). 问题: 构造函数的原型对象开始时是空的!如何向原型对象中添加共有属性和方法?——唯一的办法只有“强行赋值”! 构造函数.prototype.共有方法名=function(){ … }
4. 总结:
今后,只要所有子对象都要共有的方法或属性值,都要集中保存在构造函数的原型对象中!
5. 问题:
问:为什么指同一个原型对象,从构造函数访问时,要用prototype属性,而从子对象访问时却要用_ proto 。构造函数的prototype属性和子对象的 proto 指的到底是不是同一个对象呢? 答: prototype和 proto _,指的是同一个对象。只不过,通过不同辈分的其它对象访问同一个原型对象时,称呼不同!
6. 问题:
问:原型对象中的this指谁? (1). 错误: 因为构造函数.prototype.共有方法=function(){ … } 所以共有方法中的this指.前的原型对象! 因为: 这句话只是向原型对象中添加函数,并不是调用共有方法。 (2). 正确: 判断this不要看定义在哪儿,应该看何时被谁调用! 因为共有方法迟早会被"子对象.共有方法()"调用。所以,共有方法中的this不确定。但是肯定指将来正在调用这个共有方法的.前的某个子对象。
7. 示例: 为所有学生的原型对象中添加共用的自我介绍方法
8. 自有属性和共有属性:
示例: 为李磊和hmm添加共有属性className,存储二人共同的班级名,并用正确和错误的方式分别修改共有属性className,查看结果:
9. 内置对象的原型对象:
(1). 什么是内置类型/内置对象: ECMAscript标准中规定的,浏览器已经实现的对象或类型。 (2). 11种:
(3). 什么是类型: (其实除了Math和global之外,其余9种都是类型,都由两部分组成) 构造函数+原型对象形成的整体
(4). 问题: 我怎么知道数组家共有哪些函数? 只要看类型.prototype中有哪些函数即可! (5). 问题: 如果经常对数组执行一种操作,但是原型对象中没有提供现成的函数可用,怎么办?——只要自定义一个函数,强行添加到数组的原型对象中即可!
(6). 示例: 为数组类型添加一个对所有元素求和的共有函数
10. 原型链:
(1). 什么是原型链: 由多级父对象,逐级继承,形成的链式结果 (2). 原型链保存着一个对象可用的所有属性和方法 (3). 原型链控制着属性和方法的使用顺序:先自有,再共有——就近原则 (4). 问题: 不同类型的子对象,调用toString(),输出的结果格式千差万别!
1. 什么是多态:
一个函数,不同情况下,表现出不同的状态!
2. 其实多态分两种情况:
3. 什么是重写:
在子对象中定义一个和父对象中成员同名的成员!
4. 为什么:
因为从爹继承来的东西,不总是好用的!
5. 何时重写:
今后,只要从爹继承来的东西不好用!就用重写自己的!
6. 如何:
只要在当前子对象中定义一个和父对象中不好用的成员同名的新成员即可。
7. 结果:
根据就近原则,优先使用子对象自己定义的。不再使用父对象中不好用——推翻
8. 问题:
问:不同类型的子对象,调用toString(),输出的结果格式千差万别? 答: 所有内置类型的原型对象中,为了自己家孩子着想,已经重写了适合自己家孩子的toString()
9. 问题:
问: 但是,咱们自己定义的类型中还没有重写toString()方法,所以非常不便于调试对象的内容! 解决: 强烈建议所有自定义类型和自定义对象,都要重写自定义的toString()方法,便于调试!
10. 示例: 为自定义类型重写toString()方法
补:
1. 问题:如果整个父对象都不是想要的!
解决: 其实可以修改一个对象的父对象——换爹
2. 如何: 2种:
运行结果: (2). 更换所有子对象的父对象: a. 其实就是更换构造函数的prototype属性: 构造函数.prototype=新父对象 b. 时机: 必须在创建子对象之前就要更换! c. 示例: 同时更换lilei和hmm的父对象
运行结果:
- 什么是严格模式: 比普通的js运行机制要求更严格的新的运行机制。
- 为什么: js语言本身有很多广受诟病的缺陷
- 何时使用严格模式: 今后几乎所有的js程序都必须运行在严格模式下
- 如何启用严格模式: 在当前作用域的顶部: “use strict”; 启用 严格
- 新规定:
阻止别人的程序对我们自己的对象执行不合理的非法的修改操作。
1. 保护属性
示例: 使用defineProperty()操作属性的开关
示例: 使用访问器属性保护对象的年龄属性
运行结果:
2. 保护结构:
防止别人的程序擅自篡改对象的属性结构!3个级别:
3.总结
- 问题: 如果在没有构造函数的情况下,也想创建一个子对象,继承父对象
- 解决: Object.create()
- 如何: 一句话三件事:
-
原理:
- 先创建一个新的空对象
- 自动让新对象继承指定的父对象
- 为子对象添加自有属性
-
示例: 使用Object.create()创建子对象,继承父对象
- 问题: 有时函数中的this指向的对象,不是我们想要的!
- 解决: 其实如果函数中的this指向的对象不是我们想要的,我们是可以修改的!
- 第一种: 只在本次调用函数时,临时修改一次this指向的对象
示例: 定义公共计算器函数,用不同对象替换函数中的this
- 第二种情况: 只在本次调用函数时,临时修改一次this指向的对象,并打散数组传参
示例: 如果李磊的薪资是放在一个数组中整体给的,如何替换this并传参?
- 第三种情况: 基于原函数,创建一个新函数副本,并永久绑定this为指定对象
示例: 创建一个新的计算器函数,永久绑定this为lilei,base为10000
- 总结: 3种:
1. 判断:
(1).every:
(2). some:
示例: 使用some函数,判断哪个数组包含偶数:
2. 遍历:
(1). forEach: 单纯简化for循环!
示例: 使用forEach简化遍历数组中每个元素
(2). map: 遍历出原数组中每个元素值,加工后,放入新数组中返回!
示例: 对数组中每个值*2,返回新数组,原数组保持不变
3. filter过滤:
复制出原数组中符合要求的元素值放入新数组中返回,原数组保持不变!
示例: 过滤出数组中偶数位置的元素,放入新数组中返回
4. 汇总:
遍历数组中每个元素,经过求和或其他汇总方式,统计出一个最终结论
示例: 计算数组中所有元素的和
示例: 使用模板字符串拼接各种字符串
- 参数默认值:
- 剩余参数:
- 打散数组:
从一个大的数组或对象中仅提取出个别想要的值,单独使用!3种:
- 数组解构: 从一个数组中,仅提取出想要的个别元素单独使用
- 对象解构:从一个大的对象中仅提取出个别属性值和方法,单独使用!
- 参数解构
1.1. 区别实例对象与函数对象
- 实例对象: new 函数产生的对象, 称为实例对象, 简称为对象
- 函数对象: 将函数作为对象使用时, 简称为函数对象
1.2. 二种类型的回调函数
1.2.1. 同步回调
- 理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
- 例子: 数组遍历相关的回调函数 / Promise 的 excutor 函数
1.2.2. 异步回调
- 理解: 不会立即执行, 会放入回调队列中将来执行
- 例子: 定时器回调 / ajax 回调 / Promise 的成功|失败的回调
1.3. JS 的 error 处理
1.3.1. 错误的类型
- Error: 所有错误的父类型
- ReferenceError: 引用的变量不存在
- TypeError: 数据类型不正确的错误
- RangeError: 数据值不在其所允许的范围内
- SyntaxError: 语法错误
1.3.2. 错误处理
- 捕获错误: try … catch
- 抛出错误: throw error
1.3.3. error 对象的结构
- message 属性: 错误相关信息
- stack 属性: 函数调用栈记录信息
2.1. Promise 是什么?
2.1.1. 理解
- 抽象表达: Promise 是 JS 中进行异步编程的新的解决方案(旧的是谁?)
- 具体表达: (1) 从语法上来说: Promise 是一个构造函数 (2) 从功能上来说: promise 对象用来封装一个异步操作并可以获取其结果
2.1.2. promise 的状态改变
- pending 变为 resolved
- pending 变为 rejected 说明: 只有这 2 种, 且一个 promise 对象只能改变一次 无论变为成功还是失败, 都会有一个结果数据 成功的结果数据一般称为 vlaue, 失败的结果数据一般称为 reason
2.1.3. promise 的基本流程 2.1.4. promise 的基本使用
2.2. 为什么要用 Promise?
2.2.1. 指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
- promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函 数(甚至可以在异步任务结束后指定/多个)
2.2.2. 支持链式调用, 可以解决回调地狱问题
- 什么是回调地狱? 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
- 回调地狱的缺点? 不便于阅读 不便于异常处理
- 解决方案? promise 链式调用
- 终极解决方案? async/await
2.3. 如何使用 Promise?
2.3.1. API
-
Promise构造函数: Promise (excutor) {} excutor函数: 同步执行 (resolve, reject) => {} resolve函数: 内部定义成功时我们调用的函数 value => {} reject函数: 内部定义失败时我们调用的函数 reason => {} 说明: excutor会在Promise内部立即同步回调,异步操作在执行器中执行
-
Promise.prototype.then方法: (onResolved, onRejected) => {} onResolved函数: 成功的回调函数 (value) => {} onRejected函数: 失败的回调函数 (reason) => {} 说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调 返回一个新的promise对象
-
Promise.prototype.catch方法: (onRejected) => {} onRejected函数: 失败的回调函数 (reason) => {} 说明: then()的语法糖, 相当于: then(undefined, onRejected)
-
Promise.resolve方法: (value) => {} value: 成功的数据或promise对象 说明: 返回一个成功/失败的promise对象
-
Promise.reject方法: (reason) => {} reason: 失败的原因 说明: 返回一个失败的promise对象
-
Promise.all方法: (promises) => {} promises: 包含n个promise的数组 说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败
-
Promise.race方法: (promises) => {} promises: 包含n个promise的数组 说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
2.3.2. promise 的几个关键问题
-
如何改变promise的状态? (1)resolve(value): 如果当前是pendding就会变为resolved (2)reject(reason): 如果当前是pendding就会变为rejected (3)抛出异常: 如果当前是pendding就会变为rejected
-
一个promise指定多个成功/失败回调函数, 都会调用吗? 当promise改变为对应状态时都会调用
-
改变promise状态和指定回调函数谁先谁后? (1)都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调 (2)如何先改状态再指定回调? ①在执行器中直接调用resolve()/reject() ②延迟更长时间才调用then() (3)什么时候才能得到数据? ①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据 ②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
-
promise.then()返回的新promise的结果状态由什么决定? (1)简单表达: 由then()指定的回调函数执行的结果决定 (2)详细表达: ①如果抛出异常, 新promise变为rejected, reason为抛出的异常 ②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值 ③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
-
promise如何串连多个操作任务? (1)promise的then()返回一个新的promise, 可以开成then()的链式调用 (2)通过then的链式调用串连多个同步/异步任务
-
promise异常传/穿透? (1)当使用promise的then链式调用时, 可以在最后指定失败的回调, (2)前面任何操作出了异常, 都会传到最后失败的回调中处理
-
中断promise链? (1)当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数 (2)办法: 在回调函数中返回一个pendding状态的promise对象
-
async 函数 函数的返回值为promise对象 promise对象的结果由async函数执行的返回值决定
-
await 表达式 await右侧的表达式一般为promise对象, 但也可以是其它的值 如果表达式是promise对象, await返回的是promise成功的值 如果表达式是其它值, 直接将此值作为await的返回值
-
注意: await必须写在async函数中, 但async函数中可以没有await 如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理