This article is going to look at object prototypes and how we can use constructor functions to extend prototypes in new objects.
本文将研究对象原型,以及如何使用构造函数扩展新对象中的原型。
JavaScript is a prototype-based language, which means object properties and methods can be shared through objects that have the ability to be cloned and extended. This is known as prototypal inheritance.
JavaScript是一种基于原型的语言 ,这意味着可以通过具有克隆和扩展功能的对象共享对象的属性和方法。 这被称为原型继承。
In my previous article, Understanding objects in JavaScript, I went over how to create an object, how to access object properties and how to modify them. Now we are going to learn how prototypes can be used to extend objects.
在上一篇文章“ 理解JavaScript中的对象”中 ,我介绍了如何创建对象,如何访问对象属性以及如何对其进行修改。 现在,我们将学习如何使用原型来扩展对象。
Nearly all objects in JavaScript are instances of Object
which sit on the top of a prototype chain. This means they have an internal property called prototype
. The prototype
is a reference to another object and it gets used whenever JavaScript can’t find the property it is looking for on the current object.
JavaScript中几乎所有对象都是位于原型链顶部的Object
实例。 这意味着它们具有一个称为prototype
的内部属性。 prototype
是对另一个对象的引用,当JavaScript在当前对象上找不到它要查找的属性时,就会使用该prototype
。
We can check this if we create a new, empty object.
如果我们创建一个新的空对象,我们可以检查一下。
const newObject = {};console.log(newObject);
If we look in the console we can see the __proto__
property which exposes the internal prototype
of the object. This consists of several built-in properties and methods.
如果在控制台中查看,则可以看到__proto__
属性,该属性公开了对象的内部prototype
。 这由几个内置的属性和方法组成。
{}
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
To get the prototype
of this new object we use the getPrototypeOf() method.
为了获得这个新对象的prototype
,我们使用getPrototypeOf()方法 。
const newObject = {};const prototype = Object.create(newObject);console.log(Object.getPrototypeOf(prototype) === newObject); // true
Let’s look at a more detailed code example. We are going to create a Sandwich
constructor. We will then create an instance of Sandwich
and assign it to a new variable called mySandwich
.
让我们看一个更详细的代码示例。 我们将创建一个Sandwich
构造函数。 然后,我们将创建Sandwich
的实例,并将其分配给名为mySandwich
的新变量。
function Sandwich(fillings = [], customer, bread) {
this.fillings = fillings;
this.customer = customer;
this.bread = bread;
this.describe = function() {
return `This sandwich is for ${this.customer} with the toppings ${this.fillings.join(', ')} on ${this.bread} bread`;
}
}const mySandwich = new Sandwich(['cheese', 'ham', 'lettuce', 'tomato', 'cucumber'], 'Gemma', 'wholemeal');console.log(mySandwich.describe());// This sandwich is for Gemma with the toppings cheese, ham, lettuce, tomato, cucumber on wholemeal bread
If we look at mySandwich
in the console we can see the describe function exists as an instance property.
如果在控制台中查看mySandwich
,我们可以看到describe函数作为实例属性存在。
mySandwich
Sandwich {fillings: Array(5), customer: "Gemma", bread: "wholemeal", describe: ƒ}
bread: "wholemeal"
customer: "Gemma"
describe: ƒ ()
fillings: (5) ["cheese", "ham", "lettuce", "tomato", "cucumber"]
__proto__: Object
Let’s create another instance of Sandwich
using some different parameters.
让我们使用一些不同的参数创建另一个Sandwich
实例。
const mySandwichVeggie = new Sandwich(['lettuce', 'tomato', 'onion', 'pickles', 'olives', 'capscicum', 'spinach', 'carrot'], 'Sam', 'white');console.log(mySandwichVeggie.describe());// This sandwich is for Sam with the toppings lettuce, tomato, onion, pickles, olives, capscicum, spinach, carrot on white bread
What happens if we directly compare our newly created sandwich describe()
function with the original mySandwich
describe
method.
如果我们直接将新创建的mySandwich
describe
describe()
函数与原始mySandwich
describe
方法进行比较,会发生什么情况。
mySandwich.describe === mySandwichVeggie.describe // false
They are not the same function; we are duplicating functionality. If we create 50 sandwiches we would have 50 copies of the same function which could potentially cause performance issues and make our code hard to maintain.
它们是不同的功能。 我们正在复制功能。 如果我们创建50个三明治,我们将有50个相同功能的副本,这可能会导致性能问题并使我们的代码难以维护。
To solve the duplication issue we can add this functionality to the prototype
.
为了解决复制问题,我们可以将此功能添加到prototype
。
Sandwich.prototype.describe = function() {
return `This sandwich is for ${this.customer} with the toppings ${this.fillings.join(', ')} on ${this.bread} bread`;
}
Now if we look at mySandwich
or mySandwichVeggie
, describe()
no longer exists as an instance property, it is, however, visible in the __proto__
.
现在,如果我们查看mySandwich
或mySandwichVeggie
, describe()
不再作为实例属性存在,但是它在__proto__
可见。
mySandwich
Sandwich {fillings: Array(5), customer: "Gemma", bread: "wholemeal"}
bread: "wholemeal"
customer: "Gemma"
fillings: (5) ["cheese", "ham", "lettuce", "tomato", "cucumber"]
__proto__:
describe: ƒ ()
constructor: ƒ Sandwich(fillings = [], customer, bread)
__proto__: Object
This means that every time we create a new Sandwich
we now have access to the describe()
function.
这意味着,每次创建新的Sandwich
,现在都可以访问describe()
函数。
JavaScript will check for a property on the instance first and if it doesn’t exist it will check the prototype
.
JavaScript将首先在实例上检查属性,如果不存在,它将检查prototype
。
If we add a new instance property to the prototype
and to the instance itself we can see this in action.
如果我们向prototype
和实例本身添加新的实例属性,那么我们可以看到这一点。
function Sandwich(fillings = [], customer, bread) {
this.fillings = fillings;
this.customer = customer;
this.bread = bread;
this.price = '5.50';
}Sandwich.prototype.price = '4.00';mySandwich
Sandwich {fillings: Array(5), customer: "Gemma", bread: "wholemeal", price: "5.50"}
bread: "wholemeal"
customer: "Gemma"
fillings: (5) ["cheese", "ham", "lettuce", "tomato", "cucumber"]
price: "5.50"
__proto__: Object
The price of mySandwich
is 5.50
because JavaScript doesn’t need to go to the prototype
, the property exists on the instance.
mySandwich
的价格为5.50
因为JavaScript不需要进入prototype
,该属性存在于实例上。
Summary
摘要
- JavaScript is a prototype-based language JavaScript是一种基于原型的语言
- Prototypal inheritance means object properties and methods can be shared through objects that have the ability to be cloned and extended 原型继承意味着可以通过具有克隆和扩展功能的对象共享对象属性和方法
Nearly all objects in JavaScript are instances of Object which sit on the top of a prototype chain which means they have an internal property called
prototype
JavaScript中几乎所有对象都是位于原型链顶部的Object实例,这意味着它们具有一个称为
prototype
的内部属性。The
prototype
is a reference to another object and it gets used whenever JavaScript can’t find the property it is looking for on the current objectprototype
是对另一个对象的引用,当JavaScript在当前对象上找不到它要查找的属性时,就会使用该prototype
JavaScript will first check for a property on the instance then check the
prototype
JavaScript将首先检查实例上的属性,然后检查
prototype