จะแสดงวิธีการทั้งหมดของวัตถุได้อย่างไร


249

ฉันต้องการทราบวิธีการแสดงรายการวิธีการทั้งหมดที่มีอยู่สำหรับวัตถุเช่น:

 alert(show_all_methods(Math));

สิ่งนี้ควรพิมพ์:

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

คำตอบ:


298

คุณสามารถใช้Object.getOwnPropertyNames()เพื่อรับคุณสมบัติทั้งหมดที่เป็นของวัตถุไม่ว่าจะนับได้หรือไม่ ตัวอย่างเช่น:

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

จากนั้นคุณสามารถใช้filter()เพื่อรับวิธีการเท่านั้น:

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

ในเบราว์เซอร์ ES3 (IE 8 และต่ำกว่า) คุณสมบัติของวัตถุในตัวนั้นไม่สามารถนับได้ วัตถุที่ชอบwindowและdocumentไม่ได้มีในตัวพวกมันถูกกำหนดโดยเบราว์เซอร์และน่าจะนับได้มากที่สุดจากการออกแบบ

จากECMA-262 Edition 3 :

วัตถุทั่วโลก
มีวัตถุสากลที่ไม่ซ้ำกัน (15.1) ซึ่งถูกสร้างขึ้นก่อนที่การควบคุมเข้าสู่บริบทการดำเนินการใด ๆ เริ่มแรกวัตถุทั่วโลกมีคุณสมบัติดังต่อไปนี้:

•ในตัววัตถุเช่นคณิตศาสตร์, String, วันที่, parseInt ฯลฯแอตทริบิวต์เหล่านี้มี {}
•คุณสมบัติที่กำหนดไว้สำหรับโฮสต์เพิ่มเติม ซึ่งอาจรวมถึงคุณสมบัติที่มีค่าเป็นวัตถุทั่วโลกเอง; ตัวอย่างเช่นในโมเดลวัตถุเอกสาร HTML คุณสมบัติหน้าต่างของวัตถุทั่วโลกเป็นวัตถุทั่วโลกเอง

เมื่อตัวควบคุมเข้าสู่บริบทการดำเนินการและเมื่อประมวลผลรหัส ECMAScript อาจมีการเพิ่มคุณสมบัติเพิ่มเติมลงในวัตถุส่วนกลางและคุณสมบัติเริ่มต้นอาจมีการเปลี่ยนแปลง

ฉันควรชี้ให้เห็นว่าสิ่งนี้หมายความว่าวัตถุเหล่านั้นไม่ใช่คุณสมบัติที่นับได้ของวัตถุทั่วโลก หากคุณดูเอกสารข้อมูลจำเพาะที่เหลือคุณจะเห็นคุณสมบัติและวิธีการในตัวของวัตถุเหล่านี้ส่วนใหญ่มี{ DontEnum }คุณสมบัติที่กำหนดไว้


อัปเดต: ผู้ใช้ CMS คนอื่น ๆ ได้นำข้อผิดพลาด IE เกี่ยวกับ{ DontEnum }ความสนใจของฉัน

แทนที่จะตรวจสอบแอตทริบิวต์ DontEnum [Microsoft] JScript จะข้ามคุณสมบัติใด ๆ ในวัตถุใด ๆ ที่มีคุณสมบัติชื่อเดียวกันในห่วงโซ่ต้นแบบของวัตถุที่มีแอตทริบิวต์ DontEnum

ในระยะสั้นระวังเมื่อตั้งชื่อคุณสมบัติวัตถุของคุณ หากมีคุณสมบัติต้นแบบหรือวิธีการในตัวที่มีชื่อเดียวกัน IE จะข้ามไปเมื่อใช้for...inลูป


Andy E ขอบคุณที่ชี้ให้เห็น เห็นได้ชัดว่าฉันไม่ได้ตระหนักถึงเรื่องนี้และฉันขอขอบคุณความพยายามของคุณในการขุดและกล่าวถึงที่นี่ ขอขอบคุณอีกครั้ง :)
Roland Bouman

@Roland: ไม่ต้องกังวล อาจจะเศร้าเล็กน้อย แต่ฉันมีข้อกำหนดเก็บไว้ในโฟลเดอร์เอกสารของฉันดังนั้นจึงไม่จำเป็นต้องขุดมากนัก!
Andy E

จะไม่มีวิธีรับรายการของเมธอดทั้งหมดในการปรับใช้ JS ที่ใหม่กว่าหรือไม่? ชอบ Node.js และ V8 ไหม เราจะทำอย่างไรในการสะท้อนและใคร่ครวญวัตถุเช่นที่เราเคยทำเช่นสำหรับวัตถุจำลองกรอบ ฯลฯ ฉันคิดว่าฉันเพิ่งจะลืม JS แต่ฉันคิดว่าสิ่งต่าง ๆ ได้เปลี่ยนไปในช่วงหลายปีที่ผ่านมา :)
d11wtq

2
@ d11wtq ด้วยการใช้งาน ES5 คุณสามารถเรียกใช้Object.getOwnPropertyNames()ซึ่งจะส่งคืนคุณสมบัติและเมธอดที่ไม่สามารถนับได้
Andy E

