JS高级篇 浏览器深入解析、正则表达式、JS面向对象编程、JS函数进阶
一、浏览器深入解析
1.浏览器组成
浏览器从原理上分为七个模块,分别是User Interface(用户界面)、Browser engine(浏览器引擎)、Rendering engine(渲染引擎)、Networking(网络)、JavaScript Interpreter(js解释器)、UI Backend(UI后端)、Date Persistence(数据持久化存储)。
2.浏览器内核
内核分为渲染引擎和js引擎。渲染引擎主要负责 HTML、CSS 的解析,页面布局、渲染与复合层合成。JavaScript引擎负责 JavaScript 代码的解释与执行。
3.浏览器渲染过程
从上图我们可以看到,浏览器渲染过程如下:
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上。
构建渲染树,浏览器完成以下工作: - 从DOM树的根节点开始遍历每个可见节点。
- 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
- 根据每个可见节点以及其对应的样式,组合生成渲染树
不可见节点: - 一些不会渲染输出的节点,比如script、meta、link等。
- 一些通过css进行隐藏的节点。比如display:none。注意,利用visibility和opacity隐藏的节点,还是会显示在渲染树上的(因为还占据文档空间)。只有display:none的节点才不会显示在渲染树上。 注意:渲染树只包含可见的节点。
4、回流和重绘
回流:构造渲染树后,还需要计算可见dom节点在设备视口内的确切位置和大小,计算阶段为回流。
重绘:将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段叫做重绘节点。
发生回流的情况:
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
回流一定会触发重绘,而重绘不一定回流。
标题减少回流和重绘
1.最小化回流和重绘
减少重绘和回流的发生次数,可以合并多次对dom和样式的修改然后一次处理掉。
1.使用cssText
``javascript
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;
2.修改css的class,注意要加空格
```javascript
el.className += ' active';
2.批量修改DOM
- 隐藏元素,应用修改,重新显示
- 使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
- 将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。
3.对于复杂动画效果,使用绝对定位让其脱离文档流
4.css3硬件加速
使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。
二、正则表达式
1.正则表达式组成
1.字符类
字符类 | 说明 |
---|---|
[abc] | 只能是a b c中的任意一个,也就是说[]里面的内容只能选择任意一个 |
[^abc] | 任何字符,除了 a、b 或 c(否定) |
[a-z] | a 到 z 中的任意一个 |
[^a-z] | 非a 到 z 中的任意一个 |
[a-zA-Z] | a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
[0-9] | 0-9之间的任意一个 |
[\u4e00-\u9fa5] | 表示汉字的取值范围 |
2.元字符
元字符 | 说明 |
---|---|
\d | 匹配数字 |
\D | 匹配任意非数字的字符 |
\w | 匹配字母或数字或下划线 |
\W | 匹配任意不是字母,数字,下划线 |
\s | 匹配任意的空白符 |
\S | 匹配任意不是空白符的字符 |
. | 匹配除换行符以外的任意单个字符 |
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
3.限定符
限定符 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
4.条件
条件 | 说明 |
---|---|
(?![0-9]+$) | 不能全是数字 |
(?![a-zA-Z]+$) | 不能全是字母 |
5.其它
其他 | 说明 |
---|---|
[] | 字符串用中括号括起来,表示匹配其中的任一字符 |
[^] | 匹配除中括号以内的内容 |
\ | 转义符 |
| | 或者,选择两者中的一个。注意 | 将左右两边分为两部分,而不管左右两边有多长多乱 |
() | 从两个直接量中选择一个,分组 |
6.参数
标志 | 说明 |
---|---|
i | 忽略大小写 |
g | 全局匹配 |
gi | 全局匹配+忽略大小写 |
2.正则汇总
1.校验数字的表达式
数字:^[0-9]*$
n位的数字:^\d{
n}$
至少n位的数字:^\d{
n,}$
m-n位的数字:^\d{
m,n}$
零和非零开头的数字:^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(\.[0-9]{
1,2})?$
带1-2位小数的正数或负数:^(\-)?\d+(\.\d{
1,2})$
正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数:^[0-9]+(\.[0-9]{
2})?$
有1~3位小数的正实数:^[0-9]+(\.[0-9]{
1,3})?$
非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){
1,3}$ 或 ^\+?[1-9][0-9]*$
非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
非负整数:^\d+$ 或 ^[1-9]\d*|0$
非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
2.校验字符的表达式
汉字:^[\u4e00-\u9fa5]{
0,}$
英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{
4,40}$
长度为3-20的所有字符:^.{
3,20}$
由26个英文字母组成的字符串:^[A-Za-z]+$
由26个大写英文字母组成的字符串:^[A-Z]+$
由26个小写英文字母组成的字符串:^[a-z]+$
由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{
3,20}$
中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{
2,20}$
可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
禁止输入含有~的字符:[^~\x22]+
3.特殊需求表达式
Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{
0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{
0,62})+\.?
InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{
8}$
电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{
3,4}-)|\d{
3.4}-)?\d{
7,8}$
国内电话号码(0511-4405222、021-87888822):\d{
3}-\d{
8}|\d{
4}-\d{
7}
电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号): ((\d{
11})|^((\d{
7,8})|(\d{
4}|\d{
3})-(\d{
7,8})|(\d{
4}|\d{
3})-(\d{
7,8})-(\d{
4}|\d{
3}|\d{
2}|\d{
1})|(\d{
7,8})-(\d{
4}|\d{
3}|\d{
2}|\d{
1}))$)
身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X:(^\d{
15}$)|(^\d{
18}$)|(^\d{
17}(\d|X|x)$)
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{
4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{
5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{
8,10}$
强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{
8,10}$
日期格式:^\d{
4}-\d{
1,2}-\d{
1,2}
一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
钱的输入格式:
有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧。下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{
2})?$
这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{
1,2})?$
这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{
1,3}(,[0-9]{
3})*(.[0-9]{
1,2})?$
1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{
1,3}(,[0-9]{
3})*)(.[0-9]{
1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s*\r (可以用来删除空白行)
HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> ( 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号:[1-9][0-9]{
4,} (腾讯QQ号从10000开始)
中国邮政编码:[1-9]\d{
5}(?!\d) (中国邮政编码为6位数字)
IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){
3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
三、JavaScript面向对象编程
1.面向对象概述
万物皆对象
对象是:无需属性的集合,其属性可以包含基本值、对象或者函数。
面向对象的特征:封装性、继承性、多态性
2.创建对象
- new Object()创建对象
- 对象字面量创建对象
- 工厂函数创建对象,不能识别创建的哪个对象
- 构造函数创建对象
对象的constructor
属性是用来标识对象类型的,如果要检测对象的类型,使用instanceof
操作符。
构造函数和实例对象的关系:
通过new 构造函数() 得到实例对象
实例对象.constructor指向构造函数
构造函数和原型对象的关系:
构造函数的prototype属性指向原型对象
实例对象和原型对象的关系:
实例对象.__proto__指向原型对象
但是__proto__是非标准方法。
构造函数问题:
实例对象中相同的属性都为重复内容,实例对象多会浪费内存。解决办法是把共享函数定义到构造函数外面,但这样还会造成全局污染,解决办法是将多个共享函数放在一个对象中避免全局空间命名冲突。
3.原型
js中每个构造函数都有一个prototype属性,指向另一个对象,这个对象的所有属性和方法,都会被构造函数的所拥有。我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype
对象上。
这样可以提高运行效率。
构造函数、实例、原型三者之间的关系
1、构造函数的prototype属性指向 原型对象
2、通过new调用构造函数得到 实例对象
3、实例对象的__proto__属性指向 原型对象
4、实例对象的constructor指向 构造函数
5、原型对象的constructor指向 构造函数
总结:
怎么得到原型对象:构造函数.prototype、实例对象.proto
哪些指向构造函数:原型对象.constructor、 实例对象.constructor
如果我们的某些操作改变类了constructor的指向,那么我们要指向回来到原构造函数,因为他是用来明确实例对象的指向
属性成员的搜索原则:原型链
- 先在自己身上找,找到即返回
- 自己身上找不到,则沿着原型链向上查找,找到即返回
- 如果一直到原型链的末端还没有找到,则返回
undefined
实例对象读写原型对象成员
读取: - 先在自己身上找,找到即返回
- 自己身上找不到,则沿着原型链向上查找,找到即返回
- 如果一直到原型链的末端还没有找到,则返回
undefined
值类型成员写入: - 当实例期望重写原型对象中的某个普通数据成员时实际上会把该成员添加到自己身上
- 也就是说该行为实际上会屏蔽掉对原型对象成员的访问
4.继承
继承的实现方式:
- 对象字面量继承
- 构造函数的属性继承:借用构造函数
- 构造函数的原型方法继承:拷贝继承(for-in)
- 原型继承
四、函数进阶
1.函数内 this
指向的不同场景
函数的调用方式决定了this指向不同:
- 普通函数调用:this指向window
- 构造函数调用:this指向实例对象
- 对象方法调用:该方法所属对象
- 事件绑定方法:绑定事件对象
- 定时器函数:window
2.call、apply、bind
在一些特定情况下this指向是有问题的,所以需要在外部备份this引用,然后在函数内部使用this引用,但js提供了三个函数call、apply、bind来处理this指向问题。
call
call方法调用一个函数,括号内第一个是指定的this,后面为提供的参数列表。call方法和apply方法类似,唯一区别是apply方法接受一个包含多个参数的数组。
fun.call(thisArg,arg1,arg2....);
apply
apply方法调用一个函数, 括号内第一个是指定的this,后面为一个数组
fun.call(thisArg,[arg1,arg2....]);
bind
band函数会创建一个新函数,新函数与被调函数具有相同的函数体,bind与call相似,但bind调用后不会执行
总结:
- call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部
this
的指向 - call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指定了
null
或者undefined
则内部 this 指向 window
- bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递