/ Gists

Gists

On gists

Proxy object guide

JavaScript

Proxy.js #

// 1)  get + set
const target = {
    name: 'John Smith',
    age: 30,
};
const handler = {
    get: function (target, prop) {
        console.log(`Getting the property ${prop}`);
        return target[prop];
    },
    set: function (target, prop, value) {
        console.log(`Setting the property ${prop} to ${value}`);
        target[prop] = value;
    },
};
const proxy = new Proxy(target, handler);



// 2) with validator fn aka encapsulation
const withValidators = person => {
    const handler = {
        set: function (target, prop, value) {
            if ((prop === 'age' && value < 0) || value > 120) {
                throw new Error('Invalid age');
                return;
            }
            target[prop] = value;
        },
    };

    return new Proxy(person, handler);
};

const person = withValidators({
    name: 'John Smith',
    age: 30,
});



// 3) caching, proxy wrap only function
const cache = {};

const getWeather =  (city) => {
    return "fetch -> city -> " + city
};

const proxiedGetWeather = new Proxy(getWeather, {
    apply(target, thisArg, args)  {
        const city = args[0];

        if (cache[city]) {
            console.log('Loading weather data from cache...');
            return cache[city];
        }

        cache[city] =  target.apply(thisArg, args);
        return cache[city];
    },
});

console.log(proxiedGetWeather('San Francisco'))
console.log(proxiedGetWeather('San Francisco')) // console.log('Loading weather data from cache...');
console.log(proxiedGetWeather('San Francisco')) // console.log('Loading weather data from cache...');


// 4) cannot delete
const handler = {
    deleteProperty(target, key) {
        if (key.startsWith('_')) {
            throw new Error(`Cannot delete internal property '${key}'`);
        }

        delete target[key];
        return true;
    },
};

const user = {
    name: 'John Smith',
    _ssn: 'XXX-YY-ZZZ',
};

const protectedUser = protectInternalProps(user);

// Throw error `Cannot delete internal property '_ssn'`
delete protectedUser._ssn;



// 5 cannot iterate
const handler = {
    ownKeys(target) {
        return Object.keys(target).filter(key => !key.startsWith('_'));
    },
};

const user = {
    name: "John Smith",
    _ssn: "XXX-YY-ZZZ",
};

const protectedUser = protectInternalProps(user);

console.log(Object.keys(protectedUser)); // ['name']

for (let key in protectedUser) {
    console.log(key);
}
// ['name']



// 6) private props in class before exists #privateProps
// https://phuoc.ng/collection/javascript-proxy/implement-private-properties-in-a-javascript-class/
const handler = {
    get(target, key) {
        if (key.startsWith('_')) {
            throw new Error(`Cannot access private property '${key}'`);
        }

        return target[key];
    },
    set(target, key, value) {
        if (key.startsWith('_')) {
            throw new Error(`Cannot modify private property '${key}'`);
        }

        target[key] = value;
        return true;
    },
};

class ProtectedPerson {
    constructor(name, age, ssn) {
        this.name = name;
        this.age = age;
        this._ssn = ssn;

        return new Proxy(this, handler);
    }

    getSsn() {
        return this._ssn;
    }
}

// failed
const person = new ProtectedPerson('John Smith', 42, '123-45-6789');
// Uncaught Error: Cannot access private property '_ssn'
person.getSsn();


// lets improve
const handler = {
    get(target, key) {
        if (key.startsWith('_')) {
            throw new Error(`Cannot access private property '${key}'`);
        }
        const value = target[key];
        return typeof value === 'function' ? value.bind(target) : value;
    },
};

const person = new ProtectedPerson('John Smith', 42, '123-45-6789');
console.log(person.getSsn());   // 123-45-6789



// 7) exists 'in'
const handler = {
    has(target, key) {
        console.log(`Checking for ${key} property...`);
        return key in target;
    },
};

const person = {
    name: 'John Smith',
    age: 42,
    occupation: 'developer',
};

const proxy = new Proxy(person, handler);
// Logs "Checking for name property..." and returns `true`
console.log('name' in proxy);

// Logs "Checking for email property..." and returns `false`
console.log('email' in proxy);


// 9) Reflect API


