js线程和多线程js
多线程 worker.js
- 同源限制: worker线程运行的脚本 必须与主线程脚本文件同源(QQs按:这里应该指的是同域)
- 无法访问dom 不能调用阻塞主线程的alert confirm等
- 通过postmessage/onmessage与主线程通讯
- 无法访问文件系统file:// Worker可用的Web Api
单线程和阻塞操作
异步 回调 事件队列
异步与回调并没有直接的联系,只是异步操作长需要用回调函数的方式返回数据,同步函数也可以传入function作为回调函数
与typescript集成
定义一个common module1
2
3
4
5
6
7
8
9// worker-loader.d.ts
declare module "worker-loader!*" {
class WebpackWorker extends Worker {
constructor();
}
export default WebpackWorker;
}
worker1
2
3
4
5// MyWorker.ts
const worker :Worker = self as any;
worker.postMessage({ foo: "foo"});
worker.addEventListener("message", (event)=>console.log(evnet));
调用worker1
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) => {})
Apache虚拟主机
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
2AllowOverride none
Require all denied
解决跨域
复杂算法
P类问题(Polynomial Problem)
简单的认为,P问题就是可以在多项式时间被图灵机判定的语言类。这里又涉及到图灵机,那么我们可以简单的认为,如果一个算法可以在多项式时间内求解,那么就可以认为它是P类问题。这样你就会感觉好多算法都是P类问题,对!没错!如何证明一个问题是否是P类问题呢?只要它满足以下两个条(证明它在多项式时间内完成)
- 运行步骤数要有多项式上届时间
- 每一步都要保证它可以由合理的确定模型在多项式时间内完成,其实就是每一步的求解过程也是多项式时间
这样步骤是多项式时间的,而每一步也是多项式时间,整合起来整个算法还是多项式时间的。
NP类问题(Non-deterministic Polynomial Problem)
NP问题指的是,这个算法可以在多项式时间内可验证,什么意思呢?我们知道对于P类问题,可以在多项式时间内求解出来,但是NP问题不行。可以这样理解,NP虽然不能在多项式时间内被求解,但是如果给出这个问题的某个解,那么我们可以在多项式时间内验证这个解是不是这个问题的解。
P是属于NP的,但是NP是否等于P是著名的千禧年七大难题之一 (有人说证明NP=P,相当于论证,世间万事皆有捷径)
NPC(Non-deterministic Polynomial Complete Problem)
Date
方法 | 描述 |
---|---|
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
21Date.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
调用t1();输出1
2
3
4
5
6
7function t1(){
console.log(str1);
console.log(str2);
var str1="xx1";
str2="xx2"
}关键字var提示js编译器词法分析对变量进行声明,没有该关键字,分析认为是单纯赋值操作;执行时自上而下,输出str1时未赋值,其值为undefined,而str2为未声明。1
2undefined
Uncaught ReferenceError: str2 is not defined题目2
调用t2()输出10。1
2
3
4
5
6
7var i=10;
function t2(){
i=20;
for(var i=0;i<6;i++){
}
console.log(this.i);
}
this指向window对象
题目3
原型和面向对象
创建对象
1 工厂方式
1 | function createPerson(name, age, job){ |
工厂模式虽然解决了创建 多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)
2构造方法
1 | function Person(name, age, job){ |
构造的对象属于同一类型1
2
3
4
5
6
7alert(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 | function Person(){ } |
所有对象可以通过引用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]]的情形下(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
6Person.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 | function Person(name, age, job){ //属性 |
原型对象方法sayName在第一次调用构造方法Person时执行。
寄生构造方法模式
1 | function Person(name, age, job){ |
注意这种方式不能依赖 instanceof 操作符来确定对象类型
稳妥构造函数模式
1 | function Person(name, age, job){ //创建要返回的对象 |
对象实例只能调用闭包内函数以返回name,而无法直接访问任何属性
原型链和继承
原型链
假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。1
2
3
4
5
6
7
8
9
10
11
12
13function 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,这个继承也是通过原型链实现的。
两个问题缺点:父类包含的引用类型属性,随实例共享到子类的所有实例,这可能并不是我们期望的;子类原型引用的是父类实例,不能向超类型的构造函数中传递参数(在ES6中可以在子类constructor中通过超类型super调用父类方法)。
子类原型对象的constructor指向父类构造方法
借用构造方法constructor stealing
1 | function SuperType(){ |
传参1
2
3
4
5
6
7
8
9
10function 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 | function SuperType(name){ |
在注@这一行,将原型对象的构造方法指向到SubType,因为之前该对象根据继承关系图示,作为SuperType实例,其[[prototype]]指向SuperType.prototype,故而SubType.prototype.constructor原先为SuperType方法。
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。而且,instanceof 和 isPrototypeOf()也能够用于识别基于组合继承创建的对象。
原型式继承
浅复制方法1
2
3
4
5function object(o){
function F(){}
F.prototype = o;
return new F();
}
关于ES5 Object.create();
操作符instanceOf1
2
3
4
5
6
7
8
9
10
11function 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
19function 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
17var 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
10let 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
2let 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等语法