Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Jin: И снова о наследовании (https://javascript.ru/forum/project/40210-jin-i-snova-o-nasledovanii.html)

tenshi 28.07.2013 02:41

Jin: И снова о наследовании
 
Зацените крутую либу реализующую новый подход к организации кода)

Фичи:
1. множественное виртуальное наследование
2. прямой быстрый доступ к любому методу предка
3. автоматическое объявление классов и пространств имён
4. объявление методов и примешивание в произвольном порядке
5. автоматическое выявление конфликтов на уровне методов
6. типизированные свойства
7. глобально именуемые функции


Объявим простенький синглтон:

$jin.method( function URI_encode( text ){
    return encodeURIComponent( text )
})

$jin.method( function URI_decode( text ){
    return decodeURIComponent( text )
})


Данный код примерно эквивалентен следующему:

var URI = URI || {}

URI.encode = function URI_encode( text ){
    return encodeURIComponent( text )
}

URI.decode = function URI_decode( text ){
    return decodeURIComponent( text )
}


Как видно, мы избавились от необходимости дублировать имя функции при объявлении (что полезно при дебаге, но зачастую лениво), а также не беспокоимся о том, чтобы нужный неймспейс существовал до объявления методов. Символ подчёркивания, как видно, заменяется на точку. Кроме того двойное подчёркивание заменяется на ".prototype.", что будет продемонстрировано ниже.

Теперь создадим простейший класс:

$jin.mixin( '$jin_klass', 'Man' )
$jin.property( 'Man__name', String )


Тут мы примешали к пространству имён Man поведение "$jin_klass", что превращает "Man" в фабрику объектов. После чего определяем свойство "name", которое всегда является строкой. Инстанцирование происходит простым вызовом:

var Anonymous = Man()

При этом можно передать хэш со значениями свойств:

var John = Man({ name: 'John' })

Попытка передать значение для несуществующего свойства приведёт к ошибке, так как соответствующая функция не будет найдена.

Man({ age: 17 }) // Error!

Свойства - всего-лишь функции, которые в зависимости от числа переданных параметров либо возвращают своё значение, либо устанавливают.

Man().name( 'Bob' ).name() // 'Bob'

При установке значения переданный параметр прогоняется через "типа свойства" - функцию, которую мы передали вторым параметром при создании свойства.

Man().name( 17 ).name() // '17' потому что String( 17 )

Если значение ещё не установлено, то функция типа вызывается без параметров, чтобы сгенерировать дефолтное значение.

Man().name() // '' потому что String()

Разумеется можно определять различные свои типы.

Наследование реализуется через примешивание:

$jin.property( 'Artist__sceneName', String )
$jin.mixin( '$jin_klass', 'Artist', 'Man' )

Man().sceneName() // ''


Примешивать и создавать методы можно опять же в произвольном порядке.

$jin.mixin( 'Artist', '$jin_klass', 'Man' )
$jin.property( 'Artist__pseudoName', String )

Man().sceneName() // ''


Чтобы каждый раз не примешивать $jin_class можно сразу его и вызывать:

$jin.class( 'Artist', 'Man' )

Иногда могут возникать конфликты имён:

$jin.property( 'Artist__name', String )

$jin.klass( 'Artist', 'Man' )
$jin.property( 'Man__name', String )

Man().name() // Error!


Чтобы решить его нужно явно указать, что один метод может перегружать другой, посредством добавления дополнительного параметра вначале (коих может быть и несколько):

$jin.property( 'Artist__name', String )

$jin.klass( 'Artist', 'Man' )
$jin.property( 'Artist__name', 'Man__name', String )

Man().name() // ''


Тут мы указали, что в случае конфликта между "Artist__name" и "Man__name" использовано будет последнее. $jin.method действует аналогично.

$jin.method( function Man__toString( ){
    return 'Man'
})
$jin.method( function Black__toString( ){
    return 'Black'
})

$jin.klass( 'Man', 'Black', 'ManInBlack' )
$jin.method( 'Man__toString', 'Black__toString', function ManInBlack__toString( ){
    return this.Man__toString() + ' in ' + this.Black__toString()
})

ManInBlack() + '' // Man in Black


Как видно доступ к перегруженным методам возможен через их полные имена.

Указание перегружаемых методов для каждого перегружающего метода может показаться избыточным, однако перегрузка на уровне классов не может выявить конфликты при появлении новых методов в них. Поэтому такая перегрузка и не предусмотрена.

Багрепорты и идеи приветствуются)

tenshi 28.07.2013 03:21

Забыл саму либу)

this.$jin = {}

$jin.value = function $jin_value( value ){
    var func = function $jin_value_instance( ){
        return func.$jin_value
    }
    func.$jin_value = value
    return func
}

$jin.root = $jin.value( this )

