2011-12-27 30 views
8

Tôi đang viết một thư viện ngăn xếp Javascript. Thư viện cần phát hiện một đối tượng hoặc chức năng cụ thể đã được lập trình viên tạo ra hoặc đã có mặt như một phần của môi trường (bao gồm các đối tượng dựng sẵn). Các đối tượng host đang trở nên có vấn đề một chút do hành vi không thể đoán trước của chúng, vì vậy tôi đang theo một cách thức môi trường để xác định xem một đối tượng cụ thể trong Javascript có phải là một đối tượng chủ (xem ECMAScript 3 - 4.3.8). Tuy nhiên, việc phân biệt các đối tượng host với các đối tượng gốc và các giá trị nguyên thủy rất hữu ích cho các lập trình viên trong các dự án khác, đặc biệt là trong các môi trường ít trình duyệt hơn, vì vậy tôi muốn tập trung vào đó hơn là các đối tượng host gây ra trong thư viện hoặc trên trình phân biệt đối tượng được xử lý.Có cách nào để phát hiện các đối tượng lưu trữ Javascript không?

Cho đến nay tôi chỉ có thể đưa ra các giải pháp phụ thuộc vào môi trường đang chạy mã javascript. Ví dụ:

// IE Only: does not implement valueOf() in Host Objects 
var isHost = (typeof obj === 'object' && typeof obj.valueOf === 'undefined'); 

// Firefox Only: Host objects have own constructor 
var isHost = (obj.constructor && obj.hasOwnProperty('constructor')); 

tôi nhận thấy rằng phương pháp isPlainObject() riêng của jQuery cũng phụ thuộc vào môi trường, và rằng logic là khá phức tạp. Có lẽ điều này là bởi vì đó là bản chất của con thú với vật chủ (vì hành vi của chúng được xác định bởi môi trường), nhưng tôi muốn đào sâu hơn một chút để xem liệu điều này có thể và đang tự hỏi liệu có ai đó đã chạy hay không. qua vấn đề cụ thể này trước và có sẵn một giải pháp.

So. Có ai biết một giải pháp nền tảng độc lập đơn giản để kiểm tra đối tượng Host không? Và nếu nó chạy trong môi trường ít trình duyệt hơn như Node hoặc Rhino thì tốt hơn cho nó.

cách tiếp cận có thể xảy ra (có thể không hoạt động):

  • Testing cho đặc điểm của các đối tượng máy chủ có vẻ như một nguyên nhân bị mất, cho rằng không có đặc điểm kỹ thuật cho hành vi của họ, tuy nhiên thử nghiệm cho dù đối tượng là một phần của Đặc tả ES3 có thể là một khả năng.
  • Tôi đã thử sử dụng Object.prototype.toString() cho rằng nó được xác định khá cụ thể, nhưng kết quả là không thuyết phục vì một số môi trường (cụ thể là IE) chọn trả lại cùng một giá trị cho các đối tượng gốc và máy chủ.
  • Có thể thực hiện điều này bằng cách kiểm tra xem số constructor cuối cùng của một đối tượng thông qua chuỗi nguyên mẫu có thực sự là instanceof Function hay không.
+7

Sự cố bạn đang cố giải quyết là gì? –

+1

Tôi khá chắc chắn rằng điều này không hoạt động trong mọi môi trường, nhưng bạn có thể thử 'Object.prototype.toString.call (obj)' và xem bạn có nhận được '[object Object]' hay không sản xuất) hoặc một cái gì đó như '[đối tượng HTMLBodyElement]' để xác định xem nó là một đối tượng lưu trữ. – pimvdb

+0

@CamiloMartin, Im làm việc trên một thư viện stacktrace javascript, các đối tượng lưu trữ đang trở thành một chút vấn đề do hành vi không thể đoán trước của họ vì vậy phải mã xung quanh chúng. Tuy nhiên, giải pháp ở đây rất hữu ích cho các lập trình viên trong các dự án khác, đặc biệt trong môi trường ít trình duyệt hơn. –

Trả lời

0

Tôi tin rằng bản chất của đối tượng lưu trữ có nghĩa là không có cách đơn giản, môi trường bất khả tri để phát hiện chúng. Xem this discussion on SO để biết thêm, nếu bạn tò mò.

