闭包,正则

闭包

闭包的基本概念

闭包(closure)是JavaScript的一个特色,很多高级的应用都要依靠闭包来实现。

作用域

在js中,函数会形成函数作用域,在函数内部可以直接访问全局变量

1
2
3
4
5
var str = "zs";
function fn(){
console.log(str);//访问全局变量
}
fn();//zs

在函数外部却无法访问函数内部的变量

1
2
3
4
5
function fn(){
var str = "zs";
}
fn();
console.log(str);//报错 str is not defined

问题:怎么才能获取到函数内部的变量?

作用域链

在函数内部有一个函数,那么函数内部的函数是可以访问到外部函数的变量的。

解决方法:

1
2
3
4
5
6
7
8
function fn(){
var str = "zs";
function f2(){
console.log(str);
}
f2();
}
fn();

在上述代码中,fn中定义的所有变量,对于f2函数都来都是可以访问的。但是现在f2在函数的内部,我们如何在外部访问到f2这个函数呢?

1
2
3
4
5
6
7
8
9
function fn(){
var str = "zs";
function f2(){
console.log(str);
}
return f2;
}
var result = fn();
result();// "zs"

闭包的概念

闭包是函数和声明该函数的词法环境的组合。

在JavaScript中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,产生闭包。

**闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用 **

产生闭包的条件

1
当内部函数访问了外部函数的变量的时候,就会形成闭包。

闭包的作用:

  1. 私有变量,保护数据安全
  2. 持久化维持数据

闭包的应用

计数器

需求:统计一个函数的调用次数

1
2
3
4
5
6
7
8
var count = 0;
function fn(){
count++;
console.log("我被调用了,调用次数是"+count);
}
fn();
fn();
fn();

缺点:count是全局变量,不安全。

使用闭包解决这个问题

1
2
3
4
5
6
7
8
9
10
11
function outer(){
var count = 0; // 私有变量, 将count保护起来了
function add(){
count++;
console.log("当前count"+count);
}
return add;
}

var result = outer();
result();

缓存

缓存(cache):数据的缓冲区,当要读取数据时,先从缓冲中获取数据,如果找到了,直接获取,如果找不到,重新去请求数据。

计算斐波那契数列,会有很大的性能问题,因为重复的计算了很多次,因此我们可以使用缓存来解决这个性能问题。

初级优化:

使用缓存的基本步骤:

  • 如果要获取数据,先查询缓存,如果有就直接使用
  • 如果没有,就进行计算,并且将计算后的结果放到缓存中,方便下次使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 缓存
var arr = [];
var fbi = function (n) {
count++;
if (n == 1 || n == 2) {
return 1;
}
if (arr[n]) {
return arr[n];
} else {
var temp = fbi(n - 1) + fbi(n - 2);
arr[n] = temp;// 存入缓存
return temp;
}
}

缺点:既然使用缓存,就需要保证缓存的数据的安全,不能被别人修改,因此,需要使用闭包来实现缓存的私有化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function outer() {
//缓存
var arr = [];

var fbi = function (n) {
if (n == 1 || n == 2) {
return 1;
}
if (arr[n]) {
return arr[n];
} else {
var temp = fbi(n - 1) + fbi(n - 2);
arr[n] = temp;//存入缓存
return temp;
}
}
return fbi;
}
var fbi = outer();
console.log(fbi(40));

闭包存在的问题

闭包占用的内存是不会被释放的,因此,如果滥用闭包,会造成内存泄漏的问题。闭包很强大,但是只有在必须使用闭包的时候才使用。

js的垃圾回收机制

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

  • 内存:计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大,运行程序需要消耗内存,当程序结束时,内存会得到释放。
  • javascript分配内存:当我们定义变量,javascript需要分配内存存储数据。无论是值类型或者是引用类型,都需要存储在内存中。
  • 垃圾回收:当代码执行结束,分配的内存已经不需要了,这时候需要将内存进行回收,在javascript语言中,垃圾回收机器会帮我们回收不再需要使用的内存。

引用记数法清除

引用记数垃圾收集:如果没有引用指向某个对象(或者是函数作用域),那么这个对象或者函数作用域就会被垃圾回收机制回收。

1
2
3
4
5
6
7
8
var o = {
name:"zs"
}
// 对象被o变量引用,引用记数1
var obj = o; // 变量被o和obj引用,引用记数2

o = 1; // o不在引用对象了,引用记数1
obj = null; // obj不在引用对象了,引用记数0,可以被垃圾回收了。

标记清除法清除

使用引用计数法进行垃圾回收的时候,会出现循环引用导致内存泄漏的问题。因此现代的浏览器都采用标记清除法来进行垃圾回收。

这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象Window)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。

闭包占用内存释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function outer(){
var count = 0;

function fn(){
count++;
console.log("执行次数"+count);
}
return fn;
}


var result = outer();
result();
result = null;// 当函数fn没有被变量引用了,那么函数fn就会被回收,函数fn一旦被回收,那么outer调用形成的作用域也就得到了释放。

正则表达式

正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用,经常用于表单校验,高级搜索等。

创建正则表达式

构造函数的方式

1
var regExp = new RegExp(/\d/);

正则字面量

1
var regExp = /\d/;

正则的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/\d/.test("aaa1"); // 返回布尔值
/\d/.exec("aaa1");
/*
返回一个长度为1的伪数组,
数组第0项为匹配的完整子字符串,
第1项为正则第1个子分组(正则中用括号包裹起来的内容)匹配的内容,
第2项为正则第2个子分组匹配的内容,
第3项(如果还有的话)…
属性index表示下标
属性input表示匹配的原始字符串

使用全局匹配可以得到整个字符串的匹配结果,即多个上面的伪数组
*/
"aaa1".search(/\d/); // 返回下标

元字符

正则表达式由一些普通字符和元字符组成,普通字符包括大小写字母、数字等,而元字符则具有特殊的含义。

常见元字符

|表示或,优先级最低

()优先级最高,表示分组

字符类的元字符

[]在正则表达式中表示一个字符的位置,[]里面写这个位置可以出现的字符。

1
console.log(/[abc]/);//匹配a,b,c

[^]在中扩号中的^表示非的意思。

1
2
//^表示该位置不可以出现的字符
console.log(/[^abc]/);//匹配除了a,b,c以外的其他字符

[a-z] [1-9]表示范围

1
2
3
4
console.log(/[a-z]/.test("d"));//小写字母
console.log(/[A-Z]/.test("d"));//大写字母
console.log(/[0-9]/.test("8"));//数字
console.log(/[a-zA-Z0-9]/);//所有的小写字母和大写字母以及数字

边界类元字符

前面的正则只要有满足的条件的就会返回true,并不能做到精确的匹配。

^表示开头   []里面的^表示取反

$表示结尾

1
2
3
4
5
6
console.log(/^chuan/.test("dachuan"));//必须以chuan开头
console.log(/chuan$/.test("chuang"));//必须以chuan结尾
console.log(/^chuan$/.test("chuan"));//精确匹配chuan

//精确匹配chuan,表示必须是这个
console.log(/^chuan$/.test("chuanchuan"));//fasle

量词类元字符

量词用来控制出现的次数,一般来说量词和边界会一起使用

  1. *表示能够出现0次或者更多次,x>=0;
  2. +表示能够出现1次或者多次,x>=1
  3. ?表示能够出现0次或者1次,x=0或者x=1
  4. {n}表示能够出现n次
  5. {n,}表示能够出现n次或者n次以上
  6. {n,m}表示能够出现n-m次