0%

多线程 worker.js

  • 同源限制: worker线程运行的脚本 必须与主线程脚本文件同源(QQs按:这里应该指的是同域)
  • 无法访问dom 不能调用阻塞主线程的alert confirm等
  • 通过postmessage/onmessage与主线程通讯
  • 无法访问文件系统file:// Worker可用的Web Api

单线程和阻塞操作

异步 回调 事件队列

异步与回调并没有直接的联系,只是异步操作长需要用回调函数的方式返回数据,同步函数也可以传入function作为回调函数

与typescript集成

定义一个common module

1
2
3
4
5
6
7
8
9
// worker-loader.d.ts

declare module "worker-loader!*" {
class WebpackWorker extends Worker {
constructor();
}

export default WebpackWorker;
}

worker
1
2
3
4
5
// MyWorker.ts
const worker :Worker = self as any;
worker.postMessage({ foo: "foo"});

worker.addEventListener("message", (event)=>console.log(evnet));

调用worker
1
2
3
4
5
6
7
// index.ts
import Worker from "worker-loader!./Worker";
const worker = new Worker();

worker.postMessage({ a: 1 });
worker.onmessage = (event) => {};
worker.addEventListener("message", (event) => {})

web服务器

1
2
3
4
5
<VirtualHost *:80>
DocumentRoot "E:/wamp/www"
ServerName localhost
ServerAlias localhost
</VirtualHost>

访问权限

浏览器报You don’t have permission to access /index.html on this server.

1
2
3
4
5
6
7
<Directory />         
Options FollowSymLinks
AllowOverride All
Order deny,allow
Deny from all
Satisfy all
</Directory>

上述配置是没毛病的,然而只是Apache22语法,Apache24不支持
服务器启动报Invalid command Order等

Apache2.4版本中,提供了由mod_authz_host支持的新的访问控制配置语法。而2.2版本中的Order、Allow等命令在新版本中也可以得到兼容,实现这个兼容功能的模块就是mod_access_compat。所以Load这个模块后,apache2.4就能识别这些语句了。

还有,一般情况下,http.conf中可能有其他关于访问权限的配置,如默认有

1
2
AllowOverride none
Require all denied

解决跨域

P类问题(Polynomial Problem)

简单的认为,P问题就是可以在多项式时间被图灵机判定的语言类。这里又涉及到图灵机,那么我们可以简单的认为,如果一个算法可以在多项式时间内求解,那么就可以认为它是P类问题。这样你就会感觉好多算法都是P类问题,对!没错!如何证明一个问题是否是P类问题呢?只要它满足以下两个条(证明它在多项式时间内完成)

  1. 运行步骤数要有多项式上届时间
  2. 每一步都要保证它可以由合理的确定模型在多项式时间内完成,其实就是每一步的求解过程也是多项式时间

这样步骤是多项式时间的,而每一步也是多项式时间,整合起来整个算法还是多项式时间的。

NP类问题(Non-deterministic Polynomial Problem)

NP问题指的是,这个算法可以在多项式时间内可验证,什么意思呢?我们知道对于P类问题,可以在多项式时间内求解出来,但是NP问题不行。可以这样理解,NP虽然不能在多项式时间内被求解,但是如果给出这个问题的某个解,那么我们可以在多项式时间内验证这个解是不是这个问题的解。
P是属于NP的,但是NP是否等于P是著名的千禧年七大难题之一 (有人说证明NP=P,相当于论证,世间万事皆有捷径)

NPC(Non-deterministic Polynomial Complete Problem)

方法 描述
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 即星期几 (0 ~ 6)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
getUTCDate() 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
getUTCDay() 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
getUTCMonth() 根据世界时从 Date 对象返回月份 (0 ~ 11)。
getUTCFullYear() 根据世界时从 Date 对象返回四位数的年份。
getUTCHours() 根据世界时返回 Date 对象的小时 (0 ~ 23)。
getUTCMinutes() 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
getUTCSeconds() 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
getUTCMilliseconds() 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setFullYear() 设置 Date 对象中的年份(四位数字)。
setYear() 请使用 setFullYear() 方法代替。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setTime() 以毫秒设置 Date 对象。
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)。
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
setUTCSeconds() 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
toSource() 返回该对象的源代码。
toString() 把 Date 对象转换为字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。格式如:09:58:59 GMT+0800 (China Standard Time)
toDateString() 把 Date 对象的日期部分转换为字符串。格式如:Wed Nov 10 2021
~~toGMTString() 请使用 toUTCString() 方法代替。~~
toUTCString() 根据世界时,把 Date 对象转换为字符串。Wed, 10 Nov 2021 02:00:48 GMT
toLocaleString() 根据本地时间格式,把 Date 对象转换为字符串。11/10/2021, 10:01:19 AM
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。10:01:19 AM
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。11/10/2021
UTC() 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf() 返回 Date 对象的原始值。

