//jQuery本质是用一个闭包
//jQuery用闭包的原因是因为在用多个框架式 可能会有函数 方法名字一样的情况 从而产生冲突
//为了不冲突 把所有的变量 关键字 方法等都声明为局部变量 但是让局部变量在全局访问 就可以用闭包(闭包 把整个代码运行环境都传给外边)
//比如把jQuery设置为 window.jQuery 设置为全局变量 用window.xxx = xxx
//jQuery给自己传入一个window的实参是为了可以让内部的形参可以简化 比如 形参为w 实参window 这样就可以减少文件大小 方便压缩代码
//自身直接传入window 可以提高代码效率 如果不穿 在代码需要用的时候 代码需要自己去一层一层的出去找 这样效率低
//接收undefined 是为了不被外面的可能被篡改过得undefined影响 在一些低版本的浏览器中undefined的值是可以被篡改的
//在这直接接收会直接得到undefined 因为在下面的传入中只传入了一个实参 第二个固定式undefined
(function(window,undefind){
var njQuery = function(selector){
return new njQuery.prototype.init(selector);
}
njQuery.prototype = {
constructor:njQuery,
init:function(selector){
/*jQuery的入口函数传入参数的几条规律
什么都不传 和传入‘’ null undefined NaN 0 false
会返回一个空的jQuery对象
传入的是字符串
如果为代码片段会将所有找到的元素存储到jQuery对象中返回
如果为先创建对用代码片段中的元素 在把元素依次存储到jQuery对象中返回
传入的是数组或伪数组
都会将数组的每个元素依次存储到jQuery对象当中返回
传入的是上述意外的类型
如果为对象或dom元素会将传入的对象或dom元素存储到Query对象中返回
如果为基本数据类型会将传入的基本数据类型存储到Query对象中返回
*/
//进行判断前先进 去除开头 或者结尾的空格
selector = njQuery.trim(selector);// njQuery.trim在下面定义的
// 什么都不传 和传入‘’ null undefined NaN 0 false
if(!selector){// !意为“非” !'' !null !undefined !NaN !0 !false 的结果都为true
return this;
}
//方法处理
else if(njQuery.isFunction(selector)){
njQuery.ready(selector);
}
//字符串
else if(njQuery.isString(selector)){
//判断是代码片段还是选择器
if(njQuery.isHTML(selector)){
//根据代码片段创建元素
var temp = document.createElement('div');
temp.innerHTML = selector;//innerHTML就有创建代码片段中的元素 所有利用原生创建一个元素 在用该元素的innerHTML创建代码片段的元素
//吧代码片段传入jQuery对象 并且 对于有多层嵌套的代码片段 只传入第一级的
//原生的children属性 会获得该元素的一级的子元素 这就直接满足了要求
// for(var i = 0; i < temp.children.length; i++){
// this[i] = temp.children[i];
// }
// //最后给jQuery添加length属性
// this.length = temp.children.length;//直接赋值 没有length属性 会自动创建
[].push.apply(this,temp.children);//数组转换为伪数组
//通过[]数组 找到数组中的push方法
//apply会强行改变 this的指向 apply的第二个参数是个数组 将该数组的元素依次取出作为调用apply的参数
//children属性会返回一级子级
//所以 temp.children返回的是子级数组 因为apply吧这个数组作为push的参数依次传入 并把this指向切换 原本指向 [] 现在指向njQuery.prototype 因为在HTML中是用$()来调用的 $就是njQuery
//返回这个jQuery对象
// return this;
}
else{
//字符串为选择器
//根据传入的选择器找到对应的元素
var res = document.querySelectorAll(selector);
//将找到的元素添加到njQuery上
// for(var i = 0; i < res.length; i++){
// this[i] = res[i];
// }
// //最后给jQuery添加length属性
// this.length = res.length;//直接赋值 没有length属性 会自动创建
[].push.apply(this,res);
//返回加工好的this
// return this;
}
}
//数组或伪数组
//根据selectors的类型是不是object和是否含有length判断是不是数组或伪数组 但是window对象比较特殊 他不是伪数组但也满足条件 所以要排除一下
else if(njQuery.isArray(selector)){
//判断是数组 还是 伪数组 将他们两个toString 数组会将所以元素拼接成一个字符串 伪数组不同
//({}).toString.apply(arr) 通过向apply的括号传入数组 最后打印出来的结果是[object Array] 伪数组.toString 的结果是[object Object]
// if(({}).toString.apply(selector) === '[object Array]'){//数组
// [].push.apply(this,selector);//数组转换为jQuery对象 就是 数组转换为伪数组
// return this;
// }
// else{//将伪数组转回为jQuery对象
// var arr = [].slice.call(selector);//伪数组转换为数组 这样搞是为了兼容捞比IE
// [].push.apply(this,arr);//将伪数组转回为jQuery对象
// return this;
// }
//不关他是数组还是伪数组 我都先转为数组
var arr = [].slice.call(selector);
[].push.apply(this,arr);
// return this;
}
//上述意外的类型
else{//就直接将传入的参数存到jQuery对象中 并且设置length属性
this[0] = selector;
this.length = 1;
// return this;
}
return this;
},
njQuery:'1.1.0',//版本号
selector:'',//默认值
length:0,//默认值
//[].push就是找到push方法
//puhs:[].push就是相当于改this 等价于 [].push.apply(this);
puhs:[].push,
sort:[].sort,
splice:[].splice,
toArray:function(){//把实例转换为数组返回
return [].slice.call(this);//伪数组转数组
},
get:function(num){//返回的是原生DOM元素
// 为整数时 表示索引 为负数时 就是总长度加上这个参数 不传参数相当于调用toArray()
//不传参时
if(arguments.length === 0){
return this.toArray();
}
//参数为正数
else if(num >= 0){
return this[num];
}
//参数为负数
else{
return this[this.length + num];
}
},
eq:function(num){//逻辑跟get类似 但eq返回的是njQuery对象 不是原生的
if(arguments.length === 0){
return new njQuery;//没有就是返回空的njQuery对象
}
else{//不需要判断 直接交给get判断
return njQuery(this.get(num));//返回njQuery包装过的
}
},
first:function(){//返回jQuery对象 保存第一个元素
return this.eq(0);
},
last:function(){
return this.eq(-1);//返回jQuery对象 保存最后一个元素
},
each:function(fn){
return njQuery.each(this,fn);
},
map:function(){
}
}
//njquery.extend的内容直接赋给njQuery
njQuery.extend = njQuery.prototype.extend = function(obj){
for (var key in obj) {//通过遍历传入的参数obj
this[key] = obj[key];
//njquery[key] = obj[key]; key就是obj中的各个属性和方法
//njquery["isString"] = obj["isString"]; 例如
//njquery["isString"] = function(str){eturn typeof str === 'string';;}; 例如
}
}//通过上述的挨个遍历赋值把njquery.extend的内容直接赋给njQuery
//工具方法
njQuery.extend({
//判断是不是字符串
isString : function(str){
return typeof str === 'string';
},
//判断字符串是不是代码片段
isHTML : function(str){
//代码片段的开头一定会< 结尾一定是 > 而且代码片段最小最短是 3
return str.charAt(0)==='<' && str.charAt(str.length-1)==='>' && str.length >= 3;
},
//去除字符串开头跟结尾的无用空格
trim : function(str){//trim是用于去除字符串开头和结尾的空格用的 但是垃圾IE 低版本不支持
if(!njQuery.isString(str))return str;//对于不是字符串的直接返回
else if(str.trim){
return str.trim();
}
else{
//replace属性用于替换字符 第一个参数为被替换的字符 第一个参数可以是正则表达式
return str.replace(/^\s+|\s+$/g,'');//正则表达式 中 \s 为匹配空格 ^为匹配范围为字符串开头 $为匹配范围为字符串结尾 g为全局匹配 多次匹配 知道不匹配为止 |为或
}
},
//判断传入的是不是对象
isObject : function(sele){
return typeof sele === 'object';
},
//判断传入的是不是window
isWidow : function(sele){
return sele === window;
},
//判断传入的是不是数组
isArray : function(sele){
if(njQuery.isObject(sele) && !njQuery.isWidow(sele) && 'length' in sele){
return true;
}
else{
return false;
}
},
//判断传入的是不是函数
isFunction : function(sele){
return typeof sele === 'function';//如果是函数他的typeof结果就是 'function'
},
//判断DOM是否加载完毕
ready:function(fn){
if(document.readyState === 'complete'){
fn();
}
else if(document.addEventListener){
document.addEventListener('DOMContentLoaded',function(){
fn();
});
}
else{
document.attachEvent('onreadystatechange',function(){
if(document.readyState == 'complete'){//只有在 complete - 载入完成打印
fn();
}
})
}
},
//遍历方法
each:function(obj,fn){ //默认返回传入对象
//判断是不是数组
if(njQuery.isArray(obj)){
for(var i = 0; i < obj.length; i++) {
// var res = fn(i,obj[i])//遍历给外部的函数返回 索引和值
//遍历给外部的函数返回 索引和值
//jQuery中each中的this会被改向 value 就是每次遍历的值
var res = fn.call(obj[i],i,obj[i])
// 让return false相当于break 让return true 相当于continue
if(res == true){
continue
}
else if(res == false){
break;
}
}
}
//判断是不是对象
else if(njQuery.isObject(obj)){
for(var key in obj){
// var res = fn(key,obj[key]);
var res = fn.call(obj[key],key,obj[key])
if(res == true){
continue
}
else if(res == false){
break;
}
}
}
return obj;
},
map:function(obj,fn){ //默认返回空数组
var res = [];
//判断是不是数组
if(njQuery.isArray(obj)){
for(var i = 0; i < obj.length; i++){
var temp = fn(obj[i],i);
if(temp){//只要有在接收到外部函数return的返回值时 添加
res.push(temp);
}
}
}
//判断是不是对象
else if(njQuery.isObject(obj)){
for(var key in obj){
var temp = fn(obj[key],key);
if(temp){
res.push(temp);
}
}
}
return res;
},
getStyle:function(dom,styleName){
//判断是否支持getComputedStyle
if(window.getComputedStyle){
return window.getComputedStyle(dom)[styleName];
}
else{
return dom.currentStyle[styleName];
}
}
});
//DOM操作相关方法
njQuery.prototype.extend({
empty:function(){//清空元素内容
//1.遍历所有找到的元素
this.each(function(key,value){
value.innerHTML = '';
});
return this;//为了方便链式编程
},
remove:function(sele){//删除所有的元素或指定元素
//判断是否有参数传入
if(arguments.length === 0){//没有就删除所有的
//遍历元素
this.each(function(key,value){
//js中不能元素自己删除自己 只能通过父元素删除
var parent = value.parentNode;//parentNode获取指定元素的父元素
parent.removeChild(value);
});
return this;
}
else{//否则删除指定的
var $this = this;
//根据选择器找到指定的元素
njQuery(sele).each(function(key,value){//找到需要删除的元素
var type = value.tagName;
$this.each(function(k,v){//跟原本的所有的元素对比
var t = v.tagName;
if(t === type){//对上的删除
var parent = value.parentNode;//parentNode获取指定元素的父元素
parent.removeChild(value);
}
})
});
}
},
html:function(content){//设置修改元素内容 会根据字符串和代码片段改变
if(arguments.length === 0){//没有实参
return this[0].innerHTML;//直接返回指定元素的第一个内容
}
else{//否者将指定的所有元素的内容改为传入的
//需要遍历
this.each(function(key,value){
value.innerHTML = content; //而且 innerHTMl自己就会区分是代码片段 还是普通的字符串
})
}
},
text:function(content){//设置修改元素内容 不会根据字符串和代码片段改变
if(arguments.length === 0){//没有实参
var res = '';
this.each(function(key,value){
res += value.innerText;//将所有元素的内容依次拼接 最后形成字符串
})
return res;//返回这个字符串
}
else{//否者将指定的所有元素的内容改为传入的
//需要遍历
this.each(function(key,value){
value.innerText = content; //将元素的内容设置为传入的字符串 只是字符串
})
}
},
appendTo:function(sele){//作用:将一组或一个(为一个整体)需要转移的元素 转移到到一个或多个元素的内部 并且移动的位置是最后面 并且会把字符串当做选择器的处理 返回值是所有操作的元素
var $target = $(sele);//将传入的数据 在经过一下jQuery核心函数 的处理 这样就不管是什么类型参数 结果就都是jQuery对象了
var $this = this;
var res = []
//外城循环 控制的是转移的位置 内城循环 控制的是转移的元素
$.each($target,function(key,value){//传入的是通过jQuery核心函数处理的 肯定是jQuery对象
//经过遍历挨个取出里面的元素
$this.each(function(k,v){//遍历需要转移的元素 因为需要朱阿姨的可能不止一个
//获得每一个需要转移的元素
if(key === 0){//只有第一组需要转移的元素 是直接改 判断第一个转移到的位置 所有是用key判断
value.appendChild(v);
res.push(v);
}
else{//其他的都为克隆第一个转移的元素
var temp = v.cloneNode(true);
value.appendChild(temp);
res.push(temp);
}
})
})
//返回值 需要返回所有添加移动的元素 并且是以一个jQuery对象的数组
return $(res);
},
prependTo:function(sele){//与appendTo类似 只是他是移动到元素的内部最前面
var $target = $(sele);//将传入的数据 在经过一下jQuery核心函数 的处理 这样就不管是什么类型参数 结果就都是jQuery对象了
var $this = this;
var res = []
//外城循环 控制的是转移的位置 内城循环 控制的是转移的元素
$.each($target,function(key,value){//传入的是通过jQuery核心函数处理的 肯定是jQuery对象
//经过遍历挨个取出里面的元素
$this.each(function(k,v){//遍历需要转移的元素 因为需要朱阿姨的可能不止一个
//获得每一个需要转移的元素
if(key === 0){//只有第一组需要转移的元素 是直接改 判断第一个转移到的位置 所有是用key判断
value.insertBefore(v,value.firstChild);
res.push(v);
}
else{//其他的都为克隆第一个转移的元素
var temp = v.cloneNode(true);
value.insertBefore(temp,value.firstChild);
res.push(temp);
}
})
})
//返回值 需要返回操作的元素
return $(res);
},
//append和appendTo区别是
//他们的参数和调用者的顺序不一样 从字面上就看得出了一个是to一个不是
//他们两个对于参数为字符串类型的处理不同 appendTo会当做选择器处理 append不会
//append的返回值是直接返回调用者 appendTo的返回值返回所有添加移动的元素 并且是以一个jQuery对象的数组
append:function(sele){//跟appendTo类似 但是还是有区别的,append不会把参数当做选择器处理 append的返回值是直接返回调用者
//先判断是不是字符串
if(njQuery.isString(sele)){//字符串会区分代码片段跟普通字符串
//用+=就可以直接将要添加的内容直接添加到当前调用者的内部的最后
this[0].innerHTML += sele;//加[0]是因为 this指向的是jQuery对象 但是innerHTML是原生元素的属性 所有要获得以下原生的 jQuery对象里面的就是原生的
}
else{//其他的就跟appendTo一样了 只需要调整下顺
//append理解为 向xxx添加yyy
//appendTo理解为 吧yyy添加到xxx中
$(sele).appendTo(this);
}
return this;
},
prepend:function(sele){//跟prependTo类似 prepend与prependTo的不同跟append与appendTo的不同类似 同理
if(njQuery.isString(sele)){//字符串会区分代码片段跟普通字符串
// this[0].innerHTML代表调用者原来的内容 把 新的内容加调用者原来的内容 再付给他 就相当于添加在最前面了
this[0].innerHTML = sele + this[0].innerHTML;//加[0]是因为 this指向的是jQuery对象 但是innerHTML是原生元素的属性 所有要获得以下原生的 jQuery对象里面的就是原生的
}
else{
$(sele).prependTo(this);
}
return this;
},
insertBefore:function(sele){//prependTo类似 区别是他是插入在指定元素的外边的前面
//调用者.insertBefore(插入的元素,参考位置的元素); (原生的)
//insertBefore方法 是调用者是谁就会将元素添加到那个元素里面 (原生的)
//想要实现jQuery的insertBefore就是添加在指定元素外面的前面
//可以利用父元素 获得父元素 让父元素使用 原生的 insertBefore就实现了
var $target = $(sele);
var $this = this;
var res = []
//外城循环 控制的是转移的位置 内城循环 控制的是转移的元素
$.each($target,function(key,value){
var parent = value.parentNode;//获得父元素
$this.each(function(k,v){
if(key === 0){
parent.insertBefore(v,value);//这里的坐标元素 就直接用指定元素 这样就实现了 只是在指定元素前面 而不会因为兄弟元素出错
res.push(v);
}
else{
var temp = v.cloneNode(true);
parent.insertBefore(temp,value);
res.push(temp);
}
})
})
return $(res);
},
replaceAll:function(sele){
var $target = $(sele);
var $this = this;
var res = []
//外城循环 控制的是转移的位置 内城循环 控制的是转移的元素
$.each($target,function(key,value){
var parent = value.parentNode;//获得父元素
$this.each(function(k,v){
if(key === 0){
$(v).insertBefore(value);//通过jQuery的insertBefore实现插入到指定元素的外部的前面
$(value).remove();//删除指定元素
res.push(v);
}
else{
var temp = v.cloneNode(true);
$(temp).insertBefore(value);//通过jQuery的insertBefore实现插入到指定元素的外部的前面
$(value).remove();//删除指定元素
res.push(temp);
}
})
})
return $(res);
}
})
//属性操作相关方法
njQuery.prototype.extend({
//attr()
//传入一个参数 获得第一个该元素指定的属性节点的值
//传入两个参数 设置该元素所有匹配的指定的属性节点的值 第一个参数属性节点名 第二个值
//还可以传入一个对象 这个对象就是 他里面的每个属性名代表代表属性节点名 属性值就是属性节点值
//返回值是方法的调用者
attr:function(attr,value){
//先判断是字符串还是对象
if(njQuery.isString(attr)){
//判断几个参数
if(arguments.length === 1){
return this[0].getAttribute(attr);//getAttribute通过名称获取属性的值
}
else{//两个参数 循环更改所有匹配的指定的属性节点的值
this.each(function(key,ele){
ele.setAttribute(attr,value);//setAttribute通过名称更改所有匹配的指定的属性节点的值
})
}
}
else if(njQuery.isObject(attr)){
var $this = this;
//遍历取出对象中的属性和他的值
$.each(attr,function(key,value){//这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法 所有用的$
$this.each(function(k,ele){
ele.setAttribute(key,value);//传入的参数都得用 传入对象中的内容
})
})
}
return this;//返回值是方法的调用者
},
//prop()跟attr类似 但是他是操作属性的 其他一样
prop:function(attr,value){
//先判断是字符串还是对象
if(njQuery.isString(attr)){
//判断几个参数
if(arguments.length === 1){
return this[0][attr];//用中括号就可以直接获得属性
}
else{//两个参数 循环更改所有匹配的指定的属性节点的值
this.each(function(key,ele){
ele[attr] = value;//设置值就跟普通的数组,伪数组赋值差不多
})
}
}
else if(njQuery.isObject(attr)){
var $this = this;
//遍历取出对象中的属性和他的值
$.each(attr,function(key,value){//这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法 所有用的$
$this.each(function(k,ele){
ele[key] = value;//传入的参数都得用 传入对象中的内容
})
})
}
return this;//返回值是方法的调用者
},
//css跟attr类似 但是他是css样式的 其他一样
//原生js 通过window.getComputedStyle(通过dom获得元素)['需要获取的css样式名']; 后面的中括号也可以用.
//但是IE8以下不支持getComputedStyle 需要用 通过dom获得元素.currentStyle['需要获取的css样式名']
//原生js 通过 通过dom获得元素.style['需要设置的css样式名' = 样式值;
css:function(attr,value){
//先判断是字符串还是对象
if(njQuery.isString(attr)){
//判断几个参数
if(arguments.length === 1){
return $.getStyle(this[0],attr);
}
else{//两个参数 循环更改所有匹配的指定的属性节点的值
this.each(function(key,ele){
ele.style[attr] = value;
})
}
}
else if(njQuery.isObject(attr)){
var $this = this;
//遍历取出对象中的属性和他的值
$.each(attr,function(key,value){//这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法 所有用的$
$this.each(function(k,ele){
ele.style[key] = value;//传入的参数都得用 传入对象中的内容
})
})
}
return this;//返回值是方法的调用者
}
})
njQuery.prototype.init.prototype = njQuery.prototype;
window.njQuery = window.$ = njQuery;
})(window);
jQuery的原型上的核心方法和属性,DOM操作相关方法的个人理解
最新推荐文章于 2023-12-21 14:51:39 发布