一天一个仿lodash函数实现-utils系列
貌似闲置了两周没写了。
这次直接完成util系列的方法,在实现array系列的时候,开始发现一些示例用了其他utils,所以就不完全按顺序来实现,而且在实现过程中发现,其实有些方法其实是另一个方法的进阶使用。
这次主要实现了除match、mixin几个还没理解透的函数。
简单方法:
function identity(value){
return value
}
function noop(){}
function stubArray(){
return []
}
function stubFalse(){
return false
}
function stubObject(){
return {}
}
function stubString(){
return ''
}
function stubTrue(){
return true
}
工具函数:
// 运行函数,如果函数调用异常则返回异常。
function attempt(func, ...args){
let result;
try{
result = func.apply(null, args);
}catch(e){
result = e;
}finally{
return result
}
}
// 按次数执行函数,次数序号为函数调用参数
function times(n, iteratee){
return Array(n).fill(1).map((_, i)=>iteratee(i))
}
// 判断参数是否为NaN、null或undefined
function isN(val){
if(typeof val === 'number' && val !== val) {
return true
}
return val == null;
}
// 用于设定默认值
function defaultTo(val, defaultValue){
return isN(val)?defaultValue:val
}
// 把路径字符串转换成数组
function toPath(path){
return path.replace(/\]/g,'').split(/\.|\[/)
}
let idIndex = 0
function uniqueId(prefix){
return prefix!=null?`${prefix}${idIndex++}`:`${idIndex++}`;
}
function constant(value){
return () => value;
}
function bindAll(obj, methodNames){
methodNames.forEach(m => {
obj[m] = obj[m].bind(obj)
})
}
高阶函数:
// 条件判断函数,类似if判断
function cond(pairs){
return (...args)=>{
for(let i=0;i<pairs.length;i++){
if(pairs[i][0].apply(this, args)){
return pairs[i][1].apply(this, args)
}
}
return
}
}
// 满足source所有条件才返回true
function conforms(source){
return (obj) => {
const keys = Object.keys(source)
for(let i=0;i<keys.length;i++){
if(!source[keys[i]](obj[keys[i]])){
return false;
}
}
return true
}
}
function flow(funcs){
return (...args)=>{
return funcs.reduce((pre, cur)=>{
return cur.apply(this, [].concat(pre))
}, args)
}
}
function flowRight(funcs){
return (...args)=>{
return funcs.reverse().reduce((pre, cur)=>{
return cur.apply(this, [].concat(pre))
}, args)
}
}
function iteratee(arg){
let func;
if(typeof arg === 'string'){
func = obj => obj[arg]
}else if(arg instanceof Array){
func = obj => obj[arg[0]] === arg[1]
}else if(typeof arg === 'function') {
func = arg
}else{
const keys = Object.keys(arg);
func = obj => {
for(let i=0;i<keys.length;i++){
if(obj[keys[i]]!==arg[keys[i]]) return false
}
return true
}
}
return func
}
function method(path, ...args){
return obj => {
return propertyOf(obj)(path).apply(obj,args)
}
}
function methodOf(obj, ...args){
return path => {
return method.call(this, path,...args)(obj)
}
}
function nthArg(n){
return (...args)=>{
return nth(args, n)
}
}
function over(iteratees){
return (...args)=>{
return iteratees.map(it=>{
return it.apply(this, args);
})
}
}
function overEvery(predicates){
return (...args)=>{
for(let i=0;i<predicates.length;i++){
if(!predicates[i].apply(this, args)){
return false
}
}
return true;
}
}
function overSome(predicates){
return (...args)=>{
for(let i=0;i<predicates.length;i++){
if(predicates[i].apply(this, args)){
return true
}
}
return false;
}
}
function property(path){
const pathArr = path instanceof Array?path:toPath(path);
return obj=>{
return pathArr.reduce((pre, cur)=>{
return pre[cur]
}, obj)
}
}
function propertyOf(obj){
return path => {
return property(path)(obj)
}
}
// 当step为0的时候baseRange的做法是先确定数组个数,step会取1,但是在构造元素的时候通过start+=step, 这里我在实现上放弃了这种方式。
function range(...args){
let start = 0;
let end;
let step = 1;
if(args.length===1) {
end = args[0];
if(end<0){
step = -1
}
if(end===0){
return [];
}
}else if(args.length===2){
start = args[0]
end = args[1]
if(start<0) {
step = -1;
}
}else {
start = args[0]
end = args[1]
step = args[2]
}
if(step===0) {
return [];
}
if(step>0&&end<start){
return [];
}
if(step<0&&end>start){
return [];
}
let tmp = start;
let result = [];
let length = (end - start)/step;
while(length--){
result.push(tmp)
tmp+=step
}
return result;
}
function rangeRight(...args){
return range.apply(this. args).reverse();
}