$jin.glob = function $jin_glob( name, value ){
    var keyList = name.split( '_' )
    var current = $jin.root()
    var currentName = ''
    
    while( keyList.length > 1 ){
        var key = keyList.shift() || 'prototype'
        currentName += ( currentName ? '_' : '' ) + ( key === 'prototype' ? '' : key )
        
        if(!( key in current )){
            current[ key ] = $jin.trait.make( currentName )
        }
        
        current = current[ key ]
    }
    
    var key = keyList.shift() || 'prototype'
    
    if( arguments.length > 1 ){
        current[ key ] = value
    } else {
        value = current[ key ]
    }
    
    return value
}


$jin.lazy = function $jin_lazy( generator ){
    if( typeof generator === 'string' ){
        generator = $jin.lazy.glob( generator )
    }
    
    var lazy = function $jin_lazy_instance( ){
        return lazy.$jin_lazy_func.apply( this, arguments )
    }
    
    lazy.$jin_lazy_gen = generator 
    lazy.$jin_lazy_func = function $jin_lazy_generate( ){
        var func = lazy.$jin_lazy_func = lazy.$jin_lazy_gen()
        return func.apply( this, arguments )
    }
    
    return lazy
}

$jin.lazy.glob = function $jin_lazy_glob( name ){
    var lazy = function $jin_lazy_glob( ){
        return $jin.glob( lazy.$jin_lazy_name )
    }
    lazy.$jin_lazy_name = name
    return lazy
}


$jin.trait = function $jin_trait( name ){
    
    var trait = $jin.glob( name )
    if( trait ) return trait
    
    trait = $jin.trait.make( name )
    
    return $jin.glob( name, trait )
}

$jin.trait.make = function $jin_trait_make( name ){
    
    eval( 'var trait= function ' + name + '( ){ \
        return ( this instanceof trait ) ? this : trait.apply( trait, arguments ) \
    }' )
    
    trait.name = name
    trait.apply = void 0
    trait.prototype = {}
    
    return trait
}



