Làm thế nào để hiển thị tất cả các phương thức của một đối tượng?


249

Tôi muốn biết làm thế nào để liệt kê tất cả các phương thức có sẵn cho một đối tượng như ví dụ:

 alert(show_all_methods(Math));

Điều này sẽ in:

abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random,round, sin, sqrt, tan, 

Câu trả lời:


298

Bạn có thể sử dụng Object.getOwnPropertyNames()để có được tất cả các thuộc tính thuộc về một đối tượng, cho dù có thể đếm được hay không. Ví dụ:

console.log(Object.getOwnPropertyNames(Math));
//-> ["E", "LN10", "LN2", "LOG2E", "LOG10E", "PI", ...etc ]

Sau đó, bạn có thể sử dụng filter()để chỉ lấy các phương thức:

console.log(Object.getOwnPropertyNames(Math).filter(function (p) {
    return typeof Math[p] === 'function';
}));
//-> ["random", "abs", "acos", "asin", "atan", "ceil", "cos", "exp", ...etc ]

Trong các trình duyệt ES3 (IE 8 trở xuống), các thuộc tính của các đối tượng tích hợp không thể đếm được. Đối tượng thích windowdocument không tích hợp, chúng được xác định bởi trình duyệt và rất có thể là vô số theo thiết kế.

Từ ECMA-262 Phiên bản 3 :

Đối tượng toàn cầu
Có một đối tượng toàn cầu duy nhất (15.1), được tạo trước khi điều khiển đi vào bất kỳ bối cảnh thực hiện nào. Ban đầu đối tượng toàn cầu có các thuộc tính sau:

• Các đối tượng tích hợp sẵn như Math, String, Date, parseInt, v.v ... Chúng có các thuộc tính {DontEnum} .
• Thuộc tính máy chủ bổ sung được xác định. Điều này có thể bao gồm một tài sản có giá trị là chính đối tượng toàn cầu; ví dụ, trong mô hình đối tượng tài liệu HTML, thuộc tính cửa sổ của đối tượng toàn cục là chính đối tượng toàn cục.

Khi điều khiển đi vào bối cảnh thực thi và khi mã ECMAScript được thực thi, các thuộc tính bổ sung có thể được thêm vào đối tượng toàn cục và các thuộc tính ban đầu có thể được thay đổi.

Tôi nên chỉ ra rằng điều này có nghĩa là những đối tượng đó không phải là vô số thuộc tính của đối tượng Toàn cầu. Nếu bạn xem qua phần còn lại của tài liệu đặc tả, bạn sẽ thấy hầu hết các thuộc tính và phương thức dựng sẵn của các đối tượng này có { DontEnum }thuộc tính được đặt trên chúng.


Cập nhật: một người dùng SO đồng nghiệp, CMS, đã mang đến một lỗi IE liên quan{ DontEnum } đến sự chú ý của tôi.

Thay vì kiểm tra thuộc tính DontEnum, [Microsoft] JScript sẽ bỏ qua bất kỳ thuộc tính nào trong bất kỳ đối tượng nào có thuộc tính cùng tên trong chuỗi nguyên mẫu của đối tượng có thuộc tính DontEnum.

Tóm lại, hãy cẩn thận khi đặt tên thuộc tính đối tượng của bạn. Nếu có một thuộc tính hoặc phương thức nguyên mẫu tích hợp có cùng tên thì IE sẽ bỏ qua nó khi sử dụng một for...invòng lặp.


Andy E, cảm ơn vì đã chỉ ra điều này. Rõ ràng tôi đã không nhận thức được điều này và tôi đánh giá cao nỗ lực của bạn trong việc khai thác nó và đề cập đến nó ở đây. Cảm ơn một lần nữa :)
Roland Bouman

@Roland: Không phải lo lắng. Có lẽ nó hơi buồn, nhưng tôi có thông số kỹ thuật được lưu trữ trong thư mục Tài liệu của mình, vì vậy thực sự không cần phải đào nhiều!
Andy E

Có cách nào để có được một danh sách tất cả các phương thức trong các triển khai JS mới hơn không? Giống như Node.js và V8? Làm thế nào để chúng ta thực hiện các đối tượng phản chiếu và hướng nội như chúng ta đã từng làm, chẳng hạn như đối với các khung đối tượng giả, v.v? Tôi nghĩ rằng tôi đã quên JS, nhưng tôi đoán mọi thứ đã thay đổi qua nhiều năm :)
d11wtq

2
@ d11wtq, với các triển khai ES5, bạn có thể gọi Object.getOwnPropertyNames(), sẽ trả về các thuộc tính và phương thức không thể đếm được.
Andy E

vì tất cả các đối tượng kế thừa từ nguyên mẫu của chúng, sẽ tốt hơn nếu làm điều gì đó như thế Object.getOwnPropertyNames(Array.prototype) nào?
lfender6445

71

