JavaScript 构造函数入门指南
目录
1. 构造函数简介
在本系列的上一篇文章中,我们探讨了 JavaScript 中的原型继承,并讨论了重要的面向对象 (OOP) JavaScript 概念,例如原型、原型链、继承等等。我们还学习了如何使用对象的__proto__属性设置对象的原型(我们指出这不是推荐的方法),并详细讨论了this
变量。 您可以阅读以下文章:
在本文中,我们将延续上文,探讨 JavaScript 中设置对象原型的推荐方法。
虽然设置原型的方法有很多,但本文将重点介绍函数构造函数。
✨我们将在本系列的后续文章中介绍其他推荐方法。如果在一篇文章中详细讨论所有这些方法,篇幅会很长,读起来也很费劲!
构造函数
JavaScript 中的构造函数是用于构造对象的特殊函数。这个主题可能看起来很困难,但实际上非常简单。
💡理解构造函数的关键在于,它们实际上是普通的 JavaScript 函数。它们的特殊之处在于,它们总是与 JavaScript 中一个非常强大的运算符(称为 new 运算符)一起调用。
请运行下面的代码并考虑其结果。
我们上面这个小例子创建了一个新对象,并将其引用存储在Lawrence变量中。该对象拥有Person构造函数中指定的所有属性。Person
函数本身是一个普通的 JavaScript 函数;赋予它构造函数(构造对象的能力)的是这行代码:
const Lawrence = new Person();
✨ new运算符会修改其操作函数的行为。结合一些 JavaScript 设计模式,我们可以创建功能强大的构造函数。我们将在下一节中详细阐述这一点。
2. 函数、构造函数和new运算符
在第一节中,我们了解到,当 Person 构造函数(或任何其他构造函数)不使用new运算符调用时,它将被当作常规 JavaScript 函数调用。在本节中,我们将通过代码示例来详细说明这一点。
请参考下面的代码。
function Person () {
this.firstname = "Lawrence"
this.lastname = "Eagles"
this.occupation = "Software Developer"
this.gender = "male"
}
上面是Person函数的声明。我们可以从中注意到两件事:
- 它将一些属性(例如名字、姓氏、职业和性别)设置到this变量绑定(或指向)的对象上。在本例中是全局对象。
💡正如本系列上一篇文章所述, this 变量的一个规则是,当它在函数内部使用时,它指向全局对象,但当它在方法(对象中的函数)内部使用时,它将指向该对象
如果您对此不太清楚,可以随时访问我之前关于 OOP JavaScript 的文章。我已经在第一部分提供了链接。
不过,这里还是简单回顾一下。
请运行下面的代码并查看其结果。
上面的例子表明函数内部的this变量指向全局对象。
- 关于Person函数的另一件非常明显的事情是它没有返回语句,因此当调用它时它会返回 undefined。
💡 JavaScript 引擎会从任何不返回值的函数返回 undefined。这种行为在创建构造函数时得到了利用,因为我们可以使用 new 运算符修改该函数的返回值。因此,在 JavaScript 中,构造函数没有 return 语句是一种常见的模式。
新操作员
这是一个非常强大的 JavaScript 运算符,它能够修改函数的某些行为。new
运算符一开始可能会让人感到困惑,甚至有点吓人。
✨理解它的关键在于始终将其视为另一个常规 JavaScript 运算符。因此,要掌握 this 运算符的强大功能,必须充分理解 JavaScript 中的运算符。
运算符
运算符是特殊的 JavaScript 函数,其语法与常规函数不同。它们不像常规的 JavaScript 函数对象,因此将它们传递给console.dir()会引发错误。您可以在下面看到一些代码示例。
请运行以下代码并观察结果:
💡 console.dir() 显示指定 JavaScript 对象的属性的交互式列表
运行代码时,您可以看到tellDevName函数和Date构造函数的所有属性,但如果您取消注释我将运算符作为参数传递的行,并尝试运行代码,runkit 会抛出错误,这告诉我们它们不是常规函数对象。
运算符与常规函数非常相似,采用参数(称为操作数),但与常规函数不同的是,它们为我们提供了一种方便的语法,可以采用以下三种符号中的任何一种形式:
- 中缀表示法:在此表示法中,运算符位于其操作数之间。请考虑以下代码:
2 + 2 // returns 4
3 * 3 // returns 9
4 - 4 // returns 0
5 / 5 // returns 1
6 % 2 // returns 0
在上面的例子中,每个运算符都位于两个参数(操作数)之间,并返回一个值。点击此处了解更多关于中缀表示法的信息
- 后缀表示法:在此表示法中,运算符跟在其操作数后面。请考虑以下代码:
const mixNumbers = [1,2,3,4,5,6,7,8,9,10,11,12]
const evenNumbers = []
for (let i=0; i < mixNumbers.length; i++) {
if (mixNumbers[i] % 2 === 0){
evenNumbers.push(mixNumbers[i])
}
}
console.log("even numbers", evenNumbers)
上面是一个从数字列表中查找偶数的小例子。但这个例子中我们关注的是自增运算符。
此外,还有一个自减运算符。了解更多关于后缀表示法的信息,
请思考下面的代码:
i++ increment operator
i-- decrement operator
- 前缀表示法:在此表示法中,运算符位于其操作数之前。了解更多关于前缀表示法的信息, 请参考以下代码:
!true // logical NOT (!) returns false
!false // logical NOT (!) returns true
++i // prefix increment
--i // prefix decrement
new constructor() // returns the newly constructed object
从上面的例子中我们可以看到,new 运算符使用前缀符号,它接受函数(构造函数)调用并返回一个新构造的对象。
🎉我确实希望我们关于运算符的简洁论述能够让您更好地理解新运算符,从而进一步加深您对函数构造函数的理解。
通过对运算符的理解,我们现在可以清楚地看到,new运算符实际上将一个函数(构造函数)调用作为其参数(操作数),然后对其执行一些操作并返回一个值。以下是new运算符对函数构造函数
的操作。
- 创建一个空对象并将this变量绑定(指向)到新创建的对象。
- 如果函数没有返回其自身的对象,则返回this变量绑定的对象(新创建的对象) (这就是为什么构造函数不应该有 return 语句)。请运行以下代码并观察结果:
在上面的代码示例中,我故意将三个函数中的两个命名为相同的名称,但由于 JavaScript 区分大小写,因此它们是两个不同的函数。请注意,构造函数名称的首字母大写,而常规函数名称则全部小写。
💡我们在 JavaScript 中使用此模式来区分构造函数和常规函数。构造函数名称的首字母始终大写。这也能提醒你在每个构造函数中使用 new 运算符。在上面的代码示例中,Person 是构造函数,而 person 是常规函数。
从上面代码的结果我们可以看出,常规函数按预期返回undefined,但构造函数返回一个由new运算符创建的新对象,该对象还将该构造函数中的this变量绑定到该对象。
💥还要注意,BadPerson 构造函数虽然返回了自身对象,但却无法返回通过 new 运算符创建的对象。这是一个我们必须避免的遗憾。同样,构造函数通常不应该有 return 语句❗
JavaScript 构造函数设计模式
有了构造函数和new运算符的了解,我们可以轻松地为新构造的对象添加属性。这是一个常见的 JavaScript 模式。
请考虑下面的代码
function Person () {
this.firstname = "Lawrence"
this.lastname = "Eagles"
this.occupation = "Software Developer"
this.gender = "male"
}
这里唯一的限制是,任何由此构造函数创建的对象都将始终具有这些属性。为了使对象属性动态化,我们可以将它们作为参数传递给构造函数(因为构造函数本身就是常规函数)。
请运行以下代码并查看其结果:
从上面代码的运行结果可以看出,传递给每个构造函数的参数在使用new运算符调用时,会用于设置新构造对象的属性。
您可以在 MDN 上阅读有关new运算符的更多信息。
- 最后,new运算符将新创建对象的原型链接(设置)到另一个对象。在介绍中,我们提到要讨论设置对象原型的推荐方法,并且重点关注函数构造函数。至此,我们冗长的讨论又回到了主题。下一节我们将更详细地讨论它。
3.构造函数和原型继承
在 JavaScript 中,每个函数都有一个称为原型的属性。它在函数中是一个空对象,并且在整个函数生命周期中处于休眠状态。只有当该函数被用作构造函数时,它才会被激活并且非常有用。
💡函数(在 JavaScript 中是一个对象)的原型属性并非该函数对象的原型,但当该函数用作构造函数时,它会充当使用该函数构造的任何对象的原型。这个命名可能有点令人困惑,但我们热爱 JavaScript 😉
请运行下面的代码并考虑其结果:
从上面的代码结果可以看出,所有使用 Person 构造函数构造的对象都可以访问getPersonbio方法,该方法位于 Person 构造函数的prototype属性中。正如我们上面提到的,这个属性成为了每个对象的原型。
✨关于如何沿着原型链向下搜索所有构造对象以访问 getPersonBio 方法的细节,已在上一篇文章中讨论过。如果您对本节内容不太了解,我建议您先看一下。
4. JavaScript 的内置构造函数
JavaScript 内置了一些构造函数。如果你是一名 JavaScript 开发者,你很可能用过其中一些。
请运行下面的代码并查看其结果:
运行上面的代码,我们可以看到每个构造函数都返回一个对象,因为 JavaScript 中的每个构造函数都返回一个对象。
您可以从以下链接了解更多关于这些内置构造函数的信息:
Number 构造函数
String 构造函数
Date 构造函数
✨这些只是精选的几个,你可以在这里获得更多机器人列表
5. 结束语
我真的希望你能坚持到这一步。如果你做到了,我真的很感激。这是一场漫长的讨论,希望你有所收获。如果是这样,我期待在下面的评论区听到你的意见、评论、问题或请求(如果还有什么不清楚的)。
鏂囩珷鏉ユ簮锛�https://dev.to/lawrence_eagles/an-easy-guide-to-understanding-constructors-in-javascript-2mf6