/*
* @Author: 我爱科技论坛
* @Time: 20180706
* @Desc: 实现一个类似于JQuery功能的框架
// 公共框架
// 种子模块:命名空间、对象扩展、数组化、类型的判定、domReady机制,无冲突处理
(function (xframe) {
// 需要参与链式访问的(必须使用prototype的方式来给对象扩充方法)
xframe.extend({
each: function (fn) {
var i = 0,
len = this.length;
for (; i < len; i++) {
// call第一个参数传递的实际上就是this的执行,后面的参数就是目标函数fn需要传递的参数(可省略)
// this[i] 里面的取值方式类似于json取值,每一个参数存储了选择器获取的所有的nodeList元素集合中的一个元素
fn.call(this[i]);
}
return this;
}
});
// 不需要参与链式访问的
/*公共部分*/
xframe.extend(xframe, {});
/*字符串处理模块*/
xframe.extend(xframe, {
/*
* 下面的这几个都会用到正则表达式,会在后面补充
* camelCase函数的功能就是将形如background-color转化为驼峰表示法:backgroundColor
* */
camelCase: function (str) {
// all: -c, letter: c
return str.replace(/-(w)/g, function (all, letter) {
// 把所有的字母都转换为大写的状态
return letter.toUpperCase();
});
},
/**
* 去掉左边的空格 str = ' ()'
* @param str
* @returns {*}
*/
ltrim: function (str) {
/*
^ :表示以XX开头
s: 表示空格
*: 表示匹配零个或者多个
g: 表示匹配全部,如果没有的话默认只会匹配一个
(^s*): 表示以空格开头的一个或者多个字符
str.replace(, ''): 替换……
----------------------------------------------------[其他用法归纳]-------------------------------------
^, $: 匹配字符串开始,结束的位置 eg:
g, i:匹配所有,不区分大小写的字符串; eg: /a/g, /a/i
*, +, ?: 匹配任意次数, 匹配前面的字符一次或者多次, 0次或者1次
[] : 匹配一个字符集合; eg: [a-z]所有小写字母的集合, [0-9]所有数字的集合
eg: [a-zA-Z]所有大小写字母的集合
脱字符^: 匹配任何不在该集合中的字符,与上面的用法正好相反
{}: 指定重复前面的一个字符多少遍 eg:{N} 重复n遍
eg:{n, m}重复n-m遍
eg: {n, }至少重复n遍
eg:{,m}至多重复m遍
// 【熟记:同类记忆法】
s: 表示空格:包括空格、换行、回车、tab,等价于[
f]
S: 匹配非空格字符,等价于[^
f]
d: 表示十进制数字,等价于[0-9]
D: 匹配一个非数字字符, 等价于[^0-9]
w(小写): 表示字母或者数字,等价于[a-zA-Z0-9]
W: 非字母且非数字,与w相反,等价于:[^a-zA-Z0-9]*
* */
return str.replace(/(^s*)/g, '');
},
/* 去掉右边的空格, str = '() '
* @param str
*/
rtrim: function (str) {
return str.replace(/(s*$)/g, '');
},
/**
* 用于去掉两边的空格(去掉所有的空格) str =' () '
* @param str
* @returns {*}
*/
trimOld: function (str) {
return str.replace(/(s*$)/g, '');
},
/**
* 【使用模板来实现一个简单的数据绑定】
* 实现简单的数据绑定: @(name), @(sex)
* data: var user = {name : 'xiugang', role, '钻石会员'}
* str: = '欢迎@(name), 等级:@(role)光临本站!';
* @param str 原始的数据格式
* @param data 需要绑定的数据对象,是一个json格式的数据, json = {name : 'xiuxiu', age : 18}
* @returns {*}
*/
formateString: function (str, data) {
// 使用后面的值去替换掉前面的值
// 细节分析:((w+))使用括号匹配的值在JavaScript中实际上就是一个$1, 把这个参数传给match
// (w+) 第二个括号实际上匹配到的就是一个$2, 把这个参数传给key
// match: @(name), @(age), @(sex)
// key: name, age, sex
return str.replace(/@((w+))/g, function (match, key) {
// 先判断有没有匹配到相应的字符串
// 找到@()开始的字符串, 使用数据域中的数据去替换
// 如果json数据data里面么有找到相应的data[key]数据,返回的实际上就是一个空的字符串
return typeof data[key] === 'undefined' ? '' : data[key];
});
},
/**
* @param str
* @returns {*}
*/
trimLeft: function (str) {
return str.replace(/^s*/g, '');
},
/**
* @param str
* @returns {*}
*/
trimRight: function (str) {
return str.replace(/s*$/g, '');
},
/**
* 去掉所有的空格(两边的空格), 可以针对任意格式的字符串
* 先去掉左边的空格,然后去掉右边的空格
* @param str
* @returns {*}
*/
trim: function (str) {
// var regx = '/^s*s*$/g';
// return str.replace(regx, '');
// | 表示或的意思, 也就是满足| 左边的也成立, 满足 | 右面的也成立
// (^s*) 表示的就是以0个空格或者多个空格开头
// (s*$) 的意思就是, 以0个空格或者多个空格结尾
// /…/g 是正则表达式的属性, 表示全文匹配, 而不是找到一个就停止
return str.replace(/(^s*)|(s*$)/g, "");
//return this.trimRight(this.trimLeft(str));
},
/**
* 发送一个ajax请求
* @param url 请求的URL地址信息
* @param fn, 请求成功的回调函数
*/
ajax: function (url, fn) {
// 创建一个XMLHTTPRequest对象
var xhr = createXHR();
// 每当 readyState 改变时,就会触发 onreadystatechange 事件。
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 接受到响应之后,第一步检查status属性,为200则表明成功,此时responseText已经准备就绪;
// 为304表明请求资源未被修改,可以直接使用浏览器中的缓存版本。
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
fn(xhr.responseText);
} else {
alert('错误的文件!');
}
}
};
// 定义请求参数, 对于指定的url发送一个get请求
xhr.open('get', url, true);
// 发送请求
// 第三个参数:指示请求使用应该异步地执行。
// 如果这个参数是 false,请求是同步的,后续对 send() 的调用将阻塞,直到响应完全接收。
// 如果这个参数是 true 或省略,请求是异步的,且通常需要一个 onreadystatechange 事件句柄。
xhr.send();
/**
* 创建一个XHR
*/
function createXHR() {
//本函数来自于《JavaScript高级程序设计 第3版》第21章
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined") {
// arguments.callee用于指向他的回调函数
if (typeof arguments.callee.activeXString != "string") {
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"
],
i, len;
for (i = 0, len = versions.length; i < len; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex) {
//skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
},
/**
* json转换为字符串
* @param json
* @returns {string}
*/
json2String: function (json) {
return JSON.stringify(json);
},
/**
* 字符串转换为json
* @param str
* @returns {any}
*/
string2Json: function (str) {
return eval(str);
}
});
/*数组相关*/
xframe.extend(xframe, {
/**
* 将一个数组清空,并返回数组的引用
* 只需要把数组的元素置空为0即可
* @return {xframe}
*/
clear: function () {
this.length = 0;
return this;
},
/**
* 返回数组的第0个元素
* @return {*}
*/
first: function () {
return this[0];
},
/**
* 返回数组的最后一个元素
* @return {*}
*/
last: function () {
return this[this.length - 1];
},
/**
* 计算一个数组的大小尺寸
* @return {number|*}
*/
size: function () {
return this.length;
},
cacl: function (arr, callback) {
var ret;
for (var i = 0; i < arr.length; i++) {
// 专门用于处理每一项的计算机过程
ret = callback(arr[i], ret);
}
return ret;
},
/**
* 对数组里面的所有元素求和
* @return {*}
*/
sum: function () {
// 1. 正常写法
var ret;
for (var i = 0; i < this.length; i++) {
ret = ret + this[i];
}
return ret;
},
max: function () {
},
min: function () {
},
avg: function () {
},
intersect: function () {
},
union: function () {
},
diff: function () {
},
unique: function () {
},
forEach: function () {
},
map: function () {
},
filter: function () {
},
every: function () {
},
some: function () {
},
reduce: function () {
},
reduceRight: function () {
},
indexOf: function () {
},
lastIndexOf: function () {
},
enhanceUnique: function () {
},
without: function () {
},
flatten: function () {
},
random: function () {
},
removeAt: function () {
},
contains: function () {
}
});
/*Math*/
xframe.extend(xframe, {
random: function () {
}
});
/*数据类型检验*/
xframe.extend(xframe, {
// 鸭子类型(duck typing)如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。
// 只关注对象的行为,不关注对象本身面向接口编型 ,而不是面向实现编程,是设计模式中最重要的思想。
// 【理解】:一个对象有效的语义,不是由集成自特定的类或实现特定的接口, 而是由当前方法和属性的集合决定的!!!
isNumber: function (val) {
// 如果这个数字是有限的话, 而且是数字类型
return (typeof val === 'number' && isFinite(val)) && (Object.prototype.toString.call(val) === '[object Number]');
},
/***
* 判断一个变量是不是Boolean类型
* @param val
* @returns {boolean}
*/
isBoolean: function (val) {
return (typeof val === 'boolean') && (Object.prototype.toString.call(val) === '[object Boolean]');
},
/**
* 判断一个变量是不是字符串类型
* @param val
* @returns {boolean}
*/
isString: function (val) {
return (typeof val === 'string') && (Object.prototype.toString.call(val) === '[object String]');
},
/**
* 判断一个变量是不是undefined
* @param val
* @returns {boolean}
*/
isUndefined: function (val) {
// oid 0 is a correct and standard way to produce undefined.
return (val === void 0) || (typeof val === 'undefined') && (Object.prototype.toString.call(val) === '[object Undefined]');
},
/**
* 判断一个变量是不是为空
* @param val
* @returns {boolean}
*/
isNull: function (val) {
return (val === null) && (Object.prototype.toString.call(val) === '[object Null]');
},
/**
* 检测
* @param obj
* @returns {*}
*/
isNaN: function (val) {
// 只要这个数字通过判断是不是和他自身相同或者使用typef的方式去检测
return val !== val;
},
/**
* 判断一个变量是不是一个对象类型
* @param val
* @returns {boolean}
*/
isObject: function (val) {
if (val !== null && val !== undefined) {
if ((typeof val === 'object') && (Object.prototype.toString.call(val))) {
return true;
}
}
return false;
},
/**
* 判断一个对象是不是数组对象
* @param val
* @returns {boolean|void|string}
*/
isArray: function (val) {
// 判断上不是一个数组的先判断这个数组对象是不是为空, 因为如果val为空的话,就是val.constructor这个属性实际上是没有的,error
if (val !== null || typeof val !== "undefined") {
// 注意在使用constructor判断数据类型的时候比较的实际上是他的原型对象的constructor属性, 这个属性指向的实际上是这个变量的原型对象
return (val.constructor === Array) && (Object.prototype.toString.call(val));
}
return false;
}
});
/*数组化:arguments, document.forms, document.getElementsByName, document.getElementsByTagName()*/
xframe.extend(xframe, {
/**
* 把一个伪数组转换为一个新的数组
* 实现思路: 取出伪数组中的每一个元素, 然后把取出来的这些元素重新放入到一个新的数组里面去!!!
* @param start
* @param end
* @returns {Array}
*/
toArray: function (start, end) {
var result = [];
var start = start || 0,
// 这里的this指向调用的对象,使用了call之后, 改变了this的指向, 指向传进来的对象(外边必须要修改this的指向)
// 如果外边不修改this的指向,这里的this默认指向的是xframe这个框架对象
end = end || this.length;
for (var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
},
/**
* 方法二: 直接把一个伪数组转换为JavaScript中的一个数组对象
* @param obj
* @returns {T[]}
*/
slice: function (obj) {
return Array.prototype.slice.apply(obj);
}
});
/*domReady的实现*/
xframe.extend(xframe, {
//arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数
/**
* 实现一个domReady方法:所有元素都加载完毕之后一个回调函数
* @param domElement
* @param fn
*/
onDOMReady: function (fn) {
if (document.addEventListener) {
// W3C组织: 如果传过来的是一个DOM元素的话,就直接对这个DOM元素添加监听, 否则,就对整个document添加事件监听
document.addEventListener('DOMContentLoaded', fn, false);
} else {
// IE浏览器
IEContentLoaded(fn);
}
/**
* 微软的IE浏览器的处理方法
* @param fn
* @constructor
*/
function IEContentLoaded(fn) {
// 定义需要的全局变量
var done = false, document = window.document;
// 这个函数只会在所有的DOM节点树创建完毕的时候才会继续向下执行
var init = (function () {
if (!done) {
console.log('done……');
// 如果DOM树创建完毕的话
done = true;
fn();
}
})();
/*
使用这个立即函数来调用IE浏览器的内置函数实现domReady的功能
*/
(function () {
try {
// DOM树在未创建完毕之后调用 doScroll的话,会抛出错误
document.documentElement.doScroll('left');
} catch (err) {
// 延迟1秒之后再次执行这个函数, 形成一个函数递归调用的功能【回调函数】
// clllee是一个函数指针,指向的是拥有这个arguments对象的函数, 从而实现再次调用这个函数
setTimeout(arguments.callee, 1);
return;
}
// 如果没有错误的话,表示DOM树已经完全创建完毕, 此时开始执行用户的回调函数
init();
})();
// 监听document的加载状态(DOM加载的过程中会不断回调这个函数)
document.onreadystatechange = function () {
console.log('onreadystatechange……');
if (document.readyState === 'complete') {
console.log('complete……');
// 如果加载完成的话
document.onreadystatechange = null;
init();
}
}
}
}
});
})(xframe);