为什么是 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 都会选择以静态方法的形式出现,而不是原型方法。

相关推荐
林语琛42 分钟前
我写的 switch…break 被 Babel 偷偷吞了
前端·javascript·babel
liuchangng1 小时前
Jev 模型研究:从生成式大模型到决策式模型——System One、RLCD 校准与采用边界
java·javascript·人工智能·python·深度学习
小刘在重生~1 小时前
Java Lock 显式锁案例|ReentrantLock 解决多线程安全问题(超详细实战)
java·笔记·面试
倍利福猎头公司官方账号2 小时前
2026具身智能赛道还火热吗?机器人人选该如何思考下半年的工作机会?
人工智能·面试·职场和发展·机器人·求职招聘
我命由我123452 小时前
CSS - CSS 媒体查询 orientation
前端·javascript·css·html·css3·html5·js
SamDeepThinking4 小时前
关于java final关键字的可见性
java·后端·面试
志尊宝5 小时前
Vue3 零基础每日笔记(048):嵌套路由与命名视图——后台管理系统布局雏形
前端·javascript·vue.js·笔记·html5
此时不提桶,更待何时5 小时前
02-05-B-虚拟线程面试与生产事故实战
java·面试
@tangguo1236 小时前
npm 和 yarn 配置说明
前端·javascript·npm·node.js·yarn
志尊宝6 小时前
Vue3 零基础每日笔记(052):路由常见坑一次排雷——404、刷新丢参数、部署白屏
前端·javascript·vue.js·笔记·html5