月份的英文缩写

  • 使用数组
    [‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’, ‘May’, ‘June’, ‘July’, ‘Aug’, ‘Sept’, ‘Oct’, ‘Nov’, ‘Dec’][(new Date()).getMonth()]
  • 使用格式化字符串
    (new Date()).toDateString().split(“ “)[1]

星期同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Date.property.format=function(format){
// eg: format="yyyy-MM-dd hh:mm:ssS"
var o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3)/3), //quarter
"S": this.getMilliseconds()
};
if(/(y+)/.test(format)){
format = format.replace(RegExp.$1,(this.getFullYear) + "").substr(4 - RegExp.$1.length)
}
for(var k in o){
if(new RegExp("(" + k + ")").test(format)){
format = format.replace(RegExp.$1, RegExp.$1.length == 1?o[k]:("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}

基本概念

  • 作用域
  • 词法作用域
  • 函数级作用域和块级作用域
  • 作用域提升
  • 作用域闭包

    题目1

    1
    2
    3
    4
    5
    6
    7
    function t1(){
    console.log(str1);
    console.log(str2);

    var str1="xx1";
    str2="xx2"
    }
    调用t1();输出
    1
    2
    undefined
    Uncaught ReferenceError: str2 is not defined
    关键字var提示js编译器词法分析对变量进行声明,没有该关键字,分析认为是单纯赋值操作;执行时自上而下,输出str1时未赋值,其值为undefined,而str2为未声明。

    题目2

    1
    2
    3
    4
    5
    6
    7
    var i=10;
    function t2(){
    i=20;
    for(var i=0;i<6;i++){
    }
    console.log(this.i);
    }
    调用t2()输出10。
    this指向window对象

题目3

创建对象

1 工厂方式

1
2
3
4
5
6
7
8
9
function createPerson(name, age, job){ 
var o = new Object(); o.name = name; o.age = age; o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29"Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

工厂模式虽然解决了创建 多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)

2构造方法

1
2
3
4
5
6
7
function Person(name, age, job){ 
this.name = name; this.age = age;
this.job = job;
this.sayName = function(){ alert(this.name);};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

构造的对象属于同一类型

1
2
3
4
5
6
7
alert(person1.constructor == Person); //true 
alert(person2.constructor == Person); //true

alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true

其实有这么一个问题:不同实例上的同名函数是不相等的
即person1.sayName == person2.sayName为false

3原型方式

1
2
3
4
5
6
7
8
9
function Person(){ }
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){ alert(this.name);};
var person1 = new Person();
person1.sayName();
var person2 = new Person();
person2.sayName();

所有对象可以通过引用Person.prototype属性,从而实现共享属性和方法

原型对象,对象属性和原型属性

无论什么时候,只要创建了一个新函数(如上文Person),就会根据一组特定的规则为该函数创建一个原型对象prototype,在默认情况下,所有原型对象都会自动获得一个 constructor,该构造方法指向 prototype 属性所在函数(即Person.prototype.constructor=Person)通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。创建了自定义的构造函数之后,其原型对象默认只会取得 constructor 属性;至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(__proto__,注,ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但 Firefox、Safari 和 Chrome 在每个对象上都支持属性 proto 而在其他实现中,这个属性对脚本则是完全不可见的),指向构造函数的原型对象(即person1.__proto__=Person.prototype)。

![prototype](https://upload-images.jianshu.io/upload_images/3140250-a75fde4f80938b58.PNG "prototype")

在无法访问[[Prototype]]的情形下(QQs尚未接触到不支持__proto__的环境),可以通过 isPrototypeOf()方法来确定对象之间是否存在这种关系,有Person.prototype.isPrototypeOf(person1)返回true,另ES5提供Object.getPrototypeOf,有Object.getPrototypeOf(person1) == Person.prototype为true。

代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先 从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到, 则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这 个属性,则返回该属性的值。也就是说,在我们调用 person1.sayName()的时候,会先后执行两次搜 索。首先,解析器会问:“实例 person1 有 sayName 属性吗?”答:“没有。”然后,它继续搜索,再 问:“person1 的原型有 sayName 属性吗?”答:“有。”于是,它就读取那个保存在原型对象中的函数。

可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们 在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。而delete对象实例的这个属性,会重新暴露原型对象的属性

1
2
3
4
5
6
Person.prototype.name = "Nicholas";
var person1 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
delete person1.name;
alert(person1.name);//"Nicholas"——来自原型

使用hasOwnProperty()方法可以检测一个属性是存在于实例中(true),还是存在于原型中(false)。
检测属性(无论实例属性或是原型属性)是否存在可以用in,即”name” in person1返回true。

从in说到for in,遍历所有能够通过对象访问的、可枚举的(enumerated)属性,其中 既包括存在于实例中的属性,也包括存在于原型中的属性。

“可枚举”相当于”可以出现在对象属性的遍历中”,在数组上使用for in遍历,不仅会包含所有数值索引,还会包含所有可枚举属性,。最好只在对象上应用 for in 循环,如果要遍历数组就使用传统的 for 循环来遍历数值索引

Object.propertyIsEnumerable()方法可以检查给定属性名是否直接存在于实例中且enumerable = true
不可枚举属性,即将 [[Enumerable]]标记为 false 的属性,而屏蔽不可枚举属性的实例属性也会遍历到。
Object.keys()返回所有可枚举属性的字符串数组。
Object.getOwnPropertyNames()返回所有属性的数组,无论是否可以枚举

更多创建对象姿势

动态原型模式

1
2
3
4
5
6
7
8
9
function Person(name, age, job){ //属性
this.name = name; this.age = age;
this.job = job;
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}

原型对象方法sayName在第一次调用构造方法Person时执行。

寄生构造方法模式

1
2
3
4
5
6
7
8
9
10
11
function Person(name, age, job){ 
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");

注意这种方式不能依赖 instanceof 操作符来确定对象类型

稳妥构造函数模式

1
2
3
4
5
6
7
8
9
10
11
function Person(name, age, job){ //创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
var friend = Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"

对象实例只能调用闭包内函数以返回name,而无法直接访问任何属性

原型链和继承

原型链

假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
function SuperType(){ 
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){ }
SubType.prototype = new SuperType();//继承了 SuperType
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue());//true

SubType 继承了 SuperType。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于基类型的实例中的所有属性和方法,现在也存在于衍生类原型对象中了。
注意,所有引用类型的对象都继承了Object,这个继承也是通过原型链实现的。

![inherit](https://upload-images.jianshu.io/upload_images/3140250-998e96f2cdff75a3.PNG "inherit")

两个问题缺点:父类包含的引用类型属性,随实例共享到子类的所有实例,这可能并不是我们期望的;子类原型引用的是父类实例,不能向超类型的构造函数中传递参数(在ES6中可以在子类constructor中通过超类型super调用父类方法)。
子类原型对象的constructor指向父类构造方法

借用构造方法constructor stealing

1
2
3
4
5
6
7
8
9
10
11
12
function SuperType(){ 
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了 SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors);//"red,blue,green"

传参

1
2
3
4
5
6
7
8
9
10
function SuperType(name){ 
this.name = name;
}
function SubType(){
SuperType.call(this, "Nicholas");//调用SuperType构造方法
this.age = 29; //实例属性
}
var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age);//29

无法避免构造函数模式存在的问题——方法都在构造函数中定 义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

组合构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function SuperType(name){ 
this.colors = ["red", "blue", "green"];
this.name=name;
}
SuperType.prototype.sayName=function(){alert(this.name)};

function SubType(name,age){
//继承了 SuperType
SuperType.call(this,name);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;// 注@
SubType.prototype.sayAge=function(){alert(this.age)};

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors);//"red,blue,green"

在注@这一行,将原型对象的构造方法指向到SubType,因为之前该对象根据继承关系图示,作为SuperType实例,其[[prototype]]指向SuperType.prototype,故而SubType.prototype.constructor原先为SuperType方法。

组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。而且,instanceof 和 isPrototypeOf()也能够用于识别基于组合继承创建的对象。

原型式继承

浅复制方法

1
2
3
4
5
function object(o){ 
function F(){}
F.prototype = o;
return new F();
}

关于ES5 Object.create();

操作符instanceOf

1
2
3
4
5
6
7
8
9
10
11
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
var O = R.prototype;// 取 R 的显示原型
L = L.__proto__;// 取 L 的隐式原型
while (true) {
if (L === null)
return false;
if (O === L)// 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}

编写方法时,考虑到不同的业务需求(如首次加载配置项js片段,甚至是为了兼容不同内核浏览器)需要用多个if语句加以区分,这将使每次调用都回进行判断,然而往往在具体的情景下是不必要的,比如已经以chrome载入了该页面。
一种思路是覆盖方法定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function addEvent (type, element, fun) {
if (element.addEventListener) {
addEvent = function (type, element, fun) {
element.addEventListener(type, fun, false);
}
}
else if(element.attachEvent){
addEvent = function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
}
else{
addEvent = function (type, element, fun) {
element['on' + type] = fun;
}
}
return addEvent(type, element, fun);
}


另一种思想是将函数定义引用一个自执行匿名函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var addEvent = (function () {
if (document.addEventListener) {
return function (type, element, fun) {
element.addEventListener(type, fun, false);
}
}
else if (document.attachEvent) {
return function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
}
else {
return function (type, element, fun) {
element['on' + type] = fun;
}
}
})();

第二种方式不会给第一次调用增加额外开销,而是在定义时进行了判断

剔除符号

1
str.replace(/[^0-9a-zA-Z]/g,"");

1
2
3
4
5
function palindrome(str) {
str = str.replace(/[^0-9a-zA-Z]/g,"");
str = str.toLowerCase();
return str==str.split("").reverse().join("");
}

指定内容行前注#
1
2
^(Proxy.+)$
#$1

$1为()内的内容

常见用法

regExp.test(target)

判断target是否符合正则表达式规则

target.match(regExp)

返回target字符串中符合正则表达式规则的部分

匹配语法

flag: g, i

g: global

i: case insensitive

集合[]

非和开头^

^在集合中表示非

比如上面剔除符号的用法

在集合外表示以…开头

频率 +, *, ?

symbol description
+ 出现1次或者多次
* 出现任意次
? 出现一次或者不出现
{n} 重复n次
{m, n} 重复m~n次

greedy matching vs lazy matching

贪心匹配(?)匹配最长的串,惰性匹配(?)匹配最小的串

1
2
3
4
5
6
7
8
9
10
let text = "<h1>Winter is coming</h1>";
let myRegex = /<.*>/;
let result = text.match(myRegex); // matching <h1>Winter is coming</h1>

myRegex = /<.*?>/;
result = text.match(myRegex); // matching <h1>

let article = "<h1>Winter is coming</h1><div></div><h1>north remembers</h1><h1>dragon is coming</h1><h1>the Queen is coming</h1>"
let titles = article.match(/<h1>.*?<\/h1>/g)

  • 思考:折中的需求

匹配符合下列规律的字符串,规律是连续的两个parameter,即某规则重复2次

1
2
3
<parameter key="xxx" value="1" />
<any-other-marks>
<parameter key="yyy" value="2" type="0"/>

可以这样
1
xmlInfo.match(/<parameter.*?\/>.*?<parameter.*?\/>/)

里面有重复了两段规则,见下文()部分

代码占位符

symbol description
\w 字符,空格,标点,下划线等 类似但不等价于[A-Za-z0-9_]
\W 非字符
\d 任意数字 相当于[0-9]
\D 任意非数字
\s 匹配任意不可见字符,包括空格、制表符、换页符等 相当于[ \f\n\r\t\v]
\S 匹配任意可见字符

lookaheads

暂时不知道如何翻译

例 密码规则不少于6位,且至少包含连续2位数字

1
2
let pwRegex = /(?=\w{6,})(?=\D*\d{2})/;
let result = pwRegex.test(sampleWord);

(?=\D*\d{2})应理解为任意非数字内容(或者不存在这部分内容)加两个数字
(?=…)检测包含…的字符串,(?!…)检测不包含…的字符串,只检测是否符合条件,不返回匹配片段

()

括号的本义是封装模式,封装的模式可以用\1 \2的方式代替
上面有个遗留问题,重复部分可写作

1
xmlInfo.match(/(<parameter.*?\/>).*?\1/)

上述代码存在问题

正/反向断言

  • (?=pattern):正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。
    非获取匹配,即符合该模式的匹配结果不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。
    不消耗字符,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

  • (?!pattern):正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。

  • (?<=pattern): 反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。

  • (?<!pattern):反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
    从结果理解正向反向:匹配得到的“Windows”,如果对其接下来的内容有要求,正则表达式/Windows/要追加一个断言模式,如果对其之前的内容有要求,除了在/Windows/之前加断言模式,还要用“?<”表明这是反向断言

    写过的例子

  • 常见邮箱格式
    1
    /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/
  • 以,分隔的邮箱地址
    1
    /^(([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4}),)*(([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4}))$/
  • 获取文件扩展名

    1
    filename.match(/\.(\w+)$/);

    返回match的Array,取第一项为扩展名

  • 查找代码中所有的描述字符串(目的是做多语言替换)

    1
    (?<!from |require\(|: |console\.log\()'.+'

    即非空字符串且排除import from,require,以及console.log等语法