ตั้งแต่วัตถุทั้งหมดสืบทอดมาจากต้นแบบมันจะดีกว่าObject.getOwnPropertyNames(Array.prototype) ไหมถ้าทำอย่างนั้น
lfender6445

71

ไม่สามารถใช้ ES3 ได้เนื่องจากคุณสมบัติมีDontEnumแอตทริบิวต์ภายในซึ่งทำให้เราไม่สามารถระบุคุณสมบัติเหล่านี้ได้ ในทางตรงกันข้าม ES5 จัดเตรียม descriptor คุณสมบัติสำหรับการควบคุมความสามารถในการแจงนับของคุณสมบัติเพื่อให้ผู้ใช้กำหนดและคุณสมบัติเนทิฟสามารถใช้อินเทอร์เฟซเดียวกันและเพลิดเพลินไปกับความสามารถเดียวกันซึ่งรวมถึงการดูคุณสมบัติที่ไม่นับด้วยโปรแกรม

getOwnPropertyNamesฟังก์ชั่นสามารถนำมาใช้เพื่อระบุมากกว่าคุณสมบัติทั้งหมดที่ส่งผ่านในวัตถุรวมทั้งผู้ที่ไม่ได้นับ จากนั้นtypeofสามารถใช้การตรวจสอบอย่างง่ายเพื่อกรองข้อมูลที่ไม่ใช่ฟังก์ชั่น น่าเสียดายที่ Chrome เป็นเบราว์เซอร์เดียวที่ใช้งานได้ในปัจจุบัน

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

console.log(getAllMethods(Math));

บันทึก["cos", "pow", "log", "tan", "sqrt", "ceil", "asin", "abs", "max", "exp", "atan2", "random", "round", "floor", "acos", "atan", "min", "sin"]ในลำดับใดไม่มี


+1 สำหรับสิ่ง ES5 IE9 จะสนับสนุน ES5 อย่างเต็มที่ดังนั้นสิ่งนี้เป็นสิ่งที่ดีที่จะรู้
Andy E

1
@Andy - ไมโครซอฟท์คือการ IE9 อย่างจริงจังซึ่งจะทำให้ฉันมีความสุข :)
อนุรักษ์

console.log (ฟังก์ชั่น (a) {return Object.getOwnPropertyNames (a) .filter (ฟังก์ชั่น (b) {return "ฟังก์ชั่น" == typeof [b]})} (คณิตศาสตร์)); ขอบคุณ!
19h

1
getOwnPropertyNames คือตั๋ว มันใช้งานได้ใน Nashorn พวกเขาเพิ่งเปลี่ยนชื่อของวิธีการของวัตถุ Java และฉันก็สามารถที่จะคิดออกชื่อใหม่โดยใช้ Object.getOwnPropertyNames (Java)
cayhorstmann

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

objวิธีนี้คุณจะได้รับวิธีการทั้งหมดที่คุณสามารถเรียกร้อง ซึ่งรวมถึงวิธีการที่ "สืบทอด" จากต้นแบบ (เช่นgetMethods()ใน java) หากคุณต้องการดูวิธีการที่กำหนดโดยตรงเท่านั้นobjคุณสามารถตรวจสอบกับhasOwnProperty:

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

ใช่ฉันเพิ่งสังเกตเห็นเช่นกัน เมื่อฉันใช้บางอย่างเช่นdocumentหรือwindowฉันโชคดี ตรงไปตรงมามันเป็นสิ่งที่คาดไม่ถึงฉันไม่รู้ว่าทำไมมันไม่ทำงานกับคณิตศาสตร์เป็นต้น
Roland Bouman

4
@Roland: เป็นเพราะdocumentและwindowเป็นวัตถุที่มีคุณสมบัติมากมายที่จัดทำโดยเบราว์เซอร์พวกเขาไม่ได้เป็นส่วนหนึ่งของรันไทม์สคริปต์ วัตถุพื้นเมืองเป็นและแน่นอนคุณสมบัติไม่นับ
Andy E

1
E ใด ๆ ฉันไม่เห็นด้วยมันชัดเจน ฉันหมายความว่ามันชัดเจนเพราะเราไม่สามารถระบุได้ แต่ฉันไม่เห็นเหตุผลว่าทำไมบิวด์อินเหล่านั้นควรป้องกันการแจงนับคุณสมบัติของพวกเขา แค่อยากรู้อยากเห็นมีบางส่วนของมาตรฐานที่บอกว่าบิวด์อินเหล่านี้ไม่ควรมีคุณสมบัติที่นับไม่ได้หรือไม่?
Roland Bouman

@Roland: ขอโทษฉันหมายความว่ามันชัดเจนว่าพวกเขาจะไม่นับเพราะพวกเขาไม่ได้ปรากฏตัวพร้อมสำหรับใน ดูคำตอบของฉันด้านล่างเพื่ออ้างจากข้อมูลจำเพาะ
Andy E

@Mic: คณิตศาสตร์เป็นวัตถุในตัวซึ่งคุณสมบัติไม่สามารถนับได้
Andy E

