为什么这个递归函数返回undefined

时间:2017-11-11 06:19:24

标签: javascript recursion

我见过太多与我有同样问题的问题,但我发现问题在于他们在调用函数时没有使用return关键字。所以,请不要将其标记为重复。

这是我的递归函数:

function checkSchema(fields, schema) {

    console.log(schema.hasOwnProperty('schema'));
    if (schema.hasOwnProperty('schema')) {
        Object.keys(schema.schema).forEach(function (key) {
        // for (const key in schema.schema) {

            const innerSchema = schema.schema[key];

            if (key === 'custom') {

                if (typeof innerSchema === 'function') {
                    const result = innerSchema(fields);
                    if (result !== null) {
                        console.log('4');
                        return result;
                    }
                } else {
                    console.log('5');
                    return notAFunction;
                }

            } else {

                if (innerSchema.hasOwnProperty('required') && innerSchema.required 
                        && (fields[key] === undefined || fields === null)) {
                            console.log('6');
                            return sendError(innerSchema, 'required');
                }

                if (innerSchema.hasOwnProperty('type')) {

                    if (innerSchema.type !== (fields[key] === undefined ? undefined : fields[key].constructor)) {
                        if (fields[key] === undefined) {
                            if (innerSchema.hasOwnProperty('required') && innerSchema.required) {
                                console.log('7');
                                return sendError(innerSchema, 'type');
                            }
                        } else {
                            console.log('8');
                            return sendError(innerSchema, 'type');
                        }
                    }

                    if (innerSchema.type === Array || innerSchema.type === String) {

                        if (innerSchema.hasOwnProperty('len') && innerSchema.len.constructor === String) {

                            const arrLen = innerSchema.len.split(',');
                            let flag = true; 
                            let el = null;
                            let tempElement = null;
                            const trim = innerSchema.type === String && innerSchema.hasOwnProperty('trim') && innerSchema.trim;

                            arrLen.forEach((element) => {
                                if (element.startsWith('-')) {
                                    el = element.substring(1);
                                    tempElement = isNaN(Number(el)) ? 0 : Number(el);
                                    if (trim ? fields[key].trim().length <= tempElement : fields[key].length <= tempElement) {
                                        flag = false;
                                        return;
                                    }
                                } else if (element.endsWith('-')) {
                                    el = element.substring(0, element.length - 1);
                                    tempElement = isNaN(Number(el)) ? 0 : Number(el);
                                    if (trim ? fields[key].trim().length >= tempElement : fields[key].length >= tempElement) {
                                        flag = false;
                                        return;
                                    }
                                } else {
                                    el = element;
                                    tempElement = isNaN(Number(el)) ? 0 : Number(el);
                                    if (trim ? fields[key].trim().length === tempElement : fields[key].length === tempElement) {
                                        flag = false;
                                        return;
                                    }
                                }
                            }, this);

                            if (flag) {
                                console.log('9');
                                return sendError(innerSchema, 'len');
                            }

                        }

                        if (innerSchema.type === Array && innerSchema.hasOwnProperty('unique') 
                                && innerSchema.unique && (new Set(fields[key])).size !== fields[key].length) {
                                    console.log('10');
                                    return sendError(innerSchema, 'unique');
                        }

                        if (innerSchema.type === String && innerSchema.hasOwnProperty('match') 
                                && innerSchema.match instanceof RegExp && !innerSchema.match.test(fields[key])) {
                                    console.log('11');
                                    return sendError(innerSchema, 'match');
                        }

                    }

                    if ((innerSchema.type === Array || innerSchema.type === Object) && innerSchema.hasOwnProperty('schema')) {
                        console.log('12');
                        return checkSchema(fields[key], innerSchema);
                    }

                } 

            }
        });
    }
    console.log('13');    
}

以下是sendError中使用的checkSchema()函数:

function sendError(schema, prop) {
    const error = schema.errors === undefined ? undefined : schema.errors[prop];
    return (error || defaultError);
}

以下是我调用递归函数的方法:

const x = checkSchema(fields, schema);
console.log(x);

如果有人需要,请提供额外的详细信息:

const notAFunction = {
    errorCode: '9999',
    message: 'Not a function'
};

const defaultError = {
    errorCode: '9999',
    message: 'Error message not specified'
};

以下是我传递给此函数的数据:

字段的值是:

