2010-06-01 25 views
11

Tôi đang cố gắng thêm các phần tử vào một mảng được đánh giá lười biếng. Điều này có nghĩa là giá trị cho chúng sẽ không được tính hoặc biết cho đến khi chúng được truy cập. Điều này giống như một previous question I asked nhưng đối với các đối tượng.Bất kỳ cách nào để xác định getters cho biến lười biếng trong mảng Javascript?

Những gì tôi đã kết thúc làm cho các đối tượng là

Object.prototype.lazy = function(var_name, value_function) { 
    this.__defineGetter__(var_name, function() { 
    var saved_value = value_function(); 
    this.__defineGetter__(var_name, function() { 
     return saved_value; 
    }); 
    return saved_value; 
    }); 
} 

lazy('exampleField', function() { 
    // the code that returns the value I want 
}); 

Nhưng tôi đã không tìm ra một cách để làm điều đó cho Mảng thực. Mảng không có setters như thế. Bạn có thể đẩy một hàm vào một mảng, nhưng bạn phải gọi nó như một hàm để nó trả về đối tượng mà bạn thực sự muốn. Những gì tôi đang làm bây giờ là tôi tạo ra một đối tượng mà tôi coi là một mảng.

Object.prototype.lazy_push = function(value_function) { 
    if(!this.length) 
    this.length = 0; 
    this.lazy(this.length++, value_function); 
} 

Vì vậy, điều tôi muốn biết là, có cách nào để làm điều này trong khi vẫn thực hiện trên mảng chứ không phải mảng giả?

CẬP NHẬT: Hàm sau chỉ hoạt động nếu value_function trả về kiểu dữ liệu nguyên thủy.

Array.prototype.lazy_push = function(value_function) { 
    var a = this, 
    i = this.length; 
    this.push({ 
    toString: function() { 
     return a[i] = value_function(); 
    } 
    }); 
} 

Nếu bạn cố gắng nói một đối tượng có thuộc tính trong đó, bạn sẽ không thể truy cập thuộc tính cho đến khi bạn truy cập trực tiếp đối tượng. Điều này không xảy ra với setters, đó là lý do tại sao tôi muốn một số loại cú pháp thiết lập cho Javascript. Bây giờ tôi sẽ sử dụng mảng giả, đủ cho những gì tôi đang làm.

+1

Việc phân lớp đối tượng Array có được coi là tạo một "mảng giả" không? –

+0

Có. Ngay cả khi bạn đã làm điều đó, một số cú pháp mảng chẳng hạn như cho trong sẽ không hoạt động. – fent

+3

for..in không nên được sử dụng để lặp lại các mảng – Anurag

Trả lời

0

Không có. Thật không may, đây là một vấn đề lớn.

+0

Cảm ơn. Thật là một phản ứng cực kỳ hữu ích. – missingfaktor

+0

@missingfaktor Sự thật đơn giản là câu trả lời đúng, luôn luôn. Tôi xin lỗi mà không làm việc cho bạn. – orokusaki

+0

Khi tôi đang di chuyển, tôi đã nghĩ, "Tôi nghĩ câu trả lời cho câu hỏi này là 'Không'." Câu trả lời được đề xuất chỉ là thực hiện các chức năng chứ không phải 'chỉ là một phần tử Mảng'. Nếu có thể thì bạn có thể làm một cái gì đó như thế này: // cố gắng làm cho 'getter' thành một mục mảng var test = []; kiểm tra ["mẫu"] = ( var x, y; x = 1; y = 2; trả lại x + y); console.log (kiểm tra ["mẫu"]); // exception ném // đặt mục mảng thành hàm var test = []; test ["sample"] = function() { var x, y; x = 1; y = 2; trả lại x + y}; console.log (kiểm tra ["mẫu"]()); // trả về 3; – jaybro

0

Bleh. Đó là một bummer lớn, bạn không thể quá tải các nhà điều hành lập chỉ mục trong JavaScript. Oh well. Chúng ta sẽ phải sáng tạo và đưa ra một giải pháp khác. Đây là một điều tốt (và vui). :-)

LLer, giải pháp bạn giải quyết là tốt nhất. Thanh danh. Thật thú vị khi gặp những người thực sự hiểu JavaScript ở mức độ đó.

Sau khi đọc câu hỏi này, tôi đã bị ấn tượng với một ý tưởng sử thi và đã viết một số mã cho niềm vui của nó. Giải pháp của tôi cho vấn đề này rất giống với giải pháp của bạn và nhiều vấn đề khác đã được thực hiện trước đây. Nhưng, tôi cảm thấy tôi đã nghĩ ra một thứ gì đó độc đáo và rất gọn gàng nên tôi muốn chia sẻ nó. Vì vậy, tôi đang lưu trữ một dự án của tôi trên CodePlex, nơi tôi sử dụng một kỹ thuật jQuery-esque để xác định các đặc tính (các hàm getter/setter khép kín) rất giống với cái bạn đang sử dụng. Đối với các giải pháp tôi đã đưa ra với tôi chỉ đơn giản là ngoại suy từ mã này đã tồn tại từ trước của tôi. Đây là cách tiếp cận của tôi đối với tải lười biếng của các chỉ mục mảng. Bắt đầu từ đầu ...