Như bạn đã lưu ý, dự án jQuery cũng có tried to detect host objects và chạy vào các sự cố tương tự. Cuộc thảo luận trên trang lỗi đó rất rõ ràng.

+0

jQuery chỉ là jQuery, nhiều như mã của chúng là thông minh theo nhiều cách nó vẫn còn giới hạn về phạm vi và cụ thể cho mục đích cụ thể của nó (thao tác DOM trong trình duyệt). –

+0

SO liên kết dường như đã chết – the8472

0

Tôi có ý tưởng có thể không áp dụng được trong mọi ngữ cảnh.

Hãy chắc chắn rằng kịch bản của bạn là đầu tiên một để thực hiện, và bọc nó trong một đóng cửa, giống như khuôn khổ JS làm.
Sau đó, lặp qua tất cả các đối tượng trong phạm vi toàn cầu của bạn (Nếu bạn đang ở trên một cái gì đó không phải là một trình duyệt, window sẽ được undefined, vì vậy tại beggining của kịch bản làm một window = this), và lặp qua con của nó, và như vậy trên.Tất cả các đối tượng ngoại trừ bạn sẽ là đối tượng lưu trữ! thì bạn có thể thêm nó vào cơ sở dữ liệu cục bộ hoặc thậm chí lưu trữ nó và liên kết nó với môi trường đang chạy để sử dụng trong tương lai.

+0

Cố gắng nó, không làm việc, một số đối tượng lưu trữ không bằng với chính mình do đó kết quả của một bằng so sánh giữa hai tài liệu tham khảo của cùng một đối tượng lưu trữ là không thể đoán trước. –

+0

Điều đó quá tệ. Nhưng, đó là những gì? Tôi tò mò. –

+0

window.frames trong IE là tồi tệ nhất. thử console.log (frames === frames); –

5

Khi bạn nhìn vào đối tượng definition of host object - "do môi trường máy chủ cung cấp để hoàn thành môi trường thực thi của ECMAScript". - nó trở nên khá rõ ràng rằng không có cách đơn giản để xác định nếu một đối tượng là máy chủ hoặc bản địa.

Không giống như các đối tượng gốc, đối tượng lưu trữ xác định các thuộc tính bên trong (chẳng hạn như [[Prototype]], [[Class]], vv) theo cách thực hiện cụ thể. Đó là bởi vì specification allows them to do this. Tuy nhiên, không có yêu cầu "PHẢI" đối với các đối tượng lưu trữ để thực hiện hành vi nội bộ theo cách thực hiện cụ thể; đó là loại yêu cầu "CÓ THỂ". Vì vậy, chúng tôi không thể dựa vào điều này. Các đối tượng này có thể hoặc không thể hành động "lạ". Không có cách nào để nói.

Trước đây đã có ít nỗ lực để phát hiện đối tượng lưu trữ, nhưng tất cả chúng đều dựa vào các quan sát của một số môi trường nhất định (MSHTML DOM là một trong số chúng) - hãy nhớ rằng đối tượng lưu trữ không có bất kỳ kiểu mẫu duy nhất nào/đặc điểm để xác định. Peter Michaux documented most of the inferences here (hãy xem phần "Tính năng Thử nghiệm đối tượng lưu trữ"). Các typeof ... == "unknown" khét tiếng đến từ MSHTML DOM và các đối tượng lưu trữ dựa trên ActiveX của nó. Lưu ý rằng Peter nói về chủ vật chủ chủ yếu trong ngữ cảnh của trình duyệt kịch bản, và ông thu hẹp các kiểm tra xuống "đây có phải là phương thức lưu trữ không?", "Đây là máy chủ lưu trữ bộ sưu tập đối tượng", v.v.

Trong một số môi trường đối tượng lưu trữ không kế thừa từ Object.prototype (giúp dễ dàng kiểm tra) hoặc có một số thuộc tính nào đó ném lỗi (ví dụ: "nguyên mẫu" trên một số đối tượng "giao diện" trong IE) hoặc thậm chí tự ném lỗi khi truy cập.

