14 个你必须知道的JavaScript 函数_缓存

英文 | https://javascript.plainenglish.io/you-must-understand-these-14-javasript-functions-1f4fa1c620e2

翻译 | 杨小爱


1、确定任意对象的具体类型

众所周知,JavaScript 中有六种原始数据类型(Boolean、Number、String、Null、Undefined、Symbol)和一个对象数据类型。 但是你知道对象数据类型可以细分为很多种子类型吗? 一个对象可能是数组、函数、map等,如果我们要获取对象的具体类型,应该怎么做呢?

代码:

function toRawType (value) {
let _toString = Object.prototype.toString;


let str = _toString.call(value)


return str.slice(8, -1)
}

解释

ECMAScript 有以下规则:

14 个你必须知道的JavaScript 函数_javascript_02

对于不同的对象,调用 Object.prototype.toString() 时会返回不同的结果。

14 个你必须知道的JavaScript 函数_javascript_03


而且,Object.prototype.toString() 的返回值总是‘[object’+‘tag’+‘]’的格式。 如果我们只想要中间的标签,我们可以通过正则表达式或者String.prototype.slice()删除两边的字符。

例子:

toRawType(null) 
// "Null"
toRawType(/sdfsd/)
//"RegExp"

2、缓存函数计算结果

如果有这样的功能:

function computed(str) {
// Suppose the calculation in the funtion is very time consuming
console.log('2000s have passed')
return 'a result'
}

我们要缓存函数操作的结果, 稍后调用时,如果参数相同,则不再执行该函数,而是直接返回缓存中的结果。 我们能做什么?

代码:

function cached(fn){
// Create an object to store the results returned after each function execution.
const cache = Object.create(null);


// Returns the wrapped function
return function cachedFn (str) {


// If the cache is not hit, the function will be executed
if ( !cache[str] ) {
let result = fn(str);


// Store the result of the function execution in the cache
cache[str] = result;
}


return cache[str]
}
}

例子:

14 个你必须知道的JavaScript 函数_i++_04

3、实现Array.prototype.map

这是 JavaScript 中一个有用的内置方法,你应该能够自己实现此功能。

代码:

const selfMap = function (fn, context) {
let arr = Array.prototype.slice.call(this)
let mappedArr = Array()
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue;
mappedArr[i] = fn.call(context, arr[i], i, this)
}
return mappedArr
}


Array.prototype.selfMap = selfMap;

例子:

14 个你必须知道的JavaScript 函数_缓存_05

4、实现Array.prototype.filter

这是 JavaScript 中一个有用的内置方法,你应该能够自己实现此功能。

代码:

const selfFilter = function (fn, context) {
let arr = Array.prototype.slice.call(this)
let filteredArr = []
for (let i = 0; i < arr.length; i++) {
if(!arr.hasOwnProperty(i)) continue;
fn.call(context, arr[i], i, this) && filteredArr.push(arr[i])
}
return filteredArr
}


Array.prototype.selfFilter = selfFilter;

例子:

14 个你必须知道的JavaScript 函数_缓存_06

5、实现 Array.prototype.some

这是 JavaScript 中一个有用的内置方法,你应该能够自己实现此功能。

代码:

const selfSome = function (fn, context) {
let arr = Array.prototype.slice.call(this)
if(!arr.length) return false
for (let i = 0; i < arr.length; i++) {
if(!arr.hasOwnProperty(i)) continue;
let res = fn.call(context,arr[i],i,this)
if(res)return true
}
return false
}


Array.prototype.selfSome = selfSome;

例子:

14 个你必须知道的JavaScript 函数_缓存_07

6、实现 Array.prototype.reduce

这是 JavaScript 中一个有用的内置方法,你应该能够自己实现此功能。

代码:

const selfReduce = function (fn, initialValue) {
let arr = Array.prototype.slice.call(this)
let res
let startIndex
if (initialValue === undefined) {
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue
startIndex = i
res = arr[i]
break
}
} else {
res = initialValue
}


for (let i = ++startIndex || 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue
res = fn.call(null, res, arr[i], i, this)
}
return res
}


Array.prototype.selfReduce = selfReduce;

例子:

14 个你必须知道的JavaScript 函数_javascript_08

7、实现 Array.prototype.flat

代码:

const selfFlat = function (depth = 1) {
let arr = Array.prototype.slice.call(this)
if (depth === 0) return arr
return arr.reduce((pre, cur) => {
if (Array.isArray(cur)) {
return [...pre, ...selfFlat.call(cur, depth - 1)]
} else {
return [...pre, cur]
}
}, [])
}


Array.prototype.selfFlat = selfFlat;

例子:

14 个你必须知道的JavaScript 函数_javascript_09

8、柯里化

柯里化是一种将具有多个参数的函数评估为具有单个参数的函数序列的技术。

换句话说,当一个函数不是一次接受所有参数时,而是接受第一个参数并返回一个新函数,该函数接受第二个参数并返回一个新函数,该函数接受第三个参数,依此类推,直到所有参数都已履行。

