Cách tốt hơn để có được loại biến Javascript?


260

Có cách nào tốt hơn để có được loại biến trong JS hơn typeofkhông? Nó hoạt động tốt khi bạn làm:

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

Nhưng nó vô dụng khi bạn thử:

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

Tôi biết instanceof, nhưng điều này đòi hỏi bạn phải biết loại trước.

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

Có cách nào tốt hơn?


1
FYI, sự cố typeof new RegExp(/./); // "function"trong Chrome dường như đã được khắc phục trong Chrome 14.
user113716


Cảnh báo: Nếu bạn đang thu nhỏ JS của mình và bạn đang sử dụng 'đối tượng tùy chỉnh', chẳng hạn như các lớp bản thảo, một số câu trả lời dưới đây sẽ cung cấp cho bạn tên hàm bị xáo trộn, nthay vì tên gốc dự kiến. ví dụ: constructor.namecó thể cung cấp cho bạn nthay vì tên đầy đủ dự kiến
Simon_Weaver

Câu trả lời:


221

Angus Croll gần đây đã viết một bài đăng blog thú vị về điều này -

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

Anh ta trải qua những ưu và nhược điểm của các phương thức khác nhau sau đó định nghĩa một phương thức mới 'toType' -

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

3
Lưu ý bên thú vị ở đây - điều này có khả năng phá vỡ nơi một đối tượng "máy chủ" được chuyển đến chức năng. ActiveXObjectVí dụ Internet Explorer .
Andy E

Nếu bạn đã tạo loại đối tượng của riêng mình (kế thừa nguyên mẫu), làm thế nào bạn có thể khiến nó trả về một giá trị cụ thể hơn "đối tượng"? Điều này cũng được nêu ra như một vấn đề ở đây , nhưng không bao giờ được giải quyết.
EleventyOne

1
Nếu cách đặt tên của bạn chứa: hoặc _ bạn có thể mở rộng regex này thànhmatch(/\s([a-z,_,:,A-Z]+)/)
masi

6
Regex cũng cần bao gồm các số cho những thứ như Uint8Array. Thay vào đó, hãy xem xét tổng quát hơn match(/\s([^\]]+)/)nên xử lý bất cứ điều gì.
Lily Finley

2
Tôi khuyên bạn nên sử dụng \wtoán tử từ thay vì đào
kaiser

51

Bạn có thể thử sử dụng constructor.name.

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

Như với mọi thứ JavaScript, cuối cùng ai đó sẽ luôn luôn chỉ ra rằng điều này là xấu xa, vì vậy đây là một liên kết đến một câu trả lời bao gồm điều này khá tốt.

Một cách khác là sử dụng Object.prototype.toString.call

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

10
namelà một phần mở rộng cho ECMAScript và sẽ không hoạt động trong tất cả các trình duyệt.
Andy E

16
Trả lời bỏ phiếu do xác nhận nghi ngờ rằng mọi thứ được thực hiện trong javascript là bằng cách nào đó xấu xa.
liljoshu

1
EVIL: nếu bạn thu nhỏ mã của mình thì constructor.namecó thể cuối cùng sẽ giống như ntên đối tượng bạn mong đợi. Vì vậy, xem ra cho điều đó - đặc biệt là nếu bạn chỉ giảm thiểu trong sản xuất.
Simon_Weaver

nullundefinedkhông may không có nhà xây dựng.
Andrew Grimm

JavaScript là xấu. Và ngớ ngẩn. Thực tế là câu hỏi này không có câu trả lời tốt là bằng chứng sống cho điều đó.
user2173353 6/11/18

38

Một chức năng chụp loại khá hợp lý là chức năng được YUI3 sử dụng :

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

Điều này nắm bắt nhiều nguyên thủy được cung cấp bởi javascript, nhưng bạn luôn có thể thêm nhiều hơn bằng cách sửa đổi TYPESđối tượng. Lưu ý rằng typeof HTMLElementCollectiontrong Safari sẽ báo cáo function, nhưng loại (HTMLEuityCollection) sẽ trả vềobject


31

Bạn có thể thấy chức năng sau đây hữu ích:

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

Hoặc trong ES7 (nhận xét nếu cải tiến thêm)

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

  return type.toLowerCase();
}

Các kết quả:

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

Cảm ơn @johnrees đã thông báo cho tôi về: lỗi, lời hứa, chức năng tạo


Cảm ơn vì điều đó. Nó cũng hoạt động cho các đối tượng ngày:typeOf(new Date())
James Irwin

Ah, tôi đã quên điều đó - cảm ơn bạn, giờ nó đã được bao gồm.
Vix

1
typeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
johnrees

Tôi không thể có câu trả lời này để làm việc trong bản thảo. Đưa ra lỗi:[ts] Cannot redeclare block-scoped variable 'toString'
DauleDK