Có vẻ như bạn chỉ có thể kiểm tra xem một đối tượng có phải là một trong những đối tượng được xác định trong đặc điểm kỹ thuật hay không và nếu không, coi đó là máy chủ lưu trữ. Nhưng điều đó sẽ không thực sự hữu ích; nó sẽ chỉ cung cấp cho bạn các đối tượng không phải là được xây dựng trong. Một số đối tượng không chuẩn này vẫn có thể là bản địa (có nghĩa là chúng sẽ thực hiện ngữ nghĩa thông thường như được mô tả trong đặc tả).

Đặt cược tốt nhất của bạn sẽ là kiểm tra đối với hành vi cụ thể của ứng dụng/tập lệnh của bạn, mà đối tượng lưu trữ có thể nhạy cảm. Đây luôn là cách an toàn nhất. Bạn đang có kế hoạch để truy cập một cái gì đó tắt của một đối tượng? Xóa nội dung nào đó khỏi đối tượng? Thêm một cái gì đó để phản đối? Kiểm tra cho nó. Xem nếu nó hoạt động. Nếu không - bạn có khả năng giao dịch với đối tượng lưu trữ.

+0

Hi @kangax, trong khi tôi đồng ý với hầu hết câu trả lời của bạn, thông số ECMAScript cũng nói rằng 'Bất kỳ đối tượng nào không phải là một đối tượng lưu trữ.' *, Vì vậy về mặt lý thuyết có thể kiểm tra đối tượng Host bằng cách kiểm tra xem chúng là * không * Native (như được định nghĩa trong spec). Bí quyết ở đây là làm thế nào để kiểm tra một đối tượng thực sự * là * có nguồn gốc, vì môi trường có thể làm cho nó khó khăn bằng cách sao chép nhiều hành vi này trong các đối tượng lưu trữ của chúng. –

2

hầu như giải quyết

Hầu như quản lý để có được điều này để làm việc.

Giải pháp thiếu trong các đối tượng Lưu trữ đôi khi không thể phân biệt được với các đối tượng Gốc. Mã bên dưới không thành công khi thử nghiệm isNative(window.alert) trên Chrome dưới dạng công cụ webkit xác định hàm alert cho đến nay trông giống hệt với kiểu gốc.

Nó sử dụng javascript đơn giản theo ES3 và dựa trên thử nghiệm rằng một đối tượng là bản địa (trái với đối tượng Host). Tuy nhiên, theo định nghĩa ES3 của đối tượng host: 'Bất kỳ đối tượng nào không phải là native đều là đối tượng host.' chức năng này có thể được sử dụng để phát hiện các đối tượng lưu trữ.

// ISNATIVE OBJECT DETECTION 

function isNative(obj) { 

    switch(typeof obj) { 
     case 'number': case 'string': case 'boolean': 
      // Primitive types are not native objects 
      return false; 
    } 

    // Should be an instance of an Object 
    if (!(obj instanceof Object)) return false; 

    // Should have a constructor that is an instance of Function 
    if (typeof obj.constructor === 'undefined') return false; 
    if (!(obj.constructor instanceof Function)) return false; 

    return true; 
} 

// CHECK IF AN OBJECT IS HOST OR NATIVE 

if (typeof myObject === 'object' || typeof myObject === 'function') 
    alert(isNative(myObject) ? 'Native Object' : 'Host Object'); 

Đây là một list of JsFiddle tests có thể được sử dụng để kiểm tra điều này trong IE/Firefox/Chrome.

Tôi đã thử nghiệm môi trường không phải trình duyệt vì nó phức tạp hơn một chút nhưng vì mã này rất cơ bản nên tôi không nghĩ rằng nó sẽ có bất kỳ vấn đề gì.

// ASSERT HELPER FUNCTION 

var n = 0; 
function assert(condition, message) { 
    n++; 
    if (condition !== true) { 
     document.write(n + '. FAILS: ' + (message || '(no message)') + '<br/>'); 
    } else { 
     document.write(n + '. PASS: ' + (message || '(no message)') + '<br/>'); 
    } 
} 

// USER CREATED OBJECTS 