那就是我们将函数调用 add(1,2,3) 转换为 add(1)(2)(3) 。 通过使用这种技术,可以轻松地配置和重用小块。

为什么有用?

  • 柯里化可以帮助您避免一次又一次地传递相同的变量。
  • 它有助于创建高阶函数,它对事件处理非常有帮助。
  • 小部件可以轻松配置和重用。

让我们看一个简单的添加函数。 它接受三个操作数作为参数,并返回所有三个操作数的总和作为结果。

function add(a,b,c){
return a + b + c;
}

你可以用太少(结果奇怪)或太多(多余的参数被忽略)来调用它。

add(1,2,3) --> 6 
add(1,2) --> NaN
add(1,2,3,4) --> 6 //Extra parameters will be ignored.

如何将现有函数转换为 curried 版本?

代码:

function curry(fn) {
if (fn.length <= 1) return fn;
const generator = (...args) => {
if (fn.length === args.length) {


return fn(...args)
} else {
return (...args2) => {


return generator(...args, ...args2)
}
}
}
return generator
}

例子:

14 个你必须知道的JavaScript 函数_i++_10

9、去抖动

去抖动只不过是减少不必要的耗时计算,以提高浏览器性能。在某些情况下,某些功能需要更多时间来执行某个操作。例如,以电子商务网站上的搜索栏为例。

假设用户想要获得“Tutorix 学习套件”。他在搜索栏中键入产品的每个字符。输入每个字符后,从浏览器到服务器都会进行一次 Api 调用,以获取所需的产品。由于他想要“Tutorix 学习套件”,用户必须从浏览器到服务器进行 17 次 Api 调用。

想象一个场景,当数百万人进行相同的搜索从而调用数十亿个 Api 时。所以一次调用数十亿个 Api 肯定会导致浏览器性能变慢。为了减少这个缺点,去抖动出现了。

在这种情况下,去抖动将在两次击键之间设置一个时间间隔,假设为 2 秒。如果两次击键之间的时间超过 2 秒,则只会进行 Api 调用。在这 2 秒内,用户可以输入至少一些字符,从而减少 Api 调用的这些字符。由于 Api 调用减少,浏览器性能将提高。必须注意,每次击键都会更新 Debouncing 功能。

代码:

const debounce = (func, time = 17, options = {
leading: true,
context: null
}) => {
let timer;
const _debounce = function (...args) {
if (timer) {
clearTimeout(timer)
}
if (options.leading && !timer) {
timer = setTimeout(null, time)
func.apply(options.context, args)
}else{
timer = setTimeout(() => {
func.apply(options.context, args)
timer = null
}, time)
}
};


_debounce.cancel = function () {
clearTimeout(timer)
timer = null
};
return _debounce
};

10、 节流

节流将以这样一种方式更改函数,即它可以在一个时间间隔内最多触发一次。 例如,无论用户单击按钮多少次,限制将在 1000 毫秒内仅执行一次该功能。

14 个你必须知道的JavaScript 函数_缓存_11

代码:

const throttle = (func, time = 17, options = {


leading: true,
trailing: false,
context: null
}) => {
let previous = new Date(0).getTime()
let timer;
const _throttle = function (...args) {
let now = new Date().getTime();


if (!options.leading) {
if (timer) return
timer = setTimeout(() => {
timer = null
func.apply(options.context, args)
}, time)
} else if (now - previous > time) {
func.apply(options.context, args)
previous = now
} else if (options.trailing) {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(options.context, args)
}, time)
}
};


_throttle.cancel = () => {
previous = 0;
clearTimeout(timer);
timer = null
};
return _throttle
};

11、 延迟加载图片

延迟加载图片意味着在网站上异步加载图片——也就是说,在首屏内容完全加载之后,甚至有条件地,只有当它们出现在浏览器的视口中时。 

这意味着如果用户不一直向下滚动,则放置在页面底部的图像甚至不会被加载。

代码:

// getBoundingClientRect
let imgList1 = [...document.querySelectorAll(".get_bounding_rect")]
let num = imgList1.length


let lazyLoad1 = (function () {
let count = 0
return function () {
let deleteIndexList = []
imgList1.forEach((img,index) => {
let rect = img.getBoundingClientRect()
if (rect.top < window.innerHeight) {
img.src = img.dataset.src
// Add picture to delete list after loading successfully
deleteIndexList.push(index)
count++
if (count === num) {
//When all pictures are loaded, unbind scroll event
document.removeEventListener('scroll',lazyLoad1)
}
}
})
// Delete loaded pictures
imgList1 = imgList1.filter((_,index)=>!deleteIndexList.includes(index))


}
})()

12、数组随机无序

我们经常需要打乱一个数组。

代码:

// Randomly select one of all elements after the current element to exchange with the current element
function shuffle(arr) {
for (let i = 0; i < arr.length; i++) {
let randomIndex = i + Math.floor(Math.random() * (arr.length - i));
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
}
return arr
}