ES3 không thể thực hiện được vì các thuộc tính có DontEnumthuộc tính bên trong ngăn chúng ta liệt kê các thuộc tính này. Mặt khác, ES5 cung cấp các mô tả thuộc tính để kiểm soát các khả năng liệt kê của các thuộc tính để các thuộc tính gốc và do người dùng xác định có thể sử dụng cùng một giao diện và tận hưởng các khả năng tương tự, bao gồm khả năng nhìn thấy các thuộc tính không thể đếm được theo chương trình.

Các getOwnPropertyNameschức năng có thể được sử dụng để liệt kê trên khắp các thuộc tính của đối tượng thông qua năm, bao gồm cả những đều là phòng không đếm được. Sau đó, một typeofkiểm tra đơn giản có thể được sử dụng để lọc ra các chức năng không. Thật không may, Chrome là trình duyệt duy nhất hiện đang hoạt động.

function getAllMethods(object) {
    return Object.getOwnPropertyNames(object).filter(function(property) {
        return typeof object[property] == 'function';
    });
}

console.log(getAllMethods(Math));

nhật ký ["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]không theo thứ tự cụ thể.


+1 cho công cụ ES5. IE9 được cho là sẽ hỗ trợ đầy đủ ES5 vì vậy những thứ này rất tốt để biết.
Andy E

1
@Andy - Microsoft đang dùng IE9 rất nghiêm túc, điều đó làm tôi rất vui :)
Anurag

console.log (function (a) {return Object.getOwnPropertyNames (a) .filter (function (b) {return "function" == typeof a [b]})} (Math)); Cảm ơn bạn!
19h

1
getOwnPropertyNames là vé. Nó thậm chí hoạt động ở Nashorn. Họ chỉ thay đổi tên của các phương thức của đối tượng Java và tôi đã có thể tìm ra tên mới bằng cách chạy Object.getOwnPropertyNames (Java)
cayhorstmann

60
var methods = [];
for (var m in obj) {
    if (typeof obj[m] == "function") {
        methods.push(m);
    }
}
alert(methods.join(","));

Bằng cách này, bạn sẽ nhận được tất cả các phương thức mà bạn có thể gọi obj. Điều này bao gồm các phương thức mà nó "kế thừa" từ nguyên mẫu của nó (như getMethods()trong java). Nếu bạn chỉ muốn xem các phương thức được xác định trực tiếp bởi objbạn có thể kiểm tra với hasOwnProperty:

var methods = [];
for (var m in obj) {        
    if (typeof obj[m] == "function" && obj.hasOwnProperty(m)) {
        methods.push(m);
    }
}
alert(methods.join(","));

vâng, tôi cũng nhận thấy điều đó Khi tôi sử dụng đôi khi thích documenthoặc windowtôi nhận được nhiều may mắn hơn. Thành thật mà nói thì hơi bất ngờ, tôi không biết tại sao nó không hoạt động cho môn Toán v.v.
Roland Bouman

4
@Roland: Đó là bởi vì documentwindowlà các đối tượng có vô số thuộc tính được cung cấp bởi trình duyệt, chúng không phải là một phần của thời gian chạy tập lệnh. Các đối tượng bản địa và rõ ràng là các thuộc tính không thể đếm được.
Andy E

1
Bất kỳ E, tôi không đồng ý nó rõ ràng. Ý tôi là, rõ ràng vì chúng ta dường như không thể liệt kê chúng. Nhưng tôi không thấy logic là tại sao những người dựng sẵn đó nên ngăn chặn việc liệt kê các thuộc tính của họ. Chỉ tò mò, có một phần của tiêu chuẩn nói rằng những phần dựng sẵn này không nên có vô số thuộc tính?
Roland Bouman

@Roland: xin lỗi, ý tôi là rõ ràng họ không thể đếm được vì họ không xuất hiện với một for-in. Xem câu trả lời của tôi dưới đây để báo giá từ spec.
Andy E

@Mic: Math là một đối tượng tích hợp có thuộc tính không thể đếm được.
Andy E

31

Hầu hết các hỗ trợ trình duyệt hiện đại console.dir(obj), sẽ trả về tất cả các thuộc tính của một đối tượng mà nó được kế thừa thông qua hàm tạo của nó. Xem tài liệu của Mozilla để biết thêm thông tin và hỗ trợ trình duyệt hiện tại.

console.dir(Math)
=> MathConstructor
E: 2.718281828459045
LN2: 0.6931471805599453
...
tan: function tan() { [native code] }
__proto__: Object

4

Các câu trả lời khác ở đây hoạt động cho một cái gì đó như Math, là một đối tượng tĩnh. Nhưng họ không làm việc cho một ví dụ của một đối tượng, chẳng hạn như một ngày. Tôi tìm thấy những điều sau đây để làm việc:

function getMethods(o) {
  return Object.getOwnPropertyNames(Object.getPrototypeOf(o))
    .filter(m => 'function' === typeof o[m])
}
//example: getMethods(new Date()):  [ 'getFullYear', 'setMonth', ... ]

https://jsfiddle.net/3xrsead0/

Điều này sẽ không hoạt động cho một cái gì đó giống như câu hỏi ban đầu (Toán học), vì vậy hãy chọn giải pháp của bạn dựa trên nhu cầu của bạn. Tôi đang đăng bài này lên đây vì Google đã gửi cho tôi câu hỏi này nhưng tôi muốn biết làm thế nào để làm điều này cho các trường hợp của các đối tượng.