assert(isNative({}), '{} -- Plain object'); 
assert(isNative(function() {}), 'function() {} -- Plain function'); 
assert(isNative([]), '[] -- Plain array'); 

assert(isNative(/regex/), '/regex/ - Native regex'); 
assert(isNative(new Date()), 'new Date() - Native date object through instantiation'); 

assert(isNative(new String('string')), 'new String("string") - Native string object through instantiation'); 
assert(isNative(new Number(1)), 'new Number(1) - Native number object through instantiation'); 
assert(isNative(new Boolean(true)), 'new Boolean(true) - Native boolean object through instantiation'); 
assert(isNative(new Array()), 'new Array() - Native array object through instantiation'); 
assert(isNative(new Object()), '{} -- new Object() - Native object through instantiation'); 
assert(isNative(new Function('alert(1)')), '{} -- Native function through instantiation'); 

// USER OBJECT INSTANTIATION AND INHERITANCE 

var Animal = function() {}; 
var animal = new Animal(); 

var Dog = function() {}; 
Dog.prototype = animal; 
var dog = new Dog(); 

assert(isNative(Animal), 'Animal -- User defined type'); 
assert(isNative(animal), 'animal -- Instance of User defined type'); 

assert(isNative(Dog), 'Dog -- User defined inherited type'); 
assert(isNative(dog), 'dog -- Instance of User defined inherited type'); 

// BUILT IN OBJECTS 

assert(isNative(Object), 'Object -- Built in'); 
assert(isNative(Array), 'Array -- Built in'); 
assert(isNative(Date), 'Date -- Built in'); 
assert(isNative(Boolean), 'Boolean -- Built in'); 
assert(isNative(String), 'String -- Built in'); 
assert(isNative(Function), 'Function -- Built in'); 

// PRIMITIVE TYPES 

assert(!isNative('string'), '"string" - Primitive string'); 
assert(!isNative(1), '1 - Primitive number'); 
assert(!isNative(true), 'true - Primitive boolean'); 
assert(!isNative(null), 'null - Primitive null'); 
assert(!isNative(NaN), 'NaN - Primitive number NotANumber'); 
assert(!isNative(Infinity), 'Infinity - Primitive number Infinity'); 
assert(!isNative(undefined), 'undefined - Primitive value undefined'); 

// HOST OBJECTS 

assert(!isNative(window), 'window -- Host object'); 
assert(!isNative(alert), 'alert -- Host function'); // fails on chrome 
assert(!isNative(document), 'document -- Host object'); 
assert(!isNative(location), 'location -- Host object'); 
assert(!isNative(navigator), 'navigator -- Host object'); 
assert(!isNative(parent), 'parent -- Host object'); 
assert(!isNative(frames), 'frames -- Host object'); 
+0

'process.binding ('evals') NodeScript.constructor instanceof Function === true;' Tôi khá chắc chắn NodeScript được định nghĩa trong C++ trong node.js và nên được cho là đủ điều kiện như là một "đối tượng host". Yoru giải pháp là khó khăn để mở rộng đến môi trường không trình duyệt – Raynos

+0

Tôi không chắc chắn hay không điều này là chính xác nhưng 'alert' chính nó là một chức năng bản địa theo' isNative'. – pimvdb

+0

Bắt tốt @pimvdb, nhưng chỉ trên chrome/webkit, tôi tự hỏi tại sao? –

4

Dưới đây là một phiên bản mới hơn của isNative mà từ chối tất cả các đối tượng với một thực hiện bản địa cho toString, mà giải quyết vấn đề cho thư viện stack trace nhưng không trả lời câu hỏi được đăng ở đây một cách thỏa đáng. Trường hợp phương pháp này không thành công cho phương pháp này, nó lọc ra tất cả các định nghĩa kiểu dựng sẵn như Object, Date, String, Math, v.v., không phải là đối tượng lưu trữ. Ngoài ra, giải pháp này phụ thuộc vào cách môi trường xuất ra các định nghĩa hàm gốc/tích hợp sẵn (nó phải bao gồm "[mã nguồn]" để hàm hoạt động). Vì hành vi của hàm là khác nhau, nó được đổi tên thành isUserObject.

// USER OBJECT DETECTION 

