jQuery의 내부 프레임워크 구조와 동작에 대한 간략한 논의

高洛峰
풀어 주다: 2016-11-05 16:50:53
원래의
975명이 탐색했습니다.

전체 구조 확립

개인 변수를 보호하고 전역 오염을 방지하기 위해 샌드박스 생성(함수(창){})(winow)

창을 형식 매개변수로 전달 두 가지 장점:

1. 범위 검색 감소

2. 압축 효율성 향상

생성자 생성, Guoqi(){} // 함수: 모든 Guoqi 개체는 의사- 배열

프로토타입 객체에 init 메소드를 생성하고 생성자에서 new Guoqi.prototype.init();

을 직접 반환하여 새 핵심 단어

를 성공적으로 숨길 수 있습니다. 하지만 가장 중요한 단계는 Guoqi.prototype.init.prototype = Guoqi.prototype을 설정하는 것입니다

이 경우 init의 모든 인스턴스는 Guoqi.prototype 프로토타입의 메서드를 사용할 수 있습니다

마지막으로 , window.Guoqi = window.Q = Guoqi 및 Guoqi 및 Q를 창에 인터페이스로 걸어 직접 외부 호출을 용이하게 합니다

길이를 프로토타입 객체의 객체로 넣고 프로토타입에 기본값은 0. Q 객체가 의사 배열인지 확인하세요

프로토타입 객체를 Guoqi.prototype = {}로 바꾸세요. 참고: 생성자 속성은 생성자 자체를 가리켜야 합니다

작성을 용이하게 하기 위해 인스턴스를 생성할 때마다 new 키워드를 사용할 필요가 없습니다.

Q 객체에 여러 매개변수를 전달할 수 있으므로 이를 강화하는 방법이 있습니다.

'

그렇지 않으면(DOM 요소가 전달됨)

개체가 문자열로 전달되면

사례가 전달됩니다. DOM 개체가 selector.nodeType에 전달되면 다음을 얻기 위해 qsa 메서드를 사용하여 선택기로 사용됩니다. this[0] = selector;this .length = 1; 의사 배열인지 확인하세요

Q 객체가 전달되면 selector.constructor.name = Guoqi입니다. Q 객체는 모두 Guoqi 생성자를 사용하여 생성됩니다), 직접 선택기를 반환합니다

함수 function이 전달되면 당분간은 선택기 === "함수" 유형과 같은 작업이 수행되지 않습니다. jQuery의 입력 함수 $(function(){})

Incoming 배열(의사 배열)인 경우 selector.length >=0, 배열의 값을 이 항목에 순차적으로 전달합니다.

그렇지 않으면 무엇을 전달하든 this[0] = selector; length = 1;

이렇게 하면 Q()가 무엇을 전달하든 __pseudo를 얻게 됩니다. array__

extend

Guoqi.extend = Guoqi.prototype.extend = function(){ }에서 Pass의 확장 메서드 확장( obj ) 함수를 추가합니다. 생성자와 생성자 프로토타입에 각각

extend는 혼합 상속의 원리를 사용하여 obj의 함수를 this 객체에 복사합니다(참고: 현재 두 개의 this 지점은 다릅니다)

Guoqi.extend({})는 생성자를 확장하는 메서드로 정적 메서드라고 합니다. 도구 메서드로는 (each, map, next...)

를 사용하여 각을 추가합니다. 참고: 이는 탐색된 현재 객체를 가리키며, 반환 값이 false이면 탐색을 중지합니다. 즉,

if( callback.call( array[i], i, array[i; ] ) === false ) break;

창을 가리키는 매핑 메소드입니다. 반환 값이 정의되지 않거나 null이 아닌 경우에만 순회된 요소가 반환된 새 배열에 추가됩니다. 그렇지 않은 경우:

if( callback( array[i], i ) != 정의되지 않음){ newArr.push( callback( array[i], i ) ) }; 마지막으로 __return newArr__;

