以下摘取的函数,在 shared 目录下公用的工具方法。文件在 util.js 中,githu地址。提取了一些常用通用的函数进行剖析,主要包含以下内容:
export const emptyObject = Object.freeze({})
一旦创建不能给这个对象添加任何属性。
function isUndef (v) {
return v === undefined || v === null
}
在源码中很多地方会判断一个值是否被定义,所以这里直接抽象成一个公共函数。
传入任意值,返回是一个布尔值。
function isDef (v) {
return v !== undefined && v !== null
}
当传入的值,既不是 undefined 也不是 null 返回true。
function isPrimitive (value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
在js中提供了两大类数据类型:
function isObject (obj: mixed) {
return obj !== null && typeof obj === 'object'
}
传入的值排除掉 null,因为在js中 null 使用运算符 typeof 得到的值是 object,这是一个 bug。因为历史原因放弃修复了。具体可以参考这里查看
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
function isPromise (val) {
return (
isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
当一个对象存在 then 方法,并且也存在 catch 方法,可以判定为 Promise 对象。
这个方法有效的避免了进行删除数组某一项时,都要进行查找位置再删除的重复工作。
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 的返回值。
用高阶函数的好处是无需暴露不同要求的缓存对象在外面,形成一个闭包。下面这个函数的技巧,应用在工作中,可以提高代码运行的效率。
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 时,从缓存对象中取值,命中,直接返回,无需再进行计算操作。
判断两个对象是否相同,主要是判断两个对象包含的值都是一样的,如果包含的值依然是个对象,则继续递归调用判断是否相同。
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
}
}
如果两个都会对象,则分为两种情况,数组和对象。
例子:
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
同样利用高阶函数,在闭包内操作标识的真假,来控制执行一次。
function once (fn) {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}
实际运用:
function test(){
console.log('我只被执行一次');
}
let test2 = once(test);
test2(); // 我只被执行一次
test2();
test2();
test2();
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 函数中,实际上调用的是 call或 apply。
这个方法写的相对简单,如果更深入了解,可以戳此查看这篇文章
来自:https://segmentfault.com/a/1190000019679638
我理解的 JavaScript 函数式编程,都认为属于函数式编程的范畴,只要他们是以函数作为主要载体的。
给你的代码增加一点点函数式编程的特性,最近我对函数式编程非常感兴趣。这个概念让我着迷:应用数学来增强抽象性和强制纯粹性,以避免副作用,并实现代码的良好可复用性。同时,函数式编程非常复杂。
Async/await以及它底层promises的应用正在猛烈地冲击着JS的世界。在大多数客户端和JS服务端平台的支持下,回调编程已经成为过去的事情。当然,基于回调的编程很丑陋的。
如果你曾经了解或编写过JavaScript,你可能已经注意到定义函数的方法有两种。即便是对编程语言有更多经验的人也很难理解这些差异。在这篇博客的第一部分,我们将深入探讨函数声明和函数表达式之间的差异。
随着软件应用的复杂度不断上升,为了确保应用稳定且易拓展,代码质量就变的越来越重要。不幸的是,包括我在内的几乎每个开发者在职业生涯中都会面对质量很差的代码。这些代码通常有以下特征:
在js开发中,程序代码是从上而下一条线执行的,但有时候我们需要等待一个操作结束后,再进行下一步操作,这个时候就需要用到回调函数。 在js中,函数也是对象,确切地说:函数是用Function()构造函数创建的Function对象。
这篇文章主要介绍ES5中函数的4种调用,在ES5中函数内容的this指向和调用方法有关。以及ES6中函数的调用,使用箭头函数,其中箭头函数的this是和定义时有关和调用无关。
函数的三种定义方法分别是:函数定义语句、函数直接量表达式和Function()构造函数的方法,下面依次介绍这几种方法具体怎么实现,在实际编程中,Function()构造函数很少用到,前两中定义方法使用比较普遍。
微软 称excel就实现面向开发者的功能,也就是说我们不仅可以全新定义的公式,还可以重新定义excel的内置函数,现在Excel自定义函数增加了使用 JavaScript 编写的支持,下面就简单介绍下如何使用js来编写excel自定义函数。
这篇文章主要讲解:js立即执行函数是什么?js使用立即执行函数有什么作用呢?js立即执行函数的写法有哪些?
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!