Cara yang lebih baik untuk mendapatkan jenis variabel Javascript?

260

Apakah ada cara yang lebih baik untuk mendapatkan jenis variabel dalam JS daripada typeof? Ini berfungsi dengan baik ketika Anda melakukannya:

> typeof 1
"number"
> typeof "hello"
"string"

Tapi itu tidak berguna ketika Anda mencoba:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

Saya tahu instanceof, tapi ini mengharuskan Anda untuk mengetahui jenisnya sebelumnya.

> [1,2] instanceof Array
true
> r instanceof RegExp
true

Apakah ada cara yang lebih baik?

Aillyn
sumber
1
FYI, typeof new RegExp(/./); // "function"masalah di Chrome tampaknya diperbaiki di Chrome 14.
user113716
Peringatan: Jika Anda mengecilkan JS Anda dan Anda menggunakan 'objek khusus' seperti kelas naskah, maka beberapa jawaban di bawah ini akan memberi Anda nama fungsi yang dikaburkan seperti nalih-alih nama asli yang diharapkan. misalnya constructor.namemungkin memberi Anda nalih-alih nama lengkap yang diharapkan
Simon_Weaver

Jawaban:

221

Angus Croll baru-baru ini menulis posting blog yang menarik tentang ini -

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

Dia melewati pro dan kontra dari berbagai metode kemudian mendefinisikan metode baru 'toType' -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
sumber
3
Catatan menarik di sini - ini berpotensi merusak di mana "host" objek diteruskan ke fungsi. ActiveXObjectContoh Internet Explorer , misalnya.
Andy E
Jika Anda membuat jenis objek Anda sendiri (warisan prototypal), bagaimana Anda bisa mendapatkannya untuk mengembalikan nilai yang lebih spesifik daripada "objek"? Ini juga diangkat sebagai masalah di sini , tetapi tidak pernah ditangani.
EleventyOne
1
Jika penamaan tipe Anda mengandung: atau _ Anda dapat memperpanjang regex ini kematch(/\s([a-z,_,:,A-Z]+)/)
masi
6
Regex juga perlu menyertakan angka untuk hal-hal seperti Uint8Array. Sebaliknya, pertimbangkan yang lebih umum match(/\s([^\]]+)/)yang harus menangani apa pun.
Lily Finley
2
Saya sarankan untuk menggunakan \wkata operator sebagai gantinya ...
kaiser
51

Anda dapat mencoba menggunakan constructor.name.

[].constructor.name
new RegExp().constructor.name

Seperti halnya semua JavaScript, seseorang pada akhirnya akan selalu menunjukkan bahwa ini entah bagaimana jahat, jadi di sini ada tautan ke jawaban yang mencakup hal ini dengan cukup baik.

Alternatifnya adalah menggunakan Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Alex Turpin
sumber
10
nameadalah ekstensi untuk ECMAScript dan tidak akan berfungsi di semua browser.
Andy E
16
Jawab dipilih karena mengkonfirmasi kecurigaan bahwa semua yang dilakukan dalam javascript entah bagaimana jahat.
liljoshu
1
JAHAT: jika Anda mengecilkan kode Anda maka constructor.namekemungkinan akan berakhir menjadi seperti nbukan nama objek yang Anda harapkan. Jadi hati-hati untuk itu - terutama jika Anda hanya mengecil dalam produksi.
Simon_Weaver
nulldan undefinedsayangnya tidak memiliki konstruktor.
Andrew Grimm
JavaScript itu sendiri jahat. Dan konyol. Fakta bahwa pertanyaan ini tidak memiliki jawaban yang baik adalah bukti nyata dari itu.
user2173353
38

Fungsi penangkapan tipe yang cukup bagus adalah yang digunakan oleh YUI3 :

var TYPES = {
    'undefined'        : 'undefined',
    'number'           : 'number',
    'boolean'          : 'boolean',
    'string'           : 'string',
    '[object Function]': 'function',
    '[object RegExp]'  : 'regexp',
    '[object Array]'   : 'array',
    '[object Date]'    : 'date',
    '[object Error]'   : 'error'
},
TOSTRING = Object.prototype.toString;

