วิธีที่ดีกว่าในการรับตัวแปรของ Javascript


260

มีวิธีที่ดีกว่าในการรับชนิดของตัวแปรใน JS มากกว่าtypeof? มันทำงานได้ดีเมื่อคุณ:

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

แต่มันไร้ประโยชน์เมื่อคุณลอง:

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

ฉันรู้instanceofแต่สิ่งนี้คุณต้องรู้ล่วงหน้า

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

มีวิธีที่ดีกว่า?


1
FYI typeof new RegExp(/./); // "function"ปัญหาใน Chrome ดูเหมือนจะได้รับการแก้ไขใน Chrome 14
113716


คำเตือน: หากคุณกำลังลดขนาด JS ของคุณและคุณกำลังใช้ 'ออบเจกต์ที่กำหนดเอง' เช่นคลาส typescript คำตอบบางส่วนด้านล่างนี้จะทำให้คุณมีชื่อฟังก์ชันที่ยุ่งเหยิงเช่นnแทนที่จะเป็นชื่อดั้งเดิมที่คาดไว้ เช่นconstructor.nameอาจให้คุณnแทนชื่อเต็มที่คาดไว้
Simon_Weaver

คำตอบ:


221

Angus Croll เพิ่งเขียนบทความบล็อกที่น่าสนใจเกี่ยวกับเรื่องนี้ -

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

เขาต้องผ่านข้อดีข้อเสียของวิธีการต่าง ๆ แล้วกำหนดวิธีการใหม่ 'toType' -

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

3
ข้อความด้านข้างที่น่าสนใจที่นี่ - สิ่งนี้อาจเกิดการแตกที่วัตถุ "โฮสต์" ถูกส่งผ่านไปยังฟังก์ชัน ActiveXObjectอินสแตนซ์ของInternet Explorer เช่น
Andy E

หากคุณสร้างประเภทออบเจ็กต์ของคุณเอง (การสืบทอดต้นแบบ) คุณจะให้มันคืนค่าที่เจาะจงได้มากกว่า "object" ได้อย่างไร? สิ่งนี้ได้รับการยกเป็นปัญหาที่นี่แต่ไม่เคยได้รับการแก้ไข
EleventyOne

1
หากการตั้งชื่อประเภทของคุณมี: หรือ _ คุณสามารถขยาย regex นี้ไปยังmatch(/\s([a-z,_,:,A-Z]+)/)
masi

6
regex Uint8Arrayยังจะต้องรวมถึงตัวเลขสำหรับสิ่งที่ต้องการ ให้พิจารณาเรื่องทั่วไปmatch(/\s([^\]]+)/)ที่ควรจัดการกับสิ่งอื่นแทน
Lily Finley

2
ฉันขอแนะนำให้ใช้ตัว\wดำเนินการคำแทน…
kaiser

51

constructor.nameคุณสามารถลองใช้

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

เช่นเดียวกับ JavaScript ทุกอย่างในที่สุดบางคนก็จะชี้ว่านี่เป็นสิ่งที่ชั่วร้ายดังนั้นนี่คือลิงก์ไปยังคำตอบที่ครอบคลุมสิ่งที่ดีงามนี้

ทางเลือกหนึ่งคือการใช้ Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)

10
nameเป็นส่วนขยายของ ECMAScript และจะไม่ทำงานในทุกเบราว์เซอร์
Andy E

16
ตอบรับการโหวตเนื่องจากยืนยันความสงสัยว่าทุกอย่างที่ทำในจาวาสคริปต์นั้นเป็นสิ่งที่ชั่วร้าย
liljoshu

1
ความชั่วร้าย: หากคุณกำลังลดขนาดโค้ดของคุณคุณconstructor.nameอาจจะกลายเป็นสิ่งที่ต้องการnแทนที่จะเป็นชื่อวัตถุที่คุณคาดหวัง ดังนั้นระวังให้ดี - โดยเฉพาะถ้าคุณลดการผลิตลง
Simon_Weaver

