среда, 9 сентября 2015 г.

Интересный способ записи условия If или Switch

function card (icon) {
    return icon === 'MasterCard' ? 'master'
             : icon === 'Maestro' ? 'maestro'
             : icon === 'Visa Classic' ? 'visa-classic'
             : icon === 'Visa Electron' ? 'visa-electron'
             : icon === 'PRO100' ? 'pro100'
             : icon === 'American Express' ? 'amex'
             : '';
}

var class = card('PRO100');

понедельник, 7 сентября 2015 г.

Упрощенный способ создания класса в JavaScript

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

<!DOCTYPE html>
<html>
<head>
<script>

var ParentClass = new Class (function () {

    // constructor
    function ParentClass (initVar) {
        this.initVar = initVar;
        this._x = 0; // getter | setter
        document.addEventListener('click', this._privateMethod, false);
        console.log('ParentClass constructed.');
    }

    // destructor
    ParentClass.prototype.destroy = function () {
        document.removeEventListener('click', this._privateMethod, false);
        console.log('ParentClass destroyed.');
    };

    // default
    ParentClass.prototype.defaultVar = 'ParentClass default var.';

    // public
    ParentClass.prototype.publicMethod = function () {return 'ParentClass public method.';};

    // private - interface check
    ParentClass.prototype._privateMethod = function () {console.log('ParentClass private method.');};

    // abstract - interface check
     ParentClass.prototype.abstractMethod = function () {};

    // final - interface check
    ParentClass.prototype.finalMethod = function () {return 'ParentClass final method.';};

    // static
    ParentClass.staticMethod = function () {return 'ParentClass static method.';};

    // getter | setter
    Object.defineProperty(ParentClass.prototype, 'x', {
          get: function () {return this._x;}
        , set: function (value) {this._x = value;}
        , enumerable: true
        , configurable: true
    });

    return ParentClass;

});

var MixinClass = new Class (function () {

    // constructor
    function MixinClass () {}

    // public
    MixinClass.prototype.mixinPublicMethod = function () {return 'MixinClass public method.';};

    // static
    MixinClass.mixinStaticMethod = function  () {return 'MixinClass static method.';};

    return MixinClass;

});

var ChildClass = new Class ({extends: ParentClass, mixin: MixinClass}, function (_super) {

    // singleton
    var singleton;

    // constructor
    function ChildClass (initVar) {
        if (singleton) {return singleton} else {singleton = this;}
        _super.call(this, initVar);
    }

    // public - parent call
    ChildClass.prototype.childPublicMethod = function () {
        return 'ChildClass childPublicMethod call ' + _super.prototype.publicMethod.call(this);
    };

    // override
    ChildClass.prototype.abstractMethod = function () {return 'ChildClass override ParentClass abstarctMethod.';};

    // override private
    ChildClass.prototype._privateMethod = function () {console.log('ChildClass private method.');};

    return ChildClass;

});

function _mixin (ChildClass) {
    var mixinClasses = Array.prototype.slice.call(arguments, 1)
        , key
        , len
        , i;
    if (mixinClasses.length > 0) {
        if (Object.prototype.toString.call(mixinClasses[0]) === '[object Array]') {mixinClasses = mixinClasses[0];}
        for (i = 0, len = mixinClasses.length; i < len; i++) {
            for (key in mixinClasses[i]) {if (mixinClasses[i].hasOwnProperty(key)) {ChildClass[key] = mixinClasses[i][key];}}
            for (key in mixinClasses[i].prototype) {if (key !== 'constructor') {ChildClass.prototype[key] = mixinClasses[i].prototype[key];}}
        }
    }
}

function Class () {
    var Constructor
        , len
        , key
        , i
        , tempObject;
    function F () {}
    if (
              arguments.length === 1
        && Object.prototype.toString.call(arguments[0]) === '[object Function]'
    ) {
        return arguments[0]();
    } else if (
              arguments.length === 2
        && Object.prototype.toString.call(arguments[0]) === '[object Object]'
        && Object.prototype.toString.call(arguments[1]) === '[object Function]'
    ) {
        if (arguments[0].hasOwnProperty('extends')) {
            Constructor = arguments[1](arguments[0]['extends']);
            for (key in arguments[0]['extends']) {
                if (!Constructor.hasOwnProperty(key)) {Constructor[key] = arguments[0]['extends'][key];}
            }
            F.prototype = arguments[0]['extends'].prototype;
            tempObject = new F();
            for (key in tempObject) {
                if (!Constructor.prototype.hasOwnProperty(key)) {Constructor.prototype[key] = tempObject[key];}
            }
        } else {
            Constructor = arguments[1]();
        }
        if (arguments[0].hasOwnProperty('mixin')) {_mixin(Constructor, arguments[0]['mixin']);}
        return Constructor;
    } else {
        throw new Error('Argument of new Class constructor must be object or function');
    }
}