function type(o) {
    return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
};

Ini menangkap banyak primitif yang disediakan oleh javascript, tetapi Anda selalu dapat menambahkan lebih banyak dengan memodifikasi TYPESobjek. Perhatikan bahwa typeof HTMLElementCollectiondi Safari akan melaporkan function, tetapi ketik (HTMLElementCollection) akan kembaliobject

Nick Husher
sumber
31

Anda mungkin menemukan fungsi berikut bermanfaat:

function typeOf(obj) {
  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
}

Atau di ES7 (komentar jika perbaikan lebih lanjut)

function typeOf(obj) {
  const { toString } = Object.prototype;
  const stringified = obj::toString();
  const type = stringified.split(' ')[1].slice(0, -1);

  return type.toLowerCase();
}

Hasil:

typeOf(); //undefined
typeOf(null); //null
typeOf(NaN); //number
typeOf(5); //number
typeOf({}); //object
typeOf([]); //array
typeOf(''); //string
typeOf(function () {}); //function
typeOf(/a/) //regexp
typeOf(new Date()) //date
typeOf(new Error) //error
typeOf(Promise.resolve()) //promise
typeOf(function *() {}) //generatorfunction
typeOf(new WeakMap()) //weakmap
typeOf(new Map()) //map

Terima kasih @johnrees karena memberi tahu saya tentang: kesalahan, janji, fungsi generator

Vix
sumber
Terima kasih untuk ini. Ini juga berfungsi untuk objek tanggal:typeOf(new Date())
James Irwin
Ah, saya sudah lupa tentang itu - terima kasih, sekarang sudah termasuk.
Vix
1
DantypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees
Saya tidak bisa mendapatkan jawaban ini berfungsi dalam naskah. Memberikan kesalahan:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK
Tidak yakin tentang TypeScript. Sudahkah Anda mencoba versi ES7? toString tidak secara eksplisit dinyatakan mungkin menjadi penyebabnya?
Vix
15

Kita juga dapat mengubah sedikit contoh dari ipr101

Object.prototype.toType = function() {
  return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}

dan panggil sebagai

"aaa".toType(); // 'string'
Greymaster
sumber
6
Ingat anak-anak, memanipulasi objek berbasis JavaScript dapat menyebabkan kompatibilitas dan masalah aneh lainnya. Cobalah untuk menggunakan ini dengan hemat di perpustakaan dan proyek yang lebih besar!
Alexander Craggs
9

fungsi satu baris:

function type(obj) {
    return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
}

ini memberikan hasil yang sama dengan jQuery.type()

Yukulélé
sumber
3

Anda dapat menerapkan Object.prototype.toStringke objek apa pun:

var toString = Object.prototype.toString;

console.log(toString.call([]));
//-> [object Array]

console.log(toString.call(/reg/g));
//-> [object RegExp]

console.log(toString.call({}));
//-> [object Object]

Ini bekerja dengan baik di semua browser, dengan pengecualian IE - ketika memanggil ini pada variabel yang diperoleh dari jendela lain, itu hanya akan dimuntahkan [object Object].

Andy E
sumber
1
@ Xeon Saya pikir kebencian terhadap IE agak terlalu banyak. IE9 adalah browser yang jauh lebih baik daripada pendahulunya.
Aillyn
4
@pessimopoppotamus tetapi tidak ada orang yang akan memperbarui browser mereka ke IE9 menggunakan IE.
Alex Turpin
1
IE 9 adalah langkah ke arah yang benar, IE 10 terlihat cukup bagus. Kebetulan, bug yang saya sebutkan adalah regresi di IE 9 - mereka memperbaikinya di IE 8.
Andy E
3

