بررسی کنید که آیا یک متغیر در جاوااسکریپت رشته است


چگونه میتوانم تعیین کنم که یک متغیر در جاوااسکریپت رشته است یا چیز دیگری؟

پاسخ شماره ۱

این چیزی است که برای من جواب میدهد:

if (typeof myVar === 'string' || myVar instanceof String)
// it's a string
else
// it's something else

// Test this approach:

let isString = value => typeof value === 'string' || value instanceof String;

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

پاسخ شماره ۲

میتوانید از عملگر typeof استفاده کنید:

var booleanValue = true;
var numericalValue = 354;
var stringValue = "This is a String";
var stringObject = new String("This is a String Object");
console.log(typeof booleanValue) // displays "boolean"
console.log(typeof numericalValue) // displays "number"
console.log(typeof stringValue) // displays "string"
console.log(typeof stringObject) // displays "object"

مثالی از این صفحه وب. (هرچند مثال کمی تغییر داده شده بود).

این در مورد رشتههایی که با new String() ساخته شدهاند، آنطور که انتظار میرود کار نخواهد کرد، اما این به ندرت استفاده میشود و توصیه نمیشود[1][2]. برای نحوه رسیدگی به اینها، به پاسخهای دیگر مراجعه کنید، اگر میخواهید.

// Test this approach:

let isString = value => typeof value === 'string';

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

  1. راهنمای سبک جاوااسکریپت گوگل میگوید که هرگز از بستهبندیهای شیء اولیه استفاده نکنید.
  2. داگلاس کراکفورد توصیه کرد که بستههای شیء اولیه منسوخ شوند.

پاسخ شماره ۳

function isString(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

یا، به صورت درونخطی (من برای این یک تنظیم UltiSnip دارم):

Object.prototype.toString.call(myVar) === "[object String]"

برای اطلاع شما، پاسخ پابلو سانتا کروز اشتباه است، زیرا typeof new String("string") برابر با object است

پاسخ DRAX دقیق و عملی است و باید پاسخ درست باشد (چون پابلو سانتا کروز مطمئناً نادرست است و من هم نمیخواهم با رأی اکثریت مخالفت کنم.)

با این حال، این پاسخ نیز قطعاً درست است و در واقع بهترین پاسخ است (به جز شاید پیشنهاد استفاده از lodash/underscore). سلب مسئولیت: من در کدپایه lodash 4 مشارکت داشتهام.

پاسخ اصلی من (که واضح است بسیاری از افراد متوجه آن نشدند) به شرح زیر است:

من این را از underscore.js تبدیل کد کردهام:

['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'].forEach( 
    function(name) { 
        window['is' + name] = function(obj) {
              return toString.call(obj) == '[object ' + name + ']';
    }; 
});

این isString، isNumber و غیره را تعریف خواهد کرد.


در Node.js، این میتواند به عنوان یک ماژول پیادهسازی شود:

module.exports = [
  'Arguments',
  'Function', 
  'String', 
  'Number', 
  'Date', 
  'RegExp'
].reduce( (obj, name) => {
  obj[ 'is' + name ] = x => toString.call(x) == '[object ' + name + ']';
  return obj;
}, {});

[ویرایش]: Object.prototype.toString.call(x) همچنین برای تمایز بین توابع و توابع غیرهمزمان (async) کار میکند:

const fn1 = () => new Promise((resolve, reject) => setTimeout(() => resolve({}), 1000))
const fn2 = async () => ({})

console.log('fn1', Object.prototype.toString.call(fn1))
console.log('fn2', Object.prototype.toString.call(fn2))

// Test this approach:

function isString(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

let falseCases = [
  [ 'null', null ],
  [ 'undefined', undefined ],
  [ 'object', { a: 1, b: 2 } ],
  [ 'array', [ 1, 2, 3 ] ],
  [ 'number', 123 ],
  [ 'zero', 0 ],
  [ 'RegExp', new RegExp('hello') ],
  [ 'number with valueOf returning string', Object.assign(10, { valueOf: () => 'abc' }) ],
  [ 'object pretending to be string', { constructor: String } ]
];
let trueCases = [
  [ 'empty literal string', '' ],
  [ 'unicode string literal', String.fromCharCode(10000) ],
  [ 'empty boxed string', new String('') ],
  [ 'unicode boxed string', new String(String.fromCharCode(10000)) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: Array }) ],
  [ 'string with overwritten "toString"', Object.assign('hi', { toString: 123 }) ],
  [ 'string with overwritten "valueOf"', Object.assign('hi', { valueOf: 123 }) ],
  [ 'string with overwritten "constructor"', Object.assign('hi', { constructor: RegExp }) ],
  [ 'proxied string', new Proxy(new String('hello'), {}) ],
];

