1.js事件轮询机制
事件轮询
JS是单线程的所以执行任务需要排队,但这样会影响页面加载,
所以JS选择使用异步。添加了一个消息队列,将一些需要等待的事件放到消息队列里,先执行那些可以直接进行的操作,之后不停的去去问消息队列,有没有任务可以执行呀,有没有任务可以执行呀。如果有的话,就把需要执行的操作取出来放到主线程里执行,如果没有的话则继续之前的操作,这也就是js的事件轮询
console.log('start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
console.log('end')
输出结果为:“start” , “end” , “setTimeout”
执行过程
1.所有任务都在主线程上执行,形成一个执行栈。
2.所有任务都在主线程上执行,形成一个执行栈。
3.如果执行栈中的所有同步任务执行完毕,js就会读取消息队列中的异步任务,如果有可以执行的任务就把他放入执行栈中并开始执行。
4.主线程不断重复上面的第三步,这样的一个循环称为事件循环。
宏任务:
- setTimeout,setInterval,setImmediate,
- I/O
- UI rendering
微任务:
- promise
- process.nextTick
- MutationObserver(html5新特性)
实例:
console.log('script start')
setTimeout(function(){
console.log('setTimeOut')
}, 0)
new Promise(function(resolve){
console.log('promise1')
resolve()
}).then(function(){
console.log('promise2')
})
console.log('script end')
输出结果:script start’, ‘promise1’, ‘script end’ , ‘promise2’ ,‘setTimeOut’
注:(promise里的是同步代码,.then里的才是异步的)
执行过程
(些文章,也把同步任务视为宏任务,这时的执行就是宏任务 => 微任务 => 宏任务 => 微任务……的循环(其实对执行顺序而言并没有差异))
1、所有任务都在主线程上执行,形成一个执行栈。
2、主线程发现有异步任务,如果是微任务就把他放到微任务的消息队列里,如果是宏任务就把他放到宏任务的消息队列里。
3、执行栈所有同步任务执行完毕。
4、执行微任务队列,之后再执行宏任务队列。
5、轮询第4步。
2.vue中this.nextTick
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法Vue.nextTick一样,不同的是回调的 this 自动绑定到调用它的实例上.
(简单的说就是js是单线程的 他的执行有执行的队列,有些时候数据更新后 并不一定会发生dom更新 这时候你对更新数据的dom进行操作是不会有用的 因为他还未更新当你更新完数据立刻使this.nextTick就会将该操作延迟 等到发生更改的dom更新完成在执行。此时才有效。)
Object.keys() 方法
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
getFoo: {
value: function () { return this.foo; }
}
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']
3.toFixed() 方法
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字
NumberObject.toFixed(num)
//例如
var percent = 74.444555
console.log(percent.toFiexd(2))
输出为:74.44
num:规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替。
17种正则表达式
7.17种正则表达式
复制代码代码如下:
"^//d+$"//非负整数(正整数 + 0)
"^[0-9]*[1-9][0-9]*$"//正整数
"^((-//d+)|(0+))$"//非正整数(负整数 + 0)
"^-[0-9]*[1-9][0-9]*$"//负整数
"^-?//d+$"//整数
"^//d+(//.//d+)?$"//非负浮点数(正浮点数 + 0)
"^(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*))$"//正浮点数
"^((-//d+(//.//d+)?)|(0+(//.0+)?))$"//非正浮点数(负浮点数 + 0)
"^(-(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*)))$"//负浮点数
"^(-?//d+)(//.//d+)?$"//浮点数
"^[A-Za-z]+$"//由26个英文字母组成的字符串
"^[A-Z]+$"//由26个英文字母的大写组成的字符串
"^[a-z]+$"//由26个英文字母的小写组成的字符串
"^[A-Za-z0-9]+$"//由数字和26个英文字母组成的字符串
"^//w+$"//由数字、26个英文字母或者下划线组成的字符串
"^[//w-]+(//.[//w-]+)*@[//w-]+(//.[//w-]+)+$"//email地址
"^[a-zA-z]+://(//w+(-//w+)*)(//.(//w+(-//w+)*))*(//?//S*)?$"//url
正则表达式详情
// 1.利用RegExp构建正则表达式
// var regexp = new Regexp (/表达式/)
var regexp = new Regexp (/123/)
//2.利用字面量创建
var reg = /123/;
// 3.检测正则表达式规范test
regExpObj.test(str)
// 1.regExpObj正则表达式
// 2.str 我们要测试的文本
// 3. 就是检测str文本是否符合我们写的正则表达式规范
reg.test(123) // true
reg.test(abc) // false
//5.正则表达式里得特殊字符
// 5.1边界符号 ^, $
// ^表示匹配行首得文本以谁开始
// $表示匹配行尾得文本以谁结束
// 若^ 和 $ 同时出现哎i你则表示 必须 必须是精确匹配 重复出现也不可以
var rg = /abc/ //正则表达式不需要加引号
// 只要包含abc这个字符串返回的都是true
rg.test('abc') //true
rg.test('abcd') //true
rg.test('aabcd') // true
console.log('-----------------')
var reg1 = /^abc/ //表示 必须以a开头
reg1.test('abc') //true
reg1.test('abcd') //true
reg1.test('aabcd') // true
var var reg2 = /^abc$/ //表示 必须是abc字符串才符合规定
reg2.test('abc') //true
reg2.test('abcd') //false
reg2.test('aabcd') // false
reg2.test('abcabc') // false
// 5.2 [] 表示有一系列的字符可以供选择,只要匹配其中的一个就可以了
var reg3 = /[abc]/
reg3.test('aqqq') // true
reg3.test('baby') // true
reg3.test('ecop') // true
reg3.test('red') // false
var reg4 = /^[abc]$/ // 表示三选一, 只有a, 或者b 或者c z这三个字母才能返回true
reg4.test('a') //ture
reg4.test('b') //ture
reg4.test('c') //ture
reg4.test('aa') //false
reg4.test('abc') //false
reg4.test('dcb') //false
// 5.3 [-] 表示在这个范围内有任何一个都可以
var reg5 = /^[a-z]$/ // a-z 26个字母范围内任何一个字符都可以
reg5.test('a') //ture
reg5.test('b') //ture
reg5.test('c') //ture
reg5.test('A') //false
reg5.test('1') //false
// 字符组合
var reg6 = /^[a-zA-Z0-9_-]$/; //26个英文字母(大写和小写都可以)或者事 _ 或者是- 任何一个字母 返回 true 只能多选一
reg6.test('a') //ture
reg6.test('2') //ture
reg6.test('_') //ture
reg6.test('A') //ture
reg6.test('!') //false
reg6.test('aa') //false
var reg7 = /^[^a-zA-Z0-9_-]$/; //如果中括号[]里面有^表示取反的的意思,不能和边界符^混淆,和上边正好是相反 有任何一个都是false
reg7.test('a') //false
reg7.test('2') //false
reg7.test('_') //false
reg7.test('A') //false
reg7.test('!') //true
// 5.4 量词符: 用来设定某个模式出现得次数
// 简单理解: 或是让下面这个a 这个字符重复出现多少次
// 量词符 * :相当于 >=0 可以出现0次或者很多次
var reg8 = /^a*$/
reg8.test('a') //true
reg8.test('') //ture
// 量词符 + :相当于 >=0 可以出现1次或者很多次
var reg9 = /^a+$/
reg9.test('a') //true
reg9.test('') //fasle
// 量词符 ? :相当于 出现 1次或者 0次
var reg10 = /^a?$/
reg10.test('a') //true
reg10.test('') //ture
reg10.test('aaaaa') //false
// 量词符 {3}: 重复出现3次
var reg11 = /^a{3}$/
reg10.test('aaaaa') //false
reg10.test('aaa') //ture
// 量词符 {3,}: 重复出现大于等于3次
var reg11 = /^a{3,}$/
reg10.test('aaaaa') //true
// 量词符 {3,16}: 重复出现大于等于3次小于等于16次
var reg11 = /^a{3,16}$/
// 6.用户名限制
// 只能输入英文字母数字下划线 短横线 , 量词中间不要有空格{6,16}
var reg12 = /^[a-zA-Z0-9_-]{6,16}$/
reg12.test('a') //false
reg12.test('0') //false
reg12.test('andy_001') //true
reg12.test('Andy-99') //true
reg12.test('andy!19') // false
// 7.[] : 字符集 匹配方括号中的任意字符
// {} : 量词符里面表示得重复次数
var reg13 = /^abc{3}$/ //表示 只让c重复三次
reg13.test('abc') //false
reg132.test('abcabcabc') //false
reg13.test('abccc') // true
// (): 表示优先级
var reg13 = /^(abc){3}$/
reg13.test('abcabcabc') //true
// 8. 座机号码验证 : 两种模式 010-12345678 0426-9999328
// 正则里面得 或者 符号 |
var reg14 = /^\d{3}-\d{8}| \d{4}-\d{7}$/
// 或者
var reg15 = /^\d{3,4}-\d{7,8}$/
// 9.正则表达式参数
// g:全局匹配
// i:忽略大小写
// gi:全局匹配+忽略大小写
/表达式/g
构造函数 原型对象(prototype) 和 对象的原型( __ proto __)
每一个对象都有 __ proto __ 属性 它指向的是构造函数中的原型对象 prototype 所以 构造函数的原型对象中的方法 实例对象也能使用
- { } -》propto -》 构造函数的prototype (prototype 是个对象 内部装着 可以共同使用的 方法)
对象的原型 等价于 构造函数的原型对象
JSON.parse 与 JSON.stringfy得用处
1。可以实现对象深拷贝
//深拷贝
function deepClone(data) {
let _data = JSON.stringify(data),
dataClone = JSON.parse(_data);
return dataClone;
};
//测试
let arr = [1,2,3],
_arr = deepClone(arr);
arr[0] = 2;
console.log(arr,_arr)//[2,2,3] [1,2,3]
2.判断数组是否包含某对象,或者判断对象是否相等
//判断数组是否包含某对象
let data = [
{name:'echo'},
{name:'听风是风'},
{name:'天子笑'},
],
val = {name:'天子笑'};
JSON.stringify(data).indexOf(JSON.stringify(val)) !== -1;//true
//判断两数组/对象是否相等
let a = [1,2,3],
b = [1,2,3];
JSON.stringify(a) === JSON.stringify(b);//true
3.让localStorage/sessionStorage可以存储对象。
//存
function setLocalStorage(key,val){
window.localStorage.setItem(key,JSON.stringify(val));
};
//取
function getLocalStorage(key){
let val = JSON.parse(window.localStorage.getItem(key));
return val;
};
//测试
setLocalStorage('demo',[1,2,3]);
let a = getLocalStorage('demo');//[1,2,3]
Object.assign() 是浅拷贝还是深拷贝?
1.如果对象的属性值为简单类型(如string, number),通过Object.assign({},srcObj);得到的新对象为深拷贝;如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。
实现深拷贝的几种方法
1.JSON.stringify 和 JSON.parse如上
2.Object.assign()深拷贝简单数据类型(number, string)
3.lodash.cloneDeep()实现深拷贝
let _ = require('lodash');
let obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
let obj2 = _.cloneDeep(obj1);
4.使用递归的方式实现深拷贝
function isDeepClone (source){
let target
if(source.typeof === 'object'){ //如果是对象的话
target = Array.isArray(source)? []: {} //因为数组也是对象 所以 判断是对象后还要继续判断 是不是数组
for(let key in source){
if(source.hasOwnProperty(key){ //用来检测 source 是否存在 key 这个属性 返回值是 布尔值 如果有key 则继续向下判断
if(source[key].typeof === 'object'){ 判断内部是否还有 引用数据类型 有的话 继续重复进行 深拷贝的方法
isDeepClone(source[key])
}else{
target[key] = source[key]
}
}
}
} else{
target = source
}
return target
}
箭头函数没有 this 指针 如果调用箭头函数 出现this 则该this 为该箭头函数定义位置上下文的this
async await
async函数会返回一个promise,并且Promise对象的状态值是resolved(成功的)
1.如果你没有在async函数中写return,那么Promise对象resolve的值就是是undefined
function log(time){
setTimeout(function(){
console.log(time);
return 1;
},time)
}
async function fun(){
let a = await log(1000);
let b = await log(3000);
let c = log(2000);
console.log(a);
console.log(1)
}
fun();
// 立即输出 undefined 1
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000
会立即输出得原因就是 log()函数没有return
2.如果你写了return,那么return的值就会作为你成功的时候传入的值
// 使用async/await获取成功的结果
// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('获取成功')
},3000)
})
}
async function test(){
let a = await getSomeThing();
console.log(a)
}
test(); // 3秒后输出:获取成功
3.如果asycn里的代码都是同步的,那么这个函数被调用就会同步执行
async function fn(){
console.log('a')
}
fn()
console.log('b')
//a
//b
promise.all()
可以并发执行多条异步请求,节约时间
promise.all()该方法用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1,p2,p3]);
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
判断数据类型
typeof只能判断基本数据类型 ’string’,number, boolean, object,undefined,对于复杂的数据类型都只返回 object
此时我们可以使用Object.prototype.toString.call() 方法判断具体得数据类型 返回值是一个数组形式
Object.prototype.toString()本身是允许被修改的,而我们目前所讨论的关于Object.prototype.toString()这个方法的应用都是假设toString()方法未被修改为前提的。
1.基本数据类型
Object.prototype.toString.call(null);// ”[object Null]”
Object.prototype.toString.call(undefined);// ”[object Undefined]”
Object.prototype.toString.call(“abc”);// ”[object String]”
Object.prototype.toString.call(123);// ”[object Number]”
Object.prototype.toString.call(true);// ”[object Boolean]”
2.原生引用类型
2.1 函数数据类型
function fn(){
console.log('nihao')
}
Object.prototype.toString.call(fn) //[object, 'Function']
2.2 数组数据类型
var arr = [1,2,3];
Object.prototype.toString.call(arr);//”[object Array]”
2.3 日期类型
var date = new Date();
Object.prototype.toString.call(date);//”[object Date]”
2.4正则表达式
var reg = /[hbc]at/gi;
Object.prototype.toString.call(arr);//”[object Array]”
2.5 自定义类型
function Person(name, age) {
this.name = name;
this.age = age;
}
var person = new Person("Rose", 18);
Object.prototype.toString.call(arr); //”[object Object]”
3. 判断原生JSON 对象
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON);
console.log(isNativeJSON);//输出结果为”[object JSON]”说明JSON是原生的,否则不是;
web会话跟踪的几种方式(cookie)
1.token
2.cookie
3.session
4.URL重写
5.隐藏表单项
1.token 无状态的,是多用户下处理认证的最佳方式
当第一次客户端向服务器发起请求时,服务器端生成一串字符串,并将这个字符串作为一个令牌发放给客户端,当客户端再次发起请求时只需带上token 即可不需要再次带上用户名和密码
最简单的 token 组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,使用 hash/encrypt 压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。
2.cookie
是服务器生成的,以键值对的形式 存放在某个目录下的文件中,发放给浏览器,当浏览器再次向服务器发起请求时被携带并发送给服务器。由于cookie是存在客户端的 所以为防止恶意使用,每个域的cookie 数量有限
缺点:
1.每个特定域名下cookie数量有限
2.大小有限制只能是4kB
3.每次http都需要发送到服务器端,影响获取资源的效率
session :有状态的
存储于服务器或者硬盘中,可以理解为一个状态列表,拥有一个唯一识别符号 sessionID ,客户端通常存放于 cookie 中。服务器收到 cookie 后解析出 sessionID ,再去session列表中查找,找到相应的session,如果cookie被禁 也可以放在url中,
用户认证流程
1.第一次客户端请求服务器,服务器根据用户信息创建session并返回对应的sessionID给浏览器
2.浏览器接收sessionID并把它存到cookie中,并同时记录session属于哪个域名
3.客户端再次向服务器端发起请求时 ,请求会自动判断此域名下是否有cookie信息,如果存在会把cookie信息发给服务端,然后服务端会在Cookie中获取sessionID,在根据sessionID在状态列表中找到对应的session信息,没找到就说明该用户没有登录或者登录失效,找到了则证明当前用户已登录可执行后面的操作。
4.如果web服务器做了负载均衡,那么下一个操作请求到了另外的一个域名 则session 会丢失
URL重写
在url的尾部添加一些额外的数据,这些数据标识当前的会话,服务器将这些数据与它存储的相关数据关联起来。即使在浏览器不支持Cookie的情况下,这种方案也能正常工作
1、必须对所有指向本 Web 站点的 URL 进行编码。
2、所有页面都必须动态生成。
3、不能使用预先记录下来的URL进行访问,或者从其他的网站链接进行访问。
4、用户 ID 及登录密码等重要信息可能以参数的形式暴露在URL上,造成安全隐患。
5、大部分的 URL 的长度有限制,不能传送大量的数据。
session 与cookie的区别
存储位置: session存储在服务器端,cookie 存储在客户端
安全性: session 比cookie更安全,别人可以分析存放在本地的cookie进行cookie欺骗
存储大小:cookie的数据不能超过4kB,浏览器对同一个站点有cookie数量限制,session 中存储的数据远高于cookie
存储类型不同:cookie支持字符串类型,想要设置其他类型 需要进行转换,session 可以存储任意类型
session会在一定时间内保存在服务器上当访问增多时 会比较占用服务器的性能 ,所以可以把登陆的信息存放在session上,其他信息如果需要保存可以存放在cookie里
节流和防抖得区别
防抖:当事件被触发后 延迟n秒后在执行 如果在这n秒中又被触发,则重新计时,等同于 游戏中的回城
1.使用场景–输入框是按键触发请求时
用户连续输入一串字符时,可以通过防抖策略,只有在输入完后, 在执行请求 ,减少请求次数,节约请求资源
var time = null
//定义一个要发生的数据
getdata(data){
//data是发送的数据
//发送请求
}
//防抖的timer
//定义防抖的函数,调用这个函数后,会设置一个定时器
//request是发送请求的函数。
function fn(keword,request){
//keword是你要传递的参数
//request是你发送请求的函数名
//开启定时器后,会返回一个定时器的id,可以接受这个id,清除的时候也是根据这个id来清除的。
time = setTimeout({
//发送请求
request(keword)
},500)
}
input.keyup = function(){
clearTimeout(timer)
fn(keywords,getdata)
//keywords是用户输入的值
}
//给输入框绑定事件
//按键抬起后触发, 先清除定时器,
节流:就是 减少一段时间内事件的触发频率,当按键触发后 一段时间内 输入或触发都不会在执行, 即只触发第一次得事件,只有等这个事件执行完后才能执行下一次事件
1.使用场景—鼠标连续点击触发事件
2.懒加载时要监听计算滚动条得位置
//vue中的场景,time是data里的变量
cleartime(){
//第一步是清除定时器,
clearTimeout(this.time)
//第二部要把存定时器的值清空,不然还存着定时器的那个id
this.time = null
},
//请求函数
getdata(){
//如果定时器存在,则退出函数
if(this.time){
return
}
//不存在就创建一个定时器
this.time = setTimeout(()=>{
//用箭头函数this是vue
//不用箭头函数,this是window
//执行你的逻辑。
//之后执行清空定时器函数在上面
this.cleartime()
},3000)
}
节流和防抖得区别
防抖相当于游戏中得回城 点击后延迟一段事件在触发请求 如果多次触发取最后一次触发开始延迟一定时间后在发起请求
节流相当于游戏中的释放技能,触发后立刻发起请求,然后间隔一段时间内不在发起 就算点击也不再发起请求,单位i时间过去后在次点击才触发第二次请求
本地存储 localStorage 与 sessionStorage
区别:
- 生命周期不同
localstorage:生命周期为永久生效,可以多个窗口共享
sessionstorage: 生命周期为关闭浏览器窗口,在同一窗口下数据可以共享,
2.存储容量上:
localStorage约为20M
sessionStorage: 约为5M
3.在数据共享上
localStorage:同一个浏览器多个窗口(页面)数据可共享
sessionStorage:同一浏览器 同一窗口(页面)数据可共享,窗口关闭 sessionStorage 生命周期结束