nullและundefinedน่าเสียดายที่ไม่มีคอนสตรัคเตอร์
Andrew Grimm

จาวาสคริปต์เองก็ชั่วร้าย และงี่เง่า ความจริงที่ว่าคำถามนี้ไม่มีคำตอบที่ดีคือหลักฐานการมีชีวิตของที่
user2173353

38

ที่ดีพอสมควรฟังก์ชั่นการจับภาพประเภทหนึ่งที่ใช้โดย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');
};

สิ่งนี้จะจับภาพดั้งเดิมจำนวนมากที่มีให้โดย javascript แต่คุณสามารถเพิ่มได้มากขึ้นโดยการแก้ไขTYPESวัตถุ โปรดทราบว่าtypeof HTMLElementCollectionใน Safari จะรายงานfunctionแต่ประเภท (HTMLElementCollection) จะกลับมาobject


31

คุณอาจพบว่าฟังก์ชั่นต่อไปนี้มีประโยชน์:

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

หรือใน ES7 (แสดงความคิดเห็นหากการปรับปรุงเพิ่มเติม)

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

  return type.toLowerCase();
}

ผล:

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

ขอบคุณ @ Johnrees ที่แจ้งให้ฉันทราบถึง: ข้อผิดพลาดสัญญา generatorfunction


ขอบคุณสำหรับสิ่งนี้. นอกจากนี้ยังใช้งานได้กับวัตถุวันที่:typeOf(new Date())
เจมส์เออร์วิน

อ่าฉันลืมไปแล้ว - ขอบคุณมันรวมอยู่ด้วยแล้ว
Vix

1
และtypeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
2560

ฉันไม่สามารถรับคำตอบนี้ให้ทำงานใน typescript ให้ข้อผิดพลาด:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK

ไม่แน่ใจเกี่ยวกับ TypeScript คุณลองรุ่น ES7 แล้วหรือยัง? toString ไม่ได้ถูกประกาศอย่างชัดเจนอาจเป็นสาเหตุหรือไม่
Vix

15

นอกจากนี้เรายังสามารถเปลี่ยนตัวอย่างเล็กน้อยจากipr101

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

และโทรเป็น

"aaa".toType(); // 'string'

6
จดจำเด็ก ๆ การจัดการวัตถุฐาน JavaScript อาจทำให้เกิดความเข้ากันได้และปัญหาแปลก ๆ อื่น ๆ ลองใช้สิ่งนี้เท่าที่จำเป็นทั้งในห้องสมุดและโครงการขนาดใหญ่!
Alexander Craggs

9

ฟังก์ชั่นหนึ่งบรรทัด:

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

สิ่งนี้ให้ผลลัพธ์เช่นเดียวกับ jQuery.type()


3

คุณสามารถนำObject.prototype.toStringไปใช้กับวัตถุใด ๆ :

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]

นี้ทำงานได้ดีในเบราว์เซอร์ที่มีข้อยกเว้นของ IE - [object Object]เมื่อโทรนี้ในตัวแปรที่ได้รับจากหน้าต่างมันก็จะคายออกอีก


1
@ Xeon ฉันคิดว่าความเกลียดชังต่อ IE นั้นมากเกินไป IE9 เป็นเบราว์เซอร์ที่ดีกว่ารุ่นก่อนมาก
Aillyn

4
@pessimopoppotamus แต่ไม่มีใครที่จะอัปเดตเบราว์เซอร์เป็น IE9 ใช้ IE
Alex Turpin

1
IE 9 เป็นขั้นตอนในทิศทางที่ถูกต้อง IE 10 ดูดีทีเดียว บังเอิญข้อผิดพลาดที่ฉันกล่าวถึงคือการถดถอยใน IE 9 - พวกเขาแก้ไขใน IE 8
Andy E

3