Guoqi.prototype.extend({} ) 인스턴스에 추가된 메서드를 인스턴스 메서드라고 하며, 각 인스턴스 개체는

을 호출하여 인스턴스에 메서드를 추가할 수 있습니다. 구문: Q().each(function(){}); 각 메소드를 Guoqi .fn.extend({each:function(callback){ return Guoqi.each(this,callback); }})인 AppendTo, Each, map

과 같은 Q 인스턴스 객체로 전송해야 합니다. ;

Array, get

에 핵심 메서드 추가 index 에 하나가 전달되면 해당 index 값을 반환하는 DOM 요소

는 음수 전달을 지원합니다. 뒤에서 앞으로 계산합니다. 예를 들어 -1을 전달하면 객체는 길이가 1인 DOM 요소입니다.

toArray 함수: 획득한 Q 객체(의사 배열)를 실제 배열로 반환합니다. DOM 요소

get(index) 함수: 인덱스가 전달되지 않으면 DOM 요소가 반환됩니다. 실제 배열

DOM 작업 모듈

은 먼저 함수parseHtml을 정의합니다. ( str ){} 전달된 태그

를 기반으로 생성된 Q 객체를 생성하기 위해 샌드박스에서 div 태그를 마음대로 생성하고 생성하려는 태그를 div에 추가하면 됩니다. div.innerHTML = str;

그런 다음 div의 childNodes를 반환합니다. 하지만 __참고__, 페이지에서 렌더링하면 childNodes의 길이가 변경됩니다.

따라서 빈 배열을 만들고 DOM 요소를 childNodes를 순서대로 배열에 집어넣고,

그러면, parsHtml 함수는 init 메소드에서 '

AppendTo(selector) 인스턴스 메소드 함수: 이 객체를 선택기의 하위 요소에 추가

이 메소드는 또한 들어오는 객체, DOM 요소, DOM 객체, Q 객체, 의사 배열에 따라 달라집니다... 판단을 내립니다. ★ (따라서 init 메소드의 완성이 전제)

완성 후 먼저 selector를 Q 객체로 변환 ---> Q(selector)

objQ 배열을 생성하고, 변수 tempDom, (다음 반환 값 고려 사항)

이것과 선택기에 대해 중첩 루프를 수행하고 이[i]를 복제합니다. 온톨로지가 마지막인지 확인하십시오. , 너무 많은

을 복제하지 말고 tempDom에 이 값을 계속 할당하여 저장하세요. tempDom = j === Q(selector).length-1 ? this[i]:this[ i] .cloneNode(true)

objQ 배열에 tempDom을 추가합니다.

반환 값 문제를 고려하여 복제된 항목을 포함하여 선택기의 모든 요소에 반환 값을 추가해야 합니다. 따라서 objQ의 요소는 우리가 반환해야 하는 값입니다

이 경우 체인 프로그래밍 체인이 파괴되므로 이전 체인 개체를 반환하려면 __end()_ _ 메서드를 만들어야 합니다. 🎜>

여기에서는 end() 메서드 호출이

을 반환할 수 있도록 현재 this를 저장해야 합니다. 즉, objQ를 Q 개체로 변환하고 this를 가리키는 preObj 속성을 추가합니다. 그런 다음 Q(obj) out

end() 인스턴스 메소드를 반환합니다. 체인이 파괴되면 이를 호출하여 상위 레벨 체인을 찾습니다(첫 번째 레벨만 찾을 수 있음)

위에서 많은 작업을 수행했으므로 end() 메서드는 this.preObj || this 만 반환하면 됩니다. (참고: 이전 레벨이 없으면 자신을 반환하세요.)

pushStack() 인스턴스 메서드는 다음과 같습니다. AppendTo 메소드 이후에 상위 객체의 내용을 저장하기 위해