$jin.method = function $jin_method( ){ // arguments: resolveName*, func
    var resolveList = [].slice.call( arguments )
    var func = resolveList.pop()
    
    var name = func.name = func.name || func.toString().match( /^\s*function\s*([$\w]*)\s*\(/ )[ 1 ]
    if( !name ) throw new Error( 'Can not register anonymous function' )
    
    func.$jin_method_name = func.$jin_method_name || name
    
    func.$jin_method_resolveList = resolveList.concat( func.$jin_method_resolveList || [] )
    
    $jin.method.define( name, func )
}

$jin.method.define = function $jin_method_define( name, func ){
    var funcName = func.$jin_method_name
    if( !funcName ) throw new Error( '$jin_method_name is not defined in [' + func + ']' )
    
    var nameList = name.split( '_' )
    var methodName = nameList.pop()
    var ownerPath = nameList.join( '_' )
    var owner = $jin.trait( ownerPath )
    
    owner[ funcName ]= func
    
    var existFunc = owner[ methodName ]
    checkConflict: {
        
        if( existFunc === void 0 ) break checkConflict
        
        if( typeof existFunc !== 'function' ){
            throw new Error( 'Can not redefine [' + existFunc + '] by [' + funcName + ']' )
        }
        
        if( func === existFunc ) return existFunc
        
        if( !existFunc.$jin_method_name ) break checkConflict
        
        func = $jin.method.merge( existFunc, func, name )
    }
    
    owner[ methodName ]= func
    
    var slaveList = owner.$jin_mixin_slaveList
    if( slaveList ) slaveList.forEach( function( slavePath ){
        $jin.method.define( slavePath + '_' + methodName, func )
    })
    
    return func
}

$jin.method.merge = function $jin_method_merge( left, right, name ){
    var leftConflicts = left.$jin_method_conflictList || [ left ]
    var rightConflicts = right.$jin_method_conflictList || [ right ]
    var conflictList = leftConflicts.concat( rightConflicts )

    var leftResolves = left.$jin_method_resolveList || []
    var rightResolves = right.$jin_method_resolveList || []
    var resolveList = leftResolves.concat( rightResolves )
    
    conflictList = conflictList.filter( function( conflict ){
        return !~resolveList.indexOf( conflict.$jin_method_name )
    })
    
    if( conflictList.length === 0 ){
        throw new Error( 'Can not resolve conflict ' + name + ' because cyrcullar resolving' )
    } else if( conflictList.length === 1 ){
        var func = conflictList[0]
    } else if( conflictList.length > 1 ){
        eval( 'var func= function ' + name + '( ){ \
            throw new Error( "Conflict in [" + func.$jin_method_name + "] by [" + func.$jin_method_conflictList + "]" )\
        }' )
        func.$jin_method_name = name
        func.$jin_method_conflictList = conflictList
    }
    
    func.$jin_method_resolveList = resolveList
    
    return func
}

$jin.method.naming = function $jin_method_naming( name, owner ){
    if( arguments.length < 2 ){
        owner = $jin.glob( name )
    }
    
    for( var key in owner ){
        if( !owner.hasOwnProperty( key ) ) continue
        
        var value = owner[ key ]
        if( Object( value ) !== value ) continue
        
        $jin.method.naming( name + '_' + key, value )
    }
    
    if( typeof owner === 'function' && !owner.$jin_method_name ){
        $jin.method.naming( name + '_', owner.prototype )
        owner.$jin_method_name = name
        owner.$jin_method_resolveList = [ '$jin_klass__constructor' ]
    }    
}




$jin.mixin = function( ){ // arguments: sourceName+, targetName
    var trait = $jin.mixin.object.apply( this, arguments )
    
    for( var index = 0; index < arguments.length; ++index ){
        arguments[ index ] += '_'
    }
    $jin.mixin.object.apply( this, arguments )
    
    return trait
}

$jin.mixin.object = function( ){ // arguments: sourceName+, targetName
    var sourcePathList = [].slice.call( arguments )
    var targetPath = sourcePathList.pop()
    var target = $jin.trait( targetPath )
    
    sourcePathList.forEach( function( sourcePath ){
        var source = $jin.trait( sourcePath )
        source.$jin_mixin_slaveList = source.$jin_mixin_slaveList || []
        if( ~source.$jin_mixin_slaveList.indexOf( targetPath ) ) return
        source.$jin_mixin_slaveList.push( targetPath )
        
        for( var key in source ){
            var func = source[ key ]
            if( typeof func !== 'function' ) continue
            if( !func.$jin_method_name ) continue
            
            $jin.method.define( targetPath + '_' + key, func )
        }
        
        if( source.constructor && source.constructor.$jin_method_name ){
            $jin.method.define( targetPath + '_constructor', source.constructor )
        }
    })
    
    return target
}



$jin.property = function $jin_property( ){ // arguments: resolveName*, name, type
    var resolveList = [].slice.call( arguments )
    var type = resolveList.pop()
    var name = resolveList.pop()
    
    var fieldName = '_' + name
    
    eval( 'var property = function ' + name + '( ){ \
        return property.apply( this, arguments ) \
    }' )
    
    property.name = name
    
    property.apply = function $jin_property_apply( obj, args ){
        if( args.length ){
            obj[ fieldName ] = property.$jin_property_ensureType( args[ 0 ] )
            return obj
        } else {
            if( obj.hasOwnProperty( fieldName ) ){
                return obj[ fieldName ]
            } else {
                return obj[ fieldName ] = property.$jin_property_ensureType()
            }
        }
    }
    
    if( typeof type === 'string' ) type = $jin.lazy.glob( type )
    
    property.$jin_property_ensureType = type || function( value ){ return nvalue }
    
    property.$jin_method_resolveList = resolveList
    
    return $jin.method( property )
}

$jin.klass = function $jin_klass( ){ // arguments: sourceName*, targetName
    $jin.mixin.apply( this, arguments )
    
    var name = arguments[ arguments.length - 1 ]
    return $jin.mixin( '$jin_klass', name )
}

$jin.method( function $jin_klass_apply( klass, args ){
    var obj = new klass
    obj.constructor.apply( obj, args )
    return obj
} )

$jin.method( function $jin_klass_toString( ){
    return this.name
} )

$jin.method( function $jin_klass__constructor( config ){
    if( !config ) return this
    for( var key in config ){
        this[ key ]( config[ key ] )
    }
    return this
} )

nerv_ 28.07.2013 12:45

Цитата:

Сообщение от tenshi
$jin_method

вЯваСкриптеCamelCase

tenshi 28.07.2013 14:33

$имяПакета_имяМодуля_уника� �ьноеИмяВРамкахМодуля

tenshi 28.07.2013 14:34

Парсер лох, чо)

tenshi 28.07.2013 14:49

Хотя, тут можно и точку наверно использовать в качестве разделителя. Более существенные замечания будут?)

Octane 28.07.2013 22:31

О, вспомнил! Надо твою статью про yield перечитать до полного понимания. :)

tenshi 28.07.2013 23:04

Она-то тут при чём? о_0" Да и не сильно она актуальна уже.. либу я ту переписал по другому.

Octane 28.07.2013 23:36

Не при чем) Читал, когда в армии был. Что-то не найду ссылку. Там где пошагово расписывает про волокна. Еще осталась эта статья?

tenshi 29.07.2013 09:48

http://hyoo.ru/?article=%D0%92%D0%BE...author=Nin+Jin


Часовой пояс GMT +3, время: 09:30.