JavaScript闭包如何直接修改内部属性?

文摘   2024-08-29 19:39   吉林  

事例代码

  var people = (function () {
      var obj = {  name'ck'age18  };
      return {
          getfunction (k) {
              return obj[k];
          }
      };
  })();

说明

如上一段代码,在不改变原代码的情况下,修改obj对象中的name、age属性。

分析

代码使用了闭包,返回一个对象,对象中有一个get函数,这个get函数只能通过peopel.get('a')的方式查看obj中的属性 如果想要直接修改是无法修改的

所有对象都有一个原型链,该原型链继承自Object,Object上有一个valueOf属性,该属性为一个函数,执行可以返回对象自身

  console.log(people.get('valueOf'))

如上代码,可以打印出来obj的valueOf结构了,直接执行console.log(people.get('valueOf')()),发现已经报错了,这里报错的原因是因为在执行valueOf的this指向错误。

如下代码,演示this指向:

const obj = Object.prototype.valueOf
console.log(obj()) // 报错

const obj = Object.prototype.valueOf()
console.log(obj) // 可以获取Object对象

那么标志着这种方式行不通

突破

这段代码定义了一个名为 ck 的 getter 方法在 Object.prototype 上,这意味着所有 JavaScript 对象现在都有一个属性 ck,当访问这个属性时,会返回该对象自身。

const admin = { id1 };
Object.defineProperty(Object.prototype'ck', {
    get() {
        return this
    }
})
console.log(admin.ck); // 输出:{ a: 1 }

以上代码是在Object的原型上添加了一个ck属性的访问器,因为是在Object的原型上,所以其他属性也可以读取ck属性 Object原型上的ck属性的访问器,返回了this,这里的this是只要哪个对象访问这个属性,this就指向哪个对象,这样就解决了this指向的问题

  var people = (function () {
      var obj = {   name'ck',   age18  };
      return {
          getfunction (k) {
              return obj[k];
          }
      };
  })();

  Object.defineProperty(Object.prototype'ck', {
      get() {
          return this
      }
  })
  console.log(people.get('ck'));

当people.get('ck')执行的时候,会触发o对象返回的get方法中返回的obj['ck'],这里就相当于people中的obj访问了ck属性,所以这里读取people.get('ck'),但是obj身上没有ck属性,就会读Object身上的ck属性,就会触发ck属性访问器中的get方法,那么this指向obj,也就是obj对象

在此时我们就可以修改obj属性值了

people.get('ck').a = 100

people.get('ck').b = 200

console.log(people.get('a'), people.get('b')); 
// 100 200

console.log(people.get('ck')) 
//{ name: 'ck', age: 18, a: 100, b: 200 }

注意事项

继承影响:由于这是在 Object.prototype 上的操作,所有 JavaScript 对象都会继承这个属性。

性能问题:修改原型链可能会导致性能问题,特别是在大型应用中。

兼容性和安全性:直接修改 Object.prototype 可能会影响其他库或框架的行为,并且可能带来安全风险。

建议

谨慎使用:在实际项目中,尽量避免直接修改 Object.prototype,因为这可能会导致不可预见的问题。

局部作用域:如果确实需要这样的功能,可以在局部作用域内定义类似的功能,而不是全局修改原型链。

替代方案:

如果你需要在特定对象上实现类似的功能,可以考虑以下方法:

  function addCKProperty(obj) {
      Object.defineProperty(obj, 'ck', {
          get() {
              return this;
          }
      });
  }

  const obj = { a1 };
  addCKProperty(obj);
  console.log(obj.ck); // 输出:{ a: 1 }

如何避免这种情况

判断对象属性是否存在

可以在读取obj的属性的时候加一个判断

var people = (function() {
   var obj = {  name'ck'age18  };
    return {
        getfunction(k) {
            if (obj.hasOwnProperty(k)) {
                return obj[k]
            }
            return undefined
        }
    }
})()

Object.defineProperty(Object.prototype'ck', {
    get() {
        return this
    }
})
people.get('ck').a = 100

people.get('ck').b = 200

console.log(people.get('a'), people.get('b')); 
// 报错 

console.log(people.get('ck')) 
//报错

这样处理后就无法读取ck属性了,以为ck属性不是obj身上存在的属性

将对象原型设置为null

var people = (function() {
   var obj = {  name'ck'age18  };
    Object.setPrototypeOf(obj, null)
    return {
        getfunction(k) {
            return obj[k]
        }
    }
})()

Object.defineProperty(Object.prototype'ck', {
    get() {
        return this
    }
})
people.get('ck').a = 100

people.get('ck').b = 200

console.log(people.get('a'), people.get('b')); 
// 报错 

console.log(people.get('ck')) 
//报错

这样obj就不存在原型了,无法去Object身上读取ck属性

我希望这篇文章对您有所帮助。

Thank you for reading.

推荐阅读

全栈开发ck
叩首问路,码梦为生