2010-03-02 31 views
12

Tôi muốn làm cho cấu trúc của một đối tượng không thay đổi được, ngăn không cho các thuộc tính của nó bị thay thế sau đó. Tuy nhiên, các thuộc tính cần phải đọc được. Điều này có thể không?Có cách nào để ngăn chặn thay thế các thuộc tính đối tượng JavaScript không?

Tôi chắc chắn không có tính năng ngôn ngữ (dọc theo các dòng final trong Java và readonly trong C#) để hỗ trợ điều này nhưng tự hỏi liệu có thể có cơ chế khác để đạt được kết quả tương tự không?

Tôi đang tìm một cái gì đó dọc theo những dòng:

var o = { 
    a: "a", 
    f: function() { 
     return "b"; 
    } 
}; 

var p = o.a;  // OK 
o.a = "b";   // Error 
var q = o.f();  // OK 
o.f = function() { // Error 
    return "c"; 
}; 

Trả lời

14

ECMAScript 5 sẽ có seal()freeze(), nhưng không có cách nào tốt để làm điều này với triển khai JavaScript hiện tại.

Source.

+1

+1: ' freeze() 'nghe như những gì tôi theo sau. –

+0

bạn đang chạy phiên bản 5?! – mkoryak

+1

Tôi vừa đề cập đến nó để tham khảo. Tôi không chủ động lập trình chống lại ECMAScript 5. – EndangeredMassa

9

điều tốt nhất bạn có thể làm là giấu tài sản của bạn bên trong một đóng cửa.

var getMap = function(){ 
    var hidden = "1"; 
    return { 
    getHidden : function() { return hidden; } 
    } 
} 

var f = getMap(); 

alert(f.getHidden()); 

Tôi đã đâm vào nó. Trong đoạn mã trên, bạn sẽ cần phải không chỉ trở về ẩn nhưng sao chép nó vào một đối tượng mới có lẽ. có lẽ bạn có thể sử dụng mở rộng của jquery để làm điều này cho bạn, vì vậy bạn sẽ trả về một đối tượng mới, không phải là tham chiếu. Điều này có thể hoàn toàn sai mặc dù =)

+0

tôi cần các thuộc tính để có thể truy cập từ bên ngoài đối tượng (câu hỏi được cập nhật để phản ánh) . –

+0

@Matthew Vì vậy, hãy tạo một chức năng truy cập công cộng. Xem câu trả lời của tôi. –

+0

chỉnh sửa câu trả lời giống như những gì bạn muốn – mkoryak

4

Như mkoryak nói, bạn có thể tạo ra một đóng cửa để che giấu tính

function Car(make, model, color) { 
    var _make = make, _model = model, _color = color; 

    this.getMake = function() { 
     return _make; 
    } 

} 

var mycar = new Car("ford", "mustang", "black"); 

mycar.getMake(); //returns "ford" 
mycar._make; //error 
+0

Nếu mkoryak đã nói vậy, tại sao lặp lại nó? – Zecc

+1

@Zecc vì câu trả lời đó không có mẫu mã ban đầu –

5

Sử dụng var trong một constructor đối tượng sẽ tạo ra một biến tư nhân. Đây thực chất là một đóng cửa. Sau đó, bạn có thể tạo một chức năng công cộng để truy cập/sửa đổi nó. Thêm thông tin và ví dụ có sẵn trên Private Members in Javascript bởi Douglas Crockford.

+1

Nhưng sau đó bạn có thể thay thế hàm để trả về bất kỳ thứ gì bạn muốn. – EndangeredMassa

+0

@Sean Chắc chắn nó có thể bị xóa/thay thế, nhưng nó không thể bị thay đổi. Tôi đoán điểm mấu chốt là không có cách nào để thay thế hàm bằng cái gì đó sẽ cho phép truy cập trực tiếp vào var riêng. –

4

Được rồi, do đó, đã có một vài câu trả lời gợi ý bạn trả lại một đối tượng bằng nhiều phương pháp getters. Nhưng bạn vẫn có thể thay thế những phương pháp đó.

Có điều này, tốt hơn một chút. Bạn sẽ không thể thay thế các thuộc tính của đối tượng mà không thay thế hoàn toàn hàm. Nhưng nó vẫn không chính xác những gì bạn muốn.

function Sealed(obj) { 
    function copy(o){ 
     var n = {}; 
     for(p in o){ 
      n[p] = o[p] 
     } 
     return n; 
    } 
    var priv = copy(obj); 
    return function(p) { 
     return typeof p == 'undefined' ? copy(priv) : priv[p]; // or maybe copy(priv[p]) 
    } 
} 

var mycar = new Sealed({make:"ford", model:"mustang", color:"black"}); 

alert(mycar('make')); // "ford" 
alert(mycar().make); // "ford" 

var newcopy = mycar(); 
newcopy.make = 'volkwagen'; 
alert(newcopy.make); // "volkwagen" :(

alert(mycar().make); // still "ford" :) 
alert(mycar('make')); // still "ford" :) 
2

Bây giờ bạn có thể buộc một thuộc tính đối tượng đơn bị đóng băng thay vì đóng băng toàn bộ đối tượng. Bạn có thể đạt được điều này với Object.defineProperty và tham số writable: false

var obj = { 
    "first": 1, 
    "second": 2, 
    "third": 3 
}; 
Object.defineProperty(obj, "first", { 
    writable: false, 
    value: 99 
}); 

Trong ví dụ này, obj.first giờ đây đã giá trị của nó bị khóa đến 99.

Các vấn đề liên quan