jQuery.extend()의 실현 방식 상세 설명 및 인 스 턴 스

8849 단어 jQuery.extend

<script type="text/javascript" src="jquery-1.5.2.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };

$.extend(true, obj1, obj2);

alert(obj1.x.xxx);  // "xxx"

obj2.x.xxx = 'zzz';
alert(obj2.x.xxx);  // "zzz"
alert(obj1.x.xxx);  // "xxx"
</script>

$.extend(true,obj 1,obj 2)는 obj 2 의 속성 확장 대상 obj 1 을 표시 하고 첫 번 째 매개 변 수 는 true 로 설정 하여 깊이 복 사 를 표시 합 니 다.obj 1 에는 원래'x'속성 이 없 었 지만 확장 을 거 친 후에 obj 1 은'x'속성 을 가지 게 되 었 을 뿐만 아니 라 obj 2 의'x'속성 에 대한 수정 도 obj 1 의'x'속성의 값 에 영향 을 주지 않 았 다.이것 이 바로'심 복사'이다.
얕 은 복제 의 실현
얕 은 복사 만 이 필요 하 다 면 다음 과 같은 쓰기 방법 을 사용 할 수 있 습 니 다.

$ = {
     extend : function(target, options) {
        for (name in options) {
            target[name] = options[name];
        }
        return target;
    }
};
즉,options 의 속성 을 target 에 간단하게 복사 하 는 것 입 니 다.우 리 는 여전히 유사 한 코드 로 테스트 할 수 있 지만 결 과 는 다르다.
딥 복제 의 실현
만약 우리 가'딥 복사'를 실현 하고 싶다 면 복사 한 대상 이 배열 이나 대상 일 때 extend 를 재 귀적 으로 호출 해 야 합 니 다.다음 코드 는'딥 복사'의 간단 한 실현 입 니 다.

<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };
$.extend(obj1, obj2);
alert(obj1.x.xxx);  // "xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx);  // "zzz"
alert(obj1.x.xxx);  // "zzz"
</script>
구체 적 으로 세 가지 상황 으로 나 눌 수 있 습 니 다.1.속성 이 배열 일 때 target[name]을 빈 배열 로 초기 화 한 다음 에 extend 를 재 귀적 으로 호출 합 니 다.2.속성 이 대상 일 때 target[name]을 빈 대상 으로 초기 화하 고 extend 를 재 귀적 으로 호출 합 니 다.3.그렇지 않 으 면 속성 을 직접 복사 합 니 다.
테스트 코드 는 다음 과 같 습 니 다.

$ = {
 extend : function(deep, target, options) {
  for (name in options) {
   copy = options[name];
   if (deep && copy instanceof Array) {
                target[name] = $.extend(deep, [], copy);
            } else if (deep && copy instanceof Object) {
                target[name] = $.extend(deep, {}, copy);
   } else {
    target[name] = options[name];
   }
  }
  return target;
 }
};
현재 딥 복사 로 지정 되면 obj 2 의 수정 은 obj 1 에 영향 을 주지 않 습 니 다.그러나 이 코드 에는'instanceof Array'가 IE5 에서 호 환 되 지 않 는 경우 가 있 을 수 있 습 니 다.jQuery 의 실현 은 사실상 더 복잡 할 것 이다.
더욱 완전한 실현
아래 의 실현 은 jQuery 의 extend()와 좀 더 가 까 워 질 것 입 니 다.

<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };
$.extend(true, obj1, obj2);
alert(obj1.x.xxx);  // "xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx); // "zzz"
alert(obj1.x.xxx); // "xxx"
</script>
우선$=  function(){...}();이 문법 은 아래 의 문법 과 유사 하 다 고 이해 할 수 있다.

$ = function() {
    var copyIsArray,
        toString = Object.prototype.toString,
        hasOwn = Object.prototype.hasOwnProperty;

    class2type = {
        '[object Boolean]' : 'boolean',
        '[object Number]' : 'number',
        '[object String]' : 'string',
        '[object Function]' : 'function',
        '[object Array]' : 'array',
        '[object Date]' : 'date',
        '[object RegExp]' : 'regExp',
        '[object Object]' : 'object'
    },

    type = function(obj) {
        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
    },

    isWindow = function(obj) {
        return obj && typeof obj === "object" && "setInterval" in obj;
    },

    isArray = Array.isArray || function(obj) {
        return type(obj) === "array";
    },

    isPlainObject = function(obj) {
        if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
            return false;
        }

        if (obj.constructor && !hasOwn.call(obj, "constructor")
                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
            return false;
        }

        var key;
        for (key in obj) {
        }

        return key === undefined || hasOwn.call(obj, key);
    },

    extend = function(deep, target, options) {
        for (name in options) {
            src = target[name];
            copy = options[name];

            if (target === copy) { continue; }

            if (deep && copy
                    && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
                if (copyIsArray) {
                    copyIsArray = false;
                    clone = src && isArray(src) ? src : [];

                } else {
                    clone = src && isPlainObject(src) ? src : {};
                }

                target[name] = extend(deep, clone, copy);
            } else if (copy !== undefined) {
                target[name] = copy;
            }
        }

        return target;
    };

    return { extend : extend };
}();