{
    first_name: 'Vishal',
    last_name: 'Sherathiya',
    phone: 9978259999,
    email: 'vishalsherathiya@gmail.com',
    password: '1234567',
    confirm_password: '1234567',
    friends: {
      name: 'ab',
      alias: 'a'
    }
  }

架构的价值:

{
    type: Object,
    unknownKeys: 'allow',
    required: true,
    schema: {
        first_name: {
            type: String,
            required: true,
            match: /^.{1,}$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.FIRST_NAME_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.FIRST_NAME_REQUIRED
                },
                match: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.FIRST_NAME_REQUIRED
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.FIRST_NAME_REQUIRED
                }
            }
        },
        last_name: {
            type: String,
            trim: true,
            required: true,
            match: /^.{1,}$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.LAST_NAME_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.LAST_NAME_REQUIRED
                },
                match: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.LAST_NAME_REQUIRED
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.LAST_NAME_REQUIRED
                }
            }
        },
        phone: {
            type: Number,
            required: true,
            match: /^.{1,}$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.PHONE_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PHONE_REQUIRED
                },
                match: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PHONE_REQUIRED
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PHONE_REQUIRED
                }
            }
        },
        email: {
            type: String,
            trim: true,
            required: true,
            match: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.EMAIL_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.EMAIL_REQUIRED
                },
                match: {
                    errorCode: errorCodes.INVALID_VALUE,
                    message: errorMessages.EMAIL_INVALID_VALUE
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.EMAIL_REQUIRED
                }
            }
        },
        password: {
            type: String,
            trim: true,
            required: true,
            match: /^.{1,}$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.PASSWORD_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PASSWORD_REQUIRED
                },
                match: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PASSWORD_REQUIRED
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.PASSWORD_REQUIRED
                }
            }
        },
        confirm_password: {
            type: String,
            trim: true,
            required: true,
            match: /^.{1,}$/,
            errors: {
                type: {
                    errorCode: errorCodes.INVALID_DATA_TYPE,
                    message: errorMessages.CONFIRM_PASSWORD_INVALID_DT
                },
                required: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.CONFIRM_PASSWORD_REQUIRED
                },
                match: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.CONFIRM_PASSWORD_REQUIRED
                },
                allowNull: {
                    errorCode: errorCodes.REQUIRED_FIELD,
                    message: errorMessages.CONFIRM_PASSWORD_REQUIRED
                }
            }
        },
        friends: {
            type: Object,
            schema: {
                name: {
                    type: String,
                    required: true,
                    len: '2', 
                    errors: {
                        type: {
                            errorCode: errorCodes.INVALID_DATA_TYPE,
                            message: errorMessages.FRIEND_NAME_INVALID_DT
                        },
                        required: {
                            errorCode: errorCodes.REQUIRED_FIELD,
                            message: errorMessages.FRIEND_NAME_REQUIRED
                        },
                        len: {
                            errorCode: errorCodes.LENGTH_MISMATCH,
                            message: errorMessages.FRIENDS_NAME_LENGTH
                        }
                    }    
                },
                alias: {
                    type: String,
                    required: true,
                    len: '2',
                    errors: {
                        type: {
                            errorCode: errorCodes.INVALID_DATA_TYPE,
                            message: errorMessages.FRIEND_NAME_INVALID_DT
                        },
                        required: {
                            errorCode: errorCodes.REQUIRED_FIELD,
                            message: errorMessages.FRIEND_NAME_REQUIRED
                        },
                        len: {
                            errorCode: errorCodes.LENGTH_MISMATCH,
                            message: errorMessages.FRIENDS_NAME_LENGTH
                        }
                    }
                }
            }
        },
        custom: (data) => {
            if (data.password === data.confirm_password) {
                return null;
            } 
            return {
                errorCode: errorCodes.VALUES_MISMATCH,
                message: errorMessages.PASSWORD_CONFIRM_PASSWORD_MISMATCH
            };
        }
    },
    errors: {
        type: {
            errorCode: errorCodes.INVALID_DATA_TYPE,
            message: errorMessages.REGISTER_USER_INVALID_DT
        },
        required: {
            errorCode: errorCodes.REQUIRED_FIELD,
            message: errorMessages.REGISTER_USER_REQUIRED
        }
    }
}

这是输出,我得到了:

true
12
true
9
13
13
undefined