체인으로 인해 손상될 수 있는 메소드가 많을 수 있으므로 메소드를 캡슐화하여 복합적으로 구현하는 효과

는 다음과 같습니다.

function pushStack(arr){ var newObjQ = Q(arr); newObjQ.preObj = this; return newObjQ; 인스턴스에서 직접 호출해야 하는데, 이는appendTo 메소드에서 호출되어야 하기 때문에 이것의 상위 체인 객체를 찾을 수 있습니다. (나중에 call/apply 메소드를 빌려 구현할 수도 있지만 이것이 더 번거롭습니다.) );


즉, private 함수(parseHtml 등)로 샌드박스에 직접 배치할 수는 없으므로 구현하기가 더 까다롭습니다.

그래서 Guoqi 생성자를 확장했습니다. next(dom) 메소드는 dom 요소를 반복하여 다음 노드를 찾습니다. nodeType === 1이면 dom을 직접 반환하고, 그렇지 않으면 null을 반환합니다. >는

입니다.each(function(){ this.parentNode.removeChild(this ); })

구현 Q(selector).prependTo(this); 체인은 파괴되지 않습니다

구현 Q(selector).appendTo(this); 체인은 파괴되지 않습니다

selector[j].insertBefore( tempDom, selector[j ].firstChild );

참고: 이 메서드는appendTo 메서드와 동일하며 체인이 삭제됩니다.

prependTo 함수: 이 개체는 선택기 개체

이 개체의 하위 요소를 추가하여 선택기 개체 추가

이 개체의 하위 요소 앞에 추가하고 앞에 선택기 개체 추가

제거 메서드 찾기 이것의 상위 노드를 삭제하고 이것 자체를 포함하여 그 안의 모든 요소를 ​​삭제합니다.

다음 메소드가 이것을 찾습니다. (모든 형제가 아닌) 객체의 다음 요소 체인이 파괴됩니다

그러면 다음 인스턴스 메서드는 pushStack ( this.map(function(v){ return Guoqi.next(v) }) );

filter(selector) 메소드는 이 인스턴스 객체와 selector 요소에 대해 동일한 값을 찾습니다

function next(dom){
        while( dom = dom.nextSibling ){
            if( dom.nodeType === 1 ){
                return dom;
            }
        }
        return null;
    }
로그인 후 복사
는 ret의 빈 배열을 준비하고 this와 Guoqi(selector)에 대해 중첩 루프 순회를 수행합니다. this[i] == Guoqi(selector)[j]인 경우 ret 배열에 추가합니다


마지막으로 ret 배열을 인스턴스 객체로 변환하고 반환합니다. return Guoqi(ret);

unique() 함수: 동일한 요소 제거

빈 배열 arr을 준비하고 this.toArray() 메서드를 호출하여 인스턴스 객체를 실제 배열 thisArr로 변환한 다음
을 반복합니다.

if( arr.indexOf( thisArr(i) )== ​​​​-1 ){ arr.push( thisArr[i]) };

Then return Guoqi( arr );

children(selector): 기능: 매개변수가 전달되지 않으면 모든 하위 요소가 검색됩니다(아들 세대로 제한됨). 매개변수가 전달되면 선택기와 일치하는 하위 요소가 검색됩니다.

原理:找到this实例对象中的所有的子元素,组成一个数组,找到selector中的所有元素 组成一个数组,然后寻找两个数组之间相同的元素即可(调用filter方法),注意:涉及到了unique去重的问题

首先

var subList = this.map(function(v){ 
  return Guoqi.map(v.children,function(v1){ 
  return v1 }) }); // 此时 sunList是一个二维数组,即:数组里面套数组
로그인 후 복사


需要把subList 合成一个 一维数组,借用concat方法:var newSub = [].concat.apply([],subList);

然后对 newSub进行去重(需要将数组转换为Q对象才能调用unique方法), var subQ = Guoqi(newSub).unique();