console.log('ParentClass static');
console.dir(ParentClass);
console.log('ParentClass public');
console.dir(ParentClass.prototype);

var parentObject = new ParentClass('ParentClass init var');
console.log('1) Constructor name: ' + parentObject.constructor.name);
console.log('2) DeafultVar value: ' + parentObject.defaultVar);
console.log('3) InitVar value: ' + parentObject.initVar);
console.log('4) PublicMethod result: ' + parentObject.publicMethod());
console.log('5) AbstractMethod result: ' + parentObject.abstractMethod());
console.log('6) FinalMethod result: ' + parentObject.finalMethod());
console.log('7) StaticMethod result: ' + ParentClass.staticMethod());
console.log('8) Get x result: ' + parentObject.x);
console.log('9) Set x.');
parentObject.x = 1;
console.log('10) Get x after set: ' + parentObject.x);
setTimeout(function(){console.log('11) Object destruction. '); parentObject.destroy(); parentObject = null;}, 5000);

console.log('MixinClass static');
console.dir(MixinClass);
console.log('MixinClass public');
console.dir(MixinClass.prototype);

console.log('ChildClass static');
console.dir(ChildClass);
console.log('ChildClass public');
console.dir(ChildClass.prototype);

var childObject = new ChildClass('ChildClass init var 1');
console.log('1) ChildClass object 1 created.');
var childObject2 = new ChildClass('ChildClass init var 2');
console.log('2) ChildClass object 2 created.');
console.log('3) Is singleton? ' + (childObject === childObject2));
console.log('4) Constructor name: ' + childObject.constructor.name);
console.log('5) DeafultVar value: ' + childObject.defaultVar);
console.log('6) InitVar value: ' + childObject.initVar);
console.log('7) PublicMethod result: ' + childObject.publicMethod());
console.log('8) ChildPublicMethod result: ' + childObject.childPublicMethod());
console.log('9) AbstractMethod result: ' + childObject.abstractMethod());
console.log('10) FinalMethod result: ' + childObject.finalMethod());
console.log('11) StaticMethod result: ' + ChildClass.staticMethod());
console.log('12) Get x result: ' + childObject.x);
console.log('13) Set x.');
childObject.x = 2;
console.log('14) Get x after set: ' + childObject.x);
console.log('15) MixinPublicMethod result: ' + childObject.mixinPublicMethod());
console.log('16) MixinStaticMethod result: ' + ChildClass.mixinStaticMethod());
setTimeout(function(){console.log('17) Object destruction. '); childObject.destroy(); childObject = null;}, 5000);

</script>
</head>
<body>
</body>
</html>

четверг, 3 сентября 2015 г.

ECMAScript 5, 6, 7 new API

ECMAScript 5 new API:

Object
  .create(proto | null, descriptors?) -> object
  .getPrototypeOf(object) -> proto | null
  .defineProperty(target, key, desc) -> target, cap for ie8-
  .defineProperties(target, descriptors) -> target, cap for ie8-
  .getOwnPropertyDescriptor(object, key) -> desc
  .getOwnPropertyNames(object) -> array
  .keys(object) -> array
  .seal(object) -> object, cap for ie8-
  .freeze(object) -> object, cap for ie8-
  .preventExtensions(object) -> object, cap for ie8-
  .isSealed(object) -> bool, cap for ie8-
  .isFrozen(object) -> bool, cap for ie8-
  .isExtensible(object) -> bool, cap for ie8-

Array
  .isArray(var) -> bool
  #slice(start?, end?) -> array, fix for ie7-
  #join(string = ',') -> string, fix for ie7-
  #indexOf(var, from?) -> int
  #lastIndexOf(var, from?) -> int
  #every(fn(val, index, @), that) -> bool
  #some(fn(val, index, @), that) -> bool
  #forEach(fn(val, index, @), that) -> void
  #map(fn(val, index, @), that) -> array
  #filter(fn(val, index, @), that) -> array
  #reduce(fn(memo, val, index, @), memo?) -> var
  #reduceRight(fn(memo, val, index, @), memo?) -> var

String
  #trim() -> str

JSON
  .parse() -> object
  .stringify() -> string

Function
  #bind(object, ...args) -> boundFn(...args)

Date
  .now() -> int
  #toISOString() -> string

ECMAScript 6 new API:

