前言
之前写了一些js中常见的简单问题
1.1手写一个new的过程
new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象实例。
一个new会进行如下操作
- 创建一个简单的javaScrip对象及{}。
- 链接该对象,及设置该对象的构造函数到另一个对象。
- 将步骤一新创建的对象作为this的上下文。
- 如果该函数没有返回对象,则返回this。
下面就根据上面列出来的几点我们逐渐实现
//首先定义一个构造函数constructor;
function constructor(name){
this.name=name;
};
constructor.prototype.sayName=function(){
console.log(this.name)
};
constructor.prototype
//创建一个函数来实现new的功能
function createConstructor(){
//1.创建一个新的对象
let o={};
//2.获取构造函数,以便实现作用域的绑定;
//[].shift.call( arguments )
//[].shift.call( arguments ) 这便是一个例子。
//shift() 方法删除数组第一项,并返回删除项。
//根据上边的理解,这句代码意思就是: “删除并拿到arguments的第一项”
//[].shift.call( arguments )
//[].shift.call( arguments ) 这便是一个例子。
//shift() 方法删除数组第一项,并返回删除项。
//根据上边的理解,这句代码意思就是: “删除并拿到arguments的第一项”
var _constructor = [].shift.call(arguments);
//3.由于通过new操作创建的对象实例内部的不可访问的属性[[ProtoType]](有些浏览器里面为_proto_);
//指向的是构造函数的原型对象,所以这里实现手动绑定
o.__proto__ =_constructor.prototype;
//4.作用域的绑定使用apply改变this的指向
_constructor.apply(o,arguments);
return o;
};
var personl = createConstructor(constructor,'yinwei');
personl.sayName();
1.2函数粉防抖和节流
函数节流:函数节流是优化高频率执行js代码的一种手段。大家都知道onInput,onkeypress,onscroll,resize等事件触发频率非常高,那么如果在这些事件触发时执行代码,就会相应的将代码执行很多次。但是通常大量的重复执行是没有必要的,比如说大家非常熟悉的搜索引擎的联想查询功能。它是在用户进行键入的同时进行的ajax数据请求。但是键盘事件触发的频率是按照字母来计算的,不是按照汉字或者单词,如果每键入一个字母都触发一次数据请求,效率就非常的低。在这种情况下,我们就有必要降低这种操作的频率,保证一定时间内,核心代码只执行一次。这样在核心代码比较沉重的时候就会大大提升我们的性能
//函数节流
//一、时间戳版
function timeStamp(fn,time=500){
lat last = 0;//设置初始时间
return function(...arges){
let now = new Date().getTime()//获取当前时间
if(now-last>time){//新旧时间对比时间差大于规定的时间
last = now;//若时间差符合我们设置的时间差就将新老时间赋值
fn.apply(arges);//调整this指向并执行
}
}
};
//二、定时器执行
let timeSramp=function(fn,delay=500){
let time = null;
return function(...args){
let that = this;
if(!time){
time = setTimeout(function(){
time=null;
fn.apply(that,args)
},delay)
}
}
};
模拟执行
let i=0;
let fn=function(){
console.log('测试执行')
};
setInterval(()=>{
console.log(`第${i++}次`);
timeSramp(fn,1000)()
},500);
函数防抖 在事件被触发n秒后执行调用,如果在这n秒内又被触发则重新计时。
function Shake(fn,time){
console.log('da')
var timer = null ;
return function(){
let that = this;
let args = arguments;
if(timer){
clearTimeout(timer);
timer = null;
};
timer = setTimeout(function(){
fn.apply(that,args )
},time)
}
};
let fnc = function(){
console.log('函数事件')
};
setInterval(Shake(fnc,1000),500);
1.3 输入url到展示的过程
-
1 DNS解析
-
2 TCP三次握手
-
3 发送请求,分析url,设置请求头
-
4 服务器返回请求文件(html)
-
5 浏览器解析渲染
- 解析html文件,生成dom树
- 解析css文件,生成style树
- 结合dom树和style树,生成渲染树(render tree)
- layout 布局渲染
- GPU像素绘制页面
1.4 函数柯里化
柯里化:把接收多个参数的函数变换成接受一个单一参数的函数(单一参数为多个参数中的第一个)
函数柯里化的思想:一个JS预处理的思想,降低通用性,提高适用性
特点:
1.参数复用:需要输入多个参数,但最终只输入一个,其余通过arguments来获取
2.提前返回:避免重复去判断某一条件是否符合,不符合的return不在继续执行下面的操作
3.延迟执行:避免重复的去执行程序,等真正需要结果的时候在去执行
一个简单的柯里化函数示例
//非柯里化
function fn(x,y){
return x+y;
};
fn(1,2)==3;//true
//柯里化
function fny(y){
return function(x){
return x+y
};
};
fny(2)(1)==3;//true
函数柯里化的好处:
- 1.代码复用,减少维护成本
- 2.尽可能的函数话,便于阅读
下面是一个关于函数柯里化的题目
//实现一个add方法,使计算可以满足下面条件
add(1)(2)(3)=6;
add(1,2,3)(4)=10;
add(1)(2)(3)(4)(5)=15;
1.5 重绘与回流
1.重绘
当元素样式发生改变,但不影响布局时,浏览器将使用重绘进行元素更新,由于此时只需要UI层面的绘制,因此损耗较小。
2.回流
当元素尺寸、结构或者触发某些属性的时候,浏览器会重新渲染页面,这就叫回流。此时,浏览器需要重新计算,重新进行页面布局,所以损耗较大
一般有以下几种操作:
-
1、页面初次渲染。
-
2、浏览器窗口大小改变。
-
3、元素尺寸、位置、内容改变。
-
4、元素字体大小改变。
-
5、添加或删除可见的dom元素。
-
6、触发CSS伪类,如:hover
-
7、查询某些属性或者调用某些方法。
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
回流必定触发重绘,重绘不一定触发回流,重绘代价小,回流代价大
如何避免重绘和回流
CSS:
- 避免使用table布局。
- 尽可能在dom树的末端修改class。
- 避免使用多层内联样式。
- 将动画效果应用到position:absolute || fixed 上。
- 避免使用css表达式(例如calc)。
- CSS3硬件加速(GPU加速)
javaScript:
- 避免频繁操作样式,最好一次性修改style属性,或者将样式列表定义成class,并一次性更改class属性。
- 避免频繁操作dom,创建一个documentFragement,在他上面应用所有的dom操作,最后再把他添加到文档中。
- 也可以先为元素设置display:none,操作结束后再把它显示出来,因为在display为none的元素上进行dom操作不会引发重绘和回流。
- 避免频繁读取会引发重绘回流的属性,如果需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使他脱离文档流,否则会引起父元素及后续元素频繁回流。