Many programming languages provide const keywords to declare a constant, and ES6 provides const, but const on the front end, unlike other programming languages, does not mean that the declared variable is a constant. Const b = {} declares a constant b, but there is no error by modifying object B with b = 1. We are modifying an object that we thought was a constant but was actually a variable.

Why is that?

In fact, const variables hold Pointers to actual data. For basic data types String, Boolean, Number, undefined, NULL, and Symbol, their values are stored in simple data segments on the stack and accessed by value, which is equivalent to constants. But as with reference data types, const only guarantees that Pointers to objects stored in heap memory remain unchanged. In other words, const guarantees that variables always point to the same object.

So, how exactly do you implement a constant in the front end!

Object.freeze

Object.freeze You can freeze an Object and cannot add or delete attributes. In addition, existing attributes of the Object cannot be enumerated, configured, or written. It should be noted that this method can only allow the object to be shallow frozen, and its internal properties can still be tampered with when the object is an object. To achieve complete freezing, the following operations are required.

function deepConst(data){
  Object.freeze(data);
  for(let key in data){
    let prop = data[key];
    if(! data.hasOwnProperty(key) || ! (typeof prop === "object") | |Object.isFrozen(prop)){
      continue; } deepConst(prop); }}Copy the code

Object.defineproperty, Object.preventExtensions, Object.seal

Object.preventExtensions

This method can make the object unextensible, that is, the object cannot add new attributes, but the original attributes of the object can still be deleted or modified. At the same time, if the value of the attribute is the object, the attribute whose value is the object can still be added although the attribute cannot be added.

Here’s an example:

let obj = {a:1.b:2.c: {d:3}};
Object.preventExtensions(obj);
obj.d = 1;
obj.a = 2;
delete obj.b;
obj.c.e = 10;
/ / o {a: 1, c: {10} d: 3, e:
console.log(obj);
Copy the code

Object.seal

In contrast to object.preventExtensions, this method can also change the Object to be unable to add new properties, and it prevents deleting properties of the Object. Similarly, if the value of an attribute is an object, the attribute value can still add new attributes or delete attributes.

For example

let obj = {a:1.b:2.c: {d:3}};
Object.seal(obj);
obj.e = 10;
delete obj.a;
delete obj.c.d;
obj.c.f = 10;
/ / o {2, a: 1, b: c: {10} f:
console.log(obj);
Copy the code

Object.defineProperty

Object.defineproperty (obj, prop, Descriptor) shines in MVVM, which can also be used to freeze objects completely. Before writing the code, we need to know the writable and different information, which is the key of the freeze.

writable

Whether the value of an object property can be overwritten. True indicates that the value is allowed, false indicates that the value is disallowed, and false is the default. If the value of a property is an object, the value of the property can be overridden despite being set not to be overridden.

Here’s an example:

let obj = {a:1.b:2.c: {d:3}};
Object.defineProperty(obj,"a", {writable:true});
Object.defineProperty(obj,"b", {writable:false});
Object.defineProperty(obj,"c", {writable:false});
Object.defineProperty(obj,"e", {writable:false});
obj.a = 2;
obj.b = 3;
obj.c.d = 4;
// The output is 2, that is, the value of the a attribute has been overridden
console.log(obj.a);
// The output is still 2, that is, the value of the b attribute has not been overridden
console.log(obj.b);
{d:4} {d:4} {d:4} {d:4} {d:4} {d:4}
console.log(obj.c);
Copy the code
Configurable

The 64x feature indicates whether the attributes of the object can be deleted and whether any other features other than writable can be modified. The default value is false. If the value of a property is an object, the property whose property is an object can be modified even though the property cannot be modified. For example

let obj = {a:1.b:2.c: {d:3}};
Object.defineProperty(obj,"a", {configurable:true});
Object.defineProperty(obj,"b", {configurable:false});
Object.defineProperty(obj,"c", {configurable:false});
delete obj.a;
delete obj.b;
delete obj.c;
{b:2,c:{}}, if the value of the attribute is an object, the attribute can be modified even though the attribute cannot be modified.
console.log(obj);
Copy the code

The above three methods alone don’t do a perfect job of turning an object into a constant, but we can combine them to produce a constant.

function deepConst(data){
  if(! data ||typeofdata ! = ='object') {
    return;
  }
  //Object.preventExtensions(data); It can also be implemented
  Object.seal(data);
  Object.keys(data).forEach(function(key) {
    unWriteConfig(data, key, data[key]);
  });
}
function unWriteConfig(data, key, val) {
  deepConst(val);
  Object.defineProperty(data, key, {
    writable:false.configurable:false
  });
}
Copy the code

Proxy

The Proxy carries out a layer of interception before the target object, and the external access and modification of the object need to pass this layer of interception, so we can control the interception to control the access and modification of the object. There are many interception operations supported by Proxy, and the following are only related to this article. If you want to learn more about Proxy, see this article.

function createDeepProxy(target) {
  function makeHandler() {
    return {
      set(target, key, value, receiver) {
        return false;
      },
      deleteProperty(target, key) {
        return false; }}}function proxify(obj, path) {
    for(let key of Object.keys(obj)) {
      if(typeof obj[key] === 'object') { obj[key] = proxify(obj[key], [...path, key]); }}let p = new Proxy(obj, makeHandler());
    return p;
  }
  return proxify(target, []);
}
Copy the code