第44条: 使用 null 原型以防止原型污染
防止原型污染的最简单的方式之一就是一开始就不使用原型。但在ES5未发布之前,并没有标准的方式创建一个空原型的新对象。你可能会尝试设置一个构造函数的原型属性为 null或者undefined。
function C() { }
C.prototype = null
但实例化该构造函数仍然得到的是Object
的实例。
var o = new C();
Object.getPrototypeOf(o) === null // false
Object.getPrototypeOf(o) === Object.prototype // true
ES5首先提供了标准方法来创建一个没有原型的对象。Object.create
函数能够使用一个用户指定的原型链和一个属性描述符动态地构造对象。属性描述符描述了新对象属性的值及特性。通过简单地传递一个 null
原型参数和一个空的描述符,我们就可以建立一个真正的空对象。
var x = Object.create(null);
Object.getPrototypeOf(x) === null; // true
原型污染无法影响这样的对象的行为。
一些不支持Object.create函数的旧的JavaScript环境可能支持另一种值得一提的方式。 在许多环境中,特殊的属性一proto一(参阅第31条和第32条)提供了对对象内部原型链的读写访问。对象字面量语法也支持初始化一个原型链为null的新对象。
var x = { __proto__: null };
x instanceof Object // false (non-standard)
这样的语法同样方便,但有了Object.create
函数后,Object.create
函数是更值得信赖的方式。__proto__
属性是非标准的并且并不是所有使用都是可移植的。JavaScript的实现不能保证在未来任然支持它,所以应当尽可能地坚持使用标准的Object.create
函数。
不幸的是,虽然非标准的__proto__
可以解决一些问题,但也带来了自身额外的问题,即阻止自由原型(prototype-free)对象作为真正健壮的字典实现。第45条描述了在某些JavaScript环境中,属性关键字"__proto__"
自身是如何污染对象的,甚至可以污染没有原型的对象。如果不确定字典中从未将字符串"__proto__"
作为key
使用,那么你就应当考虑使用第45条中描述的更健壮的Dict
类。
提示
- 在ES5环境中,使用
Object.crate(null)
创建的自由原型的空对象是不太容易被污染的。 - 在一些较老的环境中,考虑使用
{__proto__: null}
。 - 但要注意
__proto__
既不标准,也不是完全可移植的,并且可能会在未来的JavaScript环境中去除。 - 绝对不要使用
"__proto__"
名作为字典中的key
,因为一些环境将其作为特殊的属性对待。