วิธีที่ถูกต้องที่สุดในการตรวจสอบประเภทของวัตถุ JS?


144

ตัวtypeofดำเนินการไม่ได้ช่วยให้เราค้นหาประเภทของวัตถุจริงๆ

ฉันเห็นรหัสต่อไปนี้แล้ว:

Object.prototype.toString.apply(t)  

คำถาม:

เป็นวิธีตรวจสอบประเภทของวัตถุที่ถูกต้องที่สุดหรือไม่?


2
ดูบทความนี้: javascriptweblog.wordpress.com/2011/08/08/…
James Allardice

4
ดูที่โพสต์นี้: stackoverflow.com/questions/332422/…
isJustMe


3
วิธีที่ถูกต้องที่สุดคือ ... ไม่ทดสอบประเภท ทำไมคุณถึงต้องการประเภท?
hugomg

Object.prototype.toString.call / Object.prototype.toString.apply
xgqfrms

คำตอบ:


198

ข้อกำหนดของ JavaScript ให้วิธีหนึ่งที่เหมาะสมในการกำหนดคลาสของอ็อบเจ็กต์:

Object.prototype.toString.call(t);

http://bonsaiden.github.com/JavaScript-Garden/#types


5
หากคุณกำลังมองหาประเภทที่เฉพาะเจาะจงคุณอาจต้องการทำบางสิ่งบางอย่างตามแนว: Object.prototype.toString.call(new FormData()) === "[object FormData]"ซึ่งจะเป็นจริง คุณยังสามารถใช้slice(8, -1)เพื่อส่งคืนFormDataแทน[object FormData]
Chris Marisic

4
มีความแตกต่างระหว่างการใช้Object.prototypeและ{}หรือไม่?
ฟรี

3
อาจจะมีการเปลี่ยนแปลงในช่วงหลายปีที่ผ่านมา แต่Object.prototype.toString.call(new MyCustomObject())กลับมา[object Object]ในขณะnew MyCustomObject() instanceOf MyCustomObject returns trueที่สิ่งที่ฉันต้องการ (Chrome 54.0.2840.99 ม.)
Maslow

@ Maslow ฉันพบปัญหาเดียวกันกับที่คุณพูด หลังจากดูเอกสารออนไลน์แล้วฉันก็ใช้new MyCustomObject().constructor === MyCustomObject.
solstice333

3
มันทำให้เกิดคำถามทำไมพวกเขาไม่ห่อรหัสนี้ด้วยวิธีที่สะดวกกว่านี้หรืออนุญาตให้ตัวดำเนินการเพิ่มเติมรวบรวมสิ่งนี้ ฉันรู้ว่าคุณเป็นเพียงผู้ส่งสาร แต่ตรงไปตรงมานั่นแย่มาก
Andrew S

60

Object.prototype.toStringเป็นวิธีที่ดี แต่ประสิทธิภาพการทำงานที่เลวร้ายที่สุด

http://jsperf.com/check-js-type

ตรวจสอบประสิทธิภาพประเภท js

ใช้typeofแก้ปัญหาพื้นฐานบางอย่าง (String, Number, Boolean ... ) และใช้Object.prototype.toStringแก้ปัญหาที่ซับซ้อน (เช่น Array, Date, RegExp)

และนี่คือทางออกของฉัน:

var type = (function(global) {
    var cache = {};
    return function(obj) {
        var key;
        return obj === null ? 'null' // null
            : obj === global ? 'global' // window in browser or global in nodejs
            : (key = typeof obj) !== 'object' ? key // basic: string, boolean, number, undefined, function
            : obj.nodeType ? 'object' // DOM element
            : cache[key = ({}).toString.call(obj)] // cached. date, regexp, error, object, array, math
            || (cache[key] = key.slice(8, -1).toLowerCase()); // get XXXX from [object XXXX], and cache it
    };
}(this));

ใช้เป็น:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"

การทดสอบบน jsPerf นั้นไม่แม่นยำนัก การทดสอบเหล่านั้นไม่เท่ากัน (การทดสอบสำหรับสิ่งเดียวกัน) เช่น typeof [] ส่งคืน "object", typeof {} ยังส่งคืน "object" แม้ว่าอันหนึ่งจะเป็น object Array และอีกอันคือ object Object มีปัญหาอื่น ๆ อีกมากมายในการทดสอบนั้น ... โปรดระวังเมื่อดูที่ jsPerf ว่าการทดสอบกำลังเปรียบเทียบแอปเปิ้ลกับแอปเปิ้ล
kmatheny

typeฟังก์ชันของคุณดี แต่ดูว่าเป็นอย่างไรเมื่อเทียบกับtypeฟังก์ชันอื่น ๆ http://jsperf.com/code-type-test-a-test
Progo

