>  기사  >  웹 프론트엔드  >  jQuery 2.0.3 소스코드 분석 핵심(1) 전체 아키텍처_jquery

jQuery 2.0.3 소스코드 분석 핵심(1) 전체 아키텍처_jquery

WBOY
WBOY원래의
2016-05-16 16:46:401073검색

오픈소스 프레임워크에 대해 읽을 때 가장 배우고 싶은 것은 디자인 아이디어와 구현 기술입니다.

말도 안되는 소리는 아닙니다. jquery 분석은 수년 동안 좋지 않았습니다. 오래 전에 읽었습니다.

그런데 최근 몇 년간 모바일 단말기 작업을 하며 늘 zepto를 사용해왔는데 최근에는 jquery를 다시 스캔하는 데 시간이 좀 걸렸습니다

소스코드를 대본대로 번역하지는 않으니, 본인의 실제 경험을 바탕으로 읽어주세요!

github의 최신 버전은 AMD 사양에 합류한 jquery-master입니다. 공식 최신 2.0.3을 참고하겠습니다

전체 구조

jQuery 프레임워크의 핵심은 HTML 문서의 요소를 일치시키고 해당 요소에 대해 작업을 수행하는 것입니다.

예:

코드 복사 코드는 다음과 같습니다.

$().find().css ()
$().hide().html('....').hide().

위 글에서 적어도 2가지 문제점을 발견할 수 있습니다

1. jQuery 객체 구성 방법

2. jQuery 메소드 호출 방법

분석 1: jQuery의 new-free 구축

자바스크립트는 함수형 언어로 클래스를 구현할 수 있습니다. 클래스는 객체지향 프로그래밍의 가장 기본적인 개념입니다.

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
//생성자
}
aQuery.prototype = {
//Prototype
이름:function(){},
age:function(){}
}

var a = new aQuery();

a.name();

이것은 일반적인 사용 방법입니다. jQuery는 이런 방식으로 작동하지 않는 것이 분명합니다.

jQuery는 new 연산자를 사용하여 jQuery 표시를 인스턴스화하지 않거나 해당 함수를 직접 호출하지 않습니다.

jQuery의 작성방식을 따른다

코드 복사 코드는 다음과 같습니다.

$().ready()
$( ).noConflect()

이를 달성하려면 jQuery를 클래스로 간주해야 하며 $()는 클래스의 인스턴스를 반환해야 합니다

그러므로 코드를 변경하세요.

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
새로운 aQuery() 반환;
}
aQuery.prototype = {
name:function(){},
age:function(){}
}

new aQuery()를 통해 인스턴스가 반환되더라도 여전히 명백한 문제인 무한 루프를 볼 수 있습니다!

그렇다면 올바른 인스턴스를 반환하는 방법은 무엇입니까?

자바스크립트의 인스턴스는 프로토타입에만 관련됩니다

그런 다음 jQuery 클래스를 팩토리 메소드로 사용하여 인스턴스를 생성하고 이 메소드를 jQuery.prototye 프로토타입에 넣을 수 있습니다

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
Return aQuery.prototype.init();
}
aQuery.prototype = {
init:function(){
return this;
}
이름:함수 (){ },
연령:기능(){}
}

aQuery()가 실행될 때 반환되는 인스턴스:

분명히 aQuery()는 aQuery 클래스의 인스턴스를 반환하므로 init의 이는 실제로 aQuery 클래스의 인스턴스를 가리킵니다

문제는 init의 this가 aQuery 클래스를 가리킨다는 것입니다. init 함수가 생성자로도 사용되는 경우 내부 this를 어떻게 처리해야 할까요?

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
Return aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
return this;
},
이름: function() {},
나이: 20
}

aQuery().age //18

이 경우에는 aQuery 클래스만 가리키기 때문에 문제가 발생하므로 독립적인 범위를 설계해야 합니다

JQuery 프레임워크 별도 범위 처리

코드 복사 코드는 다음과 같습니다.

