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

相关推荐
软件开发技术深度爱好者38 分钟前
小学英语单词魔法学园HTML5实现
javascript·html5·英语学习
勉灬之43 分钟前
Next.js + Prisma 跨平台部署踩坑记
开发语言·javascript·ecmascript
糖果店的幽灵1 小时前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph
我叫黑大帅1 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
吃饱了得干活1 小时前
亿级订单表分库分表设计,从0到1全流程
java·数据库·面试
这是个栗子1 小时前
前端开发中的常用工具函数(九)
开发语言·javascript·ecmascript·at
Revolution613 小时前
一段 JavaScript 代码执行时,到底发生了什么
前端·javascript
程序员爱钓鱼4 小时前
Go 开发环境安装(Windows、macOS、Linux)
后端·面试·go
触底反弹4 小时前
🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚!
人工智能·设计模式·面试
uzong11 小时前
一个好的面试官,应该让候选人感到被尊重
面试