滚动加载
原理就是监听页面滚动事件,分析clientHeight、scrollTop、scrollHeight三者的属性关系。
window.addEventListener('scroll', function() {
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
if (clientHeight + scrollTop >= scrollHeight) {
// 检测到滚动至页面底部,进行后续操作
// ...
}
}, false);
将虚拟 Dom 转化为真实 Dom
{
tag: 'DIV',
attrs:{
id:'app'
},
children: [
{
tag: 'SPAN',
children: [
{
tag: 'A', children: [] }
]
},
{
tag: 'SPAN',
children: [
{
tag: 'A', children: [] },
{
tag: 'A', children: [] }
]
}
]
}
把上面虚拟Dom转化成下方真实Dom
<div id="app">
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>
实现
// 真正的渲染函数
function _render(vnode) {
// 如果是数字类型转化为字符串
if (typeof vnode === "number") {
vnode = String(vnode);
}
// 字符串类型直接就是文本节点
if (typeof vnode === "string") {
return document.createTextNode(vnode);
}
// 普通DOM
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
// 遍历属性
Object.keys(vnode.attrs).forEach((key) => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
});
}
// 子数组进行递归操作
vnode.children.forEach((child) => dom.appendChild(_render(child)));
return dom;
}
实现JSON.parse
var json = '{"name":"cxk", "age":25}';
var obj = eval("(" + json + ")");
此方法属于黑魔法,极易容易被xss攻击,还有一种new Function
大同小异。
实现字符串的repeat方法
输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。
function repeat(s, n) {
return (new Array(n + 1)).join(s);
}
递归:
function repeat(s, n) {
return (n > 0) ? s.concat(repeat(s, --n)) : "";
}
深克隆(deepclone)
简单版:
const newObj = JSON.parse(JSON.stringify(oldObj));
局限性:
-
他无法实现对函数 、RegExp等特殊对象的克隆
-
会抛弃对象的constructor,所有的构造函数会指向Object
-
对象有循环引用,会报错
面试版:
/**
* deep clone
* @param {[type]} parent object 需要进行克隆的对象
* @return {[type]} 深克隆后的对象
*/
const clone = parent => {
// 判断类型
const isType = (obj, type) => {
if (typeof obj !== "object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
};
// 处理正则
const getRegExp = re => {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
// 维护两个储存循环引用的数组
const parents = [];
const children = [];
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== "object") return parent;
let child, proto;
if (isType(parent, "Array")) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, "RegExp")) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
}
// 处理循环引用
const index = parents.indexOf(parent);
if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
局限性:
- 一些特殊情况没有处理: 例如Buffer对象、Promise、Set、Map
- 另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间
原理详解实现深克隆
实现防抖函数(debounce)
防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
那么与节流函数的区别直接看这个动画实现即可。
手写简化版:
// 防抖函数
const debounce = (fn, delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
适用场景:
- 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
- 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似
生存环境请用lodash.debounce
实现数组的push方法
let arr = [];
Array.prototype.push =