Không chắc chắn về TypeScript. Bạn đã thử phiên bản ES7 chưa? toString không được tuyên bố rõ ràng có thể là nguyên nhân?
Vix

15

Ngoài ra chúng ta có thể thay đổi một ví dụ nhỏ từ ipr101

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

và gọi như

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

6
Hãy nhớ rằng trẻ em, thao túng các đối tượng cơ sở JavaScript có thể dẫn đến khả năng tương thích và các vấn đề kỳ lạ khác. Cố gắng sử dụng một cách tiết kiệm trong cả thư viện và các dự án lớn hơn!
Alexander Craggie

9

chức năng một dòng:

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

điều này cho kết quả tương tự như jQuery.type()


3

Bạn có thể áp dụng Object.prototype.toStringcho bất kỳ đối tượng:

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]

Điều này hoạt động tốt trong tất cả các trình duyệt, ngoại trừ IE - khi gọi điều này trên một biến có được từ một cửa sổ khác, nó sẽ chỉ nhổ ra [object Object].


1
@Xeon Tôi nghĩ rằng sự thù hận với IE là quá nhiều. IE9 là một trình duyệt tốt hơn nhiều so với người tiền nhiệm của nó.
Aillyn

4
@pessimopoppotamus nhưng không ai cập nhật trình duyệt của họ lên IE9 sử dụng IE.
Alex Turpin

1
IE 9 là một bước đi đúng hướng, IE 10 có vẻ khá tốt. Ngẫu nhiên, lỗi tôi đã đề cập là một hồi quy trong IE 9 - họ đã sửa nó trong IE 8.
Andy E

3

2 của tôi! Thực sự, một phần lý do tôi ném nó lên đây, mặc dù có một danh sách dài các câu trả lời, là để cung cấp một all in onegiải pháp loại nhỏ hơn và nhận lại một số phản hồi trong tương lai về cách mở rộng nó để bao gồm nhiều hơn real types.

Với giải pháp sau đây, như đã nói ở trên, tôi đã kết hợp một vài giải pháp được tìm thấy ở đây, cũng như kết hợp một bản sửa lỗi để trả về giá trị của jQueryđối tượng được xác định jQuery nếu có . Tôi cũng nối thêm phương thức vào nguyên mẫu Object gốc. Tôi biết điều đó thường là điều cấm kỵ, vì nó có thể can thiệp vào các phần mở rộng khác như vậy, nhưng tôi để điều đó user beware. Nếu bạn không thích cách làm này, chỉ cần sao chép hàm cơ sở ở bất cứ đâu bạn thích và thay thế tất cả các biến thisbằng tham số đối số để truyền vào (chẳng hạn như đối số [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;
})();

Sau đó, chỉ cần sử dụng một cách dễ dàng, như vậy:

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

Lưu ý: Có 1 đối số có thể qua được. Nếu là bool của true, thì lợi nhuận sẽ luôn ở dạng chữ thường .

Thêm ví dụ:

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

Nếu bạn có bất cứ điều gì để thêm có thể hữu ích, chẳng hạn như xác định khi nào một đối tượng được tạo bằng thư viện khác (Moo, Proto, Yui, Dojo, v.v.), vui lòng bình luận hoặc chỉnh sửa nội dung này và giữ cho nó sẽ được nhiều hơn chính xác và chính xác. HOẶC lăn qua cái GitHubtôi làm cho nó và cho tôi biết. Bạn cũng sẽ tìm thấy một liên kết nhanh đến một tệp 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();
}

Trong các thử nghiệm sơ bộ của tôi, điều này đang hoạt động khá tốt. Trường hợp đầu tiên sẽ in tên của bất kỳ đối tượng nào được tạo bằng "mới" và trường hợp thứ 2 sẽ bắt mọi thứ khác.

Tôi đang sử dụng (8, -1)vì tôi cho rằng kết quả sẽ luôn bắt đầu [objectvà kết thúc ]nhưng tôi không chắc chắn điều đó đúng trong mọi kịch bản.


2

Tôi đoán giải pháp phổ biến nhất ở đây - là kiểm tra undefinednullđầu tiên, sau đó chỉ cần gọi 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'

Theo tôi biết - .constructor.nameluôn luôn chứa giá trị chuỗi. Đối với không xác định / null, chúng ta có các chuỗi thích hợp được trả về bởi hàm.
Arsenowitch

Cảm ơn - xóa bình luận của tôi
mai

constructorlà một thuộc tính được thừa kế, điều này phá vỡ các đối tượng được tạo bằng Object.create(null). Đủ đơn giản để sửa chữa, nhưng gọi nó là gì? "[đối tượng null]"?
traktor53

0

typeof điều kiện được sử dụng để kiểm tra loại biến, nếu bạn đang kiểm tra loại biến trong điều kiện if-other, vd

if(typeof Varaible_Name "undefined")
{

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