31

การสนับสนุนเบราว์เซอร์ที่ทันสมัยส่วนใหญ่console.dir(obj)ซึ่งจะส่งคืนคุณสมบัติทั้งหมดของวัตถุที่สืบทอดผ่านตัวสร้าง ดูเอกสารประกอบของ Mozilla สำหรับข้อมูลเพิ่มเติมและการสนับสนุนเบราว์เซอร์ปัจจุบัน

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

4

คำตอบอื่น ๆ ที่นี่ทำงานกับคณิตศาสตร์เช่นซึ่งเป็นวัตถุคงที่ แต่มันจะไม่ทำงานสำหรับอินสแตนซ์ของวัตถุเช่นวันที่ ฉันพบสิ่งต่อไปนี้ในการทำงาน:

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/

วิธีนี้ใช้ไม่ได้กับคำถามเดิม (คณิตศาสตร์) ดังนั้นให้เลือกวิธีการแก้ปัญหาตามความต้องการของคุณ ฉันโพสต์สิ่งนี้ที่นี่เพราะ Google ส่งคำถามนี้ให้ฉัน แต่ฉันต้องการทราบวิธีการทำเช่นนี้สำหรับอินสแตนซ์ของวัตถุ


3

คำตอบสั้น ๆ คือคุณทำไม่ได้เพราะMathและDate(จากส่วนหัวของฉันฉันแน่ใจว่ามีคนอื่น) ไม่ใช่วัตถุธรรมดา หากต้องการดูสิ่งนี้ให้สร้างสคริปต์ทดสอบอย่างง่าย:

<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>

คุณเห็นมันนำเสนอเป็นวัตถุแบบเดียวกับที่เอกสารทำโดยรวม แต่เมื่อคุณลองและดูในวัตถุนั้นจริง ๆ คุณจะเห็นว่ามันเป็นรหัสเนทีฟ


1

Mathมีวิธีการแบบคงที่คุณสามารถเรียกโดยตรงเช่นMath.abs()ในขณะที่DateมีวิธีการคงเหมือนDate.now()และวิธีการเช่นที่คุณจำเป็นต้องสร้างตัวอย่างใหม่ครั้งแรกต่อการเรียกร้องvar time = new Date()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);

แน่นอนคุณจะต้องกรองคีย์ที่ได้รับสำหรับวิธีการแบบสแตติกเพื่อรับชื่อวิธีการที่แท้จริงเนื่องจากคุณสามารถรับlength, nameที่ไม่ใช่ฟังก์ชันในรายการ

แต่ถ้าเราต้องการได้รับวิธีการที่ใช้ได้ทั้งหมดจากคลาสที่ขยายคลาสอื่น? แน่นอนคุณจะต้องสแกนผ่านรากของต้นแบบเช่นการใช้
__proto__เพื่อประหยัดเวลาของคุณคุณสามารถใช้สคริปต์ด้านล่างเพื่อรับวิธีแบบคงที่และวิธีการแบบลึก

// 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));

หากคุณต้องการได้รับวิธีการจากอินสแตนซ์ที่สร้างอย่าลืมที่จะผ่านconstructorมัน


0

ฉันเชื่อว่ามีเหตุผลทางประวัติศาสตร์ที่เรียบง่ายว่าทำไมคุณไม่สามารถระบุวิธีการของวัตถุในตัวเช่น Array ได้ นี่คือเหตุผล:

เมธอดคือคุณสมบัติของต้นแบบวัตถุพูด Object.prototype นั่นหมายความว่าวัตถุ - อินสแตนซ์ทั้งหมดจะสืบทอดวิธีการเหล่านั้น นั่นเป็นเหตุผลที่คุณสามารถใช้วิธีการเหล่านั้นกับวัตถุใด ๆ ตัวอย่างเช่น. toString ()

ดังนั้นหากวิธีการนับได้และฉันจะพูดซ้ำมากกว่า {a: 123} ด้วย: "สำหรับ (คีย์ใน {a: 123}) {... }" จะเกิดอะไรขึ้น ลูปนั้นจะถูกดำเนินการกี่ครั้ง?

มันจะถูกทำซ้ำหนึ่งครั้งสำหรับปุ่มเดียว 'a' ในตัวอย่างของเรา แต่ยังหนึ่งครั้งสำหรับทุกคุณสมบัตินับจำนวนของ Object.prototype ดังนั้นหากวิธีการนับจำนวน (โดยค่าเริ่มต้น) แล้วห่วงใด ๆ มากกว่าวัตถุใด ๆ จะห่วงมากกว่าวิธีการสืบทอดทั้งหมดของมันเช่นกัน


1
ตั้งแต่ primitives มักจะสืบทอดมาจาก protype จึงเป็นไปได้Object.getOwnPropertyNames(Array.prototype)เช่น
lfender6445

คุณหมายถึงวิธีใดคือคุณสมบัติของ Object.prototype ? ทุกคุณสมบัติเป็นคุณสมบัติของ Object.prototype ในกรณีของ Object
debugmode
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.