2013-08-26 35 views
9

ApplicationJavaScript, đối tượng ghi đè mà không làm mất tài liệu tham khảo

tôi đang làm việc trên một ứng dụng web đơn giản mà được xây dựng trên đầu trang của AngularJS. Ứng dụng sẽ có thể hoạt động ngoại tuyến cũng như trực tuyến. Khi người dùng ngoại tuyến, các thay đổi đối với dữ liệu được lưu trữ cục bộ. Do đó, id được sử dụng trong ứng dụng này trong chế độ ẩn chỉ là tạm thời của id, họ nhận được thay thế khi tải lên máy chủ

Vấn đề

Các dữ liệu được sử dụng trong các ứng dụng bao gồm các đối tượng phức tạp (với các quan hệ/tham chiếu đến các đối tượng khác). Khi tôi đang lưu vào máy chủ, tôi muốn các chế độ xem được cập nhật với id "thực" mới. Tuy nhiên, vì JavaScript hoạt động với các đối tượng dưới dạng tham chiếu nên không thể thực hiện những gì tôi muốn: $scope.data = newdata Điều này không ghi đè $ scope.data nhưng tạo đối tượng mới. Tham chiếu cũ cho dữ liệu cũ vẫn còn đó.

dụ Giản

var x = {id: 1, name: "myObject"} 
var c = x // c = {id: 1, name: "myObject"} 
x = {id: 2, name: "myNewObject"} 
// c = {id: 1, name: "myObject"} 

Như bạn thấy, c vẫn là một tham chiếu đến đối tượng cũ. Trong thực tế, điều này khiến cho chế độ xem của tôi không được cập nhật với dữ liệu mới vì nó vẫn bị ràng buộc với dữ liệu cũ. Những gì tôi cần là ghi đè lên các thuộc tính của, trong ví dụ này, x. Tôi cần phải làm điều này một cách đệ quy vì các đối tượng thực sự của tôi rất phức tạp, tuy nhiên nó không nên nhập bất kỳ tham chiếu vòng tròn nào, vì điều này có thể sẽ gây ra tràn bộ đệm. Nếu tôi ghi đè a bằng b và a có các thuộc tính mà b không có, các thuộc tính đó phải được loại bỏ.

gì tôi cần

Tôi cần một số loại chức năng ghi đè tất cả tài sản trong một (object cũ) với các thuộc tính trong b (đối tượng mới). Tất cả các thuộc tính tồn tại trong một nhưng không phải trong b nên được loại bỏ.

Trả lời

5

Tôi đã tìm thấy giải pháp sau khi suy nghĩ. Nó có lẽ không phải là giải pháp hiệu quả nhất, nhưng nó có công việc cho tôi. Độ phức tạp thời gian có thể tốt hơn và mọi đề xuất cải tiến đều được hoan nghênh. Tham số đầu tiên là đối tượng được mở rộng, thứ hai là đối tượng cần mở rộng. Cái thứ ba được cho là một boolean, cho biết các thuộc tính trong một cái không tồn tại trong b có bị loại bỏ hay không.

function extend(_a,_b,remove){ 
     remove = remove === undefined ? false : remove; 
     var a_traversed = [], 
      b_traversed = []; 

     function _extend(a,b) { 
      if (a_traversed.indexOf(a) == -1 && b_traversed.indexOf(b) == -1){ 
       a_traversed.push(a); 
       b_traversed.push(b); 
       if (a instanceof Array){ 
        for (var i = 0; i < b.length; i++) { 
         if (a[i]){ // If element exists, keep going recursive so we don't lose the references 
          a[i] = _extend(a[i],b[i]); 
         } else { 
          a[i] = b[i]; // Object doesn't exist, no reference to lose 
         } 
        } 
        if (remove && b.length < a.length) { // Do we have fewer elements in the new object? 
         a.splice(b.length, a.length - b.length); 
        } 
       } 
       else if (a instanceof Object){ 
        for (var x in b) { 
         if (a.hasOwnProperty(x)) { 
          a[x] = _extend(a[x], b[x]); 
         } else { 
          a[x] = b[x]; 
         } 
        } 
        if (remove) for (var x in a) { 
         if (!b.hasOwnProperty(x)) { 
          delete a[x]; 
         } 
        } 
       } 
       else{ 
        return b; 
       } 
       return a; 
      }  
     } 

     _extend(_a,_b); 
    } 
4

