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

相关推荐
考研保研资料分享27 分钟前
2022双非保研华科热能工程:三校考核、传热题与报名复盘
面试
weixin_307779136 小时前
RAG 面试陷阱——混合检索并非无条件优越:一个条件反转实验
人工智能·python·面试·职场和发展
Dxy12393102167 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷7 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
nice_lcj5208 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试
椒哥9 小时前
一天被问几十次"允许",我统计了 517 次,然后写了个插件
前端·javascript·github
kyriewen10 小时前
我测了 6 种防请求竞态的写法:只有 1 种真能打
前端·javascript·程序员
OpsEye10 小时前
企业大模型压测,需要重点关注哪些指标?
javascript·ai编程
D e re k ZH11 小时前
C++ 并发、未定义行为与工程化难点(No 3)
数据结构·c++·算法·面试
像我这样帅的人丶你还11 小时前
我html生成的doc文档为何在手机文档显示不了图片?
前端·javascript