需要对selector进行判断,如果 selector存在的话,那么 return subQ.filter( Guoqi(selector) ); 如果不存在的话,那么直接 return subQ;

find(selector) : 功能:find一般是找指定的元素,在this实例对象的所有后代元素中找,所以一般都传入参数

find方法和children方法一样,只需要把

var subList = this.map(function(v){
  return Guoqi.map(__v.querySelectorAll(selector)__,function(v1){
  return v1 }) });
로그인 후 복사


用v.querySelectorAll(selector); 来找到所有的后代元素(应该还有更好的办法,欢迎来补充)

nextAll 工具方法 跟next方法如出一辙:

function next(dom){
       while( dom = dom.nextSibling ){
             if( dom.nodeType === 1 ){
                        return dom;
              }
       }
       return null;}
로그인 후 복사

next,prev,nextAll,prevAll 方法 都常会在 封装实例方法中用到,所以可以封装为工具方法; prevAll,prev 两个方法和next,nextAll 原理一样,所以不再详细赘述

注意:nextAll,prevAll,children 这些链都遭到了破坏,不过可以使用end恢复

nextAll 实例方法 既然有了上面的nextAll的工具方法,那么相对应的实例方法就简单了很多,

只需要把this实例对象的每一个dom元素调用 nextAll(dom),组成一个数组(用map方法简单),

然后把这些数组用concat组合在一起进行__去重(unique)__,进而转换为Guoqi对象即可

siblings 实例方法

有了prevAll 和 nextAll方法,siblings就变得简单多了,只要两者组合在一起即可

var nextSiblings = this.nextAll().toArray();
var prevSiblings = this.prevAll().toArray();
로그인 후 복사


返回 Guoqi( prevSiblings.concat( nextSiblings ) );

事件操作 模块

on 事件的实现 (我们选择先实现on事件,是因为on事件是通用的,实现on事件之后,其他的具体事件都可以用on事件来实现)

on 事件 语法:Guoqi.fn.extend(on: function( 事件列表,callback ){ });

事件列表 可以有实现多个事件 中间用空格隔开

这就意味着 我们要对this实例对象进行遍历,也要对事件列表进行遍历

添加事件的时候,我们选择使用 addEventListener("事件名",callback);

各个事件的实现

首先我们可以从控制台打印出来所有的事件

创建一个DOM对象div,for( var k in div ){ if( k.slice(0,1) === "on" ){ arr.push(k) } }; 这样就把所有事件放到arr数组中去了

循环遍历数组中每个值,只保留事件名的部分,即:v = v.slice(2);

然后添加在原型上:Guoqi.fn[v] = function(callback){ return this.on(v,callback) };

css 样式操作模块的实现

首先我们需要考虑css的参数情况 语法:Guoqi.fn.css = function( name,value){ }

字符串( typeof name === "string" ),即:需要获取实例对象的值:一般情况下,我们获取的是实例对象中第一个元素的值
(this[0] / this.get(0)) 可以直接 return this[0].style[name] ,但是值得注意的是,这样只能获取行内样式的值

所以 我们还需要 || window.getComputedStyle(this[0])[name]; 但是getComputedStyle在低版本的IE浏览器中(currentStyle)不支持,
如果考虑严谨的话,可以封装一个getStyle获取样式的函数

只有一个参数 即:value == undefined 可能是对象,也可能是一个字符串

function getStyle(dom,style){
  return dom.currentStyle?dom.currentStyle[style]:  
window.getComputedStyle(dom)[style]}
로그인 후 복사


这样的话,只需要循环遍历this对象,即:this[i].style[name] = value; 并且要把this实例对象返回回去,实现链式编程

对象 不仅需要循环遍历this实例对象,还需要遍历name这个键值对的值:做到for( var k in name ){ this[i].style[k] = name[k] };
注意要返回 this实例对象,便于链式编程


有两个参数 name value 设置单个样式

