共计 10208 个字符,预计需要花费 26 分钟才能阅读完成。
具体内容能够查看笔记
1、节流 throttle 和 防抖 debounce
防抖和节流的作用:当某一个操作被用户频繁触发时,例如 window 的 onresize,input 的 oninput 等,应用防抖和节流能无效管制 callback 的触发频率
应用形式:input.addEventListenter(“input”,debounce(handle,delay))
防抖
const debounce = (callback,delay)=>{
let timer = null
return function(){if(timer != null) clearTimeout(timer)
timer = setTimeout(()=>{callback.apply(this,arguments)},delay)
}
}
节流
形式(1)
const throttle = (callback,delay)=>{let prev = Date.now()
return function (){let now = Date.now()
if(now-prev>delay){callback.apply(this,arguments)
prev = Date.now()}
}
}
缺点:必须期待 wai 工夫能力首次点击,因为 throttle()初始化了 prev 工夫,若此时点击,则 now-prev<wait,不会执行 callback。并且,进行点击后,不肯定会触发之前的 callback
形式(2)
const throttle = (callback,delay)=>{
let timer = null
return function(){if(!timer){setTimeout(()=>{callback.apply(this,arguments)
timer = null
},delay)
}
}
}
缺点:首次点击必然会执行,但要期待 wai 工夫才有成果,因为是 setTimeout 定时器,并且在定时器正在执行中的时候,因为 timer=null,此时间断触发的话,会产生多个定时器
综合两者
const throttle = (callback, delay) => {let prev = Date.now()
let timer = null
return function () {let now = Date.now()
let surplus = delay - (now - prev)
clearTimeout(timer)
if (surplus <= 0) {callback.apply(this, arguments)
prev = Date.now()} else {timer = setTimeout(() => {callback.apply(this, arguments)
}, surplus)
}
}
}
2、获取元素行间款式
dom.currentStyle.left || getComputedStyle(dom,null).left
备注:getComputedStyle 第二个参数,用来获取伪元素的属性,例如 getComputedStyle(dom,”after”).width
3、元素地位 offsetWidth 等属性
一个元素 div,通常有 width,padding,margin,border
dom.clientWidth : width + padding
dom.offsetWidth : width + padding + border
dom.offsetLeft : 绝对于定位父级 (position:relative) 的 left 值
dom.offsetParent : 定位父级元素(通常状况下,要 dom.offsetParent.tagName 去拿到父级的 tagName)
height 与 width 雷同
4、节流优化 resize
(function () {var throttle = function (type, name, obj) {
obj = obj || window;
var running = false;
var func = function () {if (running) {return;}
running = true;
requestAnimationFrame(function () {obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
throttle("resize", "optimizedResize");
})();
window.addEventListener("optimizedResize", ()=>{});
5、原型、原型链
如果看不到图片:请先点击参考链接再回到本页面刷新就有图片了
原型的概念
每个 javascript 对象(null 除外)创立的时候,就会给它关联一个对象,这个对象就是咱们说的原型,每个对象都会从原型中继承属性。
(1)每个函数都有 prototype,指向函数的原型(这玩意就是函数的原型),原型也是一个对象
(2)每个对象都有__proto__,对象的原型(用来连贯实例和构造函数),值为:构造函数的 prototype。(构造函数的 prototype,不就是构造函数的原型嘛,所以这玩意也是构造函数的原型)
function Parent(){}
const parent1 = new Parent()
parent1.__proto__ = Parent.prototype
(3)constructor:每个原型都有一个 constructor 属性,指向关联的构造函数
function Parent(){} // 构造函数
Parent.prototyoe.constructor = Parent //Parent.prototype 是构造函数的原型
之前有一个误区:
function Person(){}
const person1 = new Person()
console.log(person1.constructor) //Person
其实 person1 并没有 constructor 这个属性,因而,去它的原型__proto__下面找,也就是构造函数 Person.prototype 下面找。正好 Person.prototype.constructor = Person
(4)instanceof:判断是否是实例
function A() {}
const a = new A()
console.log(a instanceof A) //true
a.__proto__ = {}
console.log(a instanceof A) //false
从这个例子能够看出:a 和 A 要在同一个原型链上,a instanceof A 才为 true,扭转了 a 的原型__prpto__之后,就为 false 了
原型链
之前说了,原型(构造函数的 prototype)也是一个对象。既然是对象,它也有原型。
原型的构造函数是 Object
因而,(构造函数.prototype).__prpto__ = Object.prototype // (构造函数.prototype)是一个对象
最初:js 规定,Object.prototype 的原型指向 null。
因而原型链如下图
例题:
Function.prototype.a = 1;
Object.prototype.b = 2;
function A() {}
var a = new A();
console.log(A.a, A.b); // 1 2
console.log(a.a, a.b); // undefined 2
console.log(Function.b, Object.a); // 2 1
6、手写一个 new 函数
new 次要实现了 4 个操作:
(1)创立一个空对象
(2)链接原型
(3)绑定 this
(4)返回一个对象
function create() {
const args = arguments
const obj = new Object() // 翻新空对象
const cradleFn = [].shift.call(args) // 拿到构造函数
obj.__proto__ = cradleFn.prototype // 链接原型
const result = cradleFn.apply(obj, args) // 绑定 this
return (typeof result == "object" ? result : obj)
}
7、new 构造函数返回值问题
new 总是会返回对象,如果构造函数外面有 return {},则返回这个{},否则返回 this 实例
function A(){return 123}
const a = new A() // a 为{}
function B(){return {name:123}
}
const b = new B() // b 为{name:123}
8、如何判断一个函数是否由 new 调用
(1)instanceof
function A(){if(this instanceof A){}}(2)new.target
function B(){if(new.target === B){}}
9、继承
(1)call 办法
function Parent(){
this.name = "zhang"
this.age = 35
}
function Child(){Parent.call(this)
this.age = 15
}
const son = new Child()
console.log(son.name) //zhang
console.log(son.age) //15
毛病:无奈继承 Parent.prototype 上的值
(2)原型
function Parent(){
this.name = "zhang"
this.arr = [1,2,3]
}
function Child(){this.age = 15}
Child.prototype = new Parent()
毛病:this.arr 是专用的,一旦其中一个实例更改了 arr,其余实例的 arr 属性也会更改
(3)call、原型组合形式
function Parent(){this.arr = [1,2,3]
this.name = "zhang"
}
function Child(){Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype) // 关键点
10、call、apply 惯例应用
Math.max.apply(null,[2,5,3,7,1])
Array.prototype.slice.call({1:”zhang”,2:true,length:5}) // 类数组(有 length 属性),转真正数组
11、浏览器同源策略
软大神的具体文档
同源:协定 (http)、域名(www.xx.com)、端口号(:8080) 必须都雷同
阮大神说:
非同源时,次要三种行为受到限制
(1)Cookie、LocalStorage 和 IndexDB 无奈读取
(2)DOM 无奈取得
(3)AJAX 申请不能发送
12、回流(重排)与重绘
参考资料
回流:当浏览器必须重新处理和绘制局部或全副页面时,回流就会产生。我了解为,浏览器依据 renderTree 计算出元素盒子的几何信息(地位、大小)
重绘:依据回流计算的几何信息,转换成理论像素
何时触发回流:
- 增加或删除可见的 DOM 元素
- 元素的地位发生变化
- 元素的尺寸发生变化(包含外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比方文本变动或图片被另一个不同尺寸的图片所代替
- 页面一开始渲染的时候(这必定防止不了)
- 浏览器的窗口尺寸变动(因为回流是依据视口的大小来计算元素的地位和大小的)
留神:回流肯定会触发重绘,而重绘不肯定会回流
获取哪些属性会触发回流重绘:
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- getComputedStyle
- getBoundingClientRect
如何缩小回流重绘:
1、应用 style.cssText 和 className
- el.style.cssText += ‘border-left: 1px; border-right: 2px; padding: 5px;’;
- el.className += ‘ active’;
2、批量批改 DOM
(1)使元素脱离文档流
(2)对其进行屡次批改
(3)将元素带回到文档中
3、防止触发同步布局事件(循环中获取触发回流重绘的属性)
function initP() {for (let i = 0; i < paragraphs.length; i++) {paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
4、css3 硬件加速
- transform
- opacity
- filters
13、try{…}catch 捕捉谬误
(1)之前
try{a}catch(e){}
无奈捕捉谬误:a. 在语法检测时就出错了,此时主线程尚未执行 try
(2)之中
try{a.b}catch(e){}
能够捕捉:a.b 未定义
(3)之后
try{setTimeout(()=>{a.b},100)
}catch(e){}
无奈捕捉:try 曾经执行实现了
总结:能被 try catch 捕捉到的异样,必须是在报错的时候,线程执行曾经进入 try catch 代码块,且处在 try catch 外面,这个时候能力被捕捉到。
14、执行上下文、作用域、作用域链
执行上下文:执行上下文是评估和执行 JavaScript 代码的环境的抽象概念,每当 Javascript 代码在运行的时候,它都是在执行上下文中运行(了解:全局 js 代码或者函数在执行之前,会先做两件事:绑定 this,同时创立一个蕴含变量和函数的环境)
执行上下文参考文档
作用域:存储和拜访变量的区域
作用域链:函数执行上下文内,拜访变量时,若不存在,就拜访该函数内部的执行上下文,若还不存在,就持续拜访内部的上下文,始终到全局的执行上下文,这样就造成了一个作用域链
15、for in,Object.keys,Object.getPropertyNames 之间的区别
function Parent() {}
Parent.color = "salmon"
Parent.prototype.pro = "pro"
const obj = new Parent()
obj.name = "zhang"
Object.defineProperties(obj, {
gender: {
value: "male",
enumerable: false
}
})
for (var key in obj) {console.log("key=", key, "value=", obj[key])
}
// key= name value= zhang
// key= pro value= pro
console.log(Object.keys(obj)) //["name"]
console.log(Object.getOwnPropertyNames(obj)) //["name","gender"]
for in 能够拜访本身和原型链上的属性
Object.keys 只能拜访本身可枚举的属性
Object.getOwnPropertyNames 能拜访本身所有属性(可枚举和不可枚举)
16、EventLoop 事件循环、宏工作、微工作
参考文档讲得通俗易懂
17、TCP/IP 三次挥手、四次握手
参考文档
http1.0、2.0、https 参考文档
18、强缓存 vs 协商缓存
参考文档
19、浏览器输出 url 之后产生了什么
参考文档
20、算法
(1)冒泡算法
function bubbleSort(arr) {if (!arr || !arr.length) return
for (let i = 0; i < arr.length - 1; i++) {for (let j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
(2)抉择排序
function selectSort(arr) {if (!arr || !arr.length) return
for (let i = 0; i < arr.length; i++) {for (let j = i; j < arr.length; j++) { // 这里要留神:j = i
if (arr[j] < arr[i]) {[arr[j], arr[i]] = [arr[i], arr[j]]
}
}
}
return arr
}
(3)插入排序
Array.prototype.insertSort = function () {let arr = [].slice.call(this)
for (let i = 1; i < arr.length; i++) { // 留神,这里是 i =1
for (let j = i; j > 0; j--) { // 留神,这里是倒序
if (arr[j] < arr[j - 1]) {[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
} else {break}
}
}
return arr
}
(4)疾速排序
Array.prototype.quickSort = function () {const arr = [].slice.call(this)
if (arr.length < 1) return arr // 递归进口
let left = [],
right = [],
cur = arr.splice(0, 1)
for (let i = 0; i < arr.length; i++) {if (arr[i] < cur) {left.push(arr[i])
} else {right.push(arr[i])
}
}
return [].quickSort.call(left).concat(cur, [].quickSort.call(right))
}
21、堆栈、队列
堆栈:后进先出
队列:先到先得
22、模仿 call、apply、bind
call 办法:
Function.prototype.callFn = function (context) {
context = context || window
context.fn = this
var args = []
for (var i = 1; i < arguments.length; i++) {args.push("arguments[" + i + "]")
}
var result = eval("context.fn(" + args + ")")
delete context.fn
return result
}
apply 办法
Function.prototype.applyFn = function (context, arr = []) {
context = context || window
context.fn = this
var args = []
for (var i = 0; i < arr.length; i++) {args.push("arr[" + i + "]")
}
var result = eval("context.fn(" + args + ")")
delete context.fn
return result
}
bind 办法
Function.prototype.bindFn = function (context) {if (typeof this != "function") {throw new Error("必须是函数")
}
var outerArgs = [].shift.call(arguments)
var self = this
var resultFn = function () {var innerArgs = [].slice.call(arguments)
return self.apply(context, outerArgs.concat(innerArgs))
}
return resultFn
}
23、深拷贝
1、递归:如果数据深度太深,会造成栈溢出
const deepClone = (source) => {const result = Array.isArray(source) ? [] : {}
for (let i in source) {if (typeof source[i] == "object") {result[i] = deepClone(source[i])
} else {result[i] = source[i]
}
}
return result
}
2、循环:只是减少了数据广度,不会造成栈溢出
function catchType(obj) {const type = Object.prototype.toString.call(obj)
return type.substring(8, type.length - 1).toLowerCase()}
function deepCloneStack(obj) {const root = {}
const stack = [{
parent: root,
data: obj
}]
while (stack.length) {const pop = stack.pop()
const {
parent,
data
} = pop
for (let i in data) {if (data.hasOwnProperty(i)) {if (catchType(data[i]) === "object") {parent[i] = {}
stack.push({parent: parent[i],
data: data[i]
})
} else if (catchType(data[i]) === "array") {parent[i] = []
stack.push({parent: parent[i],
data: data[i]
})
} else {parent[i] = data[i]
}
}
}
}
return root
}
24、公布订阅模式
参考文档
参考文档
25、mvvm
参考文档
26、模仿 map(),filter(),reduce(),some(),flag()
(1)模仿 map
Array.prototype.mapFn = function (fn, context) {const arr = [].slice.call(this)
var result = []
for (var i = 0; i < arr.length; i++) {result.push(fn.call(context, arr[i], i))
}
return result
};
Array.prototype.mapReduce = function (fn, context) {const arr = [].slice.call(this)
return arr.reduce((prev, cur, index) => {return [...prev, fn.call(context, cur, index)]
}, [])
};
(2)模仿 filter
Array.prototype.filterFn = function (fn, context) {const arr = [].slice.call(this)
const result = []
for (var i = 0; i < arr.length; i++) {fn.call(context, arr[i], i) && result.push(arr[i])
}
return result
}
Array.prototype.filterReduce = function (fn, context) {const arr = [].slice.call(this)
return arr.reduce((prev, cur, index) => {return fn.call(context, cur, index) ? [...prev, cur] : [...prev]
}, [])
};
(3)模仿 some
Array.prototype.someFn = function (fn, context) {const arr = [].slice.call(this)
for (var i = 0; i < arr.length; i++) {if (fn.call(context, arr[i], i)) return true
}
return false
}
(4)模仿 reduce
Array.prototype.reduceFn = function (fn, initial) {const arr = [].slice.call(this)
let prev
let startIndex
if (initial == undefined) {for (let i = 0; i < arr.length; i++) {if (!arr.hasOwnProperty(i)) continue
startIndex = i
prev = arr[i]
break
}
} else {prev = initial}
for (let i = ++startIndex; i < arr.length; i++) {prev = fn.call(null, prev, arr[i], i)
}
return prev
}
(5)模仿 flat
Array.prototype.flatFn = function (deep) {const arr = [].slice.call(this)
if (deep == 0) return arr
return arr.reduce((prev, cur) => {if (Array.isArray(cur)) {return [...prev, ...[].flatFn.call(cur, deep - 1)]
} else {return [...prev, cur]
}
}, [])
};
27、react 相干
参考文档