// AND rest articles and more robust solutions we can found 
// https://phuoc.ng/collection/javascript-proxy/

On gists

Proxy - nested

JavaScript

NestedProxy.js #

const user = {
  a: 'A',
  b: {
    c: "C",
    d: "D"
  },
  x: {
    y: {
      z: {
        first: "first nested",
        second: "second nested"
      }
    }
  }
}


const createNestedProxy = (obj) => {
  return new Proxy(obj, {
    get(target, prop) {
      const props = prop.split('.');
      let value = target;
      for (const p of props) {
        value = value[p];
        if (value === undefined) return undefined;
      }
      return value;
    }
  });
};

const nestedUser = createNestedProxy(user);

console.log(nestedUser["b.c"]); // Vypíše: "C"
console.log(nestedUser["x.y.z.first"]); // Vypíše: "first nested"

On gists

Pinia - defineStore with argument (each components has its own)

Vue.js

store.js #

// One storage for all ...
export const useCounterStore = defineStore('counter', () => {
    const count = ref(0);
    function increment() {
        count.value++;
    }

    return { count, increment };
});


// Dynamic storage = each components get its own
export function useCounterStore(key) {
    return defineStore(key, () => {
        const count = ref(0);
        function increment() {
            count.value++;
        }

        return { count, increment };
    })();
}


//And more ES6 style
export const useCounterStore = key => defineStore(key, () => {
    const count = ref(0);
    const increment = () => {
        count.value++;
    };

    return { count, increment };
})();

On gists

Promise - withResolvers

JavaScript

Promise.js #

// neni nutne vracet new Promise jak je vsude v prikladech

// 1
async function test () {
	let resolve, reject;

const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

	setTimeout(() => {
	 Math.random() > 0.5 ? resolve("ok") : reject("not ok");

	}, 1500)

	return promise	
}

test().then(res => console.log(res)).catch(err => console.log(err))


// 2
const { promise, resolve, reject } = Promise.withResolvers();
Math.random() > 0.5 ? resolve("ok") : reject("not ok");

On gists

Async (mark function as async??)

JavaScript

async.js #

// https://stackoverflow.com/questions/44106907/when-to-mark-function-as-async

