安全初级—正则表达式、This关键字、闭包

本文详细介绍了正则表达式的基本概念,包括字面量字符、元字符如点号、位置字符、选择符等,以及转义符、字符类、预定义模式和重复类量词符。同时,讨论了JavaScript中this关键字的使用场合,如全局环境、构造函数和对象方法,并提醒了避免多层this、数组处理方法和回调函数中的this。最后,提到了bind、call和apply方法来绑定this,以及闭包的概念及其注意事项。
摘要由CSDN通过智能技术生成

正则表达式

字面量字符

某个字符只表示它字面的含义,例如/a/匹配a/b/匹配b

元字符

(1)点字符(.)

点字符可以匹配除回车(\r)、换行(\n)、行分隔符(u2028)和段分隔符(\u2029)以外的所有字符。

同时,点字符对于码点大于0xFFFF字符也不能正确的匹配,会认为这是两个字符

c.t会匹配ct之间包含任意一个字符的情况,只要字符在同一行。

(2)位置字符

位置字符用来提示字符所处的位置,主要有两个字符。

  • ^ 表示字符串的开始位置
  • $ 表示字符串的结束位置
// test必须出现在开始位置
/^test/.test('test123') // true

// test必须出现在结束位置
/test$/.test('new test') // true

// 从开始位置到结束位置只有test
/^test$/.test('test') // true
/^test$/.test('test test') // false

(3)选择符(|

(|)表示“或关系”(or),即a|b匹配ab

多个选择符可以联合使用。

// 匹配fred、barney、betty之中的一个
/fred|barney|betty/

其他的元字符还包括\*+?()[]{}

转义符

正则表达式中,需要反斜杠转义的,一共有12个字符:^.[$()|*+?{\

这些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。

特殊字符

对于不能打印的特殊字符,正则表达式提供了表达方法

  • \cX 表示Ctrl-[X],其中的X是A-Z之中任一个英文字母,用来匹配控制字符。
  • [\b] 匹配退格键(U+0008),不要与\b混淆。
  • \n 匹配换行键。
  • \r 匹配回车键。
  • \t 匹配制表符 tab(U+0009)。
  • \v 匹配垂直制表符(U+000B)。
  • \f 匹配换页符(U+000C)。
  • \0 匹配null字符(U+0000)。
  • \xhh 匹配一个以两位十六进制数(\x00-\xFF)表示的字符。
  • \uhhhh 匹配一个以四位十六进制数(\u0000-\uFFFF)表示的 Unicode 字符。

字符类

字符类(class)表示有一系列字符可供选择,只要匹配其中一个就可以了

例如[abc] 表示abc之中任选一个匹配。

(1)脱字符(^)

如果方括号内的第一个字符是[^],则表示除了字符类之中的字符,其他字符都可以匹配。

例如[^abc] 表示abc之外都可以匹配。

如果方括号内没有其他字符,即只有[^],就表示匹配一切字符,其中包括换行符。

点号作为元字符(.)是不包括换行符的

(2)连字符(-)

某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。

例如[123456789]可以写成[1-9],同理[A-Z]表示26个大写字母。

/a-z/.test('b') // false
/[a-z]/.test('b') // true

如果(-)中没有出现在[]中,就只是匹配字面的含义,不具备简写的作用。

只有当(-)用在[]之中,才表示连续的字符序列。

合法的字符类简写形式:

[0-9.,]
[0-9a-fA-F]
[a-zA-Z0-9-]
[1-31]  //不代表`1`到`31`,只代表`1`到`3`

连字符还可以用来指定 Unicode 字符的范围。

var str = "\u0130\u0131\u0132";
/[\u0128-\uFFFF]/.test(str)
// true

预定义模式

预定义模式指的是某些常见模式的简写方式

  • \d 匹配0-9之间的任一数字,相当于[0-9]
  • \D 匹配所有0-9以外的字符,相当于[^0-9]
  • \w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
  • \W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
  • \s 匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]
  • \S 匹配非空格的字符,相当于[^ \t\r\n\v\f]
  • \b 匹配词的边界。
  • \B 匹配非词边界,即在词的内部。

通常,正则表达式遇到换行符(\n)就会停止匹配。

重复类

模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次,{n,}表示至少重复n次,{n,m}表示重复不少于n次,不多于m次。

量词符

量词符用来设定某个模式出现的次数。

  • ? 问号表示某个模式出现0次或1次,等同于{0, 1}
  • * 星号表示某个模式出现0次或多次,等同于{0,}
  • + 加号表示某个模式出现1次或多次,等同于{1,}

贪婪模式

量词符默认情况下都是最大可能匹配,即匹配到下一个字符不满足匹配规则为止。这被称为贪婪模式。

var s = 'aaa';
s.match(/a+/) // ["aaa"]

非贪婪模式,即最小可能匹配。只要一发现匹配,就返回结果,不往下检查。

将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。

var s = 'aaa';
s.match(/a+?/) // ["a"]

除了非贪婪模式的加号(+?),还有非贪婪模式的星号(*?)和非贪婪模式的问号(??)。

  • +?:表示某个模式出现1次或多次,匹配时采用非贪婪模式。
  • *?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
  • ??:表格某个模式出现0次或1次,匹配时采用非贪婪模式。

修饰符

修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。

修饰符可以单个使用,也可以多个一起使用。

// 单个修饰符
var regex = /test/i;

// 多个修饰符
var regex = /test/ig;

(1)g修饰符

正则模式含有g修饰符,每次都是从上一次匹配成功处,开始向后匹配。

var regex = /b/g;
var str = 'abba';

regex.test(str); // true
regex.test(str); // true
regex.test(str); // false

因为字符串abba只有两个b,所以前两次匹配结果为true,第三次匹配结果为false

(2)i 修饰符

正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写。

/abc/.test('ABC') // false
/abc/i.test('ABC') // true

(3)m 修饰符

m修饰符表示多行模式(multiline),会修改^$的行为。默认情况下(即不加m修饰符时),^$匹配字符串的开始处和结尾处,加上m修饰符以后,^$还会匹配行首和行尾,即^$会识别换行符(\n)。

/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true

This关键字

this就是属性或方法“当前”所在的对象。

下面是一个实际的例子。

var person = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

person.describe()
// "姓名:张三"

由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。

function f() {
  return '姓名:'+ this.name;
}

var A = {
  name: '张三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};

A.describe() // "姓名:张三"
B.describe() // "姓名:李四"

函数f内部使用了this关键字,随着f所在的对象不同,this的指向也不同。

只要函数被赋给另一个变量,this的指向就会变。

使用场合

(1)全局环境

全局环境使用this,它指的就是顶层对象window

this === window // true

function f() {
  console.log(this === window);
}
f() // true

(2)构造函数

构造函数中的this,指的是实例对象。

var Obj = function (p) {
  this.p = p;
};
//在构造函数obj,this指向p属性
var o = new Obj('Hello World!');
o.p // "Hello World!"

(3)对象的方法

如果对象的方法里面包含thisthis的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。

var obj ={
  foo: function () {
    console.log(this);
  }
};

obj.foo() // obj
//此时this指向obj
//以下的用法会改变this的指向
// 情况一
(obj.foo = obj.foo)() // window
// 等同于
(obj.foo = function () {
  console.log(this);
})()
// 等同于
(function () {
  console.log(this);
})()

// 情况二
(false || obj.foo)() // window
//等同于
(false || function () {
  console.log(this);
})()

// 情况三
(1, obj.foo)() // window
//等同于
(1, function () {
  console.log(this);
})()

使用注意点

避免多层 this

由于this的指向是不确定的,所以切勿在函数中包含多层的this

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}

o.f1()
// Object
// Window

这种情况,f1第一次指向对象o,然后代码运行到f2时,this的指向又变成了全局对象。

避免数组处理方法中的 this

数组的mapforeach方法,允许提供一个函数作为参数。这个函数内部不应该使用this

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}

o.f()
// undefined a1
// undefined a2

上面的this其实也是类似于多层this,函数f的调用也是相当于在全局环境下调用,所以这里的this指向也是指向window对象。

避免回调函数中的 this

回调函数中的this往往会改变指向,最好避免使用。

var o = new Object();
o.f = function () {
  console.log(this === o);
}

// jQuery 的写法
$('#button').on('click', o.f);

以上的代码,在调用函数f时,是在按钮对象的环境下调用的,所以this的指向也不是o,而是指向按钮的DOM对象。

绑定 this 的方法

JavaScript 提供了callapplybind这三个方法,来切换/固定this的指向。

Function.prototype.call()

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

call方法的参数,应该是一个对象。如果参数为空、nullundefined,则默认传入全局对象。

var n = 123;
var obj = { n: 456 };

function a() {
  console.log(this.n);
}

//如果`call`方法没有参数,或者参数为`null`或`undefined`,则等同于指向全局对象。
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
//如果指向全局对象,返回结果为`123`
a.call(window) // 123
//如果使用`call`方法将`this`关键字指向`obj`对象,返回结果为`456`
a.call(obj) // 456

如果call方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call方法。

var f = function () {
  return this;
};

f.call(5)  //5为参数,不是对象
// Number {[[PrimitiveValue]]: 5}

call方法还可以接受多个参数。

func.call(thisValue, arg1, arg2, ...)

Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])

