原理都是利用闭包保存变量
防抖
- 防抖是任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,一般用于输入框实时搜索
// 防抖
function debounce(fn,time){
let timer = null;
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
},time)
}
}
节流
- 节流是规定函数在指定的时间间隔内只执行一次,一般用于scroll事件
// 节流
function throttle(fn,time){
let canRun = true;
return function(){
if(!canRun){
return
}
canRun = false;
setTimeout(() => {
fn.apply(this,arguments);
canRun = true;
},time)
}
}
深拷贝
// 深拷贝(deepClone) 对象深拷贝
const deepClone = function (obj) {
var result = Array.isArray(obj) ? [] : {};
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
result[key] = deepClone(obj[key])
} else {
result[key] = obj[key];
}
}
}
return result;
}
// 数组深拷贝
const deepArrClone = function (arr) {
if (Array.isArray(arr)) {
return JSON.parse(JSON.stringify(arr));
}
}
数组乱序
const mixArr = function(arr){
return arr.sort(() => {
return Math.random() - 0.5;
})
}
数组去重
const removeDup = function(arr){
return Array.from(new Set(arr));
}
数组flat(展平)
const flattenByDeep = function (array, deep) {
var result = [];
for (let i = 0; i < array.length; i++) {
if(Array.isArray(array[i]) && deep >= 1){
result.concat(flattenByDeep(array[i]), deep-1)
}else{
result.push(array[i])
}
}
return result;
}
call && apply && bind
call实现
Function.prototype.call = function(context){
if(typeof this !== 'function'){
throw new TypeError(`${this} is not a function`);
}
context = context || window;
context.fn = this;
const args = [...arguments].slice(1);
const result = context.fn(...args);
delete context.fn;
return result;
}
apply实现
Function.prototype.apply = function(context){
if(typeof this !== 'function'){
throw new TypeError(`${this} is not a function`);
}
context = context || window;
context.fn = this;
var result;
if(arguments[1].length){
result = context.fn(...arguments[1])
}else{
result = context.fn()
}
return result;
}
bind实现
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回函数
return function F() {
// 1 判断是否用作构造函数
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 2 用作普通函数
return _this.apply(context, args.concat(...arguments))
}
}
观察者(发布-订阅)模式
- 作用: 当一个对象的状态发生改变时,能够自动通知其他关联对象,自动刷新对象状态
- 也就是提供给关联对象一种同步通信的手段,使其状态与依赖它的其他对象保持同步通信
/**
* 发布订阅模式(观察者模式)
* handles: 事件处理函数集合
* on: 订阅事件
* emit: 发布事件
* off: 删除事件
**/
class PubSub {
constructor() {
this.handles = {};
}
// 订阅事件
on(eventType, handle) {
if (!this.handles.hasOwnProperty(eventType)) {
this.handles[eventType] = [];
}
if (typeof handle == 'function') {
this.handles[eventType].push(handle);
} else {
throw new Error('缺少回调函数');
}
return this; // 实现链式调用
}
// 发布事件
emit(eventType, ...args) {
if (this.handles.hasOwnProperty(eventType)) {
this.handles[eventType].forEach((item, key, arr) => {
item.apply(null, args);
})
} else {
throw new Error(`"${eventType}"事件未注册`);
}
return this;
}
// 删除事件
off(eventType, handle) {
if (!this.handles.hasOwnProperty(eventType)) {
throw new Error(`"${eventType}"事件未注册`);
} else if (typeof handle != 'function') {
throw new Error('缺少回调函数');
} else {
this.handles[eventType].forEach((item, key, arr) => {
if (item == handle) {
arr.splice(key, 1);
}
})
}
return this; // 实现链式操作
}
}
实现instanceof
instanceof功能:instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
// instanceof
const myInstanceOf = function(left, right){
let proto = left.__proto__;
let protoType = right.prototype;
while(true){
if(proto === null){
return false;
}
if(proto === protoType){
return true;
}
proto = proto.__proto__;
}
}
new的过程
- 创建一个空的简单 JavaScript 对象(即{});
- 链接该对象(即设置该对象的构造函数)到另一个对象 ;
- 将步骤1新创建的对象作为 this 的上下文 ;
- 如果该函数没有返回对象,则返回 this。
以 var child = new Parent()为例:
function newParent(){
var obj = {}; // 首先创建一个对象
obj.__proto__ = Parent.prototype; // 然后将该对象的__proto__属性指向构造函数的protoType
var result = Parent.call(obj) // 执行构造函数的方法,将obj作为this传入
return typeof(result) == 'object' ? result : obj
}
链式调用、任务队列、流程控制
// 链式调用、任务队列、流程控制
function _LazyMan(name) {
this.name = name;
this.queue = [];
this.queue.push(() => {
console.log(`Hi! This is ${this.name}!`);
this.next()
})
}
_LazyMan.prototype.sleep = function (time) {
setTimeout(() => {
console.log(`Wake up after ${time}`);
this.next();
}, time)
return this;
}
_LazyMan.prototype.eat = function (someFood) {
console.log(`Eat ${someFood}~`);
this.next();
return this;
}
_LazyMan.prototype.sleepFirst = function (time) {
this.queue.unshift(() => {
setTimeout(() => {
console.log(`Wake up after ${time}`);
this.next();
}, time)
})
return this;
}
_LazyMan.prototype.next() = function () {
let fn = this.queue.shift();
fn && fn();
}
function LazyMan(name) {
_LazyMan(name);
}
函数柯里化
函数柯里化: 把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术,是高阶函数的一种用法
// 函数柯里化
function currying(fn,...args){
console.log(fn.length)
if(fn.length <= args.length){
return fn(...args)
}
return function(...args1){
console.log(...args,...args1, '---------')
return currying(fn,...args,...args1)
}
}
curry的性能:
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上