属性 样式操作模块的实现 (与css实现原理相似)

hasClass(className) 判断实例对象 有没有该类名,

实现:我们需要分别对this实例对象进行遍历,和他们的所有的类名进行遍历,

为了方便操作,需要把实例对象的类名转换为数组,并去除前后空格(遍历检查),然后使用__indexOf()__方法,若为-1,则返回false, >=0或者!=-1则为true;

即:var classNameValues = this(指的是实例对象中的dom).className.trim().split(" "); classNameValus.indexOf(className) >=0 --->true

可以使用some方法,(只要有一个满足条件直接放回true) 即:return this.toAArray().some(function(v){ return v.trim().split(" ")indexOf(className) >= 0 };

addClass(className) 添加类名

又需要分清况讨论了,若是没有类名:即classNameValues.length == 0,则直接添加 this.className = className;

若是有class,但是没有该类名,则需要追加;即:if( classNameValues.indexOf(className) == -1 ){this.className += " "+classNaame};__(注意要用空格分隔开)__;

若是已经有该类名了,则什么都不需要做(不能重复添加)

addClass可是直接用数组来实现简单一些:只要if( classNameValues.indexOf(className) == -1 ){ classNameValues.push(className) };this.className = classNameValues.join(" ");

removeClass(className) 删除类名

就是把指定的类名给删除掉,需要进行循环遍历所有类名数组 classNameValues,然后用splice来把指定的类名从数组中给截取掉;

即var index;(来记录查到指定类名的索引)
js while( (index= classNameValues.indexof(className))>=0 ){ classNameValues.splice(index,1);} this.className = classNameValues.join(" ");

还可以使用 map方法; 即:this.className = classNameValues.map(function(v){if(v != className){return v;}}).join(" "); 利用map方法产生一个新数组,简单一些

toggleClass(className) 切换类名

直接进行判断

if(this.hasClass(className))
{ this.removeClass(className)
  }else{ this.addClas
  s(className) 
  };
로그인 후 복사


对上面的代码实现复用,减少代码冗余; 如果有该类名的话,直接删除,没有类名的话,就添加

attr(name,value) 对属性样式的设置 和css 原理一样:还是要分情况:

一个参数时,类型为字符串,获取属性值:用getAttribute(name);

一个参数时,类型为对象,数值多个属性值 ,循环遍历该对象用setAttribute(k,name[k])来进行设置

两个参数时,设置单个属性值,直接进行设置即可:setAttribute(name,value);

prop(name,value) 与上述情况一样,分类进行考虑,

但是注意的是。prop针对的是input标签这些单属性的值,值为布尔类型,例如disabled,checked,selected

设置和获取的时候不用setAttribute,getAttribute,直接进行赋值即可,this[name] = value;

注:如果对其进行赋值,例如disabled,不论赋值为true还是false,都不可编辑,除非移除该属性removeProp

removeAttr与removeProp,个人认为实现原理一样,都是使用removeAttribute

即:遍历this实例对象,然后进行 this.removeAttribute(name);

入口函数,即init方法中selector传入的数函数的情况

方法一:直接使用window.addEventListener("load",selector); 可以实现累加,开辟不同的入口函数,互不影响

方法二:利用函数的技巧:记录下来window.onload的值,进行判断如果是一个函数的话,则他执行,在执行selector传入的,否则的话,直接把selector赋值给window.onload

即:

var oldFn = windiw.onload ; 
if(typeof oldFn == "function"){  window.onlad = function(){ 
oldFn();selector(); 
  }}else{ 
window.onload = selector(); 
  }
로그인 후 복사

方法三:利用数组的技巧:建立一个私有数组,var loads = []; 直接把一个个的selector给push到数组中去,

然后定义一个

window.onload = function(){  
  Guoqi.each(loads,function(){ this(); }) 
}   // 把数组中的selector依次执行
로그인 후 복사


관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!