(1)找出数组最大元素

JavaScript 不提供找出数组最大元素的函数。结合使用apply方法和Math.max方法,就可以返回数组的最大元素。

var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15

(2)将数组的空元素变为undefined

通过apply方法,利用Array构造函数将数组的空元素变成undefined

Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]

(3)转换类似数组的对象

另外,利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。

Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
//前提:被处理的对象必须有`length`属性,以及相对应的数字键。

Function.prototype.bind()

bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1

//`counter.inc()`方法被赋值给变量`func`。这时必须用`bind()`方法将`inc()`内部的`this`,绑定到`counter`,否则就会出错。

bind()方法有一些使用注意点。

(1)每一次返回一个新函数

bind()方法每运行一次,就返回一个新函数,这会产生一些问题。

(2)结合回调函数使用

回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。

(3)结合call()方法使用

利用bind()方法,可以改写一些 JavaScript 原生方法的使用形式。

闭包

闭包就是能够读取外层函数内部变量的函数

变量作用域

变量的作用域为两种:全局作用域和局部作用域

1)函数内部可以读取全局变量

2)函数外部无法读取函数内部的局部变量

读取函数内部的局部变量

1)在函数内部再定义一个函数

function f1() {
	let code = 200;

	function f2() {
		console.log(code);
	}
}

函数f1内部的函数f2可以读取f1中所有的局部变量。因此,若想在外部访问函数f1中的局部变量code,可通过函数f2间接访问。

2)为外部程序提供访问函数局部变量的入口

function f1() {
    let code = 200;

    function f2() {
        console.log(code);
    }

    return f2;
}

f1()();  // 200

闭包概念

  • 闭包访问的变量,是每次运行上层函数时重新创建的,是相互独立的。

  • 不同的闭包,可以共享上层函数中的局部变量

使用闭包的注意点:

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值