19
เมตริกประสิทธิภาพเหล่านี้ควรมีอารมณ์ร่วมด้วยสามัญสำนึก แน่นอนว่า Prototype.toString นั้นช้ากว่ารุ่นอื่น ๆ ตามลำดับขนาด แต่ในรูปแบบที่ยิ่งใหญ่ของสิ่งต่างๆจะใช้เวลาโดยเฉลี่ยสองร้อยนาโนวินาทีต่อการโทรหนึ่งครั้ง เว้นแต่จะมีการใช้การเรียกนี้ในเส้นทางวิกฤตซึ่งมีการดำเนินการบ่อยมากสิ่งนี้อาจไม่เป็นอันตราย ฉันอยากจะมีรหัสตรงไปตรงมามากกว่ารหัสที่เสร็จเร็วขึ้นหนึ่งไมโครวินาที
David

({}).toString.call(obj)ช้ากว่าObject.prototype.toString jsperf.com/object-check-test77
timaschew

ทางออกที่ดี ฉันยืมฟังก์ชั่นของคุณเป็น lib ของฉัน :)
Dong Nguyen

21

คำตอบที่ยอมรับนั้นถูกต้อง แต่ฉันต้องการกำหนดยูทิลิตี้เล็ก ๆ น้อย ๆ นี้ในโครงการส่วนใหญ่ที่ฉันสร้าง

var types = {
   'get': function(prop) {
      return Object.prototype.toString.call(prop);
   },
   'null': '[object Null]',
   'object': '[object Object]',
   'array': '[object Array]',
   'string': '[object String]',
   'boolean': '[object Boolean]',
   'number': '[object Number]',
   'date': '[object Date]',
}

ใช้เช่นนี้:

if(types.get(prop) == types.number) {

}

หากคุณใช้เชิงมุมคุณสามารถฉีดได้อย่างหมดจด:

angular.constant('types', types);

11
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

จับต้นแบบที่คุณคาดหวังว่าวัตถุจะมีจากนั้นเปรียบเทียบกับต้นแบบนั้น

ตัวอย่างเช่น

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true

สิ่งนี้ดีกว่า / ต่างจากที่พูดยังo instanceof String; //trueไง?
Jamie Treworgy

@jamietre เพราะ"foo" instanceof Stringหยุดพัก
Raynos

ตกลงดังนั้น "typeof (o) === 'object' && o instanceof SomeObject" ง่ายต่อการทดสอบสตริง ดูเหมือนว่าจะทำงานพิเศษโดยไม่ได้แก้ปัญหาพื้นฐานที่ต้องรู้ล่วงหน้าว่าคุณกำลังทดสอบอะไร
Jamie Treworgy

ขออภัยที่ข้อมูลโค้ดไม่สมเหตุสมผล แต่ฉันคิดว่าคุณรู้ว่าฉันหมายถึงอะไรหากคุณกำลังทดสอบสตริงให้ใช้typeof(x)==='string'แทน
Jamie Treworgy

BTW, Object.getPrototypeOf(true)ล้มเหลวที่ผลตอบแทน(true).constructor Boolean
katspaugh

6

ฉันขอยืนยันว่าโซลูชันส่วนใหญ่ที่แสดงในที่นี้ประสบปัญหาจากการถูกวิศวกรมากเกินไป อาจเป็นวิธีที่ง่ายที่สุดในการตรวจสอบว่าค่าเป็นประเภทหรือไม่[object Object]คือการตรวจสอบ.constructorคุณสมบัติของมัน:

function isObject (a) { return a != null && a.constructor === Object; }

หรือสั้นกว่าด้วยฟังก์ชันลูกศร:

const isObject = a => a != null && a.constructor === Object;

a != nullส่วนหนึ่งเป็นสิ่งจำเป็นเพราะหนึ่งอาจจะผ่านnullหรือundefinedและคุณไม่สามารถดึงคุณสมบัติคอนสตรัคจากทั้งสองนี้

ใช้งานได้กับวัตถุใด ๆ ที่สร้างผ่าน:

  • ที่ Objectสร้าง
  • ตัวอักษร {}

คุณสมบัติที่เป็นระเบียบอีกประการหนึ่งของมันคือความสามารถในการรายงานที่ถูกต้องสำหรับคลาสแบบกำหนดเองที่ใช้ประโยชน์Symbol.toStringTagได้ ตัวอย่างเช่น:

class MimicObject {
  get [Symbol.toStringTag]() {
    return 'Object';
  }
}

ปัญหาคือเมื่อเรียกObject.prototype.toStringอินสแตนซ์ของมันรายงานเท็จ[object Object]จะถูกส่งกลับ:

let fakeObj = new MimicObject();
Object.prototype.toString.call(fakeObj); // -> [object Object]

แต่การตรวจสอบกับตัวสร้างให้ผลลัพธ์ที่ถูกต้อง:

let fakeObj = new MimicObject();
fakeObj.constructor === Object; // -> false

4