jQuery = function( selector, context ) {
// jQuery 객체는 실제로는 'enhanced' 초기화 생성자일 뿐입니다.
return new jQuery.fn.init( selector, context, rootjQuery );
},

분명히 인스턴스 초기화 함수를 통해 이를 분리하고 상호작용 혼란을 피하기 위해 매번 새로운 초기화 인스턴스 객체가 생성됩니다

그래서 동일한 개체가 아니기 때문에 새로운 문제가 발생해야 합니다

예:

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
Return new aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
return this;
} ,
이름: function() {},
나이: 20
}

//잡히지 않은 TypeError: 개체 [object Object]에 'name' 메서드가 없습니다.
console.log(aQuery().name())

오류가 발생하고 이 메소드를 찾을 수 없으므로 new의 초기화가 jquery 클래스의 이것과 분리되어 있음이 분명합니다

jQuery 클래스 프로토타입의 속성과 메서드에 어떻게 액세스하나요?

어떻게 범위를 분리할 수 있을 뿐만 아니라 jQuery 프로토타입 객체의 범위도 사용할 수 있나요? 반환된 인스턴스에서 jQuery 프로토타입 객체에도 액세스할 수 있나요?

실시 포인트

코드 복사 코드는 다음과 같습니다.

// 초기화 함수에 jQuery 프로토타입 제공 나중에 인스턴스화
jQuery.fn.init.prototype = jQuery.fn;

프로토타입 전달을 통해 문제 해결, jQuery 프로토타입을 jQuery.prototype.init.prototype에 전달

즉, jQuery의 프로토타입 객체는 init 생성자의 프로토타입 객체를 재정의합니다

참조로 전달되기 때문에 이 순환 참조의 성능 문제는 걱정할 필요가 없습니다

코드 복사 코드는 다음과 같습니다.

var aQuery = function(selector, context) {
Return new aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
return this;
},
이름 : function( ) {
return this.age
},
age: 20
}
aQuery.prototype.init.prototype = aQuery.prototype;
console.log(aQuery ().이름()) //20

바이두는 쉽고 직접적인 이해를 위해 네티즌의 사진을 빌려왔습니다.

fn에 대해 설명하세요. 사실 이 fn은 특별한 의미가 없으며 단지 jQuery.prototype에 대한 참조일 뿐입니다

분석 2: 체인콜

DOM 체인 호출 처리:

1. JS 코드를 저장합니다.

2. 동일한 객체가 반환되므로 코드 효율성이 향상될 수 있습니다

간단히 프로토타입 메소드를 확장하고 이를 반환함으로써 브라우저 간 체인 호출을 달성할 수 있습니다.

JS에서 간단한 팩토리 패턴을 사용하여 동일한 DOM 객체에 대한 모든 작업에 대해 동일한 인스턴스를 지정합니다.

이 원리는 매우 간단합니다

코드 복사 코드는 다음과 같습니다.

aQuery().init().name ()
분해
a = aQuery();
a.init()
a.name()

코드를 분석해 보면 체이닝을 구현하기 위한 기본 조건은 인스턴스 this의 존재임이 분명하며, 마찬가지입니다

코드 복사 코드는 다음과 같습니다.

aQuery.prototype = {
init : function( ) {
return this;
},
name: function() {
return this
}
}

따라서 현재 인스턴스의 this를 반환하므로 연결된 메소드에서만 액세스하면 됩니다. 그래서 우리는 자체 프로토타입에 액세스할 수 있습니다.

코드 복사 코드는 다음과 같습니다.

aQuery.init().name()

장점: 코드 양을 줄이고 코드 효율성을 높이며 코드가 더욱 우아해 보입니다

가장 나쁜 점은 모든 객체 메서드가 객체 자체를 반환한다는 점인데, 이는 반환 값이 없다는 의미이므로 어떤 환경에도 적합하지 않을 수 있습니다.