console.log('NEGATIVE TESTS:');
for (let [ name, val ] of falseCases) {
  console.log(`Test ${name}:\n  Expect: false\n  Got:    ${isString(val)}`); 
}

console.log('\nPOSITIVE TESTS:');
for (let [ name, val ] of trueCases) {
  console.log(`Test ${name}:\n  Expect: true\n  Got:    ${isString(val)}`); 
}

پاسخ شماره ۴

من استفاده از توابع داخلی jQuery یا lodash/Underscore را توصیه میکنم. استفاده از آنها سادهتر است و خواندنشان آسانتر است.

هر کدام از این توابع حالت ذکر شده توسط DRAX را مدیریت میکنند... یعنی، آنها هر دو بررسی میکنند که (A) متغیر یک رشته ثابت است یا (B) نمونهای از شیء String است. در هر صورت، این توابع به درستی مقدار را به عنوان یک رشته شناسایی میکنند.

lodash / Underscore.js

if(_.isString(myVar))
   //it's a string
else
   //it's something else

جیکوئری

if($.type(myVar) === "string")
   //it's a string
else
   //it's something else

برای جزئیات بیشتر به مستندات lodash برای _.isString() مراجعه کنید.

برای جزئیات بیشتر به مستندات jQuery برای $.type() مراجعه کنید.

پاسخ شماره ۵

ویرایش: روش فعلی برای انجام این کار typeof value === 'string' است. برای مثال:

const str = 'hello';
if (typeof str === 'string') { ... }

زیر از نسخهٔ ۴ نود منسوخ شده است.

اگر در محیط node.js کار میکنید، میتوانید بهسادگی از تابع داخلی isString در utils استفاده کنید.

const util = require('util');
if (util.isString(myVar)) {}

پاسخ شماره ۶

function isString (obj) {
  return (Object.prototype.toString.call(obj) === '[object String]');
}

من آن را اینجا دیدم:

http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

پاسخ شماره ۷

بهترین روش:

var s = 'String';
var a = [1,2,3];
var o = {key: 'val'};

(s.constructor === String) && console.log('its a string');
(a.constructor === Array) && console.log('its an array');
(o.constructor === Object) && console.log('its an object');
(o.constructor === Number || s.constructor === Boolean) && console.log('this won\'t run');

هر یک از اینها توسط تابع کلاس مناسب خود ساخته شدهاند، مانند «new Object()» و غیره.

همچنین، نوعگیری اردکی: «اگر شبیه اردک به نظر میرسد، مثل اردک راه میرود و بوی اردک میدهد - حتماً یک آرایه است» به این معنا که ویژگیهای آن را بررسی کنید.

امیدوارم این کمک کند.

ویرایش؛ ۱۲/۰۵/۲۰۱۶

به یاد داشته باشید، شما همیشه میتوانید از ترکیب روشها نیز استفاده کنید. در اینجا مثالی از استفاده از نقشه درونخطی اقدامات با typeof آورده شده است:

var type = { 'number': Math.sqrt.bind(Math), ... }[ typeof datum ];

در اینجا یک مثال 'دنیا واقعی' تر از استفاده از نقشههای درون خطی آورده شده است:

function is(datum) {
    var isnt = !{ null: true, undefined: true, '': true, false: false, 0: false }[ datum ];
    return !isnt;
}
console.log( is(0), is(false), is(undefined), ... );  // >> true true false

این تابع از [سفارشی] «تبدیل نوع» -- یا بهتر است بگوییم «نگاشت نوع/مقدار» -- استفاده میکند تا بفهمد آیا یک متغیر واقعاً «وجود دارد» یا نه. حالا میتوانید آن موی مزاحم را بین null و 0 تقسیم کنید!

بارها حتی به نوع آن اهمیت نمیدهید. راه دیگری برای دور زدن تایپینگ، ترکیب مجموعههای نوع اردک است:

this.id = "998";  // use a number or a string-equivalent
function get(id) {
    if (!id || !id.toString) return;
    if (id.toString() === this.id.toString()) http( id || +this.id );
    // if (+id === +this.id) ...;
}

هر دو Number.prototype و String.prototype دارای .toString() method هستند. شما فقط مطمئن شدید که معادل رشتهای عدد یکسان باشد، و سپس مطمئن شدید که آن را به عنوان یک Number به تابع http پاس دادهاید. به عبارت دیگر، ما حتی اهمیتی ندادیم که نوع آن چه باشد.

امیدوارم این به تو چیز بیشتری برای کار کردن بدهد :)

پاسخ شماره ۸

