JavaScript提供了大量的内置方法,用于执行一些重要和常用的操作。

通常而言,只要我们不重写这些方法。直接使用他们就好了。

但是在某些情况下,或者当我们需要和第三方代码打交道,或者我们需要处理我们不能控制的数据时,这可能引发问题。

下面以对象的hasOwnProperty方法为例进行说明。

hasOwnProperty方法通常用于遍历对象属性时,对继承自原型对象的属性进行过滤。(有更好的方法实现对对象实例属性的遍历,这里不做讨论。)

1
2
3
4
5
6
7
8
9
10
const obj = {
  foo: 1,
  bar: 2
};

for ( let key in obj ) {
  if ( obj.hasOwnProperty(key) ) {
    // do something with own property
  }
}

因为hasOwnproperty实际存在于原型链顶端Object.prototype对象中,当某个对象的原型链中不包含Object.prototype时,就会报错。

1
2
3
4
5
6
7
8
9
10
const obj = Object.create(null);

obj.foo = 1;
obj.bar = 2;

for ( let key in obj ) {
  if ( obj.hasOwnProperty(key) ) {    // => Uncaught TypeError: obj.hasOwnProperty is not a function
    // do something with own property
  }
}

除了对象的原型链中没有Object.prototype时会引发问题外,在原型链上的任何地方对hasOwnproperty方法进行重新定义,也会引发问题。

因为对hasOwnproperty的搜索是从当前对象开始,沿着原型链一级一级进行的,如果在到达Object.prototype之前就遇到了hasOwnProperty属性就会使用。

1
2
3
4
5
6
7
8
9
10
11
const obj = {
  foo: 1,
  bar: 2,
  hasOwnProperty: ''
};

for ( let key in obj ) {
  if ( obj.hasOwnProperty(key) ) {    // => Uncaught TypeError: obj.hasOwnProperty is not a function
    // do something with own property
  }
}

对于这两种情况的解决方法也很简单,直接从Object.prototype中获取hasOwnproperty方法,然后使用call/apply进行调用。

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
  foo: 1,
  bar: 2
};

const hasOwn = Object.prototype.hasOwnproperty;

for ( let key in obj ) {
  if ( hasOwn.call(obj, key) ) {
    // do something with own property
  }
}

这个方法可以在很多流行的库的源码中看到。不过,一般的场景下,不需要这么复杂,直接使用就是了。在健壮性要求较高时最好用上,比如造轮子时。

当然这个方法也没有办法解决重写Object.prototype.hasOwnProperty的问题,不过谁要真这么玩也没办法拦着,只能默默送上一句「祝你好运」。