2 ¢ saya! Sungguh, bagian dari alasan saya melemparkan ini di sini, meskipun daftar panjang jawaban, adalah untuk memberikan all in onesolusi tipe yang lebih sedikit dan mendapatkan umpan balik di masa depan tentang bagaimana mengembangkannya untuk memasukkan lebih banyak real types.

Dengan solusi berikut, seperti yang disebutkan di atas, saya menggabungkan beberapa solusi yang ditemukan di sini, serta memasukkan perbaikan untuk mengembalikan nilai jQuerypada objek yang didefinisikan pada jQuery jika tersedia . Saya juga menambahkan metode ke prototipe Object asli. Saya tahu itu sering kali tabu, karena bisa mengganggu ekstensi lain, tapi saya biarkan begitu user beware. Jika Anda tidak suka cara melakukannya, cukup salin fungsi basis di mana saja Anda suka dan ganti semua variabel thisdengan parameter argumen untuk dilewatkan (seperti argumen [0]).

;(function() {  //  Object.realType
    function realType(toLower) {
        var r = typeof this;
        try {
            if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
            else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
        }
        catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
        return !toLower ? r : r.toLowerCase();
    }
    Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
        ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
})();

Maka cukup gunakan dengan mudah, seperti:

obj.realType()  //  would return 'Object'
obj.realType(true)  //  would return 'object'

Catatan: Ada 1 argumen yang bisa diterima. Jika bool of true, maka pengembalian akan selalu dalam huruf kecil .

Lebih banyak contoh:

true.realType();                            //  "Boolean"
var a = 4; a.realType();                    //  "Number"
$('div:first').realType();                   // "jQuery"
document.createElement('div').realType()    //  "HTMLDivElement"

Jika Anda memiliki sesuatu untuk ditambahkan yang mungkin bermanfaat, seperti mendefinisikan kapan suatu objek dibuat dengan perpustakaan lain (Moo, Proto, Yui, Dojo, dll ...) silakan berkomentar atau edit ini dan pertahankan agar lebih akurat dan tepat. ATAU berguling ke arah yang GitHubsaya buat untuk itu dan beri tahu saya. Anda juga akan menemukan tautan cepat ke file min cdn di sana.

SpYk3HH
sumber
2
function getType(obj) {
    if(obj && obj.constructor && obj.constructor.name) {
        return obj.constructor.name;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

Dalam tes pendahuluan saya, ini bekerja dengan cukup baik. Kasing pertama akan mencetak nama objek yang dibuat dengan "baru", dan kasing kedua harus menangkap yang lainnya.

Saya menggunakan (8, -1)karena saya berasumsi bahwa hasilnya akan selalu dimulai [objectdan diakhiri dengan ]tetapi saya tidak yakin itu benar dalam setiap skenario.

Mpen
sumber
2

Saya kira solusi paling universal di sini - adalah untuk memeriksa undefineddan nullpertama, lalu panggil saja constructor.name.toLowerCase().

const getType = v =>
  v === undefined
    ? 'undefined'
    : v === null
      ? 'null'
      : v.constructor.name.toLowerCase();




console.log(getType(undefined)); // 'undefined'
console.log(getType(null)); // 'null'
console.log(getType('')); // 'string'
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(new Set())); // `set'
console.log(getType(Promise.resolve())); // `promise'
console.log(getType(new Map())); // `map'
Arsenowitch
sumber
Sejauh yang saya tahu - .constructor.nameselalu berisi nilai string. Untuk undefined / null kita memiliki string yang sesuai dikembalikan oleh fungsi.
Arsenowitch
Terima kasih - menghapus komentar saya ...
Bergi
Karena constructormerupakan properti yang diwarisi, ini memecah untuk objek yang dibuat dengan Object.create(null). Cukup sederhana untuk diperbaiki, tetapi bagaimana menyebutnya? "[objek nol]"?
traktor53
0

typeof kondisi digunakan untuk memeriksa tipe variabel, jika Anda memeriksa tipe variabel dalam kondisi if-else misalnya

if(typeof Varaible_Name "undefined")
{

}
MzkZeeshan
sumber