วิธีที่ดีที่สุดในการค้นหาประเภท REAL ของวัตถุ (รวมถึงทั้ง Object ดั้งเดิมหรือชื่อ DataType (เช่น String, Date, Number, .. ฯลฯ ) และประเภท REAL ของวัตถุ (แม้แต่วัตถุที่กำหนดเอง) คือการจับ คุณสมบัติชื่อของคอนสตรัคเตอร์ของต้นแบบวัตถุ:

ประเภทเนทีฟ Ex1:

var string1 = "Test";
console.log(string1.__proto__.constructor.name);

แสดง:

String

Ex2:

var array1 = [];
console.log(array1.__proto__.constructor.name);

แสดง:

Array

คลาสที่กำหนดเอง:

function CustomClass(){
  console.log("Custom Class Object Created!");
}
var custom1 = new CustomClass();

console.log(custom1.__proto__.constructor.name);

แสดง:

CustomClass

นี้ล้มเหลวถ้าวัตถุอย่างใดอย่างหนึ่งหรือnull undefined
Julian Knight

2

คำถามเก่าที่ฉันรู้ คุณไม่จำเป็นต้องแปลง ดูฟังก์ชันนี้:

function getType( oObj )
{
    if( typeof oObj === "object" )
    {
          return ( oObj === null )?'Null':
          // Check if it is an alien object, for example created as {world:'hello'}
          ( typeof oObj.constructor !== "function" )?'Object':
          // else return object name (string)
          oObj.constructor.name;              
    }   

    // Test simple types (not constructed types)
    return ( typeof oObj === "boolean")?'Boolean':
           ( typeof oObj === "number")?'Number':
           ( typeof oObj === "string")?'String':
           ( typeof oObj === "function")?'Function':false;

}; 

ตัวอย่าง:

function MyObject() {}; // Just for example

console.log( getType( new String( "hello ") )); // String
console.log( getType( new Function() );         // Function
console.log( getType( {} ));                    // Object
console.log( getType( [] ));                    // Array
console.log( getType( new MyObject() ));        // MyObject

var bTest = false,
    uAny,  // Is undefined
    fTest  function() {};

 // Non constructed standard types
console.log( getType( bTest ));                 // Boolean
console.log( getType( 1.00 ));                  // Number
console.log( getType( 2000 ));                  // Number
console.log( getType( 'hello' ));               // String
console.log( getType( "hello" ));               // String
console.log( getType( fTest ));                 // Function
console.log( getType( uAny ));                  // false, cannot produce
                                                // a string

ต้นทุนต่ำและเรียบง่าย


ส่งคืนfalseหากวัตถุทดสอบเป็นnullหรือundefined
Julian Knight

หรือtrueหรือfalse
Julian Knight

@JulianKnight false ไม่เป็นไรที่ null หรือ undefined ก็ไม่มีประโยชน์อะไร แล้วประเด็นคืออะไร?
Codebeat

ตัวอย่างของคุณส่งคืนข้อมูลที่ไม่สอดคล้องกัน ผลการค้นหาบางประเภทข้อมูลและอื่น ๆ falseมีค่า สิ่งนี้ช่วยตอบคำถามได้อย่างไร
Julian Knight

1
@JulianKnight เห็นการเปลี่ยนแปลงนั่นคือสิ่งที่คุณต้องการหรือไม่? หากคุณต้องการผลลัพธ์ที่ไม่ได้กำหนดหรือ "ไม่ได้กำหนด" คุณสามารถแทนที่เท็จสุดท้ายได้หากต้องการ
Codebeat

1

ทางออกที่ดีที่สุดคือtoString(ตามที่ระบุไว้ข้างต้น):

function getRealObjectType(obj: {}): string {
    return Object.prototype.toString.call(obj).match(/\[\w+ (\w+)\]/)[1].toLowerCase();
}

ป้อนคำอธิบายภาพที่นี่

FAIR คำเตือน: toStringพิจารณาดังนั้นคุณต้องปกป้องในภายหลังด้วยNaNnumberNumber.isNaN(value)ดังนั้นคุณต้องปกป้องในภายหลังด้วย

วิธีแก้ปัญหาอื่น ๆ ที่แนะนำโดยใช้Object.getPrototypeOf ล้มเหลวด้วยnullและundefined

ใช้ตัวสร้าง


0

ฉันรวบรวมยูทิลิตี้ตรวจสอบประเภทเล็ก ๆ น้อย ๆ ที่ได้รับแรงบันดาลใจจากคำตอบที่ถูกต้องด้านบน:

thetypeof = function(name) {
        let obj = {};
        obj.object = 'object Object'
        obj.array = 'object Array'
        obj.string = 'object String'
        obj.boolean = 'object Boolean'
        obj.number = 'object Number'
        obj.type = Object.prototype.toString.call(name).slice(1, -1)
        obj.name = Object.prototype.toString.call(name).slice(8, -1)
        obj.is = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type === obj[ofType])? true: false
        }
        obj.isnt = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type !== obj[ofType])? true: false
        }
        obj.error = (ofType) => {
            throw new TypeError(`The type of ${name} is ${obj.name}: `
            +`it should be of type ${ofType}`)
        }
        return obj;
    };

ตัวอย่าง:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something

1
ดูเหมือนจะไม่ทำงานให้กับวัตถุที่อยู่nullหรือundefinedหรือtrueหรือfalse
จูเลียนอัศวิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.