3

Câu trả lời ngắn gọn là bạn không thể bởi vì MathDate(ngoài đỉnh đầu của tôi, tôi chắc chắn có những người khác) không phải là đối tượng bình thường. Để xem điều này, hãy tạo một kịch bản thử nghiệm đơn giản:

<html>
  <body>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
    <script type="text/javascript">
      $(function() {
        alert("Math: " + Math);
        alert("Math: " + Math.sqrt);
        alert("Date: " + Date);
        alert("Array: " + Array);
        alert("jQuery: " + jQuery);
        alert("Document: " + document);
        alert("Document: " + document.ready);
      });
    </script>
  </body>
</html>

Bạn thấy nó thể hiện dưới dạng một đối tượng giống như cách mà tài liệu thực hiện, nhưng khi bạn thực sự thử và nhìn thấy trong đối tượng đó, bạn sẽ thấy đó là mã gốc và một cái gì đó không được đưa ra theo cách tương tự để liệt kê.


1

Mathcó phương thức tĩnh nơi bạn có thể gọi trực tiếp như Math.abs()trong khi Datecó phương thức tĩnh Date.now()và phương thức cá thể nơi bạn cần tạo phiên bản mới trước var time = new Date()để gọi time.getHours().

// The instance method of Date can be found on `Date.prototype` so you can just call:
var keys = Object.getOwnPropertyNames(Date.prototype);

// And for the static method
var keys = Object.getOwnPropertyNames(Date);

// But if the instance already created you need to
// pass its constructor
var time = new Date();
var staticKeys = Object.getOwnPropertyNames(time.constructor);
var instanceKeys = Object.getOwnPropertyNames(time.constructor.prototype);

Tất nhiên bạn sẽ cần lọc các khóa thu được cho phương thức tĩnh để có được tên phương thức thực tế, bởi vì bạn cũng có thể nhận đượclength, name đó không phải là một hàm trong danh sách.

Nhưng làm thế nào nếu chúng ta muốn có được tất cả các phương thức có sẵn từ lớp mở rộng một lớp khác?
Tất nhiên bạn sẽ cần quét qua thư mục gốc như sử dụng __proto__. Để tiết kiệm thời gian của bạn, bạn có thể sử dụng tập lệnh bên dưới để lấy phương thức tĩnh và thể hiện phương thức sâu.

// var keys = new Set();
function getStaticMethods(keys, clas){
    var keys2 = Object.getOwnPropertyNames(clas);

    for(var i = 0; i < keys2.length; i++){
        if(clas[keys2[i]].constructor === Function)
            keys.add(keys2[i]);
    }
}

function getPrototypeMethods(keys, clas){
    if(clas.prototype === void 0)
        return;

    var keys2 = Object.getOwnPropertyNames(clas.prototype);
    for (var i = keys2.length - 1; i >= 0; i--) {
        if(keys2[i] !== 'constructor')
            keys.add(keys2[i]);
    }

    var deep = Object.getPrototypeOf(clas);
    if(deep.prototype !== void 0)
        getPrototypeMethods(keys, deep);
}

// ====== Usage example ======
// To avoid duplicate on deeper prototype we use `Set`
var keys = new Set();
getStaticMethods(keys, Date);
getPrototypeMethods(keys, Date);

console.log(Array.from(keys));

Nếu bạn muốn có được các phương thức từ thể hiện đã tạo, đừng quên truyền vào constructornó.


0

Tôi tin rằng có một lý do lịch sử đơn giản tại sao bạn không thể liệt kê các phương thức của các đối tượng tích hợp như Array chẳng hạn. Đây là lý do tại sao:

Các phương thức là các thuộc tính của đối tượng nguyên mẫu, giả sử Object.prototype. Điều đó có nghĩa là tất cả các thể hiện Object sẽ kế thừa các phương thức đó. Đó là lý do tại sao bạn có thể sử dụng các phương thức đó trên bất kỳ đối tượng nào. Nói .toString () chẳng hạn.

Vì vậy, các phương thức IF là vô số, và tôi sẽ lặp lại nói {a: 123} với: "for (key in {a: 123}) {...}" điều gì sẽ xảy ra? Vòng lặp đó sẽ được thực hiện bao nhiêu lần?

Nó sẽ được lặp lại một lần cho một phím 'a' trong ví dụ của chúng tôi. NHƯNG CŨNG một lần cho mỗi đếm tài sản của Object.prototype. Vì vậy, nếu các phương thức là vô số (theo mặc định), thì bất kỳ vòng lặp nào trên bất kỳ đối tượng nào cũng sẽ lặp lại trên tất cả các phương thức được kế thừa của nó.


1
vì người nguyên thủy thường thừa hưởng từ một loại chính, Object.getOwnPropertyNames(Array.prototype)ví dụ , điều này có thể xảy ra
lfender6445

ý nghĩa của các phương thức là thuộc tính của Object.prototype. ? Mỗi tài sản là thuộc tính của Object.prototype trong trường hợp đối tượng
DebugMode
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.