How should I go about registering JavaScript that isn’t a file? [duplicate]

You can hook the wp_footer action to output arbitrary JavaScript. This the same action that WordPress uses to output footer enqueued scripts.

Here’s an example that encapsulates the shortcode, data, and script output in a class:

<?php
/*
Plugin Name: WPD_Example
*/

class WPD_Example_Plugin {

    public $data = array();

    function __construct() {
        add_shortcode( 'wpd_example', array( $this, 'shortcode_func' ) );
        add_action( 'wp_footer', array( $this, 'footer_scripts' ), 9999 );
    }

    function shortcode_func( $atts ) {
        $atts = shortcode_atts( array(
            'title' => 'title'
        ), $atts, 'wpd_example' );
        $id = uniqid();
        $this->data[$id] = $atts['title'];
        wp_enqueue_script( 'jquery' );
        return '<div data-id="' . $id . '">' . $atts['title'] . '</div>';
    }

    function footer_scripts() {
        if( !empty( $this->data ) ){
            ?>
            <script type="text/javascript">
                if ( undefined !== window.jQuery ) {
                    jQuery(document).ready(function($) {
                        var myData = <?php echo wp_json_encode( $this->data ); ?>;
                        $.each( myData, function( id, title ) {
                            console.log( id + ' : ' + title );
                        });
                    });
                }
            </script>
            <?php
        }
    }

}

$wpd_plugin = new WPD_Example_Plugin;

EDIT

Here’s another example that’s similar to above, but enqueues a script and passes data via wp_localize_script rather than printing js directly to the page. You can access any of this data from the enqueued script in the wpdScriptData js object. Read the comments below this answer for an explanation as to why this method is safer.

<?php
/*
Plugin Name: WPD_Example
*/

class WPD_Example_Plugin {

    public $data = array();

    function __construct() {
        add_shortcode( 'wpd_example', array( $this, 'shortcode_func' ) );
        add_action( 'wp_footer', array( $this, 'footer_scripts' ), 0 );
    }

    function shortcode_func( $atts ) {
        $atts = shortcode_atts( array(
            'title' => 'title'
        ), $atts, 'wpd_example' );
        $id = uniqid();
        $this->data[$id] = $atts['title'];
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'wpd-script', plugins_url( '/js/script.js', __FILE__ ), array( 'jquery' ), null, true );
        return '<div data-id="' . $id . '">' . $atts['title'] . '</div>';
    }

    function footer_scripts() {
        if( !empty( $this->data ) ){
            wp_localize_script(
                'wpd-script',
                'wpdScriptData',
                $this->data
            );
        }
    }

}

$wpd_plugin = new WPD_Example_Plugin;