Hãy xem xét thuộc tính có tên "PageSize". Đây là cách các tài sản sẽ được sử dụng với kỹ thuật của tôi:

var MyClass = function() { }; // MyClass constructor. 

var instance = new MyClass(); 
instance.PageSize(5); 

alert(instance.PageSize()); 

chú ý rằng bất động sản là một chức năng duy nhất, nơi cung cấp một giá trị như các tham số đầu tiên gọi setter và rời ra tham số gọi các getter. Các "PageSize" bất động sản sẽ được định nghĩa như là một phần của lớp MyClass như sau:

MyClass.prototype.PageSize = function(v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading); }; 

chức năng sở hữu những chỉ đơn thuần là một wrapper quanh một cuộc gọi đến các phương pháp GetSetProperty utitily mà không thực tế nhận được và thiết lập.Đây là đoạn trích của hàm GetSetProperty:

Object.prototype.GetSetProperty = function(name, value, loadFunction) { 
    if (!this.Properties) 
    { 
     this.Properties = {}; 
    } 

if (value) 
{ 
    this.Properties[name] = value; 
} 
else 
{  
    if (!this.Properties[name] && loadFunction) 
    { 
     this.Properties[name] = loadFunction(); 
    } 

    return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided. 
} 
}; 

Vì vậy, xử lý các thuộc tính. Nhưng, để cung cấp một phương tiện để truy cập vào các giá trị được lập chỉ mục của một tài sản có thể loại Mảng tôi sửa đổi mã này tiếp tục như vậy:

Object.prototype.GetSetProperty = function(name, value, loadFunction, index) { 
    if (!this.Properties) 
    { 
    this.Properties = {}; 
    } 

    if (typeof index === "number" && this.Properties[name] && this.Properties[name].constructor == Array) 
    { 
    return this.GetSetArrayProperty(name, index, value, loadFunction); 
    } 
    else 
    { 
    value = index; 
    } 

    if (value) 
    { 
    this.Properties[name] = value; 
    } 
    else 
    {  
    if (!this.Properties[name] && loadFunction) 
    { 
     this.Properties[name] = loadFunction(); 
    } 

    return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided. 
    } 
}; 


Object.prototype.GetSetArrayProperty = function(name, index, value, loadFunction) { 
    if (value) 
    { 
    this.Properties[name][index] = value; 
    } 
    else 
    { 
    if (!this.Properties[name][index] && loadFunction) 
    { 
     this.Properties[name][index] = loadFunction(); 
    } 

    return this.Properties[name][index]; 
    } 
}; 

Việc kê khai nguyên mẫu sẽ cần phải được sửa đổi như sau:

MyClass.prototype.PageSize = function(i, v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading, i); }; 

mọi người đọc bài viết này có thể truy cập một tập làm việc của mã ở đây: http://jsbin.com/ajawe/edit

Dưới đây là một danh sách đầy đủ của mã với các xét nghiệm:

Object.prototype.GetSetProperty = function(name, value, loadFunction, index) { 
    if (!this.Properties) 
    { 
    this.Properties = {}; 
    } 

    if (typeof index === "number" && this.Properties[name] && this.Properties[name].constructor == Array) 
    { 
    return this.GetSetArrayProperty(name, index, value, loadFunction); 
    } 
    else 
    { 
    value = index; 
    } 

    if (value) 
    { 
    this.Properties[name] = value; 
    } 
    else 
    {  
    if (!this.Properties[name] && loadFunction) 
    { 
     this.Properties[name] = loadFunction(); 
    } 

    return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided. 
    } 
}; 


Object.prototype.GetSetArrayProperty = function(name, index, value, loadFunction) { 
    if (value) 
    { 
    this.Properties[name][index] = value; 
    } 
    else 
    { 
    if (!this.Properties[name][index] && loadFunction) 
    { 
     this.Properties[name][index] = loadFunction(); 
    } 

    return this.Properties[name][index]; 
    } 
}; 


// Here's a nifty function that declares the properties for you. 
Function.prototype.CreateProperty = function(propertyName, loadFunction) { 
    eval("this.prototype['" + propertyName.toString() + "'] = function(i, v) { return this.GetSetProperty('" + propertyName.toString() + "', v, " + eval(loadFunction) + ", i); };"); 
}; 




