/ Gists / Proxy object guide
On gists

Proxy object guide

JavaScript

Proxy.js Raw #

// 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/