من صادقانه نمیتوانم بفهمم چرا کسی به سادگی از typeof در این مورد استفاده نکند:

if (typeof str === 'string') {
  return 42;
}

بله، این در برابر رشتههای پوشیده شده با شیء (مثلاً new String('foo')) شکست خواهد خورد، اما این روش به طور گسترده به عنوان یک روش نادرست تلقی میشود و اکثر ابزارهای توسعه مدرن احتمالاً استفاده از آن را منع میکنند. (اگر یکی دیدید، فقط آن را اصلاح کنید!)

ترفند Object.prototype.toString چیزی است که همه توسعهدهندگان فرانتاند روزی در طول حرفه خود مرتکب آن شدهاند، اما اجازه ندهید ظاهر هوشمندانهاش شما را فریب دهد: به محض اینکه چیزی پروتوتایپ Object را دستکاری کند، خراب خواهد شد:

const isString = thing => Object.prototype.toString.call(thing) === '[object String]';

console.log(isString('foo'));

Object.prototype.toString = () => 42;

console.log(isString('foo'));

پاسخ شماره ۹

عملکرد

امروز 2020.09.17 من آزمایشها را روی MacOs HighSierra 10.13.6 در Chrome نسخه 85، Safari نسخه 13.1.2 و Firefox نسخه 80 برای راهحلهای انتخابشده انجام میدهم.

نتایج

برای تمام مرورگرها (و هر دو مورد تست)

  • راهحلهای typeof||instanceof (A, I) و x===x+'' (H) سریع/سریعترین هستند
  • راهحل _.isString (کتابخانه lodash) متوسط/سریع است
  • راهحلهای B و K کندترین هستند

بهروزرسانی: 2020.11.28 من نتایج ستون x=123 Chrome را بهروزرسانی میکنم - برای راهحل I احتمالاً قبلاً یک مقدار خطا وجود داشت (=69M خیلی کم) - من از Chrome 86.0 برای تکرار آزمونها استفاده میکنم.

جزئیات

من دو مورد آزمایشی برای راهحلهای A B C D E F G H I J K L انجام میدهم

  • وقتی متغیر رشته است - میتوانید آن را اینجا اجرا کنید
  • وقتی متغیر رشته نیست - میتوانید آن را اینجا اجرا کنید

قطعه زیر تفاوتهای بین راهحلها را نشان میدهد

// https://stackoverflow.com/a/9436948/860099
function A(x) {
  return (typeof x == 'string') || (x instanceof String)
}

// https://stackoverflow.com/a/17772086/860099
function B(x) {
  return Object.prototype.toString.call(x) === "[object String]"
}

// https://stackoverflow.com/a/20958909/860099
function C(x) {
  return _.isString(x);
}

// https://stackoverflow.com/a/20958909/860099
function D(x) {
  return $.type(x) === "string";
}

// https://stackoverflow.com/a/16215800/860099
function E(x) {
  return x?.constructor === String;
}

// https://stackoverflow.com/a/42493631/860099
function F(x){
  return x?.charAt != null
}


// https://stackoverflow.com/a/57443488/860099
function G(x){
  return String(x) === x
}

// https://stackoverflow.com/a/19057360/860099
function H(x){
  return x === x + ''
}

// https://stackoverflow.com/a/4059166/860099
function I(x) {
  return typeof x == 'string'
}

// https://stackoverflow.com/a/28722301/860099
function J(x){
  return x === x?.toString()
}

// https://stackoverflow.com/a/58892465/860099
function K(x){
  return x && typeof x.valueOf() === "string"
}

// https://stackoverflow.com/a/9436948/860099
function L(x) {
  return x instanceof String
}

// ------------------
//     PRESENTATION
// ------------------

console.log('Solutions results for different inputs \n\n');
console.log("'abc' Str  ''  ' ' '1' '0'  1   0   {} [] true false null undef");

let tests = [ 'abc', new String("abc"),'',' ','1','0',1,0,{},[],true,false,null,undefined];

[A,B,C,D,E,F,G,H,I,J,K,L].map(f=> {  
console.log(
  `${f.name}   ` + tests.map(v=> (1*!!f(v)) ).join`   `
)})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>


This shippet only presents functions used in performance tests - it not perform tests itself!

و در اینجا نتایج نمونه برای کروم آمده است

پاسخ شماره ۱۰

این یک مثال عالی از دلیل اهمیت عملکرد است:

انجام کاری به سادگی یک آزمایش برای یک رشته میتواند هزینهبر باشد اگر به درستی انجام نشود.

برای مثال، اگر میخواستم یک تابع بنویسم تا بررسی کنم چیزی رشته است یا نه، میتوانستم آن را به یکی از دو روش انجام دهم:

