一、DOM
事件冒泡
当父子元素绑定同一事件类型时,子级元素先触发,父级元素后触发(自底向上)
事件捕获
当父子元素绑定同一事件类型时,父级元素先触发,子级元素后触发(自顶向下)
事件类型
DOM0、1、2、3级
DOM0级
在1998 年 10 月 DOM1级规范成为 W3C 的推荐标准,在此之前的实现我们就习惯称为DOM0级,其实本是没有这个标准的。
DOM0级事件处理方式
一开始浏览器处理事件的时候只有原始事件模型,事件处理程序被设置为js代码串作为html的性质值。删除DOM0事件处理程序,只要将对应事件属性置为null即可。
缺点:一个事件处理程序只能对应一个处理函数。
DOM1级
主要定义的是HTML和XML文档的底层结构。DOM1级由DOM核心(DOM Core)和 DOM HTML两个模块组成,映射文档结构和提供基本的文档操作方法。
DOM1级主要是定义,没有对应的事件模型。
DOM2级
DOM2级就是在DOM1的基础上增加了视图、事件、样式、遍历和范围的接口,和支持XML命名空间。
DOM2级事件处理方式
包含3个事件:事件捕获阶段、处于目标阶段和事件冒泡阶段,DOM2级事件处理方式指定了加事件处理程序和删除事件处理程序的方法。
区别
在使用attachEvent方法和DOM0级事件处理程序的主要区别在于事件处理程序的作用域。采用DOM0级处理方式,事件处理程序会在其所属元素的作用域内运行。使用attachEvent,事件处理程序会在全局作用域内运行,因此this等于window。
tip:还可使用句柄进行封装
DOM3级
在前面DOM基础上,引入了以统一方式加载和保存文档的方法,新增了验证文档的方法,同时也对DOM核心进行了扩展,开始支持XML1.0规范。
DOM3级事件处理方式
DOM浏览器中可能发生的事件有很多种,不同事件类型具有不同的信息,DOM3级事件规定了UI事件、焦点事件、鼠标事件、滚轮事件、文本事件、键盘事件等多种事件,在DOM2级事件的基础上重新定义了这些事件,也添加了一些新事件。DOM3级还包含自定义事件。
事件委托
利用事件冒泡的机制把里层所需要响应的事件绑定到外层;
可以理解为把孩子的工作统一交给父母来做;
可以避免重复的工作;
详见demo;
二、正则表达式
通常用于输入检验;
创建形式
正则表达式保持不变
使用正则表达式字面量,由包含在斜杠之间的模式组成
var re = /ab+c/;
正则表达式会改变
使用构造函数
var re = new RegExp("ab+c");
编写
1.简单模式 2.简单模式+特殊字符
tip:在正则表达式中,括号被用作记忆设备;
简单模式
由你想直接找到的字符构成,但是需要完全按照所给出的顺序和位置出现才可以匹配。
/abcd/ cabcd√ abc de×
特殊字符
断言(表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。);字符类(区分不同类型的字符);组和范围(表示表达式字符的分组和范围);量词(表示匹配的字符或表达式的数量);Unicode属性转义(基于 unicode 字符属性区分字符);
字符 | 含义 |
\ | 转义字符 |
^ | 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置 |
$ | 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置 |
* | 匹配前一个表达式 0 次或多次。等价于 {0,} |
+ | 匹配前面一个表达式 1 次或者多次。等价于 {1,} |
? | 匹配前面一个表达式 0 次或者 1 次。等价于
|
. | (小数点)默认匹配除换行符之外的任何单个字符 |
(x) | 匹配 'x' 并且记住匹配项 |
(?:x) | 匹配 'x' 但是不记住匹配项,这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式 |
x(?=y) | 匹配'x'仅仅当'x'后面跟着'y'.这种叫做先行断言 |
(?<=y)x | 匹配'x'仅当'x'前面是'y'.这种叫做后行断言 |
x(?!y) | 仅仅当'x'后面不跟着'y'时匹配'x',这被称为正向否定查找 |
(?<!y)x | 仅仅当'x'前面不是'y'时匹配'x',这被称为反向否定查找 |
x|y | 匹配‘x’或者‘y’ |
{n} | n 是一个正整数,匹配了前面一个字符刚好出现了 n 次 |
{n,} | n 是一个正整数,匹配前一个字符至少出现了 n 次 |
{n,m} | n 和 m 都是整数。匹配前面的字符至少 n 次,最多 m 次。如果 n 或者 m 的值是 0,这个值被忽略 |
[xyz] | 一个字符集合,匹配方括号中的任意字符,包括转义序列,可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义,他们不必进行转义,不过转义也是起作用的 |
[^xyz] | 一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。你可以使用破折号(-)来指定一个字符范围。任何普通字符在这里都是起作用的 |
[\b] | 匹配一个退格 (U+0008) |
\b | 匹配一个词的边界,就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,一个匹配的词的边界的内容的长度是 0 |
\B | 匹配一个非单词边界,匹配如下几种情况:字符串第一个字符为非“字”字符;字符串最后一个字符为非“字”字符;两个单词字符之间;两个非单词字符之间;空字符串 |
\cX | 当 X 是处于 A 到 Z 之间的字符的时候,匹配字符串中的一个控制符 |
\d | 匹配一个数字。 等价于 [0-9] |
\D | 匹配一个非数字字符。 等价于 [^0-9] |
\f | 匹配一个换页符 (U+000C) |
\n | 匹配一个换行符 (U+000A) |
\r | 匹配一个回车符 (U+000D) |
\s | 匹配一个空白字符,包括空格、制表符、换页符和换行符 |
\S | 匹配一个非空白字符 |
\t | 匹配一个水平制表符 (U+0009) |
\v | 匹配一个垂直制表符 (U+000B) |
\w | 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_] |
\W | 匹配一个非单字字符。等价于 [^A-Za-z0-9_] |
\n | 在正则表达式中,它返回最后的第 n 个子捕获匹配的子字符串 (捕获的数目以左括号计数) |
\0 | 匹配 NULL(U+0000)字符,不要在这后面跟其他小数,因为 \0<digits> 是一个八进制转义序列 |
\xhh | 匹配一个两位十六进制数(\x00-\xFF)表示的字符 |
\uhhhh | 匹配一个四位十六进制数表示的 UTF-16 代码单元 |
\u{hhhh}或\u{hhhhh} | (仅当设置了 u 标志时)匹配一个十六进制数表示的 Unicode 字符 |
转义
使用任何特殊字符的字面值,必须通过在它前面放一个反斜杠来转义它,使其成为文字而非特殊符号。
搜索'a'后跟'*'后跟'b' /a\*b/
tip:编写正则表达式文字需要匹配斜杠,反斜杠,构造函数创建字符串中的反斜杠时,需要在前面加反斜杠进行转义;
插入语
任何正则表达式的插入语都会使这部分匹配的副字符串被记忆。一旦被记忆,这个副字符串就可以被调用于其他用途
/Chapter (\d+)\.\d*/
用来记忆第一个匹配的数字字符
方法
RegExp
方法 | 描述 |
exec | 一个在字符串中执行查找匹配的 RegExp 方法,它返回一个数组(未匹配到则返回 null) |
test | 一个在字符串中测试是否匹配的 RegExp 方法,它返回 true 或 false |
String
方法 | 描述 |
match | 一个在字符串中执行查找匹配的 String 方法,它返回一个数组,在未匹配到时会返回 null |
matchAll | 一个在字符串中执行查找所有匹配的 String 方法,它返回一个迭代器(iterator) |
search | 一个在字符串中测试匹配的 String 方法,它返回匹配到的位置索引,或者在失败时返回 -1 |
replace | 一个在字符串中执行查找匹配的 String 方法,并且使用替换字符串替换掉匹配到的子字符串 |
split | 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法 |
tip:如果你使用 exec 或 match 方法并且匹配成功了,那么这些方法将返回一个数组并且更新相关的正则表达式对象的属性和预定义的正则表达式对象;如果你需要访问一个正则表达式的属性,则需要创建一个对象初始化生成器,你应该首先把它赋值给一个变量。
注意str.match(re)与re.exec(str)的区别
高级搜索
正则表达式有六个可选参数 (flags) 允许全局和不分大小写搜索等。这些参数既可以单独使用也能以任意顺序一起使用,并且被包含在正则表达式实例中。
标志 | 描述 |
g | 全局搜索 |
i | 不区分大小写搜索 |
m | 多行搜索 |
s | 允许.匹配换行符 |
u | 使用 unicode 码的模式进行匹配 |
y | 执行“粘性(sticky)”搜索,匹配从目标字符串的当前位置开始 |
语法
var re = /pattern/flags;
或
var re = new RegExp("pattern", "flags");
tip:标志是一个正则表达式的一部分,它们在接下来的时间将不能添加或删除;
图形化工具网站:https://regexper.com/
三、闭包
闭包可以让你从内部函数访问外部函数作用域。
MDN中对闭包的定义如下:
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。
也就是说闭包是由函数及其引用的变量构成的。那么所有函数只要内部引用外部的变量都能构成一个闭包,从技术角度讲,所有的JavaScript都是闭包。
为了防止闭包没有意义,MDN补充道:
也就是说,闭包可以让你从内部函数访问外部函数作用域。
所以引用的变量应该为函数作用域内的变量,闭包应存在于函数作用域内部;
闭包的常见用法就是提供间接访问一个变量的方法,有时候我们需要在某些地方用到一个变量,但是又不能定义为全局变量,存在着篡改的风险,那么我们就可以用定义位私有变量,然后通过闭包暴露出去。
demo;
四、立即执行函数
函数声明和函数表达式的区别
//这个叫函数声明
function test1() {
console.log('test1')
}
//我们把一个函数声明式赋值给一个变量的形式,这叫函数表达式(这里是一个匿名函数,当然函数有名字也可以)
let a = function () {
console.log('hello')
}
当一个函数需要立即执行的情况,该函数必须形成表达式形式
我们在函数声明前面加上“+”、“!”、“~”,和用括号括起来等等,都可以将函数声明形成表达式
W3C推荐的立即执行函数规范是用括号将函数声明和执行符号包起来
(function() {}())
实践中我们一般将执行符号放在外面,这样看起来更清晰
不推荐
(function(){})();
推荐
(function(){}());
tip:在前面加分号;
在前面加上分号是为了避免代码编译错误。很多时候我们写js代码都是不写分号的,而js引擎在解析代码时会自动加上,但是像下面代码,括号多了,它也分辨不出哪个跟哪个是一组,所以需要手动加上分号。由于很多开发者语句后面不打分号,为了避免,就约定俗成的在立即执行函数前面加上分号
特点:可以创建一个与外界没有任何关联的作用域(独立作用域);执行完成以后自动销毁;向外部抛出一系列属性和方法;
立即执行函数为什么是闭包
立即执行函数和函数被当成值传递的共性:作用域,函数定义,(函数传递),函数执行。
函数的定义和执行也是在不同的作用域;
重点在于传递,(从函数行为和过程去定义)函数被传递到其他作用域后执行,就会产生闭包。(从结果上定义)能够读取其他函数内部变量的函数特性。
总结一下,闭包就是:函数被传递到其他作用域后执行,仍保持对其定义时作用域的访问的过程。
tip:初学者常因着重点不同产生误解。
五、this
几种方法
1.在一般函数方法中使用 this 指代全局对象
function test(){
this.x = 1;
alert(this.x);
}
test(); // 1
2.作为对象方法调用,this 指代上级对象
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
3.作为构造函数调用,this 指代new 出的对象
var x = 2;
function test(){
this.x = 1;
}
var o = new test();
alert(x); //2
alert(o.x); // 1
4.单独使用 this,则它指向全局(Global)对象。
5.在函数中,在严格模式下,this 是未定义的(undefined)。
"use strict";
function myFunction() {
return this;
}
6.在事件中,this 表示接收事件的元素。
<button onclick="this.style.display='none'">
点我后我就消失了
</button>
7.apply 调用 ,apply方法作用是改变函数的调用对象,此方法的第一个参数为改变后调用这个函数的对象,this指代第一个参数;call方法也可以切换this绑定的对象。
var x = 0;
function test(){
alert(this.x);
}
var o={};
o.x = 1;
o.m = test;
o.m.apply(); //0
//apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。如果把最后一行代码修改为
o.m.apply(o); //1
//当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法
var person1 = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person2 = {
firstName:"John",
lastName: "Doe",
}
person1.fullName.call(person2); // 返回 "John Doe"