节流
高频事件触发,无论多频繁触发在n秒内只会执行一次,所以节流会稀释函数的执行频率。
scroll加载更多可以用到节流
<input type="text" id="search" value="12">
function jieliu(fn, duration, isFirst) {
let timer;
return function (...args) {
if(isFirst){
isFirst = false;
fn.apply(this, args);
return;
}
if(!timer){
timer = setTimeout(()=>{
fn.apply(this, args);
timer = null;
}, duration)
}
}
}
document.getElementById('search').addEventListener("input", jieliu((e)=>{
console.log(e.target.value);
}, 1000, true))
防抖
你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐! 常见的例子:有一个搜索输入框,为了提升用户体验,希望在用户输入后可以立即展现搜索结果,而不是每次输入完后还要点击搜索按钮
<input type="text" id="search">
function fangdou(fn, duration) {
let timer;
return function (...args) {
if(timer){
clearTimeout(timer);
}
timer = setTimeout(()=>{
fn.apply(this, args);
}, duration)
}
}
document.getElementById('search').addEventListener('input', fangdou((e)=>{
console.log(e.target.value)
}, 1000));
call()手写
Function.prototype.myCall = function(thisArg, ...args) {
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}
//测试
foo.myCall(obj) // 输出'写代码像'
apply手写
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
语法:func.apply(thisArg, [argsArray])
apply()和call()类似,区别在于call()接收参数列表,而apply()接收一个参数数组,所以我们在call()的实现上简单改一下入参形式即可
Function.prototype.myApply = function(thisArg, args) {
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数(此处说明一下:虽然apply()接收的是一个数组,但在调用原函数时,依然要展开参数数组。可以对照原生apply(),原函数接收到展开的参数数组)
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}
//测试
foo.myApply(obj, []) // 输出'写代码像'
bind手写
Function.prototype.bind=function(obj,arg){
var arg=Array.prototype.slice.call(arguments,1);
var context=this;
return function(newArg){
arg=arg.concat(Array.prototype.slice.call(newArg));
return context.apply(obj,arg);
}
}
promise手写实现,面试够用版:
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
// 定义链式调用的then方法
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
简单深拷贝
//简单版深拷贝,只能拷贝基本原始类型和普通对象与数组,无法拷贝循环引用
function simpleDeepClone(a) {
const b=Array.isArray(a) ? [] : {}
for (const [key, value] of Object.entries(a)) {
const type = typeof value
if (type !== 'object' || value === null) {
b[key] = value
}else {
b[key] = simpleDeepClone(value)
}
}
return b
}
扁平化
function flatArr(arr){
return arr.reduce((cal, cur) => Array.isArray(cur) ?
cal.concat(flatArr(cur)) : cal.concat(cur)
}
圣杯布局
圣杯布局和双飞翼布局是前端工程师需要日常掌握的重要布局方式。两者的功能相同,都是为了实现一个两侧宽度固定,中间宽度自适应的三栏布局。
<style>
body{
min-width: 550px;
}
#container{
padding-left: 200px;
padding-right: 150px;
}
#container .column{
float: left;
}
#center{
width: 100%;
}
#left{
width: 200px;
margin-left: -100%;
position: relative;
right: 200px;
}
#right{
width: 150px;
margin-right: -150px;
}
</style>
<div id="container">
<div id="center" class="column">center</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>
</div>