When I first started learning JavaScript and OOP I heard over and over that JavaScript is an object-oriented language though it is not based on classes but prototypes. In this article we are going to try to understand what this means and why it is important to know what a prototype is to acknowledge what we are doing.
当我刚开始学习JavaScript和OOP时,我一遍又一遍地听说JavaScript是一种面向对象的语言,尽管它不是基于类而是基于原型。 在本文中,我们将尝试理解这意味着什么,以及为什么重要的是要知道原型是什么,以承认我们在做什么。
In other object-oriented languages when you declare a class you are creating a new complex data type, that is to say, a data type composed of primitive data types. But this is not what happens in JavaScript, even though we use the keyword class since ES2015. While a class is a blueprint, prototypes are object instances. Objects inherit directly from other objects by default in JavaScript.
在其他面向对象的语言中,当您声明一个类时,您正在创建一个新的复杂数据类型,即由原始数据类型组成的数据类型。 但这并不是JavaScript中发生的事情,即使我们从ES2015开始使用关键字class。 虽然类是一个蓝图,但是原型是对象实例。 默认情况下,对象在JavaScript中直接从其他对象继承。
要了解这意味着什么,我们需要了解原型链是什么。 (To understand what this means, we need to understand what the prototype chain is.)
The prototype chain is a tree-shaped structure that connects objects functionality and at the root of this tree is where Object.prototype lays.Object.prototype provides a few methods that show up in all objects, such as toString( ), hasOwnProperty( ) or keys( ).
原型链是连接对象功能的树形结构,在此树的根部是Object.prototype所在的位置。 Object.prototype提供了一些在所有对象中显示的方法,例如toString(),hasOwnProperty()或keys()。
Almost every object in JavaScript is an instance of Object if we follow the prototype chain. And as you probably know, almost everything in JavaScript is an object, even some primitive data types (string, boolean and number specifically) can be objects for a tiny fraction of time. So, arrays are objects, functions are objects and, of course, objects are objects.
如果遵循原型链,JavaScript中几乎每个对象都是Object的实例。 您可能知道,JavaScript中的几乎所有内容都是对象,即使某些原始数据类型(特别是字符串,布尔值和数字)也可以在很短的时间内成为对象。 因此,数组是对象,函数是对象,当然,对象是对象。
The prototype chain allows us to create instances of, for example, arrays that have access to all the methods that are available for arrays, like map, forEach, reduce, filter, and a big etc. But arrays also have access to all the Object.prototype functionalities.
原型链使我们可以创建例如数组的实例,这些实例可以访问数组可用的所有方法,例如map,forEach,reduce,filter和big等。但是数组也可以访问所有Object原型功能。
这是怎么发生的? (How does this happen?)
Just to be clear before we continue and because we are using arrays for the example, arrays are syntactic sugar in JavaScript. They are objects with a special behavior to make them look and feel like an array, but under the hood they are something like this:
在继续操作之前要弄清楚,因为我们在示例中使用数组,所以数组是JavaScript中的语法糖。 它们是具有特殊行为的对象,使它们的外观和感觉像数组一样,但是在幕后它们却是这样的:
{
'0': value,
'1': value,
'2': value
}
It turns out that every object has a property called __proto__ which holds a reference to the prototype object of the constructor. So following the array example, an array has access to all the methods in Object.prototype because every array is an instance of the Array object, and the Array object is an instance of the Object object. And this chain goes on until we hit the prototype of Object.prototype which will be null.
事实证明,每个对象都有一个名为__proto__的属性,该属性持有对构造函数原型对象的引用。 因此,在数组示例之后,一个数组可以访问Object.prototype中的所有方法,因为每个数组都是Array对象的实例,而Array对象是Object对象的实例。 这个链条一直持续到我们找到Object.prototype的原型为止,该原型将为null。

This way, when we try to execute a method on an object, first JS will lookup in the properties of the object itself. If it doesn’t find a property with that name, it will look in its __proto__ property, which holds a reference to the prototype object of its constructor. If it doesn’t find it there it will look in the __proto__ property of this constructor object. This will go on until it finds it or it doesn’t find it and throws a TypeError.
这样,当我们尝试在对象上执行方法时,首先JS将在对象本身的属性中查找。 如果找不到具有该名称的属性,它将查找其__proto__属性,该属性持有对其构造函数的原型对象的引用。 如果找不到,它将在此构造方法对象的__proto__属性中查找。 这将一直进行到找到它或找不到它并引发TypeError为止。
What this means is that, for example, every time we declare an array we are creating an instance of the Array object that comes with the language. If we look at it in the console we will see that its __proto__ property is linked to the Array object:
这意味着,例如,每次我们声明一个数组时,我们都会创建该语言附带的Array对象的实例。 如果在控制台中查看它,我们将看到其__proto__属性链接到Array对象:
And if we keep looking down the rabbit hole we’ll see that the __proto__ object has a __proto__ property itself that holds a reference to Object.prototype (it’s a reference even though you see all the properties in the console because you know, DRY).
而且,如果我们继续往下看兔子洞,我们会发现__proto__对象本身具有__proto__属性,该属性持有对Object.prototype的引用(即使您在控制台中看到了所有属性,这也是一个引用,因为您知道DRY) 。

那么,有没有一种方法可以在没有JavaScript原型的情况下创建对象? (So, is there a way to create an object without a prototype in JavaScript?)
Well, yes there is. One of the ways of creating an object is with Object.create()
, to which we can pass as an argument the prototype we want that object to have, which by default is Object.prototype
. If we pass it null as an argument we will get an object that is just that, a hash table.
好吧,是的。 创建对象的方法之一是使用Object.create()
,我们可以将该对象具有的原型作为参数传递给该对象,默认情况下为Object.prototype
。 如果将null用作参数,则将得到一个仅是哈希表的对象。
const objectWithoutPrototype = Object.create(null);
My main resources for writing this article were Eloquent JavaScript by Marijn Haverbeke and Will Sentance’s course JavaScript: The Hard Parts of Object Oriented JavaScript.
我写这篇文章的主要资源是Marijn Haverbeke的Eloquent JavaScript和Will Sentance的课程JavaScript:面向对象JavaScript的硬部分。
Thanks for reading ❤
感谢您阅读❤