jQuery의 extend 메소드에 해당하는 JavaScript


94

배경

config객체를 인수로 취하는 함수가 있습니다. 함수 내에도 default객체가 있습니다. 이러한 각 개체에는 기본적으로 함수 내의 나머지 코드에 대한 설정으로 작동하는 속성이 포함되어 있습니다. config객체 내의 모든 설정을 지정할 필요가 없도록하기 위해 jQuery의 extend메서드를 사용 settings하여 default객체에 지정되지 않은 경우 객체의 기본값으로 새 객체를 채 웁니다 config.

var config = {key1: value1};
var default = {key1: default1, key2: default2, key 3: default 3};

var settings = $.extend(default, config);

//resulting properties of settings:
settings = {key1: value1, key2: default2, key 3: default 3};

문제

이것은 훌륭하게 작동하지만 jQuery 없이도이 기능을 재현하고 싶습니다. 평범한 자바 스크립트로 이것을 수행하는 똑같이 우아한 (또는 가까운) 수단이 있습니까?


편집 : 중복되지 않은 사유

이 질문은 " 어떻게 두 JavaScript 객체의 속성을 동적으로 병합 할 수 있습니까? "질문과 중복되지 않습니다 . 이 질문은 단순히 두 개의 개별 객체의 모든 키와 값을 포함하는 객체를 생성하려는 반면, 두 객체가 일부 키를 공유하지만 모든 키를 공유하고 어떤 객체가 우선 순위를 가질 경우이를 수행하는 방법을 구체적으로 설명하고 싶습니다 ( 중복 키가있는 경우 결과 개체에 대해 기본값). 그리고 더 구체적으로, 나는 이것을 달성하기 위해 jQuery의 방법을 사용하고 jQuery없이 그렇게 할 수있는 다른 방법을 찾고 싶었다. 두 질문에 대한 많은 답변이 겹치지 만 그렇다고 질문 자체가 동일하다는 의미는 아닙니다.


8
그냥 jQuery를이 수행 방식 훔쳐 james.padolsey.com/jquery/#v=1.6.2&fn=jQuery.extend - P
로켓 위험물

당신이 그 기능을 copypasta 경우 좋은 아이디어 @RocketHazmat는 참고는 같은 몇 가지 다른 jQuery를 종속성을 가지고 jQuery.isPlainObjectjQuery.isFunction
앤디 Raddatz에게

답변:


133

코드에서 결과를 얻으려면 다음을 수행하십시오.

function extend(a, b){
    for(var key in b)
        if(b.hasOwnProperty(key))
            a[key] = b[key];
    return a;
}

사용한 방법에 따라 기본 개체가 수정됩니다. 원하지 않으면

$.extend({}, default, config)

jQuery의 기능을 모방하는보다 강력한 솔루션은 다음과 같습니다.

function extend(){
    for(var i=1; i<arguments.length; i++)
        for(var key in arguments[i])
            if(arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

1
나는 평범한 js가 훨씬 더 멋지지만 너무 추상적이기 때문에 실제로 작동하는 코드 예제를보고 싶습니다.
thednp 2014-10-02

3
$ .extend처럼 반복되지 않습니다
ekkis

30

Object.assign을 사용할 수 있습니다.

var defaults = {key1: "default1", key2: "default2", key3: "defaults3"};
var config = {key1: "value1"};

var settings = Object.assign({}, defaults, config); // values in config override values in defaults
console.log(settings); // Object {key1: "value1", key2: "default2", key3: "defaults3"}

하나 이상의 소스 개체에서 모든 열거 가능한 고유 속성의 값을 대상 개체로 복사하고 대상 개체를 반환합니다.

Object.assign(target, ...sources)

IE를 제외한 모든 데스크톱 브라우저에서 작동합니다 (Edge 포함). 모바일 지원이 완화되었습니다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign 에서 직접 확인하십시오.

딥 카피 정보

그러나 Object.assign에는 jQuery의 extend 메소드에있는 딥 옵션이 없습니다.

참고 : 일반적으로 유사한 효과를 위해 JSON을 사용할 수 있습니다.

var config = {key1: "value1"};
    var defaults = {key1: "default1", key2: "default2", keyDeep: {
        kd1: "default3",
        kd2: "default4"
    }};
    var settings = JSON.parse(JSON.stringify(Object.assign({}, defaults, config))); 
    console.log(settings.keyDeep); // Object {kd1: "default3", kd2: "default4"}

예,하지만 여기서는 기본값에서 key1 위로 건너 뛰고 새 개체에 추가하지 않습니다.
누트 Necula

@Anonymous 실제로 추가하지만 구성 배열의 key1 값으로 재정의됩니다.
ling

그런 일이 일어나고 있다고 생각했습니다. 그래서 그것은 결국 그것을 추가하지 않습니다.
누트 Necula


4

이것은 jQuery 종속성을 제거하려고 시도하면서 내놓은 딥 카피와 약간 다른 접근 방식입니다. 대부분 작게 설계되었으므로 예상되는 모든 기능이 없을 수도 있습니다. ES5와 완전히 호환되어야합니다 ( Object.keys 사용으로 인해 IE9부터 시작 ) :

function extend(obj1, obj2) {
    var keys = Object.keys(obj2);
    for (var i = 0; i < keys.length; i += 1) {
      var val = obj2[keys[i]];
      obj1[keys[i]] = ['string', 'number', 'array', 'boolean'].indexOf(typeof val) === -1 ? extend(obj1[keys[i]] || {}, val) : val;
    }
    return obj1;
  }

다섯 번째 줄이 정확히 무엇을하는지 궁금 할 것입니다. obj2.key가 객체 리터럴이면 (즉, 일반 유형이 아닌 경우) 우리는 그것에 대해 재귀 적으로 extend를 호출합니다. 해당 이름의 속성이 obj1에 아직 존재하지 않는 경우 먼저 빈 객체로 초기화합니다. 그렇지 않으면 단순히 obj1.key를 obj2.key로 설정합니다.

여기에서 작동하는 일반적인 사례를 증명해야하는 몇 가지 mocha / chai 테스트가 있습니다.

it('should extend a given flat object with another flat object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 20.16,
  };
  const obj2 = {
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  });
});

