This is a rough implementation of an interface, something we'll build on top of, maybe even create something useful for developers
Simply what are proxies?
proxies are stand-in objects, they shield the real object from the "accessor", and pose as the real thing, such that any attempt to access or manipulate the real object passes thru the proxy, if the proxy allows it, it is applied to the real object vice versa. I am not going to go deep in what proxies are, because I do go over them in the eBook under the meta-programming section, extensively with multiple examples, However I am going to explain what the interface proxy does in detail
Interface?
an interface defines a shape of a particular object, such that an object that does not fit that shape or description, is not a correct object, interfaces are very useful for data validation, avoiding datatype "collision" errors, especially with JS which assumes types silently, you could end up adding a number and string in math problem, which is very catastrophic in a "real" world application, and frankly debugging types in a weakly typed language can be a nightmare, However with interfaces you can catch such error in testing,
below is a simple object, and accompying interface, as you see the interface captures the shape of the object and type of each property.
let newObj = {
name: "sk",
age: 22,
skills:"JS, react, vue, angular, ionic",
anotherObj: {}
}
let interface_ = {
name: "string",
age: "number",
skills: "string",
anotherObj: "object"
}
by shape here I mean types of properties, rather than order, we don't care about order, maybe it is something we can look at later
So what we want to do is create a function that take newObj and interface_, then returns a proxy which "listens" for property changes(set), meaning newObject.name = "new name", on set the proxy must determine whether the new value is of the type defined in the interface, if you assign a number to name, it must throw an error, because name is a string
// takes in the interface and targetObj as params
function interface(int, targetObj){
// first we make sure the interface is an object
if(typeof int !== "object") throw new Error("not an object")
// a map is an object with a nicer api and more stuff, we cover in the OOJS section
let interface_ = new Map()
//simply here we are mapping keys which correspond to keys of the real object, to their defined types
for(let key in int){
if(typeof int[key] !== "string") throw new Error(`${key} type must be string`)
// e.g name => string
interface_.set(key, int[key])
}
/*then we have a handler object, think of it as the stand-in object,
traps are defined in this object, think of traps as triggers, triggered by an operation on the object,
for example get - which simply is property access in an object, will trigger a defined get in the handler object,*/
let handler = {
interface_, // the map we defined above
// the ff will trap setting values on the object
// target - is the target object
// key and value - {key: value} in an object
set(target, key, value){
// below this is just normal JS, checking if the given value matches the type defined in the interface
let valType = typeof value;
let keyType = this.interface_.get(key)
// if types do not match throw an error
if(typeof value !== this.interface_.get(key)) throw new Error(`cannot assign typeof ${valType} to typeof ${keyType}`)
// if the types match continue to set the value in the target object
let res = Reflect.set(target, key, value)
if(res){
console.log("set successful")
}
}
}
// return a proxy
return new Proxy(targetObj, handler)
}
to test:
/**
* @type {newObj}
*/
let interedObj = interface(interface_, newObj)
interedObj.name = "ur name"
interedObj.anotherObj = {}
interedObj.age = 23;
interedObj.skills = "react"
console.log(interedObj.name)
another test if you were to try setting name to a number, it should throw an error, meaning the interface is doing it's job
interedObj.name = 23
/*
if(typeof value !== this.interface_.get(key)) throw new Error(`cannot assign typeof ${valType} to typeof ${keyType}`)
^
Error: cannot assign typeof number to typeof string
at Object.set (C:\Users\Sifundo-Mhlungu\Downloads\Video\Js\OOP\interface.js:27:62)
at Object.<anonymous> (C:\Users\Sifundo-Mhlungu\Downloads\Video\Js\OOP\interface.js:69:17)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
at internal/main/run_main_module.js:17:47
*/
If this does not make sense, don't worry, I skipped to the cool stuff, in the eBook, I do go over meta-programing basics, and more examples
also this is far from a robust interface, for one it does not check types initially but only during set, I will cover all of that
with that said this is an excerpt from an eBook I am working on: JavaScript for advanced beginners, for people who just want a little push, a well structured push towards advanced JS, there are lot of topics covered from JS code completion, OOJS, promises, Iterators and generators, meta-programming, computational media etc
it is available as a pre-order on gumroad and should release soon