JavaScript继承和原型链

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水平以及eatsleepplay的能力。 对于我们的“ 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类是完美的基础类。 这意味着它具有我们每只动物所共有的所有属性。 无论我们要创建狗,猫,长颈鹿还是猴子,它们都将具有nameenergy水平以及eatsleepplay的能力。 话虽如此,每当为每种不同的动物创建单独的类时,有没有一种方法可以利用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个参数,即nameenergybreed

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功能,以便任何狗实例都具有nameenergy水平,并且能够eatsleepplay

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 。 这意味着我们要使用Dogthis关键字来调用Animal 。 如果我们这样做的正确,那么this将里面Dog功能将具有的所有属性Animal ( nameenergy )。 如果您记得上一节 ,那么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每个实例现在都具有nameenergy属性。 同样,这样做的原因是因为这就像我们使用Dog生成的this关键字运行了Animal函数一样。 再经过我们增加了一个nameenergy属性为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的属性( nameenergy ),但是我们没有做任何事情来确保它们也具有方法( playeatsleep )。

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.constructorleo没有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.

请注意,即使charlieDog的直接实例,它也会为您提供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中,继承是一项艰巨的任务。 你现在需要了解的时候才使用继承,也是一个不错的搭配.callObject.createthisFN.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每个实例都具有nameenergy属性,我们使用.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所有实例都可以访问诸如popslicefilter等数组方法的原因是因为所有这些方法都位于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所有实例都可以访问hasOwnPropertytoString类的方法的原因是,因为这些方法都存在于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.

基本类型为booleannumberstringnullundefined并且是不可变的。 其他所有内容都是引用类型,它们都扩展了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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值