Sử dụng "kéo dài" phương pháp trong đó có sẵn trong dấu gạch dưới và jquery:

//Clear all the 'old' properties from the object 
for (prop in old_object) {delete old_object[prop]} 
//Insert the new ones 
$.extend(old_object, new_object) 
+0

Đó là khá snazzy. Beats tự viết ra tất cả các thuộc tính. – tylerjgarland

1

tôi thêm một câu trả lời, mặc dù tất cả mọi người đã giải thích lý do tại sao cả hai và giải pháp.

Lý do tôi thêm câu trả lời là bởi vì tôi đã tìm kiếm câu trả lời này một vài lần trong những năm qua và về cơ bản luôn đi đến cùng câu hỏi SO 2/3. Tôi đặt các giải pháp trong rổ quá cứng, bởi vì mã tôi đã làm việc với nhiều mô đun đều theo các mẫu thiết kế tương tự nhau; nó chỉ là quá nhiều công việc để thử và giải quyết những gì đun sôi xuống cùng một vấn đề bạn đang gặp phải.

Những gì tôi đã học được và hy vọng nó có giá trị cho những người khác hiện tại mà tôi đã thực sự xác định lại codebase của mình để tránh vấn đề này (đôi khi có thể không thể tránh khỏi, nhưng đôi khi nó chắc chắn là), là tránh sử dụng 'biến riêng tư' để tham chiếu Đối tượng.

này có lẽ có thể được genericised hơn, nhưng lấy ví dụ:

var G = { 
    'someKey' : { 
     'foo' : 'bar' 
    } 
}; 

G.MySingletonClass = (function() { 

    var _private_static_data = G.someKey; // referencing an Object 

    return { 

     /** 
     * a method that returns the value of _private_static_data 
     * 
     * @method log 
     **/ 
     log: function() { 

      return _private_static_data; 

     } // eom - log() 

    }; // end of return block 

}()); // end of Class 

console.log(G.MySingletonClass.log()); 

G.someKey = { 
    'baz':'fubar' 
}; 

console.log(G.MySingletonClass.log()); 

http://jsfiddle.net/goxdebfh/1/

Như bạn có thể thấy, cùng một vấn đề kinh nghiệm của người hỏi. Trong trường hợp của tôi, và việc sử dụng các biến tĩnh riêng tham chiếu đến Object ở khắp mọi nơi, tất cả những gì tôi cần làm là tra cứu trực tiếp G.someKey; thay vì lưu nó như một biến tiện lợi cho lớp của tôi. Kết quả cuối cùng (mặc dù dài hơn do hậu quả của sự bất tiện) hoạt động rất tốt:

var G = { 
    'someKey' : { 
     'foo' : 'bar' 
    } 
}; 

G.MySingletonClass = (function() { 

    return { 

     /** 
     * a method that returns the value of _private_static_data 
     * 
     * @method log 
     **/ 
     log: function() { 

      return G.someKey; 

     } // eom - log() 

    }; // end of return block 

}()); // end of Class 

console.log(G.MySingletonClass.log()); 

G.someKey = { 
    'baz':'fubar' 
}; 

console.log(G.MySingletonClass.log()); 

http://jsfiddle.net/vv2d7juy/1/

Vì vậy, yeah, có lẽ không có gì mới cho câu hỏi đã được giải quyết, nhưng tôi cảm thấy bắt buộc phải chia sẻ rằng vì tôi thậm chí còn tin rằng ví dụ đầu tiên là cách chính xác để làm mọi thứ. Có lẽ trong một số trường hợp nó là, nó chắc chắn đã không bật ra được.

Hy vọng rằng sẽ giúp ai đó, ở đâu đó!

2

Nếu môi trường của bạn hỗ trợ ECMAScript năm 2015, bạn có thể sử dụng Object.assign():

'use strict' 

let one = { a: 1, b: 2, c: 3 }; 
let two = { b: 20, c: 30, d: 40 }; 

let three = Object.assign({}, one, two); 

console.log(three); 

// will output: Object {a: 1, b: 20, c: 30, d: 40} 

(let là phiên bản scoped tại địa phương mới của var trong ECMAScript 2015) more...


Vì vậy, trong trường hợp ví dụ đơn giản của bạn:

var x = { id: 1, name: "myObject" }; 
Object.assign(x, { id: 2, name: "myNewObject" }); 

console.log(x); 

// will output: Object {id: 2, name: "myNewObject"} 
Các vấn đề liên quan