预期产出:

true
12
true
9
-----> I expect json object with errorCode and message to be printed here

3 个答案:

答案 0 :(得分:0)

你也是在函数末尾返回一个值(或者当你调用函数时使用return关键字)“。

[asset requestContentEditingInputWithOptions:[PHContentEditingInputRequestOptions new] completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) { NSURL *imageURL = contentEditingInput.fullSizeImageURL; }]; console.log('13')的最后一个陈述)之后,您没有返回任何内容。为了更好地使用它,递归函数没有checkSchema为假的基本情况。

编辑:事实上,schema.hasOwnProperty('schema')根本不会返回任何值。您将从checkSchema中使用的匿名函数返回值,但不会从forEach返回。恢复checkSchema语句而不是for应该修复它,但除非您想要 forEach返回该情况,否则将保留无基本案例问题。即使你这样做,明确的回报也会更好。

打印到日志的第二个最后一个语句是“undefined”,之后您的初始13函数调用返回,将假定的返回值分配给checkSchema,其值为然后记录。但由于在记录x后没有返回任何内容,因此13的值未定义。

答案 1 :(得分:0)

您没有从checkSchema函数返回任何内容。因此const x = checkSchema(fields, schema);将始终评估为未定义。

无论你从这个checkSchema函数返回什么,你实际上是从Object.keys().forEach的回调中返回的。因此,每当您从Object.keys().forEach的回调中返回任何内容时,这并不意味着您从checkSchema

返回该内容

答案 2 :(得分:0)

  

我见过太多与我有同样问题的问题,但是   无处不在,我发现问题在于他们没有使用返回   调用函数时的关键字。所以,请不要将其标记为   重复。

事实是,这确实是其中一个重复。所以这里有一些指示:

function checkSchema(fields, schema) {
    console.log(schema.hasOwnProperty('schema'));
    if (schema.hasOwnProperty('schema')) {
        Object.keys(schema.schema).forEach(fnElementJob);
    }
    console.log('13');
}

我已将传递的函数留给forEach,因为它与此函数的返回相关。你在那里看到任何退货声明吗?我不是因为JavaScript为你做的!

function checkSchema(fields, schema) {
    console.log(schema.hasOwnProperty('schema'));
    if (schema.hasOwnProperty('schema')) {
        Object.keys(schema.schema).forEach(fnElementJob);
    }
    console.log('13');
    return undefined; // implicit return for the last line of every function
}

return jost中的fnElementsJob会停止该功能,以便forEach可以继续使用下一个元素。我看到你有时会返回像return result;这样的值。该值将被丢弃,因为forEach仅用于副作用,并且不保留返回的值。

对于你的一些if我建议你总是考虑替代方案。想象一下这段代码:

if (innerSchema.type === Array && innerSchema.hasOwnProperty('unique')
    && innerSchema.unique && (new Set(fields[key])).size !== fields[key].length) {
    console.log('10');
    return sendError(innerSchema, 'unique');
}

if (innerSchema.type === String && innerSchema.hasOwnProperty('match')
    && innerSchema.match instanceof RegExp && !innerSchema.match.test(fields[key])) {
    console.log('11');
    return sendError(innerSchema, 'match');
}

如果您认为自己正在处理所有案件,可以这样做:

if (innerSchema.type === Array && innerSchema.hasOwnProperty('unique')
    && innerSchema.unique && (new Set(fields[key])).size !== fields[key].length) {
    console.log('10');
    return sendError(innerSchema, 'unique');
} else if (innerSchema.type === String && innerSchema.hasOwnProperty('match')
    && innerSchema.match instanceof RegExp && !innerSchema.match.test(fields[key])) {
    console.log('11');
    return sendError(innerSchema, 'match');
} else {
    throw new Error('Unhandled case');
}

一般来说,您的代码看起来非常复杂。您迫切需要抽象出来,以便您的功能变得更容易推理。这可能是此代码成为SO问题的主要原因。

我还建议您学习如何使用调试器而不是依赖于打印提示访问代码中的哪个分支。如果你可以设置一个断点并逐步查看代码,看看变量是什么,并且返回是你不会在Stack Overflow上发布这样一个明显的重复。如果您抽象并使用调试器,您的生活将变得更加容易。它还允许您制作更复杂的代码,但不要这样做。更新100行功能并不好玩。