JavaScript loader без define

в 7:23, , рубрики: javascript, jquery, loader, Веб-разработка, метки: ,

Привет!

Всем известно решение задачи загрузки скриптов.
Например Curl.JS, Require.JS, + популярные frameworks умеют это тоже.

Вот и недавно была статья про это от уважаемого azproduction.

Но все же интересно, есть ли альтернативы AJS, CJS, etc, учитывая ссылку.

Что если есть возможность загрузить JavaScript забыв про обыденные «мелочи» типа:

define( function(){
  return MyBeautifulObject;
});

Пример для самых нетерпеливых.

Далее небольшая заметка о том как это работает.

Началось всё вот с такой бесполезной конструкции:


var fn = ( function(){
    var ffn = Function.apply( this, arguments ) ;
    var init = this;
    return ( function( ffn ){
        alert( '1: '+init )
        alert( '2: '+this )
        var zfn = function(){
            alert( '3: '+init )
            alert( '4: '+this )
            return ffn.apply( init, arguments );
        };
        return zfn;
    }).call( init, ffn );
} ).apply( 456, [ 'a', 'b', ' alert(a+b); alert( "this: " + this ); return this; ' ] );

alert( 'result: ' + fn.call( 123, 5, 5 ) );​

JS Fiddle

Потом, постепенно трансформировалось в это:


var fns = ( function(){
    var fn = new Function( 'with( this ){ return ' + Function.apply( this, arguments ).toString() + '; }' );
    fn = fn.call( this );
    return function(){
        return fn.apply( this, arguments );
    };
} ).apply( { bb: 789 }, [ 'a', 'b', ' alert(a+b); alert(this); alert( "bb: " + bb ); return bb; ' ] );
    
alert( 'result: ' + fns.call( 123, 5, 5 ) );

JS Fiddle

Как видно из кода, через with определяется ссылка на примесь из переданного в scope объекта: bb.

И, наконец, финальный код загрузчика скриптов в виде метода для jQuery:

(function( jQuery, undefined ){
jQuery.loadSubScript = function( url, scope, thisName, returnCallback ){
    // если не передан scope сделаем его глобальным
    var scope = scope || window;
    // аналогично
    var thisName = thisName || window;
    
    $.ajax( {
          url: url
        , type: 'GET'
        , dataType: 'text'
        , success: function( data ){
            try{
                
                // создаем полученный код, не забываем про with
                var fns = new Function( 

                         'with( this ){ return ' 
                                + Function.apply( null, [ data ] )
                                + '; }' 

                );

                // инициализируем замыкание с with
                fns = fns.call( scope );
                // инициализируем замыкание с this
                var turn = fns.call( thisName );
                // возвращаем результат в callback, если он есть
                returnCallback && returnCallback( turn );
                
            }catch(e){ alert(e); }
        }
        , error: function( jqXHR, textStatus, errorThrown ){ 
                alert('Loader Error:n' + errorThrown ); 
        }
    } );        
};
})( jQuery );

Тот же самый пример для Терпеливых.

Что можно?

  • Просто загрузить скрипт в глобальный объект: window.
  • Загрузить скрипт с «подменой» scope: window + YourOwnScopObject.
  • Загрузить скрипт с подменой ему this.
  • Вернуть что-нибудь в callback, например, просто написав в вашем загружаемом скрипте return.
  • Избавиться от define().

Конечно, если что нибудь будет нести var, то оно будет внутри созданного замыкания. Без var, как и должно быть — попадет в window. То, что является методами переданного в scope объекта будет доступно без точки. С this — всё как обычно, в случае его подмены.

Пользуйтесь на здоровье!

Ссылка на GIT

Можно рассказать коллеге.

Best Regards!

PS: Не знаю какую лицензию выбрать, наверное MIT + GPLv3.

Автор: wentout


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js