事例代码
var people = (function () {
var obj = { name: 'ck', age: 18 };
return {
get: function (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 = { id: 1 };
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', age: 18 };
return {
get: function (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 = { a: 1 };
addCKProperty(obj);
console.log(obj.ck); // 输出:{ a: 1 }
如何避免这种情况
判断对象属性是否存在
可以在读取obj的属性的时候加一个判断
var people = (function() {
var obj = { name: 'ck', age: 18 };
return {
get: function(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', age: 18 };
Object.setPrototypeOf(obj, null)
return {
get: function(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.