Object
  .create(proto | null, descriptors?) -> object
  .assign(target, ...src) -> target
  .is(a, b) -> bool
  .setPrototypeOf(target, proto | null) -> target (required __proto__ - IE11+)
  #toString() -> string, ES6 fix: @@toStringTag support
  .freeze(var) -> var
  .seal(var) -> var
  .preventExtensions(var) -> var
  .isFrozen(var) -> bool
  .isSealed(var) -> bool
  .isExtensible(var) -> bool
  .defineProperty(target, key, desc) -> target
  .defineProperties(target, descriptors) -> target
  .getOwnPropertyDescriptor(var, key) -> desc | undefined
  .getOwnPropertyNames(var) -> array
  .getOwnPropertySymbols(object) -> array
  .getPrototypeOf(var) -> object | null
  #propertyIsEnumerable(key) -> bool
  .keys(var) -> array

Function
  #name -> string (IE9+)
  #@@hasInstance(var) -> bool

Array (typed Array)
  .from(iterable | array-like, mapFn(val, index)?, that) -> array
  .of(...args) -> array
  #copyWithin(target = 0, start = 0, end = @length) -> @
  #fill(val, start = 0, end = @length) -> @
  #find(fn(val, index, @), that) -> val
  #findIndex(fn(val, index, @), that) -> index
  #@@unscopables -> object (cap)

String
  .fromCodePoint(...codePoints) -> str
  .raw({raw}, ...substitutions) -> str
  #includes(str, from?) -> bool
  #startsWith(str, from?) -> bool
  #endsWith(str, from?) -> bool
  #repeat(num) -> str
  #codePointAt(pos) -> uint
  #trim() -> str, ES6 fix
  #match(tpl) -> var, ES6 fix for support @@match
  #replace(tpl, replacer) -> var, ES6 fix for support @@replace
  #search(tpl) -> var, ES6 fix for support @@search
  #split(tpl, limit) -> var, ES6 fix for support @@split
  #normalize
[new] RegExp(pattern, flags?) -> regexp, ES6 fix: can alter flags (IE9+)
  #flags -> str (IE9+)
  #@@match(str) -> array | null
  #@@replace(str, replacer) -> string
  #@@search(str) -> index
  #@@split(str, limit) -> array

[new] Number (var) -> number | number object
  .EPSILON -> num
  .isFinite(num) -> bool
  .isInteger(num) -> bool
  .isNaN(num) -> bool
  .isSafeInteger(num) -> bool
  .MAX_SAFE_INTEGER -> int
  .MIN_SAFE_INTEGER -> int
  .parseFloat(str) -> num
  .parseInt(str) -> int

Math
  .acosh(num) -> num
  .asinh(num) -> num
  .atanh(num) -> num
  .cbrt(num) -> num
  .clz32(num) -> uint
  .cosh(num) -> num
  .expm1(num) -> num
  .fround(num) -> num
  .hypot(...args) -> num
  .imul(num, num) -> int
  .log1p(num) -> num
  .log10(num) -> num
  .log2(num) -> num
  .sign(num) -> 1 | -1 | 0 | -0 | NaN
  .sinh(num) -> num
  .tanh(num) -> num
  .trunc(num) -> num

Symbol (description?) -> symbol
  .hasInstance -> @@hasInstance
  .isConcatSpreadable -> @@isConcatSpreadable
  .iterator -> @@iterator
  .match -> @@match
  .replace -> @@replace
  .search -> @@search
  .species -> @@species
  .split -> @@split
  .toPrimitive -> @@toPrimitive
  .toStringTag -> @@toStringTag
  .unscopables -> @@unscopables
  .for(key) -> symbol
  .keyFor(symbol) -> key
  .useSimple() -> void
  .useSetter() -> void

Proxy

new Map (iterable (entries) ?) -> map
  #clear() -> void
  #delete(key) -> bool
  #forEach(fn(val, key, @), that) -> void
  #get(key) -> val
  #has(key) -> bool
  #set(key, val) -> @
  #size -> uint

new Set (iterable?) -> set
  #add(key) -> @
  #clear() -> void
  #delete(key) -> bool
  #forEach(fn(el, el, @), that) -> void
  #has(key) -> bool
  #size -> uint

new WeakMap (iterable (entries) ?) -> weakmap
  #delete(key) -> bool
  #get(key) -> val
  #has(key) -> bool
  #set(key, val) -> @

new WeakSet (iterable?) -> weakset
  #add(key) -> @
  #delete(key) -> bool
  #has(key) -> bool

Iterator String
  #@@iterator() -> iterator

Iterator Array
  #values() -> iterator
  #keys() -> iterator
  #entries() -> iterator (entries)
  #@@iterator() -> iterator

Iterator Arguments
  #@@iterator() -> iterator (available only in core-js methods)

Iterator Map
  #values() -> iterator
  #keys() -> iterator
  #entries() -> iterator (entries)
  #@@iterator() -> iterator (entries)

Iterator Set
  #values() -> iterator
  #keys() -> iterator
  #entries() -> iterator (entries)
  #@@iterator() -> iterator

Iterator NodeList
  #@@iterator() -> iterator

