百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

vue2.0源码用到的工具函数,12个简易的复用函数,看看有多简单

toyiye 2024-05-09 18:41 15 浏览 0 评论

戎马:https://segmentfault.com/a/1190000019679638

1. 创建一个被冻结的空对象

export const emptyObject = Object.freeze({ }) 

一旦创建不能给这个对象添加任何属性。

2. 判断是否是 undefinednull

function isUndef (v) {
 return v === undefined || v === null
}

在源码中很多地方会判断一个值是否被定义,所以这里直接抽象成一个公共函数。

传入任意值,返回是一个布尔值。

3. 判断是否不是 undefinednull

function isDef (v) {
 return v !== undefined && v !== null
}

当传入的值,既不是 undefined 也不是 null 返回true。

4. 判断是否是原始类型

function isPrimitive (value) {
 return (
 typeof value === 'string' ||
 typeof value === 'number' ||
 typeof value === 'symbol' ||
 typeof value === 'boolean'
 )
}

在js中提供了两大类数据类型:

  1. 原始类型(基础类型):String、Number、Boolean、Null、Undefined、Symbol
  2. 对象类型:Object、Array、Function

5. 判断是否是对象类型

function isObject (obj: mixed) {
 return obj !== null && typeof obj === 'object'
}

传入的值排除掉 null,因为在js中 null 使用运算符 typeof 得到的值是 object,这是一个 bug。因为历史原因放弃修复了。

6. 判断有效的数组下标

function isValidArrayIndex (val) {
 const n = parseFloat( String(val) ); // 转成数字
 // 下标大于等于0,并且不是小数,并且是有限的数
 return n >= 0 && Math.floor(n) === n && isFinite(val)
}
  • 可以传入任意值,先调用 String 转成字符串,目的是防止传入的值为 Symbol 类型,那样直接调用 parseFloat 会报错,例如:
let test = Symbol('test');
console.log(parseFloat(test))
控制台捕获错误:Uncaught TypeError: Cannot convert a Symbol value to a string

原因是在调用 parseFloat 时,内部会调用内置的 ToString 方法。而内置的 ToString 方法在遇到 Symbol类型的值时,会抛出 TypeError 错误。

跟使用一些隐式转换遇到的问题一样,例如使用 + 号:

let test = '' + Symbol('text');
控制台捕获错误:Uncaught TypeError: Cannot convert a Symbol value to a string

都是因为内部会调用内置的 ToString 方法造成的。

而如果手动调用 toString 方法或者调用 String,转换为字符串,则不会报错

let test = Symbol('test');
console.log(test.toString()); // "Symbol(test)"
console.log(String(test)) // "Symbol(test)"
  • 接下来判断 n >= 0 ,数组的下标不能小于0,这样就会排除掉小于0的数,以及 NaN
  • 并且 Math.floor(n) === n 一个数向下取整并且还等于自己,那只能是正整数,排除掉小数,因为数组的下标不能是小数。
  • 并且用 isFinite 来判定一个数字是否是有限数
console.log(isFinite(Infinity)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite(123)); // true

7. 判断是否是一个 Promise 对象

function isPromise (val) {
 return (
 isDef(val) &&
 typeof val.then === 'function' &&
 typeof val.catch === 'function'
 )
}

当一个对象存在 then 方法,并且也存在 catch 方法,可以判定为 Promise 对象。

8. 删除数组中指定元素

这个方法有效的避免了进行删除数组某一项时,都要进行查找位置再删除的重复工作。

function remove (arr, item){
 if (arr.length) {
 const index = arr.indexOf(item)
 if (index > -1) {
 return arr.splice(index, 1)
 }
 }
}
  • 先判断数组长度,如果数组是空的,则没必要进行删除操作
  • indexOf 方法查找到元素在数组中的位置,如果找到返回元素所在的位置下标,如果不存在,则返回-1
  • index>-1 代表存在数组中,则调用 splice 进行删除,并返回删除的元素组成的数组,也就是 splice 的返回值。

9. 用做缓存的高阶函数

用高阶函数的好处是无需暴露不同要求的缓存对象在外面,形成一个闭包。下面这个函数的技巧,应用在工作中,可以提高代码运行的效率。

function cached(fn) {
 // 创建一个缓存对象
 const cache = Object.create(null)
 return (function cachedFn (str) {
 // 先从缓存对象中找,要操作的值,是否已经有了操作结果
 const hit = cache[str]
 // 如果有,则直接返回;没有,则调用函数对值进行操作,并把操作结果存在缓存对象中
 return hit || (cache[str] = fn(str))
 })
}
  • 调用 cached 时会传入一个 fn 函数,这个函数对某些值进行操作,操作之后会产生返回值
  • cached 函数先定义一个没有原型的对象,会比用 {} 高效,因为不需要继承一大堆 Object.prototype 上的属性。
  • 执行完 cached 会返回一个函数 cachedFn,将来接收需要操作的值。函数 cachedFn 内部调用 fn 函数得到操作后的值,并缓存在对象 cache 中,如果再对同一个值进行操作时,则直接从缓存中取,无需再调用函数计算。