it('should deep-extend a given flat object with a nested object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 'val2',
  };
  const obj2 = {
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 'val2',
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  });
});

it('should deep-extend a given nested object with another nested object and deep-overwrite members', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: {
      propDeep1: 'deepVal1',
      propDeep2: 42,
      propDeep3: true,
      propDeep4: {
        propDeeper1: 'deeperVal1',
        propDeeper2: 777,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  };
  const obj2 = {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
      },
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  });
});

이 구현에 대한 피드백이나 의견에 만족합니다. 미리 감사드립니다!


2

for문을 사용하여 Object의 속성을 반복 할 수 있습니다 .

var settings = extend(default, config);

function extend(a, b){
    var c = {};
    for(var p in a)
        c[p] = (b[p] == null) ? a[p] : b[p];
    return c;
}

2
에 존재하지 않는 속성은 고려하지 않습니다 a. 예제에는 없지만 $.extend실제로는 모든 객체의 모든 속성을 병합하는 방식으로 작동합니다.
Felix Kling

1
jquery 기본 코드를 읽을 때 실제로 값을 복사하거나 복제하는 재귀 함수를 수행합니다. 따라서 위의 코드와 같이 첫 번째 수준에서 복사하는 것이 아니라 깊이 복제 / 복사하는 것입니다.
aladine

2

내 일반 forEachIn을 사용하고 첫 번째 개체를 엉망으로 만들지 않는이 코드를 선호합니다.

function forEachIn(obj, fn) {
    var index = 0;
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            fn(obj[key], key, index++);
        }
    }
}

function extend() {
    var result = {};
    for (var i = 0; i < arguments.length; i++) {
        forEachIn(arguments[i],
            function(obj, key) {
                result[key] = obj;
            });
    }
    return result;
}

정말로 물건을 첫 번째 객체에 병합하고 싶다면 다음을 수행 할 수 있습니다.

obj1 = extend(obj1, obj2);

이것이 가장 좋은 대답입니다. 깊은 복사를 수행하고 첫 번째 개체를 엉망으로 만들지 않으며 무한 개체를 지원하며 확장하는 동안 유형을 변경할 수 있습니다 (즉, 여기에있는 많은 답변은 문자열을 개체로 확장하지 않습니다. 이것은 내 책의 모든 체크 표시를 얻습니다. 전체 확장 교체 및 매우 이해하기 쉽습니다.
Nick Steele

0

순수한 자바 스크립트로 개발할 때 많은 도움이됩니다.

function extends(defaults, selfConfig){
     selfConfig = JSON.parse(JSON.stringify(defaults));
     for (var item in config) {
         if (config.hasOwnProperty(item)) {
             selfConfig[item] = config[item];
         }
     }
     return selfConfig;
}

0

Ivan Kuckir의 접근 방식을 적용하여 새 개체 프로토 타입을 만들 수도 있습니다.

Object.prototype.extend = function(b){
for(var key in b)
    if(b.hasOwnProperty(key))
        this[key] = b[key];
    return this;
}

var settings = default.extend(config);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.