前言
前端技术井喷发展后,不知现在还有多少小伙伴跟我一样,最开编写javascirpt的目的仅是做表单验证。从收藏正则表达式编写if-else,渡过boorstrap与各种jquery的validate插件的时代,到现在各厂UI库完整封装的表单组件直接使用,每一次,数据校对的功能犹如业务的附属品,换一个业务,写一套校验。在实际工作中,基础开发的重复劳动占比明显。类与继承的编程方式一定程度上规避了冗余,但根本办法是对功能进行更高一级的抽象化。
与其他插件的区别:
-
单一纯粹
与moment.js只做date的相关功能一样,validate.js只专注于对数据的校验。且不需要任何的外部依赖。
validate函数的参数:
- attributes --被校验的对象
- constraints --校验规则,相当于其他validate的rules
- options —(可选)配置扩展
-
可拆分/集成校验规则
约束具有以下格式:
{ <attribute>:
{ <validator name>: <validator options> }
}
约束规则支持传参配置与自定义函数,很大程度上提高了复用。而在方式上,支持单值/嵌套等方式校验也极大提高代码可读性。
-
自由的输出格式
默认的校验结果为:
- 通过:undefined
- 不通过:{'attribute':[ errorMessage ]}
通过自定义扩展,可支持boolean,object,array等各类型返回值,甚至抛出异常。
-
提供丰富的校验规则
支持对js基础类型数据,bom内置对象,甚至dom/jquery对象。 这代表着validate.js不仅可以低成本的集成入各种旧项目中,甚至可以一个约束同时支持业务逻辑,表单验证,后端(如果是nodejs)传参校验。
validate的使用
-
基础函数使用
var constraints = {
username: {
presence: true,
exclusion: {
within: ["nicklas"],
message: "'%{value}' is not allowed"
}
},
password: {
presence: true,
length: {
minimum: 6,
message: "must be at least 6 characters"
}
}
};
validate({password: "bad"}, constraints);
// => {
// "username": ["Username can't be blank"],
// "password": ["Password must be at least 6 characters"]
// }
validate({username: "nick", password: "better"}, constraints);
// => undefined
validate({username: "nicklas", password: "better"}, constraints);
// => {"username": ["Username 'nicklas' is not allowed"]}
validate({password: "better"}, constraints, {fullMessages: false});
// => {"username": ["can't be blank"]}
validate({}, constraints, {format: "flat"});
// => ["Username can't be blank", "Password can't be blank"]
validate({username: "nicklas", password: "bad"}, constraints, {format: "detailed"});
// => [
// {
// "attribute": "username",
// "value": "nicklas",
// "validator": "exclusion",
// "globalOptions": {
// "format": "detailed"
// },
// "attributes": {
// "username": "nicklas",
// "password": "bad"
// },
// "options": {
// "within": [
// "nicklas"
// ],
// "message": "'%{value}' is not allowed"
// },
// "error": "Username 'nicklas' is not allowed"
// },
// {
// "attribute": "password",
// "value": "bad",
// "validator": "length",
// "globalOptions": {
// "format": "detailed"
// },
// "attributes": {
// "username": "nicklas",
// "password": "bad"
// },
// "options": {
// "minimum": 6,
// "message": "must be at least 6 characters"
// },
// "error": "Password must be at least 6 characters"
// }
// ]
validate({}, {username: {presence: {message: "^You must pick a username"}}});
// => {"username": ["You must pick a username"]}
attributes参数只能是正常js对象或element,不支持其他如backbone的viewmodel。
如果attributes为HTML/DOM/jQuery element,需要在函数执行前调用collectFormValues。
fullMessages配置表示是否在异常信息中加入参数名与参数值。
如果需要在异常信息为开头则在message中加入^前缀,需要输出^则编写\^ 。
messeage中的%{value}将会先执行validate.stringifyValue再传入(validate.prettify函数中的默认调用)。
validate.prettify函数可提供string格式化。具体见辅助函数部分。
-
异步验证Async validation
function success(attributes) {
console.log("Success!", attributes);
}
function error(errors) {
if (errors instanceof Error) {
// This means an exception was thrown from a validator
console.err("An error ocurred", errors);
} else {
console.log("Validation errors", errors);
}
}
var constraints = {
name: {
presence: true
},
// This is so the country doesn't get removed when cleaning the attributes
country: {}
};
var attributes = {
name: "Nicklas",
country: "Sweden",
someMaliciousAttribute: "scary value"
};
// Will call the success function and log {
// name: "Nicklas",
// country: "Sweden"
// }
validate.async(attributes, constraints).then(success, error);
// Will call the error function
validate.async({}, constraints).then(success, error);
function ValidationErrors(errors, options, attributes, constraints) {
Error.captureStackTrace(this, this.constructor);
this.errors = errors;
this.options = options;
this.attributes = attributes;
this.constraints = constraints;
}
ValidationErrors.prototype = new Error();
// This isn't supported by the ES6 promises
validate.async({}, constraints, {wrapErrors: ValidationErrors})
.then(success)
.catch(ValidationErrors, function(error) {
// Handle the validation errors
console.log("ValidationErrors", error);
})
.catch(function(error) {
// Handle other errors;
console.log("SystemError", error);
});
// Supporting another promise implementation (RSVP in this case)
validate.Promise = RSVP.Promise;
validate支持异步调用,在原先validate基础上返回Promise。当环境/全局变量不支持Promise时,将抛出异常。
异步函数增加两个option参数。1.cleanAttributes:当不为false时,将在promise的resolve之前调用validate.cleanAttributes函数。2.wrapErrors :自定义方法捕捉异常。
任何符合A+规范的promise都可以通过重写validate.Promise来实现promise。请勿使用jquery的promise,因为它并非A+规范。
-
单值验证Single validation
validate.single(null, {presence: true, email: true});
// => ["can't be blank"]
validate.single("foo", {presence: true, email: true});
// => ["is not a valid email"]
validate.single("foo@bar.com", {presence: true, email: true});
// => undefined
针对基础数据类型的校验
-
嵌套验证Nested validation
var constraints = {
"addresses.shipping": {
presence: true
},
"addresses.shipping.street": {
format: {
// Must be numbers followed by a name
pattern: "^[0-9]+ .+$",
message: "^The street for the shipping address must be a valid street name"
}
}
};
validate({}, constraints);
// => {"addresses.shipping": ["Addresses shipping can't be blank"]}
validate({addresses: {shipping: {street: "Foobar"}}}, constraints);
// => {"addresses.shipping.street": ["The street for the shipping address must be a valid street name"]}
validate({"foo.bar": 3}, {"foo\\.bar": {numericality: {even: true}}});
// => {"foo\.bar": ["Foo bar must be even"]}
-
默认选项Default options
var constraints = {
name: {
presence: true
}
};
validate.options = {format: "flat"};
validate.async.options = {format: "flat", cleanAttributes: false};
validate.validators.presence.options = {message: "can't be empty"};
// The default options will be used for both the
// validator and the validate function
validate({}, constraints);
// => ["Name can't be empty"]
// The default options are not used if the constraints options are falsy
validate({format: "grouped"}, {});
// => undefined
-
错误格式Error formatting
var constraints = {
username: {
presence: true,
exclusion: {
within: ["nicklas"],
message: "'%{value}' is not allowed"
}
},
password: {
presence: true,
length: {
minimum: 6,
message: "must be at least 6 characters"
}
}
};
validate({}, constraints, {format: "flat"});
// => ["Username can't be blank", "Password can't be blank"]
validate({username: "nicklas", password: "bad"}, constraints, {format: "detailed"});
// => [
// {
// "attribute": "username",
// "value": "nicklas",
// "validator": "exclusion",
// "globalOptions": {
// "format": "detailed"
// },
// "attributes": {
// "username": "nicklas",
// "password": "bad"
// },
// "options": {
// "within": [
// "nicklas"
// ],
// "message": "'%{value}' is not allowed"
// },
// "error": "Username 'nicklas' is not allowed"
// },
// {
// "attribute": "password",
// "value": "bad",
// "validator": "length",
// "globalOptions": {
// "format": "detailed"
// },
// "attributes": {
// "username": "nicklas",
// "password": "bad"
// },
// "options": {
// "minimum": 6,
// "message": "must be at least 6 characters"
// },
// "error": "Password must be at least 6 characters"
// }
// ]
validate.formatters.custom = function(errors) {
return errors.map(function(error) {
return error.validator;
});
};
validate({username: "nicklas", password: "bad"}, constraints, {format: "custom"});
// => ["exclusion", "length"];
validate.js支持不同的错误返回格式
- grouped:依属性分组排列信息
- flat:返回数组
- detailed:返回对象
还可以通过validate.formatters来自定义返回格式
自定义一个验证器
validate.validators.custom = function(value, options, key, attributes) {
console.log(value);
console.log(options);
console.log(key);
console.log(attributes);
return "is totally wrong";
};
// Will log:
// - "some value"
// - "some options"
// - "foo"
// - {"foo": "some value"}
validate({foo: "some value"}, {foo: {custom: "some options"}});
// => {foo: ["Foo is totally wrong"]}
编写自己的验证器非常简单。只需将其添加到validate.validators对象中,它就会自动被拾取。
- value - 值在属性对象中的确切值。
- options - 验证器的选项。保证不是null或undefined。
- key - 属性名称。
- attributes - 整个属性对象。
- globalOptions - 调用时传递的选项 validate(将始终为对象,非null)
返回null或undefined将视为通过。返回字符串或字符串数数组为错误信息。
不需要再错误信息中添加属性名,函数会自动完成此功能。
自定义异步验证器
validate.validators.myAsyncValidator = function(value) {
return new validate.Promise(function(resolve, reject) {
setTimeout(function() {
if (value === "foo") resolve();
else resolve("is not foo");
}, 100);
});
};
var constraints = {name: {myAsyncValidator: true}}
, success = alert.bind(this, "The validations passed")
, error = function(errors) {
alert(JSON.stringify(errors, null, 2));
};
// Will call the success callback
validate.async({name: "foo"}, constraints).then(success, error);
// Will call the error callback with {name: ["Name is not foo"]} as the first argument
validate.async({name: "bar"}, constraints).then(success, error);
与普通验证器不同的是异步验证器返回一个new validate.Promise,并通过resolve与reject来控制校验是否通过。
内置验证器
-
date与datetime
官方建议直接使用moment.js
// Before using it we must add the parse and format functions
// Here is a sample implementation using moment.js
validate.extend(validate.validators.datetime, {
// The value is guaranteed not to be null or undefined but otherwise it
// could be anything.
parse: function(value, options) {
return +moment.utc(value);
},
// Input is a unix timestamp
format: function(value, options) {
var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss";
return moment.utc(value).format(format);
}
});
validate({}, {departure: {datetime: true}});
// => undefined
validate({departure: "foobar"}, {departure: {datetime: true}});
// => {"departure": ["Departure must be a valid date"]}
validate({departure: "2013-12-11 10:09:08"}, {departure: {datetime: true}});
// => undefined
validate({departure: "2013-12-11 10:09:08"}, {departure: {datetime: {dateOnly: true}}});
// => {"departure": ["Departure must be valid date"]}
var constraints = {
birthday: {
datetime: {
dateOnly: true,
latest: moment.utc().subtract(18, 'years'),
message: "^You need to be at least 18 years old"
}
}
};
validate({birthday: "3013-11-14"}, constraints);
// => {"birthday": ["You need to be at least 18 years old"]}
-
email
var constraints = {
from: {
email: true
}
};
validate({from: null}, constraints);
// => undefined
validate({from: ""}, constraints);
// => {"email": ["From is not a valid email"]}
validate({from: "nicklas@ansman"}, constraints);
// => {"email": ["From is not a valid email"]}
// Any TLD is allowed
validate({from: "nicklas@foo.faketld"}, constraints);
// => undefined
// Upper cased emails are allowed
validate({from: "NICKLAS@ANSMAN.SE"}, constraints);
// => undefined
constraints = {
from: {
email: {
message: "doesn't look like a valid email"
}
}
};
validate({from: "foobar"}, constraints);
// => {"email": ["From doesn't look like a valid email"]}
// It allows unicode
validate({from: "first.läst@example.com"}, constraints);
// => undefined
由于校验email的规则可能很复杂,可以通过设置validate.validators.email.PATTERN 自定义所的正则表达式匹配。
-
equality
var constraints = {
confirmPassword: {
equality: "password"
}
};
validate({password: "foo", confirmPassword: "foo"}, constraints);
// => undefined
validate({password: "foo", confirmPassword: "bar"}, constraints);
// => {confirmPassword: ["Confirm password is not equal to password"]}
constraints = {
complexAttribute: {
equality: {
attribute: "otherComplexAttribute",
message: "is not complex enough",
comparator: function(v1, v2) {
return JSON.stringify(v1) === JSON.stringify(v2);
}
}
}
};
validate({complexAttribute: [1,2,3], otherComplexAttribute: [1,2,3]}, constraints);
// => undefined
validate({complexAttribute: [1,2,3], otherComplexAttribute: [3,2,1]}, constraints);
// => {complexAttribute: ["Complex attribute is not complex enough"]}
默认通过 ===来判断,也可以自定义函数与其他字段进行匹配,通过返回true、false校验结果。
-
exclusion
var restrictedDomains = ["jp", "ch"];
validate({}, {subdomain: {exclusion: restrictedDomains}});
// => undefined
validate({subdomain: "jp"}, {subdomain: {exclusion: restrictedDomains}});
// => {"size": ["jp is restricted"]}
var constraints = {
subdomain: {
exclusion: {
within: {jp: "Japan", "ch": "China"},
message: "^We don't support %{value} right now, sorry"
}
}
};
validate({subdomain: "jp"}, constraints);
// => {"subdomain": ["We don't support Japan right now, sorry"]}
validate({subdomain: "com"}, constraints);
// => undefined
-
format
var pattern = /\d{5}(-\d{4})?/;
validate({}, {zipCode: {format: pattern}});
// => undefined
validate({zipCode: "foobar"}, {zipCode: {format: pattern}});
// => {"zipCode": ["Zip code is invalid"]};
validate({zipCode: "12345"}, {zipCode: {format: pattern}});
// => undefined
var constraints = {
username: {
format: {
pattern: "[a-z0-9]+",
flags: "i",
message: "can only contain a-z and 0-9"
}
}
};
validate({username: "Nicklas!"}, constraints);
// => {"username": ["Username can only contain a-z and 0-9"]}
validate({username: "Nicklas"}, constraints);
// => undefined
-
inclusion
var sizes = ["small", "medium", "large"];
validate({}, {size: {inclusion: sizes}});
// => undefined
validate({size: "xlarge"}, {size: {inclusion: sizes}});
// => {"size": ["xlarge is not included in the list"]}
var constraints = {
size: {
inclusion: {
within: {"Small": "s", "Medium": "m", "Large": "l"},
message: "^We're currently out of %{value}"
}
}
};
validate({size: "Extra large"}, constraints);
// => {"size": ["We're currently out of Extra large"]}
validate({size: "Medium"}, constraints);
// => undefined
-
length
var constraints = {
key1: {length: {is: 3}},
key2: {length: {minimum: 20}},
key3: {length: {maximum: 3}},
key4: {
length: {
minimum: 3,
tooShort: "needs to have %{count} words or more",
tokenizer: function(value) {
return value.split(/\s+/g);
}
}
}
};
validate({}, constraints);
// => undefined
// This is because nil and undefined are valid values.
// Use the presence validator if you don't want to allow undefined values.
var values = {
key1: "wrong length",
key2: "too short",
key3: "too long",
key4: "too short"
};
validate(values, constraints);
// => {
// "key1": ["Key1 is the wrong length (should be 3 characters)"],
// "key2": ["Key2 is too short (minimum is 20 characters)"],
// "key3": ["Key3 is too long (maximum is 3 characters)"],
// "key4": ["Key4 needs to have 3 words or more"]
// }
-
numericality
// null and undefined are valid values regardless of the options
validate({}, {duration: {numericality: true}});
//= > undefined
validate({duration: "foobar"}, {duration: {numericality: true}});
// => {"duration": ["Duration is not a number"]}
validate({duration: "3"}, {duration: {numericality: true}});
// => undefined
validate({duration: "03"}, {duration: {numericality: true}});
// => undefined
validate({duration: "03"}, {duration: {numericality: {strict: true}}});
// => {"duration": ["Duration must be a valid number"]}
validate({duration: "3"}, {duration: {numericality: {noStrings: true}}});
// => {"duration": ["Duration is not a number"]}
validate({duration: "7"}, {duration: {numericality: {divisibleBy: 3}}});
// => {"duration": ["Duration must be divisible by 3"]}
var constraints = {
duration: {
numericality: {
onlyInteger: true,
greaterThan: 0,
lessThanOrEqualTo: 30,
even: true,
notEven: "must be evenly divisible by two"
}
}
};
validate({duration: 3.14}, constraints);
// => {"duration": ["Duration must be an integer"]}
validate({duration: 4711}, constraints);
// => {
// "duration": [
// "Duration must be less than or equal to 30",
// "Duration must be evenly divisible by two"
// ]
// }
对于数字的校验,如果value为sting,默认通过+value转换。也可通过noStrings:true来禁止转换。
以下为限制类型:
- onlyInteger:只允许整数
- strict:严格的字符换数字校验,不允许0位开头的string数字
- greaterThan:大于
- greaterThanOrEqualTo:大于等于
- equalTo:等于
- lessThanOrEqualTo:小于等于
- lessThan:小于
- divisibleBy:不等于
- odd:奇数
- even:偶数
相对应的错误消息配置:
-
notValid
-
notInteger
-
notGreaterThan
-
notGreaterThanOrEqualTo
-
notEqualTo
-
notLessThan
-
notLessThanOrEqualTo
-
notDivisibleBy
-
notOdd
-
notEven
-
presence
validate({}, {username: {presence: true}});
// => {"username": ["Username can't be blank"]}
validate({username: "ansman"}, {username: {presence: true}});
// => undefined
validate({input: ""}, {input: {presence: true}});
// => undefined
validate({input: ""}, {input: {presence: {allowEmpty: false}}});
// => {"input:" ["Input can't be blank"]}
validate({}, {username: {presence: {message: "is required"}}});
// => {"username": ["Username is required"]}
validate.validators.presence.message = "is required";
validate({}, {username: {presence: true}});
// => {"username": ["Username is required"]}
默认为null与undefined,若allowEmpty:false,以下不通过:
-
{} (empty objects)
-
[] (empty arrays)
-
"" (empty string)
-
" " (whitespace only string)
-
type
validate({myAttribute: "value"}, {myAttribute: {type: "string"}});
// => undefined
validate({myAttribute: true}, {myAttribute: {type: "string"}});
// => {"myAttribute": ["My attribute must be of type string"]}
validate({myAttribute: "other"}, {myAttribute: {type: {type: function(value) { return value === "stuff"; }}}});
// => {"myAttribute": ["My attribute must be of the correct type"]}
validate.validators.type.types.customType = function (value) { return value === "stuff"; };
validate({myAttribute: true}, {myAttribute: {type: "customType"}});
// => {"myAttribute": ["My attribute must be of type customType"]}
validate.validators.type.messages.customType = "is simply wrong";
validate({myAttribute: true}, {myAttribute: {type: "customType"}});
// => {"myAttribute": ["My attribute is simply wrong"]}
-
url
validate({website: "http://google.com"}, {website: {url: true}});
// => undefined
validate({website: "google.com"}, {website: {url: true}});
// => {"website": ["Website is not a valid url"]}
validate({website: "ftp://google.com"}, {website: {url: true}});
// => {"website": ["Website is not a valid url"]}
validate({website: "ftp://google.com"}, {
website: {
url: {
schemes: ["ftp"]
}
}
});
// => undefined
validate({website: "http://localhost"}, {website: {url: true}});
// => {"website": ["Website is not a valid url"]}
validate({website: "http://localhost"}, {
website: {
url: {
allowLocal: true
}
}
});
// => undefined
validate({website: "data:,Hello%2C%20World!"}, {website: {url: true}});
// => {"website": ["Website is not a valid url"]}
validate({website: "data:,Hello%2C%20World!"}, {
website: {
url: {
allowDataUrl: true
}
}
}
);
// => undefined
- schemes:默认只允许"http", "https",可匹配正则。
- allowLocal :默认为false,是否允许本地ip,如 10.0.1.1 或 localhost。
- allowDataUrl :默认为false,是否允许转义符。
工具函数
-
capitalize
validate.capitalize("foobar");
// => "Foobar"
-
cleanAttributes
var whitelist = {
name: true,
"address.street": true,
"address.postal": true,
"something\\.with\\.periods": true
};
var attributes = {
name: "Nicklas",
address: {
street: "Drottninggatan 98",
postal: "111 60"
},
"something.with.periods": "some value",
id: 4711,
createdAt: "1970-01-01 00:00"
};
validate.cleanAttributes(attributes, whitelist);
// => {
// name: "Nicklas",
// address: {
// street: "Drottninggatan 98",
// postal: "111 60"
// },
// "something.with.periods": "some value"
// }
var constraints = {
name: {
presence: true
},
"address.street": {},
"address.postal": {},
"something\\.with\\.periods": {}
};
validate.cleanAttributes(attributes, constraints);
// => {
// name: "Nicklas",
// address: {
// street: "Drottninggatan 98",
// postal: "111 60"
// },
// "something.with.periods": "some value"
// }
返回白名单中的属性的object
-
collectFormValues
<form id="login">
<input type="text" name="username" value="ansman">
<input type="password" name="password" value="correcthorsebatterystaple">
<input type="checkbox" name="remember-me" checked>
<input type="hidden" name="some-hidden-value" data-ignored>
</form>
<script>
var form = document.querySelector("form#login");
validate.collectFormValues(form);
// => {username: "ansman", password: "correcthorsebatterystaple", remember-me: false}
</script>
验证fromData,配置:
- nullify:空字符串为null
- trim:去前后空格
不需要验证的element增加data-ignored即可。
-
contains
validate.contains({}, "foo");
// => false
validate.contains({foo: "bar"}, "foo");
// => true
validate.contains([1, 2, 3], 4);
// => false
validate.contains([1, 2, 3], 3);
// => true
-
extend
var o1 = {foo: "bar"}
, o2 = {baz: "quux"};
validate.extend(o1, o2) === o1;
// => true
o1;
// => {foo: "bar", baz: "quux"};
o2;
// => {bar: "quux"};
// Makes a copy of o1, doesn't modify o1
validate.extend({}, o1);
// => {foo: "bar", baz: "quux"};
// o1 is not touched
validate.extend({}, o1) === o1;
// => false
-
format
validate.format("Hi, my name is %{name}", {name: "Nicklas"});
// => "Hi, my name is Nicklas"
validate.format("%%{this} will not be replaced", {this: "that"});
// => "%{this} will not be replaced"
%{...}代表在属性中的值,买message中用%%{...}来打印%{...}
-
getDeepObjectValue
validate.getDeepObjectValue({foo: "bar"}, "foo");
// => "bar"
validate.getDeepObjectValue({foo: {bar: {baz: "quux"}}}, "foo.bar.baz");
// => "quux"
validate.getDeepObjectValue({"foo.bar": "baz"}, "foo\\.bar");
// => "baz"
-
isArray
validate.isArray({});
// => false
validate.isArray([]);
// => true
-
isBoolean
validate.isBoolean("true");
// => false
validate.isBoolean(true);
// => true
-
isDate
validate.isDate(new Date());
// => true
validate.isDate(null);
// => false
validate.isDate({});
// => false
-
isDefined
validate.isDefined("foobar");
// => true
validate.isDefined(null);
// => false
validate.isDefined(undefined);
// => false
-
isDomElement
validate.isDomElement({});
// => false
validate.isDomElement(document.createElement("div"));
// => true
-
isEmpty
validate.isEmpty({});
// => true
validate.isEmpty(null);
// => true
validate.isEmpty("");
// => true
validate.isEmpty(" ");
// => true
validate.isEmpty("foo");
// => false
validate.isEmpty({foo: "bar"});
// => false
-
isFunction
validate.isFunction("foobar");
// => false
validate.isFunction(function() {});
// => true
-
isHash
validate.isHash([]);
// => false
validate.isHash({foo: "bar"});
// => true
-
isInteger
validate.isInteger("foobar");
// => false
validate.isInteger(3.14);
// => false
validate.isInteger(3);
// => true
-
isNumber
validate.isNumber("foobar");
// => false
validate.isNumber(3.14);
// => true
-
isObject
validate.isPromise({});
// => false
validate.isPromise(new Promise(function() {}));
// => true
validate.isPromise({then: function() {}});
// => true
-
isString
validate.isString("");
// => true
validate.isString({});
// => false
-
prettify
validate.prettify("This.is_a-weirdString\\.");
// => "this is a weird string."
-
根据 .分割
-
反斜杠转义
-
_ 与 - 代替空格
-
切割驼峰单词
-
全小写
-
数字转字符串最多保留两位小数
-
对象 toString 转string
-
数组join(',')连接
-
result
// Not a function, returns the first argument
validate.result("foobar", 1, 2);
// => "foobar"
// Returns the result of Math.max(1, 2)
validate.result(Math.max, 1, 2);
// => 2
// Doesn't work since String#toUpperCase is not a pure function
validate.result("foo".toUpperCase);
// => Uncaught TypeError: String.prototype.toUpperCase called on null or undefined
validate.result(value, [arguments...])