즉,함 수 를 즉시 실행 하고 결 과 를$에 부여 하 는 것 이다.이런 표기 법 은 function 을 이용 하여 작용 역 을 관리 할 수 있 으 며 국부 변수 나 국부 함수 가 전역 에 영향 을 주지 않도록 할 수 있다.또한,우 리 는 사용자 가$.extend()를 호출 하고 내부 에서 실 현 된 함 수 를 숨 기 기 를 바 랍 니 다.따라서 최종 적 으로 돌아 오 는 대상 에는 extend:

func = function(){...};
$ =  func();
만 포함 되 어 있 습 니 다.그 다음 에 extend 함수 와 이전의 차 이 를 살 펴 보 겠 습 니 다.먼저 이 말 이 많 습 니 다.

return { extend : extend };
무한 순환 을 피하 기 위해 복사 할 속성 copy 가 target 과 같다 면,'자신'을'자신의 속성'으로 복사 해 예측 할 수 없 는 순환 을 초래 할 수 있다 는 것 이다.
그 다음 에 대상 이 배열 인지 아 닌 지 를 판단 하 는 방식 입 니 다.

if (target === copy) { continue; }
브 라 우 저 에 내 장 된 Array.isArray 가 있 으 면 브 라 우 저 자체 의 실현 방식 을 사용 합 니 다.그렇지 않 으 면 대상 을 String 으로 전환 하여'[object Array]'인지 확인 합 니 다.
마지막 으로 isPlainObject 의 실현 을 살 펴 보 자.

   type = function(obj) {
        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
   },
   isArray = Array.isArray || function(obj) {
        return type(obj) === "array";
    }
obj.node Type 을 정의 하면 DOM 요소 임 을 나타 낸다.이 코드 는 다음 과 같은 네 가지 상황 을 깊이 복사 하지 않 음 을 나타 낸다.1.대상 은 undefined 이다.2.String 으로 전환 할 때"[object Object]"가 아 닙 니 다.3.obj 는 DOM 요소 입 니 다.4.obj 는 window 입 니 다.DOM 요소 와 window 를 깊이 복사 하지 않 는 이 유 는 속성 이 너무 많 기 때 문 일 수 있 습 니 다.특히 window 대상 은 전역 에서 설명 하 는 모든 변 수 는 그 속성 이 고 내 장 된 속성 은 말 할 필요 도 없다.
다음은 구조 함수 와 관련 된 테스트 입 니 다.

if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
    return false;
}
대상 이 구조 함 수 를 가지 고 있 지만 자신의 속성 이 아니라면 이 구조 함 수 는 prototye 를 통 해 계승 되 었 고 이런 상황 도 깊이 복사 되 지 않 는 다 는 것 을 설명 합 니 다.이 점 은 아래 의 코드 와 결합 하여 이해 할 수 있다.

  if (obj.constructor && !hasOwn.call(obj, "constructor")
                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
        return false;
    }
이 몇 개의 코드 는 대상 의 속성 이 모두 자신의 것 인지 검사 하 는 데 사용 된다.대상 의 속성 을 옮 길 때 자신의 속성 부터 옮 겨 다 니 기 때문에 마지막 속성 이 자신의 것 인지 확인 하면 된다.이 는 대상 이 prototype 방식 으로 구조 함수 나 속성 을 계승 하면 이 대상 을 깊이 복사 하지 않 는 다 는 것 을 의미한다.이 는 이러한 대상 이 비교적 복잡 할 수 있 음 을 고려 하여 불확실 한 요 소 를 도입 하거나 대량의 속성 을 복제 하기 위해 많은 시간 을 들 이지 않도록 처리 한 것 일 수도 있 으 며,함수 명 에서 도 알 수 있 듯 이 심층 복 제 를 하 는 것 은'PlainObject'밖 에 없다.만약 에 우리 가 다음 과 같은 코드 로 테스트 를 한다 면

        var key;
        for (key in obj) {
        }
        return key === undefined || hasOwn.call(obj, key);
이런 상황 은 깊이 복사 하지 않 는 다 는 것 을 알 수 있다.한 마디 로 하면 jQuery 의 extend()실현 방식 은 브 라 우 저의 호환성 을 고려 하여 성능 이 너무 낮 지 않도록 하고 예측 할 수 없 는 오 류 를 도입 하지 않도록 하 는 등 요 소 를 고려 했다.

좋은 웹페이지 즐겨찾기