new Promise (executor(resolve(var), reject(var))) -> promise
  #then(resolved(var), rejected(var)) -> promise
  #catch(rejected(var)) -> promise
  .resolve(var || promise) -> promise
  .reject(var) -> promise
  .all(iterable) -> promise
  .race(iterable) -> promise

Reflect
  .apply(target, thisArgument, argumentsList) -> var
  .construct(target, argumentsList, newTarget?) -> object
  .defineProperty(target, propertyKey, attributes) -> bool
  .deleteProperty(target, propertyKey) -> bool
  .enumerate(target) -> iterator
  .get(target, propertyKey, receiver?) -> var
  .getOwnPropertyDescriptor(target, propertyKey) -> desc
  .getPrototypeOf(target) -> object | null
  .has(target, propertyKey) -> bool
  .isExtensible(target) -> bool
  .ownKeys(target) -> array
  .preventExtensions(target) -> bool
  .set(target, propertyKey, V, receiver?) -> bool
  .setPrototypeOf(target, proto) -> bool (required __proto__ - IE11+)

ECMAScript 7 new API:

Object
  .values(object) -> array
  .entries(object) -> array
  .getOwnPropertyDescriptors(object) -> object
  .observe

Array
  #includes(var, from?) -> bool
  .{...ArrayPrototype methods}

String
  #at(index) -> string
  #padLeft(length, fillStr = ' ') -> string
  #padRight(length, fillStr = ' ') -> string
  #trimLeft() -> string
  #trimRight() -> string

RegExp
  .escape(str) -> str

Map
  #toJSON() -> array

Set
  #toJSON() -> array

fetch ()

SIMD

-------------------------------------------------------

Polyfills library: github.com/zloirock/core-js

Пример создания класса словаря на JavaScript

var phone = (function(){
  var db = Object.create(null);
  Object.assign(db, {
    'Вася': '+7987654',
    'Петя': '+7654321'
  });
  return {
    has: function(name){
      return name in db;
    },
    get: function(name){
      return db[name];
    },
    set: function(name, phone){
      db[name] = phone;
    },
    delete: function(name){
      delete db[name];
    }
  };
})();

console.log(phone.has('Вася'));     // => true
console.log(phone.get('Вася'));     // => '+7987654'
console.log(phone.has('Дима'));     // => false
console.log(phone.get('Дима'));     // => undefined
console.log(phone.has('toString')); // => true
console.log(phone.get('toString')); // => function toString() { [native code] }

Node + Grunt + R.js + Require Config

Структура проекта:

/index.html
/js/require.js
/js/require.text.js
/js/module1.js
/js/module.2.js
/js/module3.js

/Gruntfile.js

/rungrunt.js

/node_modules/grunt
/node_modules/grunt_contrib_requirejs

Файл index.html

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <script data-main="js/main" src="js/require.js" type="text/javascript"></script>
</head>
<body></body>
</html>

Файл module1.js

require.config({paths: {module2: 'js/module2'}});

require(['module2'], function(module2){ // или define(['module2'], function(module2){
    module2.init();
});

Файл module2.js

require.config({paths: {module3: 'js/module3'}});

define(['module3'], function(module3){
    module3.init();
    return {init: function () {console.log(2);}};
});

Файл module3.js

define(function(){
    return {init: function () {console.log(3);}};
});

Файл Gruntfile.js

module.exports = function (grunt) {

    grunt.initConfig({

          // Создать единый файл из модулей Require JS
          requirejs: {
            compile: {
                options: {
                      baseUrl: "./" // <-- путь к папке с JavaScript-файлами
                    , name: "js/module1" // <-- основной стартовый файл для сборки (может содержать внутри себя как вызов require(), так и define())
                    , out: 'js/main.js' // <-- итоговый собранный файл
                    , paths: { // <-- пути ко всем модулям используемым в проекте (их наличие для сборки обязательно)
                            "module2": "js/module2"
                          , "module3": "js/module3"
                          , "text": "js/require.text"
                      }
                    , optimize: 'none' // <-- не минифицировать итоговый файл
                }
            }
          }

    });

    grunt.loadNpmTasks('grunt-contrib-requirejs');

    grunt.registerTask('default', [
        'requirejs'
    ]);

};

Файл rungrunt.js

require('grunt').cli();

Запуск сборки через консоль командной строки CMD

cd C:\Path\To\My\Project\Folder

node rungrunt.js

В итоге в папке js создается файл main.js

define('module3',[],function(){
    return {init: function () {console.log(3);}};
});
require.config({paths: {module3: 'js/module3'}});
define('module2',['module3'], function(module3){
    module3.init();
    return {init: function () {console.log(2);}};
});
require.config({paths: {module2: 'js/module2'}});
require('js/module1',['module2'], function(module2){ // define(['module2'], function(module2){
    module2.init();
});