// 立即执行函数,用于创建一个局部作用域,避免全局变量污染,在这个函数内部定义了一个基础的类继承机制以及一些与表单验证相关的功能代码。
(function () {
/**
* 所有类的基类,提供继承机制
* 这里通过一些巧妙的代码逻辑实现了一个简单的类继承模式,允许创建类并能方便地继承其他类的属性和方法,同时处理了在子类中调用父类同名方法的情况(通过 _super 机制)。
*/
// 用于检测当前JavaScript环境是否支持在函数内部通过名字访问函数自身的代码(用于判断是否是严格模式等情况),根据检测结果确定后续用于检测函数中是否包含 _super 关键字的正则表达式。
var initializing = false, fnTest = /xyz/.test(function () { xyz; })? /\b_super\b/ : /.*/;
// 创建一个名为 Class 的全局函数(在严格模式下,这里的this指向全局对象,非严格模式下指向调用者的上下文对象,一般在浏览器环境下是 window 对象),这个函数后续作为创建类的构造函数,初始时它只是一个空函数,后续会通过扩展功能来完善类的创建逻辑。
this.Class = function () { };
// 为 Class 函数添加一个 extend 方法,用于实现类的继承功能,通过传入一个包含属性和方法的对象(prop)来定义子类要扩展或重写的内容。
Class.extend = function (prop) {
// 获取当前类(调用 extend 方法的类)的原型对象,也就是父类的原型,后续用于在子类中访问父类的方法等操作,实现继承关系中的方法复用和重写逻辑。
var _super = this.prototype;
// 设置 initializing 为 true,表示正在初始化一个新的类(在继承过程中创建子类的原型对象阶段),用于后续在构造函数中判断是否是初始化阶段,避免一些不必要的操作(比如重复调用初始化方法等)。
initializing = true;
// 通过使用 new 操作符调用当前类(this 指向调用 extend 的类,也就是父类)的构造函数来创建一个新的对象,这个对象将作为子类的原型对象,它会继承父类原型上的属性和方法。
var prototype = new this();
// 完成子类原型对象的创建后,将 initializing 重新设置为 false,表示初始化阶段结束。
initializing = false;
// 遍历传入的属性对象(prop)中的每个属性(一般是方法或新定义的属性),进行相应的处理,判断是普通属性赋值还是函数属性的特殊处理(涉及到父类同名函数的调用情况)。
for (var name in prop) {
// 判断当前属性(prop[name])是否是函数类型,并且父类原型上是否存在同名的函数属性(_super[name]),同时还要检测该函数的代码中是否包含 _super 关键字(通过 fnTest 正则表达式判断),如果满足这些条件,则进行特殊的函数包装处理,用于实现子类中调用父类同名函数的功能。
prototype[name] = typeof prop[name] == "function"
&& typeof _super[name] == "function" && fnTest.test(prop[name])?
// 如果满足上述条件,返回一个新的函数,这个函数内部实现了先临时保存当前对象的 _super 属性(可能是之前保存的父类同名函数引用),然后将 _super 属性指向父类的同名函数,接着调用传入的函数(也就是子类重写的函数),在调用完成后恢复 _super 属性的原始值,最后返回函数执行的结果,这样就巧妙地实现了在子类函数中通过 this._super 调用父类同名函数的功能。
(function (name, fn) {
return function () {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
}
// 创建一个新的函数(作为子类的构造函数),在这个构造函数中,如果当前不是处于初始化阶段(即不是在创建子类原型对象过程中),并且子类定义了 init 方法(一般用于初始化操作),则调用子类的 init 方法,并传入相应的参数,实现子类实例化时的初始化逻辑。
function Class() {
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// 将前面创建并处理好的原型对象(prototype)赋值给新创建的子类构造函数(Class)的原型属性,这样通过这个构造函数创建的实例就能继承原型对象上的属性和方法了。
Class.prototype = prototype;
// 修复子类构造函数的 constructor 属性,将其指向正确的子类构造函数本身,确保在使用 instanceof 等操作时能正确识别对象的构造函数类型。
Class.prototype.constructor = Class;
// 将 extend 方法也添加到子类的构造函数上,使得子类也能继续通过调用 extend 方法来实现进一步的继承,形成继承链,这里通过 arguments.callee 引用当前的 extend 函数本身来实现这个功能(不过在严格模式下,arguments.callee 是不允许使用的,代码可能需要调整)。
Class.extend = arguments.callee;
// 返回创建好的子类构造函数,外部可以通过调用这个构造函数来创建子类的实例对象,完成类的继承和实例化操作。
return Class;
};
})();
// 使用前面定义的 Class 类及继承机制创建一个名为 validate 的类,它继承自 Class,主要用于实现表单验证相关的功能,比如验证表单字段是否符合特定的规则、显示错误提示等。
var validate = Class
.extend({
// 默认配置对象,包含了验证相关的各种默认设置,如验证规则(rules)、提交表单时执行的函数(submitFun)、用于显示错误信息的HTML标签模板(errorLabel)以及出现错误时执行的函数(errorFun)等属性。
defaultCfg: {
rules: {},
submitFun: function () { },
errorLabel: '',
errorFun: function () { }
},
// 初始化方法,在创建 validate 类的实例时会被调用(如果不是处于类继承的初始化阶段,也就是 new 操作实例化时会执行),用于初始化验证相关的配置信息以及绑定一些事件处理等操作。
init: function (cfg) {
// 使用 jQuery 的 extend 方法将传入的配置对象(cfg)与默认配置对象(defaultCfg)进行合并,得到最终的配置对象并赋值给 this.cfg,这样既可以使用默认配置,又能通过传入的参数覆盖部分默认配置。
this.cfg = $.extend({}, this.defaultCfg, cfg);
// 初始化一个标志变量(flag)为0,用于记录表单验证过程中是否出现错误等情况,后续根据这个标志来决定是否执行提交表单等操作。
this.flag = 0;
// 调用 toAction 方法并传入当前实例对象(this),开始进行表单验证相关的操作,比如遍历验证规则并对相应的表单字段进行验证。
this.toAction(this);
// 如果验证过程中没有出现错误(flag 仍然为0),则解除表单字段的 keyup 事件绑定(可能是之前绑定的用于实时验证的事件,这里在全部验证通过后解除,避免重复验证等不必要的操作),然后执行提交表单的函数(submitFun),提交表单数据等操作(具体由这个函数的实现决定)。
if (this.flag == 0) {
for (var i in this.cfg.rules) {
$("#" + i).unbind("keyup");
}
this.cfg.submitFun();
}
},
// 用于执行表单验证的主要方法,接收一个表示当前验证实例的对象(that),在这个方法内部会遍历配置的验证规则,并针对每个规则调用 toVal 方法对相应的表单字段进行验证操作。
toAction: function (that) {
for (var i in that.cfg.rules) {
this.toVal("#" + i, that.cfg.rules[i]);
}
},
// 具体验证单个表单字段的方法,接收一个表单字段的选择器(ele,一般是类似 "#inputId" 的形式,表示通过ID选择元素)以及对应的验证规则常量(constant,用于确定使用哪种验证规则来验证该字段),根据验证结果调用相应的方法来显示或移除错误提示信息。
toVal: function (ele, constant) {
// 调用 validateConstant 对象中对应的验证规则函数(通过 constant 作为键来查找),传入表单字段的值(通过 jQuery 获取元素的值)进行验证,如果验证通过(返回 true),则调用 toRemoveError 方法移除该字段的错误提示信息,否则调用 toShowError 方法显示相应的错误提示信息(传入错误提示消息,通过 errorMsg 对象根据验证规则常量获取)。
validateConstant[constant].test($(ele).val())?
this.toRemoveError(ele) : this.toShowError(ele, errorMsg[constant]);
},
// 用于移除表单字段错误提示信息的方法,接收一个表单字段的选择器(ele),首先获取当前验证实例对象(通过 this 赋值给 that 变量),然后判断该字段所在的表单组元素(通过 closest 方法查找父级的.form-group 元素)是否有 "not-allow" 属性,如果有则表示之前添加过错误提示等限制属性,现在需要移除这些属性,恢复表单字段和表单组的原始样式(通过 removeAttr 方法移除相关样式属性),同时移除该字段后面的错误提示标签(通过 next 方法找到并 remove 方法移除),最后重新绑定 keyup 事件,当用户再次输入内容时重新进行验证(调用 toVal 方法)。
toRemoveError: function (ele) {
var that = this;
if ($(ele).closest(".form-group").attr("not-allow")) {
$(ele).removeAttr("style").closest(".form-group").removeAttr("style")
.removeAttr("not-allow");
$(ele).next().remove();
$(ele).keyup(function () {
ele = ele.replace("#", "");
that.toVal("#" + ele, that.cfg.rules[ele]);
});
}
},
// 用于显示表单字段错误提示信息的方法,接收一个表单字段的选择器(ele)以及对应的错误提示消息(message),首先根据配置的错误标签模板(cfg.errorLabel)创建一个包含错误消息的