function isUserObject(obj) { 

    // Should be an instance of an Object 
    if (!(obj instanceof Object)) return false; 

    // Should have a constructor that is an instance of Function 
    if (typeof obj.constructor === 'undefined') return false; 
    if (!(obj.constructor instanceof Function)) return false; 

    // Avoid built-in functions and type definitions 
    if (obj instanceof Function && 
     Function.prototype.toString.call(obj).indexOf('[native code]') > -1) 
      return false; 

    return true; 
} 

// CHECK IF AN OBJECT IS USER-CREATED OR NOT 

if (typeof myObject === 'object' || typeof myObject === 'function') 
    alert(isUserObject(myObject) ? 'User Object' : 'Non-user Object'); 

Đây là một list of JsFiddle tests có thể được sử dụng để kiểm tra điều này trong các trình duyệt khác nhau.

// ASSERT HELPER FUNCTION 

var n = 0; 
function assert(condition, message) { 
    n++; 
    if (condition !== true) { 
     document.write(n + '. FAILS: ' + (message || '(no message)') + '<br/>'); 
    } else { 
     document.write(n + '. PASS: ' + (message || '(no message)') + '<br/>'); 
    } 
} 

// USER CREATED OBJECTS 

assert(isUserObject({}), '{} -- Plain object'); 
assert(isUserObject(function() {}), 'function() {} -- Plain function'); 
assert(isUserObject([]), '[] -- Plain array'); 

assert(isUserObject(/regex/), '/regex/ - Native regex'); 
assert(isUserObject(new Date()), 'new Date() - Native date object through instantiation'); 

assert(isUserObject(new String('string')), 'new String("string") - Native string object through instantiation'); 
assert(isUserObject(new Number(1)), 'new Number(1) - Native number object through instantiation'); 
assert(isUserObject(new Boolean(true)), 'new Boolean(true) - Native boolean object through instantiation'); 
assert(isUserObject(new Array()), 'new Array() - Native array object through instantiation'); 
assert(isUserObject(new Object()), '{} -- new Object() - Native object through instantiation'); 
assert(isUserObject(new Function('alert(1)')), '{} -- Native function through instantiation'); 

// USER OBJECT INSTANTIATION AND INHERITANCE 

var Animal = function() {}; 
var animal = new Animal(); 

var Dog = function() {}; 
Dog.prototype = animal; 
var dog = new Dog(); 

assert(isUserObject(Animal), 'Animal -- User defined type'); 
assert(isUserObject(animal), 'animal -- Instance of User defined type'); 

assert(isUserObject(Dog), 'Dog -- User defined inherited type'); 
assert(isUserObject(dog), 'dog -- Instance of User defined inherited type'); 

// BUILT IN OBJECTS 

assert(!isUserObject(Object), 'Object -- Built in'); 
assert(!isUserObject(Array), 'Array -- Built in'); 
assert(!isUserObject(Date), 'Date -- Built in'); 
assert(!isUserObject(Boolean), 'Boolean -- Built in'); 
assert(!isUserObject(String), 'String -- Built in'); 
assert(!isUserObject(Function), 'Function -- Built in'); 

// PRIMITIVE TYPES 

assert(!isUserObject('string'), '"string" - Primitive string'); 
assert(!isUserObject(1), '1 - Primitive number'); 
assert(!isUserObject(true), 'true - Primitive boolean'); 
assert(!isUserObject(null), 'null - Primitive null'); 
assert(!isUserObject(NaN), 'NaN - Primitive number NotANumber'); 
assert(!isUserObject(Infinity), 'Infinity - Primitive number Infinity'); 
assert(!isUserObject(undefined), 'undefined - Primitive value undefined'); 

// HOST OBJECTS 

assert(!isUserObject(window), 'window -- Host object'); 
assert(!isUserObject(alert), 'alert -- Host function'); 
assert(!isUserObject(document), 'document -- Host object'); 
assert(!isUserObject(location), 'location -- Host object'); 
assert(!isUserObject(navigator), 'navigator -- Host object'); 
assert(!isUserObject(parent), 'parent -- Host object'); 
assert(!isUserObject(frames), 'frames -- Host object');​