一、正则表达式及JavaScript实现
(一)正则表达式
1、正则表达式的组成
正则表达式,由普通字符,和特殊字符组成。用来声明一个特定结构的字符串。然后在一个字符串中,对该正则表达式进行匹配,从而查找是否存在子字符串。
正则表达式的普通字符,就是普通的字母、中文、数字等。特殊字符是有特殊能力的字符,例如. 代表一个任意的数字、字母、或者下划线
。
2、正则表达式的特殊字符
特殊字符 | 含义 |
---|---|
\ |
如果\ 出现在一个普通字符前面则将该普通字符转为特殊字符,例如\d 代表一个数字。也可以将一个有特殊能力的字符,转为普通字符: \\ 将\ 转为能在字符串中显示的普通\ |
. |
小数点:代表任意一个字符(换行符除外) |
\b |
匹配一个词的边界。在英文中,空格,标点,字符串开始,字符串结束,都会造成边界。 |
\d |
匹配一个数字 |
\s |
匹配一个空白字符,包括空格、回车、tab |
\w |
匹配大小写字母、数字、下划线,相当于[a-zA-Z0-9] |
[] |
备选字符集,用于匹配一个值,该值可以是[] 中的任意一个值可以使用 a-z 指定a到z的所有值。汉字的所有值,是[\u4e00-\u9fa5] |
{最小值,最大值} |
控制数量,用来声明其前面元素出现的次数,可以设置最大值和最小值,也可以值设置一个值。默认是贪婪模式,从最大开始匹配 |
* |
控制数量,说明一个匹配的元素,出现 0 次,或者多次。 |
+ |
控制数量。说明一个匹配的元素,最少出现 1 次,最多不限。 |
? |
控制数量。说明一个匹配的元素,出现 0 或 1次。 |
` | ` |
() |
捕获括号。匹配该项,并能记住匹配到的内容。可以用来对匹配字符串,进行分组。 例如在使用了 ` |
^ |
匹配输入的开始。并不是第一个字符,而是第一个字符前面的位置。 |
$ |
匹配输入的结束。并不是最后一个字符,而是最后一个字符后面的位置。 |
3、特殊字符的说明
3.1 {n,m}
两个值的匹配
如果限制匹配元素数量时,同时限定了最少的出现次数,和最大的出现次数,则优先匹配最大的出现次数。称为贪婪模式。例如:
1 | // 原始字符串: '123456789' |
(二)字符串正则API
1、查找固定词的位置:indexOf
1 | str.indexOf('查询字符串',start_index); |
2、查找符合某个规则的词出现的位置:search
1 | str.search(/regexp/) ; |
3、查找并获取到匹配的关键词 : match
1 | str.match(/reg/ig); |
如果未匹配到子字符串,该方法会返回 null
。
4、字符串内容替换 : replace
1 | str.replace(/regexp/,value); |
5、字符串分割 :split
将字符串,按照某个符合规则的子字符串进行分割。
1 | str.split(分隔符表达式); |
(二)RegExp
对象
1、RegExp
对象
专门保存一条正则表达式,并提供根据正则表达式,来查找和校验字符串你的方法的对象。
2、创建RegExp
对象
1 | // 字面量方式创建一个 RegExp 对象 |
3、RegExp().test()
方法
1 | var regs = /sss/g; |
4、RegExp().exec()
方法
1 | var regs = /正则表达式/g ; |
二、function
(一)创建函数
1、function
关键字声明函数
1 | function f_name(arguments_list){ |
使用function
关键字声明的函数,函数整体都会提升到变量作用域的最顶部。创建函数的过程:
- 先声明一个 f_name 变量,作为window 对象的一个属性
- window.f_name 属性的值,是 function 存储的地址。所以函数不执行时,函数体不解析,即使存在语法错误,如果函数不运行,也不会报错。
所以函数声明后,即使从执行顺序上来说,调用函数执行在定义函数前面,也依然能调用成功。
使用 var
声明变量时,可以将变量提升到作用域提升到最顶部(个人理解就是编译时,将var声明的变量,直接添加到Window上),但是赋值操作是等程序执行到具体代码,才能真正的赋值。所以,使用var声明的变量,在为赋值之前,依然可以打印出来,只是值为 undefined
.
2、var
声明的变量指向函数对象地址
1 | var f_name = function(){}; |
使用 var 声明的变量,存储的是函数对象的地址,则函数整体不会提升到作用域最前面。只有执行到 赋值操作时,函数才生效。
(二)函数调用和重载
1、 函数重载
函数内部,存在 arguments
参数对象,用来存储实际传入到函数内部的实参值。函数同样存在形参,而形参就相当于函数创建的局部作用域变量。形参对应的顺序和arguments
对象存储实参值的顺序是一样的。
1 | function a (name,age){ |
在JavaScript 中,根据arguments参数对象,可以来判断实际调用函数是,传入的实参类型和个数,从而在函数内部实现函数的重载,让函数可以根据不同的调用参数,而产生不同的调用逻辑。
1 | // 在实际的工作中,可以通过一个对象来存储传入到函数的实参,因为判断对象的属性是否有值,比较方便。 |
2、匿名函数
匿名函数就是不使用变量名或者函数名来持有的函数对象。由于不使用标识符持有,所以无法单独存在于程序中,一般应用在回调函数和自调用函数中(因为防止变量污染,所以基本上代码尽量放在自调用函数中,尽可能的减少变量名、函数名的产生)
1 | ( |
使用自调用函数时,需要在最外面添加一个括号:
1 | /* |
(三)作用域和作用域链
1、全局作用域
作用域就是变量的可使用范围。JavaScript中有两级作用域:全局作用域和函数作用域。当程序开始执行时,先创建全局作用域对象:window 对象。在window对象中,保存着所有的全局变量和全局函数。
2、函数作用域
函数在调用时,会生成一个活动对象。这个活动对象,存储的就是局部作用域变量,和形参等。然后和全局作用域对象,一起形成一个列表。函数自己的活动对象,永远在这个作用域列表的前部。当函数中需要使用某个变量时,先在函数自己的活动对象里找,也就是优先找函数作用域变量。如果找到,就使用,如果没找到,则继续从作用域链的下一个变量对象中寻找。
当函数调用完成时,释放掉自己的活动对象。连同函数的局部变量一起释放掉,所以函数的局部作用域变量,不可重用。
3、闭包
如果一个函数,包裹在另一个函数,且被另一个函数返回。当使用一个变量持有了这个被返回的内部函数,那么这个内部函数的作用域,就变得多了一层。广义的说,整个这个作用域的机制叫做闭包;狭义的说,内层函数,多持有了一层外部函数的作用域对象(活动对象),这个作用域对象,叫做闭包。
三、面向对象
1、创建对象
1 | var obj = { |
也可调用对象的构造函数,来创建对象,然后再给创建好的对象增加属性和方法
1 | var obj = new Object(); |
2、构造函数的方式创建对象
如果一类对象,具有相同的抽象格式,可以使用构造函数,来统一创建。
1 | // 假设,有30个学生,每个学生,都有名字,年龄,和自我介绍的方法,可以创建一个构造函数,需要产生一个学生对象的时候,就使用构造函数来实例化一个对象。 |
3、new
操作符
new
操作符,用来调用构造函数,并且能根据构造函数的内容,返回一个新的对象。new 操作符调用一个构造函数时,会经历以下几个步骤:
1 | // 1、新建一个空白对象 |
使用new操作符创建的对象,和其构造函数除了复制了属性和方法外,还存在一个原型的关系:
1 | // 构造函数.prototype 属性,指向的对象,叫做原型对象。 |
4、原型和原型链
构造函数中的属性和方法,再实例化之后,被子对象完全复制,但是再众多子对象中,所调用的构造函数的方法,功能都是一样的,却为每一个子对象都创建了一遍,这种实例方式,比较消耗内存,所以,一般子对象的公用方法,都会放在构造函数的原型中,因为所有子对象指向的构造函数的原型对象,是一个,所以解决了很大的内存。
在子对象中,调用一个方法时,先在子对象的所有自有方法中查找,是否有要调用的方法。如果找到了就直接执行,如果没找到,就通过__proto__
属性,追溯到构造函数的原型对象中,去原型对象中寻找。同样原型对象也可能存在上层的构造函数(如果不存在,则直接指向object函数的原型对象),所以子对象调用的方法,是按照原型对象指向的顺序,来依次进行查找的。这个顺序叫做原型链。
5、图示:
![1_prototype chain](C:\Users\web\Desktop\笔记\第-3-阶段:JavaScript高级\笔记\1_prototype chain.png)
6、自定义继承
如果对象原本继承的原型对象提供的方法,不满足对象的需求,或者对象需要调用另一个原型及其原型链的函数,可以更换对象的原型指向:
1 | // 假设一个对象,是直接实例化的 Object 构造函数 |
如果需要更改一个构造函数下,所有的子对象的原型对象,可以直接更改构造函数的原型对象:
1 | // 在定义完构造函数,未初始化实例之前,更换构造函数的原型对象 |
7、关于实例对象的一些问题
在实例对象中,调用方法是,是按照原型链查找:先在对象自身上寻找方法,找不到,通过__proto__
属性,追溯到原型中查找。但是方法调用的时候,只需要obj.funtion()
即可调用,不需要obj.__proto__.funtion()
,所以,调用实例对象的方法时,默认情况下,会有持有对原型对象的引用(应该是一个内部的机制)。但是如果给实例对象的方法,重写了,就会导致实例对象对这个方法(和原来能在原型链中查找的方法是同名的)的引用,不能指向原型链了。
1 | function InitFunction(){} |
四、ES5
的方法
1、严格模式
启用严格模式,会导致JavaScript产生和平时不一样的特性,会运行和普通JavaScript更严格的机制。
1 | ; |
启用严格模式:
- 禁止给未声明的变量赋值:旧的JavaScript中,可以给未使用var声明的变量赋值
- 静默失败升级为错误:以前不报错的地方,会报错,导致程序不能继续执行
- 普通函数,和匿名函数,默认调用时,this不在指向window,而是指向undefined
- 禁止使用
arguments.callee
参数:该参数指向的是函数自身,可以通过arguments.callee()
来调用函数自己,禁用则表示不推荐使用递归。
2、保护对象属性
对象的属性的分类:
1 | /* |
在E5
中,对象的属性(指的是数据属性),也是一个对象,由四个参数构成,假设:
1 | var obj = { |
构成对象的属性,虽然也是对象,但是不能通过 .
的方式来直接引用,需要使用javascript
提供的函数:
1 | var obj = {sname:"names"}; |
如果想同时设置对象的几个属性,可以使用函数:Object.defineProperties
:
1 | var obj = {sname:'names',sage:11}; |
如果需要在设置对象的属性时,增加校验逻辑,使用对象属性的对象配置,无法完成任务,需要使用 访问器属性
来实现。访问器属性类似于其他语言的 getter
和setter
,访问器属性,可以控制目标属性的值,和是否可写,但是不能控制enumerable
和configurable
属性:
3、保护对象结构
1 | /* |
4、创建对象
如果需要在没有在构造函数的情况下,以某个对象为原型,创建一个子对象:
1 | // Object.create(原型对象,{新建对象需要添加的属性}) |
5、给函数绑定this
如果函数执行时,指向的this,不是我们需要的this,而是需要给函数指定一个this。函数自身提供了绑定this 的方法。
1 | /* |
6、数组的API
1 | // 1. indexOf() |
五、ES6
的方法
1、let
声明
ES6
新增的声明变量的关键字。主要是解决变量的声明提前问题。使用 let
声明的变量,不会被声明提前,只有执行到let 变量名=值
的时候,才会具体的声明变量。而且使用let
声明的变量,能绑定在离自己最近的{}
块级作用域内。
1 | let a = 10; |
如果使用了let
声明变量:
- 在相同的作用域或块级作用域,禁止使用
let
声明两个变量 let
声明变量的所在作用域,不允许提前使用 该变量
2、箭头函数
箭头函数是对function
声明函数的简写。大多数匿名函数和回调函数,都是用箭头函数进行简写。使用箭头函数的准则:
- 去掉
function
,在参数列表和函数体之间,使用=>
进行连接 - 如果形参列表,只有一个参数时,可以省略形参列表的括号
- 当函数体只有一个语句时,也可以省略
{}
.但是如果唯一的语句还是个return
语句,则必须省略return
1 | function f1(a){ |
使用箭头函数,会导致箭头函数调用时,所指向的this 变的和函数外的this 相同了。所以对象的方法,不能使用箭头函数进行简写。因为对象的方法中,this 指向的是当前对象,而使用了箭头函数,就变成了和对象同一个作用域的那个this了。
同时,在箭头函数中,不能使用arguments
对象。使用rest
参数增强的方法来解决。
3、参数增强
ES6
支持在声明函数时,设置函数的缺省值,但是只支持对最后一个参数设置默认值。
1 | function f1(a,b=10){ |
在ES
中,对arguments
参数,使用...rest
进行了加强。...rest
能接收到未声明的形参后面,继续传入的实参值。
1 | function f(a,b,...c){ |
...
还有打散的作用,后面可以操作一个数组,或者对象,将其打散:
1 | var a = [1,2,3,4,5] |
4、for..of 遍历
1 | for(var i of arr){ |
5、解构
解构,就是将对象,或者数组的一部分数据,提取出来,存入到固定的变量中。例如 nodejs中,导入的模块有很多类,但是只需要其中一个类时,可以使用解构:
1 | // 1. 数组解构 |
6、对象方法简化
1 | // 1. 对象属性名的简化 |
7、class
声明类型
使用class
重新对类型进行封装。去除原型对象方法,对构造函数声明上造成的语义化障碍。使用class
声明一个类型的准则:
- 使用
class{}
包裹构造函数,和原型对象函数 - 类型名,接在
class
关键字之后,构造函数,使用固定的关键字constructor
声明 - 放入
class
中的原型对象函数,不在需要显示的声明为.prototype
对象的方法,所有放入class
关键字的方法,自动添加为原型对象的方法。
1 | class 类型名{ |
在使用function
来构造一个类型时,如果需要使用子类型,来继承父类型,需要的步骤比较复杂,假设已经存在一个父类,F1
:
1 | function F1(){ |
先再创建一个子类F2
来继承F1
的构造函数,和原型对象,并且添加属于子类F2
自己的构造函数代码和原型对象方法:
1 | function F2(){ |
而在使用class
声明的类型中,使用extends
关键字,就可以将以上流程简化。
1 | class F1{ |
8、promise
处理回调
如果出现多层回调函数嵌套,回调函数嵌套层次过深,则不容易维护,且可读性差。提供promise
对象,从易读性上,改善回调函数嵌套问题。
1)什么是promise
对象
1 | // promise 对象,初始化时的参数,是一个函数 |
2)使用promise
如果多层回调函数嵌套,可以利用promise执行then 函数的特性,从而使回调函数的调用,根据可读性。具体使用方法, 是使用形成一个返回promise
对象的调用链,这样可以依次的执行回调函数,但是需要注意的是,因为是依次调用,所有,后面的回调函数,执行的位置,应该是当前异步函数内:
1 | function f1() { |
完整的实例:
1 | /* |
如果 promise
对象操作的函数,在执行中发生了错误,可以使用.catch
方法,调用一个函数来执行。