|
#! iojs --harmony --harmony_proxies |
|
'use strict'; |
|
|
|
require('harmony-reflect'); |
|
|
|
// Usual class |
|
class Person { |
|
constructor(name) { |
|
this._name = name; |
|
} |
|
get name() { |
|
return this._name; |
|
} |
|
set name(name) { |
|
this._name = name; |
|
} |
|
greeting(person) { |
|
return `hi ${person.name}`; |
|
} |
|
} |
|
|
|
// Create an object from an usual class |
|
let ivanPerson = new Person('Ivan'); |
|
|
|
// Call the getter of _name |
|
console.log(`name getter from a Person instance: ${ivanPerson.name}`); |
|
// _name property is accessible because JS classes doesn't have access scope |
|
console.log(`_name property from a Person instance: ${ivanPerson._name}`); |
|
|
|
// Let's try to protect class object |
|
let ProtectedPerson = new Proxy(Person, { |
|
get(target, name) { |
|
console.log('calling getter in Person wrapped by a proxy'); |
|
if (name.startsWith('_')) { |
|
throw new Error('Accessing to a private property is not allowed'); |
|
} else { |
|
return target[name]; |
|
} |
|
} |
|
}); |
|
|
|
// Let's create an instance of the class that we wrapped with a proxy |
|
let ivanProtectedPerson = new ProtectedPerson('Ivan'); |
|
|
|
// Call the getter of _name |
|
console.log(`name getter from a protected person instance: ${ivanProtectedPerson.name}`); |
|
// _name still accessible due proxy wrapped class Person; |
|
// a class is a Function with prototype object, proxy trap calls to the |
|
// class itself hence the target is the class, not its instances |
|
console.log(`_name property from a protected person instance: ${ivanProtectedPerson._name}`); |
|
|
|
// let's protect a base class instance |
|
let ivanPersonProtected = new Proxy(ivanPerson, { |
|
get(target, name) { |
|
if (name.startsWith('_')) { |
|
throw new Error('Accessing to a private property is not allowed'); |
|
} else { |
|
return target[name]; |
|
} |
|
} |
|
}) |
|
|
|
// Call the getter of _name |
|
console.log(`name getter from a person instance which has been protected: ${ivanPersonProtected.name}`); |
|
try { |
|
// _name property call gets trapped by the proxy |
|
console.log(`_name property from a person instance which has been protected: ${ivanPersonProtected._name}`); |
|
} catch (e) { |
|
if (e.message === 'Accessing to a private property is not allowed') { |
|
console.log('Proxy did its job!!'); |
|
} else { |
|
throw e; |
|
} |
|
} |
|
|
|
// However it's painful, having to wrap in a proxy every single instance of a class |
|
// is a repeatable tiring task, so let's try to creata a class that protect its |
|
// object instances |
|
|
|
class PersonProtected { |
|
constructor(name) { |
|
this._name = name; |
|
// In es6 it also works as in es5: remember es6 class is nothing more than a Function |
|
// and `new` call the function defined by `constructor`; |
|
// in es5 works because when you call a `new` on a function the value retuned is the |
|
// value returned inside the function if it's an object otherwise returns `this`; |
|
// in es6 remains the same for backward compatibility |
|
return new Proxy(this, { |
|
get(target, name) { |
|
if (name.startsWith('_')) { |
|
throw new Error('Accessing to a private property is not allowed'); |
|
} else { |
|
return target[name]; |
|
} |
|
} |
|
}); |
|
} |
|
get name() { |
|
return this._name; |
|
} |
|
set name(name) { |
|
this._name = name; |
|
} |
|
greeting(person) { |
|
return `hi ${person.name}`; |
|
} |
|
} |
|
|
|
let ivanPersonProtectedInstance = new PersonProtected('Ivan'); |
|
|
|
// Call the getter of _name |
|
console.log(`name getter from a PersonProtected instance: ${ivanPersonProtectedInstance.name}`); |
|
try { |
|
// _name property call gets trapped by the proxy |
|
console.log(`_name property from a PersonProtected instance: ${ivanPersonProtectedInstance._name}`); |
|
} catch (e) { |
|
if (e.message === 'Accessing to a private property is not allowed') { |
|
console.log('Class which proxy `this` did its job!!'); |
|
} else { |
|
throw e; |
|
} |
|
} |
get name()
andset name(name)
in the final example are to safely expose the internal_name
property via, well, a getter/setter function. So instead of modifying_name
directly, you can use the getter/setter to add validation/filtering logic.