为什么是 Object.keys 而不是 Object.prototype.keys ?

开发中的疑问

有时候在网页开发的过程中,我们会遇到这两个函数:

JavaScript 复制代码
Object.prototype.toString.call()
Object.keys()

有的前端小伙伴可能就会产生这样的疑问:为什么 keys 方法存在 Object 上而不是 Object. prototype 上?有什么讲究吗?

答案是肯定的,本质上,就是 Object 原型方法和 Object 静态方法的区别。

不过在这之前,我们需要复习一下构造函数、对象原型以及对象的创建方法。

构造函数与对象原型

如图:

  1. 对象的 __proto__ 属性就是对象的原型对象。
  2. 原型对象的 constructor 属性也指向对象的构造函数。
  3. 构造函数的 prototype 属性指向对象的原型对象。

对象的 constructor

只有 prototype 才有 constructor,普通对象的 constructor 继承自其原型对象的 constructor。 为了方便理解,下面我们不再提及到非原型对象的这个指针。

原型链

实际上,对象原型和构造函数也是一个对象,如果把它们放到"实例对象"的位置,它们也拥有自己的对象原型和构造函数,于是,原型链(套娃)便开始了。

思考一下:

  1. Function 以及 Function. prototype 的__proto__指向哪里?
  2. Object 以及 Object. prototype 的__proto__指向哪里?
  3. Foo(用户自定义构造函数)以及 Foo. prototype 的__proto__指向哪里?

指向如图:

  1. 所有构造函数都是由 Function 创建的,所以 proto 指向 Function. prototype。
  2. 除了 Object. prototype,其它原型都是 {} ,而 {} 的 proto 显然指向 Object. prototype。
  3. Object. prototype 与其它 prototype 不同,它是 [Object:null prototype]{} 而不是 {}。它的 proto 指向 null。
  4. null 是基本类型,不具备任何属性。

创建对象的三种方法

  1. 使用大括号创建。这种创建方法隐式继承了 Object。也就是说,效果和 new Object 是一样的。普通的工厂函数创建的对象也属于这一类。
  2. 使用构造函数或者类创建。需要使用关键字 new。并会继承该构造函数的 prototye 属性作为实例对象的 proto。
  3. 使用 Object. create 静态方法创建。该方法接收一个对象(typeof 为 Object 的值,因此也允许参数为 null),作为创建对象的原型。如果 Object. create 的参数为 null,那么它和 Object. prototype 的辈分是相当的。打印的结果都是 [Object:null prototype]{}

原型方法与静态方法

有了前面这些基础知识的铺垫,相信很快就能理解 Object. prototype 和 Object 静态方法的区别,其实非常简单。

几乎所有的对象的原型链上都存在 Object. prototype,因此,放在 Object. prototype 这个对象的方法都能被大多数对象拿到。但是:

  1. 如果这个对象是通过 Object.create(null) 方式创建的,那么它就和 Object. prototype 对象同级,无法通过继承拿到 Object. prototype 对象的方法。
  2. 另外,在继承的过程中,实例对象到达 Object. prototype 的原型链上的方法往往可能被重写。

基于以上两点,我们只能避免直接调用实例对象的方法,而是通过类似 Object.prototype.xxx.call(obj,...args) 这样长的代码来保证使用的是 Object 的原型方法。

而 Object 静态方法不管对什么对象,都可以直接使用,而且也更简短。

可以说,对象原型(尤其是比较长的原型链上)的方法背弃了其设计初衷:通过实例对象就可以调用到原型链上的方法。

在新的 EcmaScript 的语法的 Api 中,更多的是以静态方法出现而不是原型方法,比如 Proxy、Reflect 的 Api,一方面就是为了避免上述问题,另一方面是为了避免一些冲突(开发者在原型上定义的方法刚好和新语法的名字一样)。所以,不难理解,以后越来越多的 Api 都会选择以静态方法的形式出现,而不是原型方法。

相关推荐
且去填词1 小时前
Go 语言的“反叛”——为什么少即是多?
开发语言·后端·面试·go
哈__3 小时前
React Native 鸿蒙跨平台开发:PixelRatio 像素适配
javascript·react native·react.js
用户6387994773054 小时前
每组件(Per-Component)与集中式(Centralized)i18n
前端·javascript
青莲8434 小时前
RecyclerView 完全指南
android·前端·面试
青莲8434 小时前
Android WebView 混合开发完整指南
android·前端·面试
DarkLONGLOVE4 小时前
Vue组件使用三步走:创建、注册、使用(Vue2/Vue3双版本详解)
前端·javascript·vue.js
DarkLONGLOVE4 小时前
手把手教你玩转Vue组件:创建、注册、使用三步曲!
前端·javascript·vue.js
冴羽5 小时前
2026 年前端必须掌握的 4 个 CSS 新特性!
前端·javascript·css
狗头大军之江苏分军6 小时前
告别旧生态:Ant Design 6 不再支持 IE 与现代前端趋势解读
前端·javascript·后端
Highcharts.js6 小时前
Highcharts Grid 表格/网格安装 |官方安装文档说明
开发语言·javascript·表格组件·highcharts·官方文档·安装说明·网格组件