자바스크립트는 비차단 언어이므로 차단할 수는 없지만 차단할 수는 없으므로 이벤트에 의해 구동되어야 하며 프로세스를 차단해야 하는 일부 작업을 비동기적으로 완료해야 합니다. 이 처리는 동기 체인일 뿐입니다. 비동기 체인 jquery Promise는 1.5부터 도입되었으며 jQuery.Deferred에 대해서는 나중에 논의하겠습니다.

분석 3: 플러그인 인터페이스

jQuery의 메인 프레임워크는 이렇지만 일반적인 디자이너의 습관에 따라 jQuery나 jQuery 프로토타입에 속성 메서드를 추가하고 개발자에게 메서드 확장을 제공하려는 경우에는 캡슐화의 관점에서는 인터페이스가 맞는데, 프로토타입을 직접 수정하는 것이 아니라 말 그대로 기능의 확장이라고 이해하면 됩니다.

jQuery는 자체 확장 속성을 지원하며 객체에 메서드를 추가하는 외부 인터페이스인 jQuery.fn.extend()를 제공합니다.

jQuery 소스 코드에서 볼 수 있듯이 jQuery.extend와 jQuery.fn.extend는 실제로 동일한 메소드를 가리키는 다른 참조입니다

코드 복사 코드는 다음과 같습니다.

jQuery.extend = jQuery.fn.extend = 함수( ) {

jQuery.extend는 jQuery 자체의 속성과 메서드를 확장합니다

jQuery.fn.extend는 jQuery.fn의 속성과 메서드를 확장합니다

extend() 함수를 사용하면 jQuery의 프로토타입 구조를 파괴하지 않고 쉽고 빠르게 함수를 확장할 수 있습니다

jQuery.extend = jQuery.fn.extend = function(){...}; 이는 연속적입니다. 즉, 동일한 함수를 가리키는 두 지점입니다. 어떻게 서로 다른 기능을 수행할 수 있습니까? 이것이 바로 이것의 힘이다!

앞서 설명한 것처럼 Fn과 jQuery는 실제로 두 개의 다른 개체입니다.

jQuery.extend가 호출되면 이는 jQuery 객체를 가리키므로(jQuery는 함수이자 객체입니다!) 여기의 확장은 jQuery에 있습니다.
jQuery.fn.extend가 호출되면 이는 fn 객체를 가리키고, jQuery.fn과 jQuery.prototype은 동일한 객체를 가리키며, fn을 확장하는 것은 jQuery.prototype 프로토타입 객체를 확장하는 것입니다.
여기에 추가되는 것이 프로토타입 메소드, 즉 객체 메소드입니다. 따라서 jQuery API는 위의 두 가지 확장 기능을 제공합니다.

확장 구현

코드 복사 코드는 다음과 같습니다.

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},    // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0]
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {    // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况
        deep = target;  // 此时target是true
        target = arguments[1] || {};    // target改为 obj1
        // skip the boolean and the target
        i = 2;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {  // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( length === i ) {   // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
        target = this;  // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
        --i;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {    // 防止自引用,不赘述
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                // 如果是深拷贝,且被拷贝的属性值本身是个对象
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {    // 被拷贝的属性值是个数组
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {    被拷贝的属性值是个plainObject,比如{ nick: 'casper' }
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

// 절대 원본 객체를 이동하지 말고 복제하세요.
대상 [name] = jquery.extEnd (deep, clone, copy) // 재귀적 ~

                                                                                                             >
// 수정된 객체를 반환합니다
return target;



요약:

init 생성자의 프로토타입 프로토타입 객체 메소드를 사용하여 new jQuery.fn.init()를 통해 새 객체를 생성합니다.
프로토타입 포인터의 포인팅을 변경하여 이 새 객체가 객체의 프로토타입도 가리키도록 합니다. jQuery 클래스 프로토타입
그래서 이렇게 구성된 객체는 jQuery.fn 프로토타입에서 정의한 모든 메서드를 계속 이어갑니다

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.