1) const isString=str => (Object.prototype.toString.call(str) === '[object String]');

۲) const isString=str => ((typeof str === 'string') || (str instanceof String));

هر دو مورد نسبتاً ساده هستند، پس چه چیزی میتواند بر عملکرد تأثیر بگذارد؟ به طور کلی، فراخوانی توابع میتواند هزینهبر باشد، به خصوص اگر ندانید داخل آنها چه میگذرد. در مثال اول، یک فراخوانی تابع به متد toString شیء وجود دارد. در مثال دوم، هیچ فراخوانی تابعی وجود ندارد، زیرا typeof و instanceof عملگر هستند. عملگرها به طور قابل توجهی سریعتر از فراخوانی توابع هستند.

وقتی عملکرد آزمایش میشود، مثال ۱، ۷۹٪ کندتر از مثال ۲ است!

تستها را ببینید: https://jsperf.com/isstringtype

پاسخ شماره ۱۱

من دوست دارم از این راهحل ساده استفاده کنم:

var myString = "test";
if(myString.constructor === String)
{
     //It's a string
}

پاسخ شماره ۱۲

من این تکنیک ساده را مفید میدانم برای بررسی نوع تایپ String -

String(x) === x // true, if x is a string
                // false in every other case

const test = x =>
  console.assert
    ( String(x) === x
    , `not a string: ${x}`
    )

test("some string")
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  // assertion failed
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

همین تکنیک برای عدد هم کار میکند -

Number(x) === x // true, if x is a number
                // false in every other case

const test = x =>
  console.assert
    ( Number(x) === x
    , `not a number: ${x}`
    )

test("some string") // assertion failed
test(123)           
test(0)             
test(/some regex/)  // assertion failed
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

و برای RegExp -

RegExp(x) === x // true, if x is a regexp
                // false in every other case

const test = x =>
  console.assert
    ( RegExp(x) === x
    , `not a regexp: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  
test([ 5, 6 ])      // assertion failed
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

همین برای شیء -

Object(x) === x // true, if x is an object
                // false in every other case

توجه داشته باشید، عبارات با قاعده، آرایهها و توابع نیز به عنوان اشیاء در نظر گرفته میشوند.

const test = x =>
  console.assert
    ( Object(x) === x
    , `not an object: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  
test([ 5, 6 ])      
test({ a: 1 })      
test(x => x + 1)    

اما، بررسی آرایه کمی متفاوت است -

Array.isArray(x) === x // true, if x is an array
                       // false in every other case

const test = x =>
  console.assert
    ( Array.isArray(x)
    , `not an array: ${x}`
    )

test("some string") // assertion failed
test(123)           // assertion failed
test(0)             // assertion failed
test(/some regex/)  // assertion failed
test([ 5, 6 ])      
test({ a: 1 })      // assertion failed
test(x => x + 1)    // assertion failed

با این حال، این تکنیک برای توابع کار نمیکند -

Function(x) === x // always false

برای @Faither -

const fmt = JSON.stringify

function test1() {
  const a = "1"
  const b = 1
  console.log(`Number(${fmt(a)}) === ${fmt(b)}`, Number(a) === b) // true
}

function test2() {
  const a = "1"
  const b = 1
  console.log(`Number.isInteger(${fmt(a)})`, Number.isInteger(a)) // false
  console.log(`Number.isInteger(${fmt(b)})`, Number.isInteger(b)) // true
}

function test3() {
  name = 1 // global name will always be a string
  console.log(fmt(name)) // "1"
  console.log(`String(${fmt(name)}) === ${fmt(name)}`, String(name) === name) // true
}

function test4() {
  const name = 1 // local name 
  console.log(fmt(name)) // 1
  console.log(`String(${fmt(name)}) === ${fmt(name)}`, String(name) === name) // false
}

test1(); test2(); test3(); test4()

پاسخ شماره ۱۳

if (s && typeof s.valueOf() === "string") {
  // s is a string
}

برای هر دو رشته ثابت let s='blah' و رشتههای شیء let s=new String('blah') کار میکند

پاسخ شماره ۱۴

برداشته شده از lodash:

function isString(val) {
   return typeof val === 'string' || ((!!val && typeof val === 'object') && Object.prototype.toString.call(val) === '[object String]');
}

console.log(isString('hello world!')); // true
console.log(isString(new String('hello world'))); // true

پاسخ شماره ۱۵

یک روش ساده و سریع برای آزمایش میتواند استفاده از ویژگی نام سازنده باشد.

let x = "abc";
console.log(x.constructor.name === "String"); // true

let y = new String('abc');
console.log(y.constructor.name === "String"); // true

عملکرد