ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 章节导航 [TOC] ### [介绍](https://www.theodinproject.com/courses/javascript/lessons/objects-and-object-constructors#introduction) 在我们的JavaScript基础课程中,您应该已经学习了[使用对象](https://www.theodinproject.com/courses/web-development-101/lessons/fundamentals-part-5)来存储和检索数据的[基础知识](https://www.theodinproject.com/courses/web-development-101/lessons/fundamentals-part-5)。让我们先来回顾一下吧。 有多种方法可以定义对象,但在大多数情况下,最好使用**对象文字**语法,如下所示: ~~~javascript const myObject = { property: 'Value!', otherProperty: 77, "obnoxious property": function() { // do stuff! } } ~~~ 还有两种方法可以从对象中获取信息:点表示法和括号表示法。 ~~~javascript // dot notation myObject.property // 'Value!' // bracket notation myObject["obnoxious property"] // [Function] ~~~ 您使用哪种方法取决于上下文。点符号更清晰,通常是首选,但有很多情况下无法使用它。例如,`myObject."obnoxious property"`将无法工作,因为该属性是一个包含空格的字符串。同样,您不能在点表示法中使用变量: ~~~javascript const variable = 'property' myObject.variable // this gives us 'undefined' because it's literally looking for a property named 'variable' in our object myObject[variable] // 'Value!' ~~~ 如果您对使用对象感到生气,现在可能是回顾并查看我们的JavaScript 101课程中[**基础5中**](https://www.theodinproject.com/courses/web-development-101/lessons/fundamentals-part-5)的内容的好时机。 ### [对象作为设计模式](https://www.theodinproject.com/courses/javascript/lessons/objects-and-object-constructors#objects-as-a-design-pattern) 您可以开始组织代码的最简单方法之一是将事物分组到对象中。从“tic tac toe”游戏中获取这些示例: ~~~javascript // example one const playerOneName = "tim" const playerTwoName = "jenn" const playerOneMarker = "X" const playerTwoMarker = "O" // example two const playerOne = { name: "tim", marker: "X" } const playerTwo = { name: "jenn", marker: "O" } ~~~ 乍一看,第一个看起来并不那么糟糕......它实际上比使用对象的示例需要更少的行,但好处是巨大的!让我来证明: ~~~javascript function printName(player) { console.log(player.name) } ~~~ 这是您在示例一设置时无法做到的事情。相反,每次你想要打印特定玩家的名字时,你必须记住正确的变量名,然后手动输入`console.log`: ~~~javascript console.log(playerOneName) console.log(playerTwoName) ~~~ 再说一次,这并不是*那么*糟糕......但如果你*不知道*你想要打印哪个球员的名字怎么办? ~~~javascript function gameOver(winningPlayer){ console.log("Congratulations!") console.log(winningPlayer.name + " is the winner!") } ~~~ 或者,如果我们不是制作2人游戏,而是更复杂的东西,例如具有大量库存的在线购物网站,该怎么办?在这种情况下,使用对象来跟踪项目的名称,价格,描述和其他事情是唯一的方法。不幸的是,在那种情况下手动输入我们对象的内容也是不可行的。我们需要一种更清洁的方式来创建我们的对象,这将我们带到...... ### [对象构造函数](https://www.theodinproject.com/courses/javascript/lessons/objects-and-object-constructors#object-constructors) 如果您需要像我们的播放器或库存项目那样复制特定类型的对象,则创建它们的更好方法是使用对象构造函数,该函数看起来像这样: ~~~javascript function Player(name, marker) { this.name = name this.marker = marker } ~~~ 通过使用关键字调用函数来使用它`new`。 ~~~javascript const player = new Player('steve', 'X') console.log(player.name) // 'steve' ~~~ 就像使用Object Literal方法创建的对象一样,您可以向对象添加函数: ~~~javascript function Player(name, marker) { this.name = name this.marker = marker this.sayName = function() { console.log(name) } } const player1 = new Player('steve', 'X') const player2 = new Player('also steve', 'O') player1.sayName() // logs 'steve' player2.sayName() // logs 'also steve' ~~~ ### [行使](https://www.theodinproject.com/courses/javascript/lessons/objects-and-object-constructors#exercise) 编写一个用于制作“book”对象的构造函数。我们将在本课结束时重新审视这个项目。你的书的对象应该有这本书的`title`,`author`,数量`pages`,以及你是否有`read`这本书 将函数放入可以报告书籍信息的构造函数中 ~~~javascript book.info() // "The Hobbit by J.R.R. Tolkien, 295 pages, not read yet" ~~~ 注意:几乎*总是*最好的`return`事情,而不是`console.log()`直接进入功能。在这种情况下,返回`info`字符串并在调用函数后记录它: ~~~javascript console.log(theHobbit.info()); ~~~ ### [原型](https://www.theodinproject.com/courses/javascript/lessons/objects-and-object-constructors#the-prototype) 在我们进一步深入研究之前,您需要了解有关JavaScript对象的重要信息。JavaScript中的所有对象都有`prototype`。简单地说,原型是原始对象*继承的*另一个对象,也就是说,原始对象可以访问其原型的所有方法和属性。 这个概念很重要,所以你有一些阅读要做。在继续之前确保你真的得到了这个! 1. [本文](http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/)是对该概念的直接介绍和演示。它还包括施工人员..审查的好时机!这里的重要部分,一旦你涵盖了基础知识,就是'基于原型的继承'和'原型链' 2. 要更深入地了解链和继承,花一些时间来阅读[这篇伟大的文章](http://javascript.info/prototype-inheritance)。像往常一样,最后做练习将有助于巩固你的思想。不要跳过它们!重要提示:本文`__proto__`一般不推荐大量使用。这里的概念是我们目前正在寻找的。我们很快将学习另一种方法来设置原型。 如果您已经理解了原型的概念,那么关于构造函数的下一部分将不会让人感到困惑! ~~~javascript function Student(name) { this.name = name this.grade = grade } Student.prototype.sayName = function() { console.log(this.name) } Student.prototype.goToProm = function() { // eh.. go to prom? } ~~~ 如果您正在使用构造函数来创建对象,则最好在该`prototype`对象上定义函数。这样做意味着将在所有Student对象之间共享每个函数的单个实例。如果我们在构造函数中直接声明函数,就像我们在第一次引入它时所做的那样,每次创建一个新学生时,该函数都会被复制。在这个例子中,这并不重要,但在一个创建数千个对象的项目中,它确实可以产生影响。 #### 推荐的原型继承方法 到目前为止,您已经看到了几种使对象从另一个对象继承原型的方法。在历史的这一点上,建议设置对象原型的方法是`Object.create`([这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create)是该方法的文档。)`Object.create`非常简单地返回一个具有指定原型的新对象以及要添加的任何其他属性。出于我们的目的,您可以这样使用它: ~~~javascript function Student() { } Student.prototype.sayName = function() { console.log(this.name) } function EighthGrader(name) { this.name = name this.grade = 8 } EighthGrader.prototype = Object.create(Student.prototype) const carl = new EighthGrader("carl") carl.sayName() // console.logs "carl" carl.grade // 8 ~~~ 你可以弄清楚这里发生了什么。在为EighthGrader创建构造函数之后,我们将它的原型设置为一个具有副本的新对象`Student.prototype`。 警告......这不起作用: ~~~javascript EighthGrader.prototype = Student.prototype ~~~ 因为它会将EighthGrader的原型设置为Student.prototype(即不是副本),如果您想在将来编辑某些内容,可能会导致问题。再考虑一个例子: ~~~javascript function Student() { } Student.prototype.sayName = function() { console.log(this.name) } function EighthGrader(name) { this.name = name this.grade = 8 } // don't do this!!! EighthGrader.prototype = Student.prototype function NinthGrader(name) { this.name = name this.grade = 9 } // noooo! not again! NinthGrader.prototype = Student.prototype NinthGrader.prototype.sayName = function() {console.log("HAHAHAHAHAHA")} const carl = new EighthGrader("carl") carl.sayName() //uh oh! this logs "HAHAHAHAHAHA" because we edited the sayName function! ~~~ 如果我们`Object.create`在这个例子中使用过那么我们可以安全地编辑`NinthGrader.prototype.sayName`函数而不改变函数`EighthGrader`。