var myFunctionThatDoesLazyLoading = function() { 
    return "Ahoy!"; 
}; 


var MyClass = function() { }; // MyClass constructor. 
MyClass.prototype.PageSize = function(i, v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading, i); }; 

var instance = new MyClass(); 
alert(instance.PageSize()); // PageSize is lazy loaded. 

instance.PageSize(5); // PageSize is re-assigned. 
alert(instance.PageSize()); // Returns the new value. 

instance.PageSize([1, 2, 3]); // PageSize is re-assigned to have an Array value. 
alert(instance.PageSize(2)); // Returns the value at index 2 of the Array value. 

instance.PageSize(2, "foo"); // Re-assigns the value at index 2. 
alert(instance.PageSize(2)); // Returns the new value at index 2. 

MyClass.CreateProperty("NewProp", function() { return ["a", "b", "c"]; }); // Demo of the CreateProperty function. 
alert(instance.NewProp()); 
alert(instance.NewProp(1)); 
+0

sửa đổi Object.prototype là verboten –

1

Ok, trừ khi tôi hiểu lầm câu hỏi của bạn, tôi tin rằng tôi đã tìm thấy một giải pháp đơn giản mà không yêu cầu chức năng "lazy_push".

Tiếp theo phương pháp từ câu trả lời trước đây của tôi, bạn tạo một lớp myArray:

function MyArray(){ 
    this.object = []; 
} 

MyArray.prototype.push = function(what){ 
    this.object.push(what); 
} 

Bây giờ là phần quan trọng là hàm getter, chúng tôi sẽ tạo một hàm getIdx() để lấy giá trị ra khỏi mảng . Hàm này sau đó sử dụng toán tử 'typeof' để xác định xem giá trị trả về có phải là một hàm hay không. Nếu đúng, trả về giá trị trả về từ hàm, nếu không trả về giá trị ban đầu.

Mã có ý nghĩa hơn:

MyArray.prototype.getIdx = function(which){ 
    if(typeof this.object[which] == 'function'){ 
     alert("a function"); 
     //NOTICE THE '()' AT THE END OF THE NEXT LINE!!! 
     return this.object[which](); 
    } 
    return this.object[which]; 
} 

Hy vọng rằng nếu tôi không hoàn toàn trả lời câu hỏi của bạn, bạn có thể hình dung nó ra từ đây.

< --------- bài của tôi ban đầu ------------->

Không thực sự là một câu trả lời, nhưng một vài điểm quan trọng.

  1. Không có mảng sản tại Javascript, một mảng chỉ là một đối tượng mở rộng (như là tất cả mọi thứ trong JS)

  2. Bạn nên lý tưởng không bao giờ thêm chức năng nguyên mẫu để các đối tượng có nguồn gốc trong JS, có lẽ bạn vô tình ghi đè lên một thuộc tính hiện có hoặc tạo các lỗi gây nhầm lẫn xuống dòng. Ví dụ, thêm vào nguyên mẫu của Object sẽ thêm vào mọi đối tượng trong JS (tất cả là mọi thứ), bạn cần phải chắc chắn rằng bạn muốn mọi kiểu trong JS có thuộc tính đó. Điều này chỉ nguy hiểm vì nếu bạn vô tình ghi đè lên các hàm Array() hoặc Object() thực tế, bạn sẽ phá vỡ javascript trong trình duyệt, thời gian, một lần làm mới trang sẽ không khắc phục được nó.

  3. Thay vì thêm vào nguyên mẫu của đối tượng bạn đang sửa đổi, hãy tạo đối tượng mới mở rộng đối tượng đó. Ví dụ nếu bạn muốn mở rộng các lớp Array:

    //create the constructor, 
    //with an object variable that holds the object you wish to extend 
    function MyArray(){ 
        this.object = []; 
    } 
    
    //create functions which reference the native functions of the object 
    MyArray.prototype.push = function(what){ 
        this.object.push(what); 
    } 
    
    //Etc... Etc.... Etc..... 
    

của nó không nhất thiết phải vui vẻ viết tất cả các phương pháp accessor cho các chức năng Object bản xứ, nhưng nó giữ cho động cơ Javascript an toàn.

0

Tôi không đặc biệt thích câu trả lời này, nhưng bạn có thể lưu trữ "biến" của bạn dưới dạng chuỗi biểu thức và sau đó eval() nó khi bạn cần? Không lý tưởng, nhưng nó nhỏ gọn ...

var x = 10, arr = []; 
arr.push("x + 10"); 
alert(eval(arr[0])); 
x = 20; 
alert(eval(arr[0])); 

Tôi đã thử nghiệm nó, và nó hoạt động, ngay cả khi nó không phải chính xác những gì bạn đang tìm kiếm.

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