英文 | 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 有以下规则:

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

而且,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 consumingconsole.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 functionreturn function cachedFn (str) {// If the cache is not hit, the function will be executedif ( !cache[str] ) {let result = fn(str);// Store the result of the function execution in the cachecache[str] = result;}return cache[str]}
}

例子:

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;

例子:

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;

例子:

5、实现 Array.prototype.some

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

代码:

const selfSome = function (fn, context) {let arr = Array.prototype.slice.call(this)if(!arr.length) return falsefor (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;

例子:

6、实现 Array.prototype.reduce

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

代码:

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

例子:

7、实现 Array.prototype.flat

代码:

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

例子:

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
}

例子:

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 毫秒内仅执行一次该功能。

代码:

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) returntimer = setTimeout(() => {timer = nullfunc.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.lengthlet lazyLoad1 = (function () {let count = 0return 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 successfullydeleteIndexList.push(index)count++if (count === num) {//When all pictures are loaded, unbind scroll eventdocument.removeEventListener('scroll',lazyLoad1)}}})// Delete loaded picturesimgList1 = 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
}

例子:

13、单例模式

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

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

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

代码:

function proxy(func) {let instance;let handler = {construct(target, args) {if (!instance) {// Create an instance if there is not existinstance = Reflect.construct(func,args)}return instance}}return new Proxy(func, handler)
}// examplefunction Person(name, age) {this.name = namethis.age = age
}const SingletonPerson = proxy(Person)let person1 = new SingletonPerson('zhl', 22)let person2 = new SingletonPerson('cyw', 22)console.log(person1 === person2) // true

例子:

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 = objconst jsonStringify = (function () {// Closure + WeakMap prevent circular referenceslet wp = new WeakMap();//It is the function in the closure that recursively calls jsonstrify, not the jsonstrify function declared by constreturn 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}
})();// examplelet 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的函数,这些函数也是我们作为一名web前端开发人员必须要知道的,希望对你有用,如果觉得对你有帮助的话,请记得点赞我,关注我,并将它分享给你身边做开发的朋友,也许能够帮助到他。

学习更多技能

请点击下方公众号

分享 14 个你必须知道的 JS 函数相关推荐

  1. 分享 28 个你应该知道的JS 实用小技巧

    文 | https://niemvuilaptrinh.medium.com/28-tip-javascript-you-should-know-5c8ca83e4f99 今天我将分享一些Javasc ...

  2. php要懂函数吗,九个你需要知道的PHP函数和功能

    9个你需要知道的PHP函数和功能 即使使用 PHP 多年,有些功能和特点我们也未必发现或未被充分利用,一旦被我们发现,就会发现它们非常有用.然而,并不是所有的人都已经从头至尾详读过 PHP 的手册和功 ...

  3. 分享 10 个前端开发者需要知道的 JS 技巧

    英文 | https://javascript.plainenglish.io/as-a-front-end-engineer-10-javascript-tricks-and-tips-you-sh ...

  4. js 正则或_Web前端工程师要知道的JS 常用正则表达式

    说起正则表达式,相信从事web前端开发的人一定很熟悉,正则表达式通常被用来检索.替换那些符合某个模式(规则)的文本,是web前端开发中经常会用到的.今天,就为大家分享JS常用的正则表达式以及创建正则表 ...

  5. js 获得明天0点时间戳_需要知道的JS的日期知识,都在这了

    译文 | https://github.com/qq449245884/xiaozhi/issues/66原文 | https://css-tricks.com/everything-you-need ...

  6. 分享一些前端开发者需要知道的 API 接口常识

    作者: 胡涂阿菌 链接:https://www.cnblogs.com/tanshaoshenghao/p/16215751.html 说实话,我非常希望自己能早点看到本篇文章,大学那个时候懵懵懂懂, ...

  7. 前端需要知道的CSS函数大全

    之前一直以为css没有几个函数,今天才发现css现在竟然已经有86个函数了,意不意外,惊不惊喜!!! 我一直比较喜欢用css来解决之前js实现的效果,这样对性能时一种优化,自己也有成就感,希望这些函数 ...

  8. 9个你需要知道的PHP函数和功能

    即使使用 PHP 多年,有些功能和特点我们也未必发现或未被充分利用,一旦被我们发现,就会发现它们非常有用.然而,并不是所有的人都已经从头至尾详读过 PHP 的手册和功能参考! 1. 函数与任意数量的参 ...

  9. scanf 接收 空格 输入_你需要知道的scanf函数用法

    scanf函数   上一节中我们讨论了,如何将整数,浮点数,字符串打印到屏幕上去.既然有输出,怎么能没有输入呢?这一节中,我们来介绍与printf相反的scanf函数.   scanf的功能是将键盘输 ...

最新文章

  1. mysql象限和投影_Camera类之orthographic-摄像机投影模式(第100篇随笔)
  2. Centos5, 6下更改系统时间和时区
  3. Windows数据库编程接口简介
  4. php 聚合和组合,reduce端连接-分区分组聚合(示例代码)
  5. RS 纠删码为什么可以提高分布式存储可靠性?| 原力计划
  6. pygame精灵组有哪些方法_利用 pygame 开发一款游戏:「跳跳兔」(六)
  7. 一看就能学会的H5视频推流方案
  8. Excel单元格下拉选择,单元格自动计算
  9. quartz 整合 postgresql 附带例子
  10. 好消息!这些城市个人手机、在家“刷脸”都能提取公积金,有你家吗?
  11. 科普类(二)先有鸡还是先有蛋?看看C语言怎么说......
  12. Eureka 没凉,别过度悲伤
  13. opengl画圆柱体、圆锥等并使用四元数旋转
  14. java当前时间减一年_Java获取时间,将当前时间减一年,减一天,减一个月
  15. 计算机网络统考outlook操作视频,网络教育计算机统考Outlook
  16. css flex所有属性总结
  17. https 被redirect成了http
  18. 『Python学习笔记』Python中的异步Web框架之fastAPI介绍RestAPI
  19. VisualC++逆序存放
  20. k8s 中的资源配置

热门文章

  1. Elasticsearch相关度评分算法(三):BM25(Okapi BM25)
  2. 记一次微信公众号认证反代理授权成功
  3. 想当厨子的司机,不是好司机
  4. storm tread 耐克_中英对照_英语谚语翻译
  5. 《语法哲学》的精华(2)
  6. 做Java程序员真的没有春天吗?直击优秀开源框架灵魂
  7. Apache 、CDH、TDH、HDP、MapR等Hadoop版本区别
  8. flash小黄油安卓_安卓系统从第1代到现在第11代,有变化吗?
  9. 问鼎OSPF(5)-内外疾行烽火令,锦绣山河尽囊中
  10. java 登录牵手_java初探(1)之登录总结