// Generate a new array, randomly take an element from the original array and put it into the new array
function shuffle2(arr) {
let _arr = []
while (arr.length) {
let randomIndex = Math.floor(Math.random() * (arr.length))
_arr.push(arr.splice(randomIndex, 1)[0])
}
return _arr
}

例子:

14 个你必须知道的JavaScript 函数_i++_12

13、单例模式

单例模式将特定对象的实例数限制为一个,这个单一实例称为单例模式。

单例在需要从单个中心位置协调系统范围的操作的情况下很有用。 一个例子是数据库连接池。 池管理整个应用程序的所有数据库连接的创建、销毁和生命周期,确保没有连接“丢失”。

单例减少了对全局变量的需求,这在 JavaScript 中尤为重要,因为它限制了命名空间污染和相关的名称冲突风险。

代码:

function proxy(func) {
let instance;
let handler = {
construct(target, args) {
if (!instance) {
// Create an instance if there is not exist
instance = Reflect.construct(func,args)
}
return instance
}
}
return new Proxy(func, handler)
}




// example


function Person(name, age) {
this.name = name
this.age = age
}


const SingletonPerson = proxy(Person)


let person1 = new SingletonPerson('zhl', 22)


let person2 = new SingletonPerson('cyw', 22)


console.log(person1 === person2) // true

例子:

14 个你必须知道的JavaScript 函数_i++_13

14、实现 JSON.stringify

这是 JavaScript 中一个有用的内置方法,你应该能够自己实现此功能。

代码:

const isString = value => typeof value === 'string';
const isSymbol = value => typeof value === 'symbol'
const isUndefined = value => typeof value === 'undefined'
const isDate = obj => Object.prototype.toString.call(obj) === '[object Date]'
const isFunction = obj => Object.prototype.toString.call(obj) === '[object Function]';
const isComplexDataType = value => (typeof value === 'object' || typeof value === 'function') && value !== null;
const isValidBasicDataType = value => value !== undefined && !isSymbol(value);
const isValidObj = obj => Array.isArray(obj) || Object.prototype.toString.call(obj) === '[object Object]';
const isInfinity = value => value === Infinity || value === -Infinity




// Symbol,undefined,function in array will become null
// Infinity,NaN will also become null
const processSpecialValueInArray = value =>
isSymbol(value) || isFunction(value) || isUndefined(value) || isInfinity(value) || isNaN(value) ? null : value;


// Handling property values according to JSON specification
const processValue = value => {
if (isInfinity(value) || isNaN(value)) {
return null
}
if (isString(value)) {
return `"${value}"`
}
return value
};




// obj.loop = obj


const jsonStringify = (function () {
// Closure + WeakMap prevent circular references
let wp = new WeakMap();


//It is the function in the closure that recursively calls jsonstrify, not the jsonstrify function declared by const
return function jsonStringify(obj) {
if (wp.get(obj)) throw new TypeError('Converting circular structure to JSON');
let res = "";


if (isComplexDataType(obj)) {
if (obj.toJSON) return obj.toJSON;
if (!isValidObj(obj)) {
return
}
wp.set(obj, obj);


if (Array.isArray(obj)) {
res += "[";
let temp = [];
obj.forEach((value) => {
temp.push(
isComplexDataType(value) && !isFunction(value) ?
jsonStringify(value) :
`${processSpecialValueInArray(value, true)}`
)
});
res += `${temp.join(',')}]`
} else {
res += "{";
let temp = [];
Object.keys(obj).forEach((key) => {


if (isComplexDataType(obj[key])) {


if (isValidObj(obj[key])) {
temp.push(`"${key}":${jsonStringify(obj[key])}`)
} else if (isDate(obj[key])) {
temp.push(`"${key}":"${obj[key].toISOString()}"`)
} else if (!isFunction(obj[key])) {
temp.push(`"${key}":{}`)
}
} else if (isValidBasicDataType(obj[key])) {
temp.push(`"${key}":${processValue(obj[key])}`)
}
});
res += `${temp.join(',')}}`
}
} else if (isSymbol(obj)) {
return
} else {
return obj
}
return res
}
})();




// example


let s = Symbol('s')
let obj = {
str: "123",
arr: [1, {e: 1}, s, () => {
}, undefined,Infinity,NaN],
obj: {a: 1},
Infinity: -Infinity,
nan: NaN,
undef: undefined,
symbol: s,
date: new Date(),
reg: /123/g,
func: () => {
},
dom: document.querySelector('body'),
};


console.log(jsonStringify(obj));
console.log(JSON.stringify(obj));

例子:

14 个你必须知道的JavaScript 函数_缓存_14

总结

以上就是我与你分享的14个JavaScript的函数,这些函数也是我们作为一名web前端开发人员必须要知道的,希望对你有用,如果觉得对你有帮助的话,请记得点赞我,关注我,并将它分享给你身边做开发的朋友,也许能够帮助到他。

14 个你必须知道的JavaScript 函数_javascript_15