This post is designed to be read after you read JavaScript Private and Public Class Fields.
阅读这篇文章后,请阅读JavaScript私有和公共类字段 。
If you prefer to watch a video instead,
如果您想观看视频,
Previously we learned how to create an Animal
class both in ES5 as well as in ES6. We also learned how to share methods across those classes using JavaScript's prototype. To review, here's the code we saw in an earlier post.
以前,我们学习了如何在ES5和ES6中创建Animal
类。 我们还学习了如何使用JavaScript的原型在这些类之间共享方法。 回顾一下,这是我们在较早的文章中看到的代码。
function Animal (name, energy) {
this.name = name
this.energy = energy
}
Animal.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Animal.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Animal.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
const leo = new Animal('Leo', 7)
class Animal {
constructor(name, energy) {
this.name = name
this.energy = energy
}
eat(amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
sleep() {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
play() {
console.log(`${this.name} is playing.`)
this.energy -= length
}
}
const leo = new Animal('Leo', 7)
Now let's say we wanted to start making individual classes for specific animals. For example, what if we wanted to start making a bunch of dog instances. What properties and methods will these dogs have? Well, similar to our Animal
class, we could give each dog a name
, an energy
level, and the ability to eat
, sleep
, and play
. Unique to our Dog
class, we could also give them a breed
property as well as the ability to bark
. In ES5, our Dog
class could look something like this
现在,我们要开始为特定动物制作单独的类。 例如,如果我们想开始制作一堆狗实例,该怎么办? 这些狗将具有哪些特性和方法? 好吧,类似于我们的Animal
课,我们可以给每只狗起一个name
,一个energy
水平以及eat
, sleep
和play
的能力。 对于我们的“ Dog
类来说,这是独一无二的,我们还可以为它们提供breed
特性以及bark
的能力。 在ES5中,我们的Dog
类可能看起来像这样
function Dog (name, energy, breed) {
this.name = name
this.energy = energy
this.breed = breed
}
Dog.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Dog.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Dog.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
Dog.prototype.bark = function () {
console.log('Woof-Woof!')
this.energy -= .1
}
const charlie = new Dog('Charlie', 10, 'Goldendoodle')
Alright, well... we just recreated the Animal
class and added a few new properties to it. If we wanted to create another animal, say a Cat
, at this point we'd again have to create a Cat
class, duplicate all the common logic located in the Animal
class to it, then add on Cat
specific properties just like we did with the Dog
class. In fact, we'd have to do this for each different type of animal we created.
好吧,好吧...我们只是重新创建了Animal
类并为其添加了一些新属性。 如果我们想创建另一只动物,比如说Cat
,这时我们将不得不再次创建Cat
类,向其复制Animal
类中的所有通用逻辑,然后像我们对Cat
那样添加特定于Cat
属性Dog
类。 实际上,我们必须对创建的每种不同类型的动物执行此操作。
function Dog (name, energy, breed) {}
function Cat (name, energy, declawed) {}
function Giraffe (name, energy, height) {}
function Monkey (name, energy, domesticated) {}
This work, but it seems wasteful. The Animal
class is the perfect base class. What that means is that it has all the properties that each one of our animals has in common. Whether we're creating a dog, cat, giraffe, or monkey, all of them will have a name
, energy
level, and the ability to eat
, sleep
, and play
. With that said, is there a way we can utilize the Animal
class whenever we create the individual classes for each different animal? Let's try it out. I'll paste the Animal
class again below for easy reference.
这项工作,但似乎很浪费。 Animal
类是完美的基础类。 这意味着它具有我们每只动物所共有的所有属性。 无论我们要创建狗,猫,长颈鹿还是猴子,它们都将具有name
, energy
水平以及eat
, sleep
和play
的能力。 话虽如此,每当为每种不同的动物创建单独的类时,有没有一种方法可以利用Animal
类? 让我们尝试一下。 我将在下面再次粘贴Animal
类,以方便参考。
function Animal (name, energy) {
this.name = name
this.energy = energy
}
Animal.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Animal.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Animal.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
function Dog (name, energy, breed) {
}
What are some things we know about the Dog
constructor function above?
关于上方的Dog
构造函数,我们了解些什么?
First, we know it takes 3 arguments, name
, energy
, and breed
.
首先,我们知道它需要3个参数,即name
, energy
和breed
。
Second, we know it's going to be called with the new
keyword so we'll have a this
object.
其次,我们知道将使用new
关键字调用它,因此我们将拥有一个this
对象。
And third, we know we need to utilize the Animal
function so that any instance of dog will have a name
, energy
level, and be able to eat
, sleep
, and play
.
第三,我们知道我们需要利用Animal
功能,以便任何狗实例都具有name
, energy
水平,并且能够eat
, sleep
和play
。
It's the third one that's the tricky one. The way you "utilize" a function is by calling it. So we know that inside of Dog
, we want to call Animal
. What we need to figure out though is how we can invoke Animal
in the context of Dog
. What that means it that we want to call Animal
with the this
keyword from Dog
. If we do that correctly, then this
inside of the Dog
function will have all the properties of Animal
(name
, energy
). If you remember from a previous section, every function in JavaScript has a .call
method on it.
这是第三个棘手的问题。 您“利用”函数的方式是通过调用它。 因此,我们知道在Dog
内部,我们要称呼Animal
。 我们需要弄清楚的是,如何在Dog
的上下文中调用Animal
。 这意味着我们要使用Dog
的this
关键字来调用Animal
。 如果我们这样做的正确,那么this
将里面Dog
功能将具有的所有属性Animal
( name
, energy
)。 如果您记得上一节 ,那么JavaScript中的每个函数都带有一个.call
方法。
.call() is a method on every function that allows you to invoke the function specifying in what context the function will be invoked.
.call()是每个函数上的一种方法,它使您可以调用该函数,以指定将在哪个上下文中调用该函数。
This sounds like exactly what we need. We want to invoke Animal
in the context of Dog
.
这听起来完全符合我们的需求。 我们想在Dog
的上下文中调用Animal
。
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
const charlie = new Dog('Charlie', 10, 'Goldendoodle')
charlie.name // Charlie
charlie.energy // 10
charlie.breed // Goldendoodle
Solid, we're half-way there. You'll notice in the code above that because of this line Animal.call(this, name, energy)
, every instance of Dog
will now have a name
and energy
property. Again, the reason for that is because it's as if we ran the Animal
function with the this
keyword generated from Dog
. Then after we added a name
and energy
property to this
, we also added a breed
property just as we normally would.
扎实,我们已经到了一半。 您会在上面的代码中注意到,由于此行Animal.call(this, name, energy)
, Dog
每个实例现在都具有name
和energy
属性。 同样,这样做的原因是因为这就像我们使用Dog
生成的this
关键字运行了Animal
函数一样。 再经过我们增加了一个name
和energy
属性为this
,我们还增加了一个breed
特性就像我们通常会。
Remember the goal here is to have each instance of Dog
have not only all the properties of Animal
, but also all the methods as well. If you run the code above, you'll notice that if you try to run charlie.eat(10)
you'll get an error. Currently every instance of Dog
will have the properties of Animal
(name
and energy
), but we haven't done anything to make sure that they also have the methods (play
, eat
, sleep
).
请记住,这里的目标是使Dog
每个实例不仅具有Animal
所有属性,而且还具有所有方法。 如果运行上面的代码,您会注意到,如果尝试运行charlie.eat(10)
,则会收到错误消息。 当前, Dog
每个实例都将具有Animal
的属性( name
和energy
),但是我们没有做任何事情来确保它们也具有方法( play
, eat
, sleep
)。
Let's think about how we can solve this. We know that all the Animal
's methods are located on Animal.prototype
. What that means is we somehow want to make sure that all instances of Dog
will have access to the methods on Animal.prototype
. What if we used our good friend Object.create
here? If you'll remember, Object.create
allows you to create an object which will delegate to another object on failed lookups. So in our case, the object we want to create is going to be Dog
's prototype and the object we want to delegate to on failed lookups is Animal.prototype
.
让我们考虑一下如何解决这个问题。 我们知道所有Animal
方法都位于Animal.prototype
。 这意味着我们要以某种方式确保Dog
所有实例都可以访问Animal.prototype
上的方法。 如果我们在这里使用我们的好朋友Object.create
怎么办? 如果您还记得的话,可以使用Object.create
创建一个对象,该对象将在失败的查找时委派给另一个对象。 因此,在本例中,我们要创建的对象将是Dog
的原型,而在失败的查找中我们要委派的对象是Animal.prototype
。
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Now, whenever there's a failed lookup on an instance of Dog
, JavaScript will delegate that lookup to Animal.prototype
. If this is still a little fuzzy, re-read A Beginner's Guide to JavaScript's Prototype where we talk all about Object.create
and JavaScript's prototype.
现在,只要在Dog
实例上查找失败,JavaScript就会将该查找委托给Animal.prototype
。 如果这仍然有些模糊,请重新阅读JavaScript原型初学者指南,其中我们将讨论Object.create
和JavaScript的原型。
Let's look at the full code together then we'll walk through what happens.
让我们一起看完整的代码,然后我们将逐步了解发生的情况。
function Animal (name, energy) {
this.name = name
this.energy = energy
}
Animal.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Animal.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Animal.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Now we've created our base class (Animal
) as well as our subclass (Dog
), let's see what it looks like under the hood when we create an instance of Dog
.
现在,我们已经创建了基类( Animal
)和子类( Dog
),让我们看看创建Dog
的实例时的外观。
const charlie = new Dog('Charlie', 10, 'Goldendoodle')
charlie.name // Charlie
charlie.energy // 10
charlie.breed // Goldendoodle
Nothing fancy so far, but let's look at what happens when we invoke a method located on Animal
.
到目前为止还算不上什么,但是让我们看一下调用位于Animal
上的方法时会发生什么。
charlie.eat(10)
/*
1) JavaScript checks if charlie has an eat property - it doesn't.
2) JavaScript then checks if Dog.prototype has an eat property
- it doesn't.
3) JavaScript then checks if Animal.prototype has an eat property
- it does so it calls it.
*/
The reason Dog.prototype
gets checked is because when we created a new instance of Dog
, we used the new
keyword. Under the hood, the this
object that was created for us delegates to Dog.prototype
(seen in comments below).
Dog.prototype
被检查的原因是因为当我们创建Dog
的新实例时,我们使用了new
关键字。 在Dog.prototype
为我们创建的this
对象委托给Dog.prototype
(在下面的评论中看到)。
function Dog (name, energy, breed) {
// this = Object.create(Dog.prototype)
Animal.call(this, name, energy)
this.breed = breed
// return this
}
The reason Animal.prototype
gets checked is because we overwrote Dog.prototype
to delegate to Animal.prototype
on failed lookups with this line
Animal.prototype
被检查的原因是因为我们Dog.prototype
以便在失败的情况下通过此Dog.prototype
其委托给Animal.prototype
Dog.prototype = Object.create(Animal.prototype)
Now one thing we haven't talked about is what if Dog
has its own methods? Well, that's a simple solution. Just like with Animal
, if we want to share a method across all instances of that class, we add it to the function's prototype.
现在,我们没有谈论的一件事是,如果Dog
有自己的方法呢? 好吧,这是一个简单的解决方案。 就像Animal
,如果我们想在该类的所有实例之间共享方法,则可以将其添加到函数的原型中。
...
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.bark = function () {
console.log('Woof Woof!')
this.energy -= .1
}
? very nice. There's just one small addition we need to make. If you remember back to the Beginner's Guide to JavaScript's Prototype post, we were able to get access to the instances' constructor function by using instance.constructor
.
? 非常好。 我们只需要添加一小部分。 如果您还记得《 JavaScript原型初学者指南》中的文章,则可以使用instance.constructor
来访问实例的构造函数。
function Animal (name, energy) {
this.name = name
this.energy = energy
}
const leo = new Animal('Leo', 7)
console.log(leo.constructor) // Logs the constructor function
As explained in the previous post, "the reason this works is because any instances of Animal
are going to delegate to Animal.prototype
on failed lookups. So when you try to access leo.constructor
, leo
doesn't have a constructor
property so it will delegate that lookup to Animal.prototype
which indeed does have a constructor
property."
如前Animal.prototype
文章中所述,“之所以起作用,是因为在失败的查找中,任何Animal
实例都将委托给Animal.prototype
。因此,当您尝试访问leo.constructor
, leo
没有constructor
属性,因此它会将查找委托给确实具有constructor
属性的Animal.prototype
。”
The reason I bring this up is because in our implementation, we overwrote Dog.prototype
with an object that delegates to Animal.prototype
.
我提这件事的原因是因为在我们的实现,我们重写了Dog.prototype
与对象委托给Animal.prototype
。
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.bark = function () {
console.log('Woof Woof!')
this.energy -= .1
}
What that means is that now, any instances of Dog
which log instance.constructor
are going to get the Animal
constructor rather than the Dog
constructor. You can see for yourself by running this code -
这意味着现在,任何记录instance.constructor的Dog
instance.constructor
都将获得Animal
构造函数而不是Dog
构造函数。 您可以通过运行以下代码亲自查看-
function Animal (name, energy) {
this.name = name
this.energy = energy
}
Animal.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Animal.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Animal.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.bark = function () {
console.log('Woof Woof!')
this.energy -= .1
}
const charlie = new Dog('Charlie', 10, 'Goldendoodle')
console.log(charlie.constructor)
Notice it gives you the Animal
constructor even though charlie
is a direct instance of Dog
. Again, we can walk through what's happening here just like we did above.
请注意,即使charlie
是Dog
的直接实例,它也会为您提供Animal
构造函数。 同样,我们可以像上面一样浏览整个过程。
const charlie = new Dog('Charlie', 10, 'Goldendoodle')
console.log(charlie.constructor)
/*
1) JavaScript checks if charlie has a constructor property - it doesn't.
2) JavaScript then checks if Dog.prototype has a constructor property
- it doesn't because it was deleted when we overwrote Dog.prototype.
3) JavaScript then checks if Animal.prototype has a constructor property
- it does so it logs that.
*/
How can we fix this? Well, it's pretty simple. We can just add the correct constructor
property to Dog.prototype
once we overwrite it.
我们该如何解决? 好吧,这很简单。 覆盖后,我们可以将正确的constructor
属性添加到Dog.prototype
。
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.bark = function () {
console.log('Woof Woof!')
this.energy -= .1
}
Dog.prototype.constructor = Dog
At this point if we wanted to make another subclass, say Cat
, we'd follow the same pattern.
在这一点上,如果我们想创建另一个子类,例如Cat
,我们将遵循相同的模式。
function Cat (name, energy, declawed) {
Animal.call(this, name, energy)
this.declawed = declawed
}
Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat
Cat.prototype.meow = function () {
console.log('Meow!')
this.energy -= .1
}
This concept of having a base class with subclasses that delegate to it is called inheritance and it's a staple of Object Oriented Programming (OOP). If you're coming from a different programming language, odds are you're already familiar with OOP and inheritance. Before ES6 classes, in JavaScript, inheritance was quite the task as you can see above. You need to understand now only when to use inheritance, but also a nice mix of .call
, Object.create
, this
, and FN.prototype
- all pretty advanced JS topics. Let's see how we'd accomplish the same thing using ES6 classes though.
具有子类可委托给它的基类的这种概念称为继承 ,它是面向对象编程(OOP)的主要内容 。 如果您来自其他编程语言,那么您很可能已经熟悉OOP和继承。 如上所示,在ES6类之前,在JavaScript中,继承是一项艰巨的任务。 你现在需要了解的时候才使用继承,也是一个不错的搭配.call
, Object.create
, this
和FN.prototype
-都相当先进的JS主题。 让我们看看如何使用ES6类完成相同的事情。
First, let's review what it looks like to go from an ES5 "class" to an ES6 class using our Animal
class.
首先,让我们回顾一下使用我们的Animal
类从ES5“类”过渡到ES6类的情况。
function Animal (name, energy) {
this.name = name
this.energy = energy
}
Animal.prototype.eat = function (amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
Animal.prototype.sleep = function (length) {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
Animal.prototype.play = function (length) {
console.log(`${this.name} is playing.`)
this.energy -= length
}
const leo = new Animal('Leo', 7)
class Animal {
constructor(name, energy) {
this.name = name
this.energy = energy
}
eat(amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
sleep() {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
play() {
console.log(`${this.name} is playing.`)
this.energy -= length
}
}
const leo = new Animal('Leo', 7)
Now that we've refactored our Animal
constructor function into an ES6 class, the next thing we need to do is figure out how to refactor our base class (Dog
). The good news is it's much more intuitive. For reference, in ES5, here's what we had.
现在我们已经将Animal
构造函数重构为ES6类,接下来需要做的是找出如何重构基类( Dog
)。 好消息是它更加直观。 作为参考,在ES5中,这就是我们所拥有的。
function Dog (name, energy, breed) {
Animal.call(this, name, energy)
this.breed = breed
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.bark = function () {
console.log('Woof Woof!')
this.energy -= .1
}
Dog.prototype.constructor = Dog
Before we get into inheritance, let's refactor Dog
to use an ES6 class as we learned in a previous post.
在继承之前,让我们重构Dog
来使用我们在上一篇文章中了解到的ES6类。
class Dog {
constructor(name, energy, breed) {
this.breed = breed
}
bark() {
console.log('Woof Woof!')
this.energy -= .1
}
}
Looks great. Now, let's figure out how to make sure that Dog
inherits from Animal
. The first step we need to make is a pretty straight forward one. With ES6 classes, you can extend
a base class with this syntax
看起来很棒。 现在,让我们弄清楚如何确保Dog
继承自Animal
。 我们需要做的第一步是一个非常简单的步骤。 使用ES6类,可以使用以下语法extend
基类
class Subclass extends Baseclass {}
Translated into our example, that would make our Dog
class look like this
翻译成我们的示例,这将使我们的Dog
类看起来像这样
class Animal {
constructor(name, energy) {
this.name = name
this.energy = energy
}
eat(amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
sleep() {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
play() {
console.log(`${this.name} is playing.`)
this.energy -= length
}
}
class Dog extends Animal {
constructor(name, energy, breed) {
this.breed = breed
}
bark() {
console.log('Woof Woof!')
this.energy -= .1
}
}
In ES5 in order to make sure that every instance of Dog
had a name
and an energy
property, we used .call
in order to invoke the Animal
constructor function in the context of the Dog
instance. Luckily for us, in ES6 it's much more straight forward. Whenever you are extending a baseclass and you need to invoke that baseclass' constructor function, you invoke super
passing it any arguments it needs. So in our example, our Dog
constructor gets refactored to look like this
在ES5中,为了确保Dog
每个实例都具有name
和energy
属性,我们使用.call
来在Dog
实例的上下文中调用Animal
构造函数。 对我们来说幸运的是,在ES6中,它更为直接。 每当扩展基类并需要调用该基类的构造函数时,您都将调用super
传递其所需的任何参数。 因此,在我们的示例中,我们的Dog
构造函数被重构为如下所示
class Animal {
constructor(name, energy) {
this.name = name
this.energy = energy
}
eat(amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
sleep() {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
play() {
console.log(`${this.name} is playing.`)
this.energy -= length
}
}
class Dog extends Animal {
constructor(name, energy, breed) {
super(name, energy) // calls Animal's constructor
this.breed = breed
}
bark() {
console.log('Woof Woof!')
this.energy -= .1
}
}
And that's it. No using .call
, no using Object.create
, no worrying about resetting constructor
on the prototype - just extends
the baseclass and make sure to call super
.
就是这样。 无需使用.call
,无需使用Object.create
,无需担心在原型上重置constructor
-只需extends
基类并确保调用super
。
What's interesting about JavaScript is the same patterns you've learned these last few posts are directly caked into the language itself. Previously you learned that the reason all instances of Array
have access to the array methods like pop
, slice
, filter
, etc are because all of those methods live on Array.prototype
.
关于JavaScript的有趣之处在于,您在最近的几篇文章中所学到的模式都是直接嵌入语言本身的。 以前,您了解到Array
所有实例都可以访问诸如pop
, slice
, filter
等数组方法的原因是因为所有这些方法都位于Array.prototype
。
console.log(Array.prototype)
/*
concat: ƒn concat()
constructor: ƒn Array()
copyWithin: ƒn copyWithin()
entries: ƒn entries()
every: ƒn every()
fill: ƒn fill()
filter: ƒn filter()
find: ƒn find()
findIndex: ƒn findIndex()
forEach: ƒn forEach()
includes: ƒn includes()
indexOf: ƒn indexOf()
join: ƒn join()
keys: ƒn keys()
lastIndexOf: ƒn lastIndexOf()
length: 0n
map: ƒn map()
pop: ƒn pop()
push: ƒn push()
reduce: ƒn reduce()
reduceRight: ƒn reduceRight()
reverse: ƒn reverse()
shift: ƒn shift()
slice: ƒn slice()
some: ƒn some()
sort: ƒn sort()
splice: ƒn splice()
toLocaleString: ƒn toLocaleString()
toString: ƒn toString()
unshift: ƒn unshift()
values: ƒn values()
*/
You also learned that the reason all instances of Object
have access to methods like hasOwnProperty
and toString
is because those methods live on Object.prototype
.
您还了解到, Object
所有实例都可以访问hasOwnProperty
和toString
类的方法的原因是,因为这些方法都存在于Object.prototype
。
console.log(Object.prototype)
/*
constructor: ƒn Object()
hasOwnProperty: ƒn hasOwnProperty()
isPrototypeOf: ƒn isPrototypeOf()
propertyIsEnumerable: ƒn propertyIsEnumerable()
toLocaleString: ƒn toLocaleString()
toString: ƒn toString()
valueOf: ƒn valueOf()
*/
Here's a challenge for you. With the list of Array methods and Object methods above, why does this code below work?
这对您来说是一个挑战。 使用上面的Array方法和Object方法的列表,为什么下面的代码起作用?
const friends = ['Mikenzi', 'Jake', 'Ean']
friends.hasOwnProperty('push') // false
If you look at Array.prototype
, there isn't a hasOwnProperty
method. Well if there isn't a hasOwnProperty
method located on Array.prototype
, how does the friends
array in the example above have access to hasOwnProperty
? The reason for that is because the Array
class extends the Object
class. So in our example above, when JavaScript sees that friends
doesn't have a hasOwnProperty
property, it checks if Array.prototype
does. When Array.prototype
doesn't, it checks if Object.prototype
does, then it invokes it. It's the same process we've seen throughout this blog post.
如果查看Array.prototype
,则没有hasOwnProperty
方法。 好吧,如果Array.prototype
上没有hasOwnProperty
方法,那么上例中的friends
数组如何访问hasOwnProperty
? 这样做的原因是因为Array
类扩展了Object
类。 因此,在上面的示例中,当JavaScript看到friends
没有hasOwnProperty
属性时,它将检查Array.prototype
是否具有。 当Array.prototype
不存在时,它检查Object.prototype
是否存在,然后调用它。 这与我们在整个博客文章中看到的过程相同。
JavaScript has two types - Primitive types and Reference types.
JavaScript有两种类型- 基本类型和引用类型。
Primitive types are boolean
, number
, string
, null
, and undefined
and are immutable. Everything else is a reference type and they all extend Object.prototype
. That's why you can add properties to functions and arrays and that's why both functions and arrays have access to the methods located on Object.prototype
.
基本类型为boolean
, number
, string
, null
和undefined
并且是不可变的。 其他所有内容都是引用类型,它们都扩展了Object.prototype
。 这就是为什么您可以向函数和数组添加属性,也就是为什么函数和数组都可以访问Object.prototype
上的方法的Object.prototype
。
function speak(){}
speak.woahFunctionsAreLikeObjects = true
console.log(speak.woahFunctionsAreLikeObjects) // true
const friends = ['Mikenzi', 'Jake', 'Ean']
friends.woahArraysAreLikeObjectsToo = true
console.log(friends.woahArraysAreLikeObjectsToo) // true
这是我的高级JavaScript课程的一部分 。
如果您喜欢这篇文章,请查看。 (This is part of my Advanced JavaScript course.
If you enjoyed this post, check it out.)
翻译自: https://www.freecodecamp.org/news/javascript-inheritance-and-the-prototype-chain-d4298619bdae/