async function fnUsingAsync() {
    // Synchronously throw half of the time
    if (Math.random() < .5)
        throw new Error('synchronous error')

    // Return fulfilled/rejected promises half of the time
    return Math.random() < .5 ?
        Promise.resolve('success') :
        Promise.reject(new Error('asynchronous error');
}

function fnNotUsingAsync(x) {
    // Use the exact same body here than function above
}



// when using the async keyword, the function always returns a promise
// which can be fulfilled or rejected.
// No need for try-catch!
fnUsingAsync()
    .then(result => /* result === 'success' */)
    .catch(error => /* catch both synchronous and asynchronous error */);

// Otherwise, you will need to use a try-catch statement, because
// the exception won't be converted to a rejected promise.
try {
    fnNotUsingAsync()
      .then(result => /* result === 'success' */)
      .catch(error => /* catch asynchronous error only */)
}
catch (error) {
    /* catch synchronous error only */
}

On gists

TransformOrigin trick

CSS CSS trick

index.css #

div {
	background: lightblue;
	width: 200px;
	height: 200px;
	border: 1px solid red;
	margin: 30px;
}

div.action {
	transition: scale 300ms;
transform-origin: right bottom;  /* zmizi animaci vpravo dole, muzu zadat i px treba 500px bottom , TO je dulezite :) */
	scale: 0;
}

On gists

JS plugin aka jQuery

JavaScript Plugin patterns Helpers-Filters-Plugins

Test.plugin.js #

class Test {
    constructor(el, options) {
        this.el = el;
        const defaultOptions = {
            onHover: (element, e) => {
                console.log(':)))', element, e);
            },
        };

        this.options = { ...defaultOptions, ...options };
        this.mouseOverHandler = this.handleMouseOver.bind(this); // Vytvoření odkazu na metodu handleMouseOver
    }

    init() {
        this.el.addEventListener('mouseover', this.mouseOverHandler); // Přidání posluchače události
    }

    destroy() {
        this.el.removeEventListener('mouseover', this.mouseOverHandler); // Odstranění posluchače události
    }

    handleMouseOver(e) {
        if (this.options.onHover) {
            this.options.onHover(this.el, e);
        }
    }
}

On gists

jQuery $.data in vanilla js

JavaScript

implementations.js #

/* 1 */
var item = document.getElementById("hi");
console.log(item);

item.data = {getType: function(){return this.TYPE},TYPE:"winner"};

var out = item.data.getType();
console.log("out", out);

var two = document.getElementById("hi")
console.log("should say 'winner': ", two.data.getType());



/* 2 */
window.$ = {
    data: function(obj, key, val) {
        if(!obj) {
            return this._data;
        } else if(!key) {
            if(!(obj in this._data)) {
                return {};
            }
            return this._data[obj];
        } else if(arguments.length < 3) {
            if(!(obj in this._data)) {
                return undefined;
            }
            return this._data[obj][key];
        } else {
            if(!(obj in this._data)) {
                this._data[obj] = {};
            }
            this._data[obj][key] = val;
        }
    },
    _data: {}
};

$.data(document.body); // Returns {} because no data has been set for this object
$.data(document.body, 'lastUpdate', new Date());//Sets 'lastUpdate' of obj to current time
$.data(document.body, 'lastUpdate'); // Gets previously set time
$.data(document.body); // Gets object of all data, including 'lastUpdate' time
$.data(document.body, 'nonexistant'); // Returns undefined because property was never set
$.data(); // Returns all metadata




/* 3*/
/** A storage solution aimed at replacing jQuerys data function.
 * Implementation Note: Elements are stored in a (WeakMap)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap].
 * This makes sure the data is garbage collected when the node is removed.
 */
window.dataStorage = {
    _storage: new WeakMap(),
    put: function (element, key, obj) {
        if (!this._storage.has(element)) {
            this._storage.set(element, new Map());
        }
        this._storage.get(element).set(key, obj);
    },
    get: function (element, key) {
        return this._storage.get(element).get(key);
    },
    has: function (element, key) {
        return this._storage.has(element) && this._storage.get(element).has(key);
    },
    remove: function (element, key) {
        var ret = this._storage.get(element).delete(key);
        if (!this._storage.get(element).size === 0) {
            this._storage.delete(element);
        }
        return ret;
    }
}

var myElement = document.getElementById("myId");
dataStorage.put(myElement, "myKey", "myValue");

On gists

defineProps - Vue (and reactive behavior)

Popular ⭐ Vue.js

parent.vue #

<script setup>
import  Child  from './Child.vue'
import { ref } from 'vue'

const test = ref('one')

setTimeout(() => {
  test.value = 'two'
}, 3000)

</script>

<template>
  <h1>Parent</h1>
  <Child :test="test"/>
</template>

On gists

v-model, defineModel, composition api

Popular ⭐ Vue.js

example.vue #

<!-- Parent -->
<template>
  <CommonInput v-model="inputValue" />
</template>

<script setup lang="ts">
import { ref } from "vue";

const inputValue = ref();
</script>


<!-- Child -->
<template>
  <input
    :value="props.modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

<script setup lang="ts">
const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
</script>


<!-- **************** With defineModel **************** -->
<!-- https://javascript.plainenglish.io/understanding-vue-3s-definemodel-for-two-way-binding-streamline-your-code-with-this-guide-ac970f365e4a -->
<!-- Parent -->
<template>
  <CommonInput v-model="inputValue" />
</template>

<script setup lang="ts">
import { ref } from "vue";

const inputValue = ref();
</script>


<!-- Child -->
<template>
  <input v-model="model" />
</template>

<script setup lang="ts">
// declares "modelValue" prop, consumed by parent via v-model
const model = defineModel();
// emits "update:modelValue" when mutated
model.value = "hello";
</script>




<!-- **************** Without defineModel **************** -->
<!-- Child -->
<template>
  <input v-model="props.modelValue" />
</template>

<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';

const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
</script>


<!-- OR with computed, probably better-->

<!-- Child -->
<template>
  <input v-model="inputValue" />
</template>

<script setup lang="ts">
import { computed } from 'vue';

const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);

const inputValue = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value),
});
</script>