2 ¢ของฉัน! จริงๆส่วนหนึ่งของเหตุผลที่ฉันโยนนี้ขึ้นที่นี่แม้จะมีรายชื่อยาวของคำตอบคือการให้เล็ก ๆ น้อย ๆการแก้ปัญหาชนิดและได้รับกลับมาฟีดในอนาคตเกี่ยวกับวิธีการขยายให้มีมากกว่าall in onereal types

ด้วยวิธีการแก้ปัญหาต่อไปนี้เป็นข้างต้นรวมทั้งคู่ของการแก้ปัญหาที่พบที่นี่เช่นเดียวกับการรวมการแก้ไขกลับค่าjQueryใน jQuery วัตถุที่กำหนดถ้ามี ฉันยังผนวกวิธีการไปยังต้นแบบวัตถุดั้งเดิม ฉันรู้ว่ามักจะเป็นข้อห้ามตามที่มันจะยุ่งเกี่ยวกับนามสกุลอื่น ๆ ดังกล่าว user bewareแต่ฉันออกจากที่ หากคุณไม่ชอบวิธีนี้เพียงคัดลอกฟังก์ชันพื้นฐานทุกที่ที่คุณต้องการและแทนที่ตัวแปรทั้งหมดthisด้วยพารามิเตอร์อาร์กิวเมนต์เพื่อส่งต่อ (เช่นอาร์กิวเมนต์ [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;
})();

จากนั้นใช้อย่างง่ายดายเช่น:

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

หมายเหตุ:มี 1 อาร์กิวเมนต์ที่สามารถผ่านได้ ถ้าเป็นบูtrueลแล้วผลตอบแทนจะเป็นตัวพิมพ์เล็กเสมอ

ตัวอย่างเพิ่มเติม:

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

หากคุณมีอะไรเพิ่มเติมที่อาจเป็นประโยชน์เช่นการกำหนดเมื่อวัตถุถูกสร้างขึ้นด้วยห้องสมุดอื่น (หมู่ Proto, Yui, Dojo, ฯลฯ ... ) โปรดแสดงความคิดเห็นหรือแก้ไขสิ่งนี้และทำให้มันเป็นมากกว่า ถูกต้องและแม่นยำ หรือหมุนไปที่GitHubฉันทำเพื่อมันและแจ้งให้เราทราบ คุณจะพบลิงค์ด่วนไปยังไฟล์ cdn min ที่นั่น


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();
}

ในการทดสอบเบื้องต้นของฉันนี้ทำงานได้ค่อนข้างดี กรณีแรกจะพิมพ์ชื่อของวัตถุใด ๆ ที่สร้างขึ้นด้วย "ใหม่" และกรณีที่ 2 ควรจับทุกอย่างอื่น

ฉันใช้(8, -1)เพราะฉันสมมติว่าผลลัพธ์จะเริ่มต้นด้วย[objectและจบด้วยเสมอ]แต่ฉันไม่แน่ใจว่าเป็นเรื่องจริงในทุกสถานการณ์


2

ผมคิดว่าวิธีการแก้ปัญหาสากลมากที่สุดที่นี่ - คือการตรวจสอบundefinedและเป็นครั้งแรกแล้วก็โทรnullconstructor.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'

เท่าที่ฉันรู้ - ค่า.constructor.nameเสมอประกอบด้วยสตริง สำหรับ undefined / null เรามีสตริงที่เหมาะสมซึ่งส่งคืนโดยฟังก์ชัน
Arsenowitch

ขอบคุณ - ลบความคิดเห็นของฉัน ...
Bergi

เนื่องจากเป็นสถานที่ให้บริการรับมรดกแบ่งนี้สำหรับวัตถุที่สร้างขึ้นด้วยconstructor Object.create(null)ง่ายพอที่จะแก้ไข แต่จะเรียกมันว่าอะไร? "[วัตถุ null]"?
traktor53

0

typeof เงื่อนไขที่ใช้ในการตรวจสอบประเภทตัวแปรถ้าคุณกำลังตรวจสอบประเภทตัวแปรในเงื่อนไข if-else เช่น

if(typeof Varaible_Name "undefined")
{

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