My idea behind this would be to actually use a custom field call within the shortcode itself, then add a filter [shortcode_atts_{$shortcode}](https://developer.wordpress.org/reference/hooks/shortcode_atts_shortcode/) to process said custom field and add it to class to return it.

`{shortcode}` in this case would be `caption` since that's what we're building off.

    add_filter( 'caption_custom_field_class', 'shortcode_atts_caption' );

    function caption_custom_field_class( $out, $pairs, $atts, $shortcode ) {
        if ( !empty( $atts['custom_field'] ) && get_post_meta( sanitize_text( $atts['custom_field'] ), $atts['id'], true ) ) {
                 $out['class'] .= ( !empty( $out['class'] ) ? ' ' : '' ) . get_post_meta( sanitize_text( $atts['custom_field'] ), $atts['id'], true );
            }
        }
        return $out;
    }


So, what we're doing is utilizing another variable field called 'custom_field' to pass our custom field name.

    [caption custom_field="mycustomfieldname"]<img src=".... />[/caption]

The function checks if `custom_field` shortcode attribute is empty or not, and checks if you can retrieve any post meta based on the passed ID.  If those are both true, then adds it to the `$out['class']` with a space depending on whether or not `$out['class']` is empty.

I may need to update this answer, because I started writing here, and I'm going to try it in a live environment.