new实现过程及源码
- 创建一个空对象
- 将空对象的隐式原型(_proto_)连接到构造函数的原型上
- 执行构造函数
- 返回这个对象
function myNew(fun){
retrun function(){
//创建一个空对象,并将其隐式原型指向构造函数的原型
let obj ={
__proto__ = fun.protoType
}
//执行构造函数
fun.call(obj,...arguments)
//返回该函数
return obj
}
}
function person(name,age){
this.name = name
this.age = age
}
console.log(myNew(person)('zs',13)) //{name:'zs',age:13}
call apply bind 的理解与区别
- call apply bind 都是函数的方法,所以需要函数调用
- call 和apply 都是改变this的指向,而bind 式绑定this的指向,然后放回一个新函数
- bind 返回对应的方法不会立即执行,调用时才执行,call 和apply是立即执行对的
let dog = {
name: '狗',
say() {
console.log('我是:' + this.name)
},
eat(food) {
console.log('喜欢吃:' + food)
}
}
let cat = {
name: '猫'
}
dog.say.call(cat)
dog.eat.call(cat, '鱼')
call 的第一个参数:指的是this指向的对象,接下来的参数是需要传递的参数
apply的第一个参数:指的是this指向的对象,接下来的第二参数是一个参数的集合
bind的第一个参数:指的是this指向的对象,接下来的参数是需要传递的参数
对数组操作的几种常见方法及其返回值
改变原数组的方法(7个):
-
pop(): 删除一个数组中的最后的一个元素,并返回这个元素
-
shift(): 删除数组的第一个元素,并返回这个元素
-
push(): 向数组的末尾添加元素,并返回新的长度
-
unshift(): 向数组的开头添加一个或更多元素,并返回新的长度
-
reverse(): 颠倒数组中元素的顺序
-
splice(): 添加/删除数组元素,然后返回被删除的项目
-
sort(): 数组排序,并返回这个数组
不改变原数组的方法(8个)
ES5: join、slice、cancat、indexOf、lastIndexOf、
ES7: includes
- join(): 数组转字符串
- slice(): 浅拷贝数组的元素,选择的数组的一部分浅拷贝到一个新数组对象
- cancat(): 方法用于合并两个或多个数组,返回一个新数组
- indexOf(): 查找数组是否存在某个元素,返回下标
- lastIndexOf(): 查找指定元素在数组中的最后一个位置
- includes(): 查找数组是否包含某个元素 返回布尔
遍历方法(12个):
js中遍历数组并不会改变原始数组的方法总共有12个:
ES5:forEach、every 、some、 fliter、map、reduce、reduceRight、
ES6:find、findIndex、keys、values、entries
闭包
-
什么是闭包:由于变量作用域的关系,一个函数外部是无法访问一个函数内部的变量。当一个函数能够访问到另一个函数内部的变量的时候,我们就称这个函数形成了闭包
-
优点:能够延长变量的生命周期,可以重复利用变量,并且还不会造成变量的污染
-
缺点:会造成内存泄漏
-
常用闭包的三种方式:
- 返回值:最常用的一种形式是函数作为返回值被返回
- 函数赋值:一种变形的形式是将内部函数赋值给一个外部变量
- 函数参数:闭包可以通过函数参数传递函数的形式来实现
//第一种:返回值 最常用的一种形式是函数作为返回值被返回 var F1 = function () { var b = 'local'; var N = function () { return b; } return N; } console.log(F1()()); //第二种:函数赋值 一种变形的形式是将内部函数赋值给一个外部变量 var inner; var F2 = function () { var b = 'local'; var N = function () { return b; }; inner = N; }; F2(); console.log(inner()); //第三种: 函数参数 闭包可以通过函数参数传递函数的形式来实现 var Inner = function (fn) { console.log(fn()); } var F3 = function () { var b = 'local'; var N = function () { return b; } Inner(N); } F3();
垃圾回收机制
浏览器的垃圾回收机制是利用了V8引擎的垃圾回收机制
主要就是将内存划分为两个区域,新生代和老年代
新生代:
大多数对象开始都会被分配到这个这里,新生代空间比较小,在64位的操作系统下,应该只有64兆,在32位的系统下只有32兆。
新生代的垃圾回收机制主要采用scavenge[ˈskævəndʒ] 算法,这种算法是一种典型的牺牲空间换取时间的算法。它将新生代内存一分为二,其中一块是放着存活的对象,是semi [ˈsemi] space from ,另一块是空闲的,是 semi space to。当from 空间快满的时候,会把from 空间的存活的对象打上标记 copy到to空间,销毁失活的对象。当复制完成之后,from 空间和 to 空间完成了一次角色对换。旧的from 空间变成新的to 空间,旧的to 空间,变成新的from空间。
老年代:
老年代的空间就比较到,在64位的操作系统里面是1400兆,在32位操作系统里面是32兆。这里面的对象的生命周期比较长。在新生代里面经过一次循环的,并且空间占to内存25%的对象。
老年代采用的是Mark-Sweep(标记清除)和Mark-Compact(标记整理)来进行管理。
过程:标记清除阶段会遍历所有的对象,并标记活着的对象,在随后的清除阶段,只销毁没有被标记的对象。这里会有一个问题就是:这里在销毁失活的对象的之后,内存空间会存在不连续的状态。对后续的内存分配会造成问题。所以此时就需要用到标记整理,标记整理会在整理的过程中,将存活的对象往一端移动,移动完成之后,直接清理掉边境外的内存。
跨域
-
什么是同源策略?
同源策略是浏览器最基本的安全策略。同源也就是指协议、域名、端口都相同。
-
什么是跨域?
跨域就是指协议、域名、端口中一个或者都不相同。
-
怎么解决跨域?
-
jsonp 的方法: 动态创建script标签,,利用script 标签中的src 不受同源策略的影响,将src指向第三方的api网址,并提供一个回调函数来接受数据。
缺点:jsonp 只能支持get请求,不能支持post请求
-
反向代理:Proxy 因为同源策略只存在浏览器端,但是不在服务器端。此时我们只需配置一个代理服务器,代理服务器的协议、域名、端口和我们的浏览器相同,利用代理服务器和目标服务器直接发送请求,解决跨域问题。
vue2.0 的项目我们都是在vue.confign.js设置devserve 中proxy来配置反项代理
-
CORS:在服务器端设置响应头中
Access-Control-Allow-Origin
为*
-
事件循环(EventLoop)
因为js是单线程运行的,所以js会先在执行栈中运行同步代码,当js遇到异步代码的时候,就会丢给浏览器,浏览器是多线程的,可以同时运行多个程序,当浏览器发现异步代码要运行的时候,就会丢到事件队列里面,事件队列里面有宏任务和微任务的区别。当js主线程的代码执行完之后,就会去执行事件队列里面的任务。
微任务:promise.then process.nextTick(node环境下的)
宏任务:setInterval setTimeout setImmediate
执行顺序:同步代码 process.nextTick 微任务 宏任务 setImmediate
浏览器渲染机制
- 浏览器会根据html 和css 构建DOM树和CSSOM (css样式表)
- 然后根据DOM树和CSSOM生成渲染树(Render树)
- 根据render树进行布局,定位坐标大小,是否显示隐藏,确定是否换行等
- 然后调用浏览器底层的api,使页面展示在屏幕上
防抖(debounce)
防抖:解决高频率事件,相当于延迟触发,也就是说事件处理函数在一段时间之后触发
应用场景:
- 搜索框输入查询
- 按钮提交事件
简版
function debounce (fn,delay){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.call(this,...arguments)
})
}
}
复杂版:
function debounce(fun, wait, immediate) {
let timeout, result
debounced = function () {
//清除定时器
clearTimeout(timeout)
//解决this指向问题
let _this = this
//解决event(事件对象) 指向问题
let args = arguments
if (immediate) {
//立即执行
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) result = fun.apply(_this, args)
} else {
//不会立即执行
timeout = setTimeout(function () {
fun.apply(_this, args)
}, wait)
}
return result
}
debounced.cancel = function () {
clearTimeout(timeout)
timeout = null
}
return debounced
}
节流(throttle)
节流:解决高频率事件,相当于稀释事件频率,也就是说事件处理函数在一段时间之内只触发一次
应用场景:
-
DOM元素的拖拽功能实现
-
监听scroll滚动事件
-
点赞功能,一秒钟只能执行一次
function throttle(fn, delay) { let flag = true return function () { if (flag) { setTimeout(() => { fn.call(this, ...arguments) //因为是setTimeout是宏任务,所以会此处的代码会比下面的flag = false 晚执行 flag = true }, delay) } flag = false } }
浏览器缓存(http 缓存)
最大的区别就是判断缓存命中时,浏览器是否需要向服务器进行询问,来协商缓存的相关信息
强制缓存
最早的时候时利用exprise,来判断缓存是否过期哦,如果没有就直接复用,但是这种方式会有问题,就是exprise 时时间戳,会存在服务器的时间和客户端的时间不同步的情况。所以推荐是使用max-age 来控制。这是只滑动的时间
cache-control:max-age-3153600
协商缓存
协商缓存,就是指在使用本地缓存之前,需要向服务器发送一次get请求,与之协商当前浏览器保存的缓存是否过期,如果没有过期,服务器会返回304,如果过期了,则会返回最新的数据回来。
协商缓存是通过响应头中etag和last-modified进行控制的
last-modified:上一次修改时间
etag:资源对应的唯一字符串