防抖与节流
防抖类似于我们去坐电梯,电梯的设计机制是当进入一个人的时候,电梯门会等2s后关闭,如果这2s内又进来了一个人,电梯会重新计算时间,在这第二个人进来的2s后关闭门。就是这样的一个场景。防抖经常用于用户输入搜索的时候,如果过快操作,会频繁向后台发送请求,此时采用防抖
let debounce = (fn,time)=>{
let timeout = null
return function(){
clearTimeout(timeout)
timeout = setTimeout(()=>{
fn.call(this,arguments)
},time)
}
}
节流是在一段时间内尽管会进行多次触发,但是只执行一次。节流通常用于鼠标连续点击触发,或监听滚动事件
let throttle = (fn,time)=>{
let flag = true
return function(){
if(!flag) return
flag = false
setTimeout(()=>{
fn.call(this,arguments)
flag = true
},time)
}
}
正则
学习正则的博客正则表达式30分钟入门教程
将 JS 正则可视化的工具:regexper.com/
用正则实现js的trim,该方法是用来去除字符串首尾空格的。 function trim(string){ return string.replace(/^\s+|\s+$/g,"") }
深拷贝
关于深浅拷贝的文章参考传送门
js数据类型,基本数据类型(number,string,boolean,null,undefined)和引用数据类型。
基本类型的数据存放于栈内存,引用类型的数据存放于堆内存(变量实际上是一个存放在栈内存里的一个指针,指向堆内存中的地址)。
基本数据类型的变量比较是值的比较,引用数据类型的变量比较是引用的比较(也就是说每次我们操作引用类型的数据时,其实操作的是对象的引用,也就是存放在栈内存里的指针。所以比较两个引用类型,是看其的引用是否指向同一个对象)。
js的浅拷贝,只考虑一层。
function clone(obj){
if(typeof obj === "object" && obj !== null){
let newobj = Array.isArray(obj) ? [] : {};
for(var val in obj){
if(obj.hasOwnProperty(val)){
newobj[val] = obj[val]
}
}
return newobj
}else{
return obj
}
}
js的深拷贝,可以使用JSON.parse,JSON.stringfy,或使用for...in加递归完成。
function clone(obj){
if (target === null) return null;
if (typeof target !== 'object') return target;
let newobj = Array.isArray(obj) ? [] : {};
for(var val in obj){
if(obj.hasOwnProperty(val)){
newobj[val] = clone(obj[val])
}
}
return newobj
}
但是该方法对于函数,date等一些特殊类型的数据不起作用。传送门
闭包以及立即执行函数
闭包就是函数内部的函数。为了能访问到函数内部的变量,因此在函数内部又声明了一个函数,就是子函数,最后把子函数return出去。闭包的用处是1》可以在函数外部访问函数内部的变量;2》可以使变量保持在内存中;需要注意的是退出函数前,及时清除掉不需要的局部变量,ie里会导致内存泄漏;另外在外面可以操控函数内部的变量,要小心使用。当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中又不会污染全局的变量时,就可以使用闭包。
立即执行函数,声明一个匿名函数,然后立即调用该匿名函数。可以创造一个独立作用域,避免了变量的全局污染。
原型
原型以及原型链的理解: 构造函数有一个prototype属性(存储了对象的共同属性,就是原型),该属性其实指向的是一个对象。这个对象就是构造函数构造出来的对象所共有的属性集合。每个对象都有一个隐藏属性,该隐藏属性指向原型(就是这个共有属性的集合)。【原型使得我们可以不必重复声明共有的属性。每个对象都有一个隐藏属性,指向原型】
网页其实是一棵树。浏览器向window上加一个document就可以使得js操作这棵树。js使用document操作网页,这就是文档对象模型(Document Object Model)。
DOM事件委托
事件流:捕获阶段,目标阶段,冒泡阶段 现代浏览器一般默认事件的处理程序是冒泡阶段进行注册。利用事件冒泡原理去实现事件委托,即把需要在子元素上触发的事件绑定在父级元素上。好处是减少了事件注册,节省内存,提升性能。另外如新加子元素的话,不需要重新绑定事件。
若一个ul里有多个li,点击任意一个li时,会触发事件。如下,分别采用普通写法和事件委托;
<ul id="testul">
<li>1</li>
<li>11</li>
<li>>1111</li>
</ul>
var ul = document.getElementById("testul");
var li = ul.getElementsByTagName('li');
//不适用事件委托
for(var i = 0; i < li.length; i++) {
li[i].onclick = function(e) {
/*操作*/
};
}
//使用事件委托
ul.addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li'){
fn() // 执行某个函数
}
})
但是如果li里有span标签,此时点击span,上面的事件委托就不起作用了,改进如下:
<ul id="testul">
<li><span>1</span></li>
<li><span>11</span></li>
<li><span>1111</span></li>
</ul>
function delegate(element,eventType,selector,f){
element.addEventListener(eventType,(e)=>{
let et = e.target;
while(!et.matches(selector)){
if(et === element){
el = null;
break;
}
et = et.parentNode;
}
et && f.call(et,e,et);
})
}
delegate(ul,"click","li",fn)
另外如果使用jQuery的话,可以直接使用$("#testul").on("click","li",fn),该用法如果点击的是内部的span,同样会触发li的事件。