例如以下运用,函数的作用是把字符串的首字母大写。

const capitalize = cached((str) => {
 return str.charAt(0).toUpperCase() + str.slice(1)
})
  • 先调用 cached 传入一个函数,这个函数是对字符串进行首字母大写的操作,并返回首字母大写的字符串结果,可以说创建了一个计算函数。
  • cached 的返回值是函数,也就是上面的 cachedFn 函数。

这时我们就可以调用 capitalize 对字符串进行首字母大写了。

capitalize('test'); // "Test"
capitalize('test'); // "Test"
capitalize('test'); // "Test"

第一次调用 capitalize 函数,先从缓存对象中取值,没有,则调用计算函数进行计算结果返回,同时存入缓存对象中。这时的缓存对象为:

{test: 'Test'}

再多次调用 capitalize 时,从缓存对象中取值,命中,直接返回,无需再进行计算操作。

10. 递归判断一个对象是否和另个一个对象完全相同

判断两个对象是否相同,主要是判断两个对象包含的值都是一样的,如果包含的值依然是个对象,则继续递归调用判断是否相同。

function isObject (obj){
 return obj !== null && typeof obj === 'object'
}
function looseEqual (a, b) {
 // 如果是同一个对象,则相同
 if (a === b) return true
 // 判断是否是对象
 const isObjectA = isObject(a)
 const isObjectB = isObject(b)
 // 两者都是对象
 if (isObjectA && isObjectB) {
 try {
 // 判断是否是数组
 const isArrayA = Array.isArray(a)
 const isArrayB = Array.isArray(b)
 // 两者都是数组
 if (isArrayA && isArrayB) {
 // 长度要一样,同时每一项都要相同,递归调用
 return a.length === b.length && a.every((e, i) => {
 return looseEqual(e, b[i])
 })
 } else if (a instanceof Date && b instanceof Date) { // 如果都是时间对象,则需要保证时间戳相同
 return a.getTime() === b.getTime()
 } else if (!isArrayA && !isArrayB) { // 两者都不是数组,则为对象
 // 拿到两者的key值,存入数组
 const keysA = Object.keys(a)
 const keysB = Object.keys(b)
 // 属性的个数要一样,递归的判断每一个值是否相同
 return keysA.length === keysB.length && keysA.every(key => {
 return looseEqual(a[key], b[key])
 })
 } else { 
 return false
 }
 } catch (e) {
 return false
 }
 } else if (!isObjectA && !isObjectB) { // 两者都不是对象
 // 转成字符串后,值是否一致
 return String(a) === String(b)
 } else {
 return false
 }
}

  • 判断两个值是否相同,无论是原始类型还是对象类型,如果相同,则直接返回true。
  • 如果两个都会对象,则分为两种情况,数组和对象。
  • 都是数组,则保证长度一致,同时调用 every 函数递归调用函数,保证每一项都一样
  • 是时间对象,则保证时间戳相同
  • 是对象,则先取出 key 组成的数组,两者 key 的个数要相同;再递归调用比较 value 值是否相同
  • 以上都不满足,直接返回false
  • 如果两者都不是对象,转成字符串后进行比较。
  • 以上都不满足,直接返回false

例子:

let a1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
let b1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a1,b1)); // true
let a2 = [1,2,3,{a:1,b:2,c:[1,2,3,4]}];
let b2 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a2,b2)); // false

11. 函数只执行一次

同样利用高阶函数,在闭包内操作标识的真假,来控制执行一次。

function once (fn) {
 let called = false
 return function () {
 if (!called) {
 called = true
 fn.apply(this, arguments)
 }
 }
}
  • 传入要执行一次的函数 fn
  • 设置标识为 false
  • 返回一个函数

实际运用:

function test(){
 console.log('我只被执行一次');
}
let test2 = once(test);
test2(); // 我只被执行一次
test2();
test2();
test2();
  • 调用 once 函数后,会返回一个函数,赋值给 test2
  • 第一次调用 test2 后,在函数的尼内部,called 初次为 false, 所以可以执行函数 test,然后把标识 called 设置为true,就类似关闭了大门,下次不再执行。
  • 之后在调用 test2 , test 将不再执行。

12. 自定义 bind 函数

function polyfillBind (fn, ctx) {
 function boundFn (a) {
 const l = arguments.length
 return l
 ? l > 1
 ? fn.apply(ctx, arguments)
 : fn.call(ctx, a)
 : fn.call(ctx)
 }
 boundFn._length = fn.length
 return boundFn
}

自定义的 bind 函数的场景,都是用来兼容不支持原生 bind 方法的环境。 在自己模拟的 bind 函数中,实际上调用的是 callapply

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码