以下是Faraz Kelhini的客座文章。其中一些东西超出了我的舒适区,所以我请凯尔辛普森为我进行技术检查。凯尔的回答(我们在办公时间会议期间所做的)非常有趣。它是:1)这篇文章在技术上是合理的。JavaScript 并没有传统意义上的类,这是大多数人硬塞进去的方式。2)我们可能想停止硬塞进去。JavaScript 有对象,我们可以按照它们的意图使用它们相同种类的东西。Kyle 称之为 OLOO(链接到其他对象的对象)。这是一个介绍。我认为两者都有学习的价值。
对构造函数有很好的理解对于真正理解 JavaScript 语言至关重要。从技术上讲,JavaScript 没有类,但它具有构造函数和原型,可以为 JavaScript 带来类似的功能。事实上,ES2015 中引入的类声明只是作为现有基于原型的继承的语法糖,并没有真正为语言添加任何额外的功能。
在本教程中,我们将详细探讨构造函数,并了解 JavaScript 如何利用它们来创建对象。
创建和使用构造函数
构造函数类似于常规函数,但我们将它们与new
关键字一起使用。构造函数有两种类型:内置构造函数,例如Array
和Object
,它们在运行时在执行环境中自动可用;和自定义构造函数,它们为您自己的对象类型定义属性和方法。
当您想要创建具有相同属性和方法的多个相似对象时,构造函数很有用。将构造函数的名称大写以将它们与常规函数区分开来是一种惯例。考虑以下代码:
function Book() {
// unfinished code
}
var myBook = new Book();
代码的最后一行创建了一个实例Book
并将其分配给一个变量。尽管Book
构造函数没有做任何事情,myBook
但仍然是它的一个实例。如你所见,这个函数和普通函数没有什么区别,只是它是用new
关键字调用的,而且函数名是大写的。
确定实例的类型
要确定一个对象是否是另一个对象的实例,我们使用instanceof
运算符:
myBook instanceof Book // true
myBook instanceof String // false
请注意,如果运算符的右侧instanceof
不是函数,则会抛出错误:
myBook instanceof {};
// TypeError: invalid 'instanceof' operand ({})
另一种查找实例类型的方法是使用constructor
属性。考虑以下代码片段:
myBook.constructor === Book; // true
myBook
指向的构造函数属性Book
,所以严格相等运算符返回true
。JavaScript 中的每个对象都constructor
从其原型继承一个属性,该属性指向创建该对象的构造函数:
var s = new String("text");
s.constructor === String; // true
"text".constructor === String; // true
var o = new Object();
o.constructor === Object; // true
var o = {};
o.constructor === Object; // true
var a = new Array();
a.constructor === Array; // true
[].constructor === Array; // true
但是请注意,使用该constructor
属性检查实例的类型通常被认为是不好的做法,因为它可以被覆盖。
自定义构造函数
构造函数就像一个千篇一律的工具,用于制作具有相同属性和方法的多个对象。考虑以下示例:
function Book(name, year) {
this.name = name;
this.year = '(' + year + ')';
}
构造Book
函数需要两个参数:name
和year
。使用new
关键字调用构造函数时,会将接收到的参数赋值给当前实例的name
andyear
属性,如下图:
var firstBook = new Book("Pro AngularJS", 2014);
var secondBook = new Book("Secrets Of The JavaScript Ninja", 2013);
var thirdBook = new Book("JavaScript Patterns", 2010);
console.log(firstBook.name, firstBook.year);
console.log(secondBook.name, secondBook.year);
console.log(thirdBook.name, thirdBook.year);
Book
如您所见,我们可以通过调用具有不同参数的构造函数来快速构建大量不同的书籍对象。这与 JavaScript 在其内置构造函数(如Array()
和)中使用的模式完全相同Date()
。
Object.defineProperty() 方法
该Object.defineProperty()
方法可以在构造函数中使用,以帮助执行所有必要的属性设置。考虑以下构造函数:
function Book(name) {
Object.defineProperty(this, "name", {
get: function() {
return "Book: " + name;
},
set: function(newName) {
name = newName;
},
configurable: false
});
}
var myBook = new Book("Single Page Web Applications");
console.log(myBook.name); // Book: Single Page Web Applications
// we cannot delete the name property because "configurable" is set to false
delete myBook.name;
console.log(myBook.name); // Book: Single Page Web Applications
// but we can change the value of the name property
myBook.name = "Testable JavaScript";
console.log(myBook.name); // Book: Testable JavaScript
此代码用于Object.defineProperty()
定义访问器属性。访问器属性不包括任何属性或方法,但它们定义了读取属性时要调用的 getter,以及写入属性时要调用的 setter。
getter 应该返回一个值,而 setter 接收分配给属性的值作为参数。上面的构造函数返回一个实例,其name
属性可以设置或更改,但不能删除。当我们得到 的值时name
,getter 将字符串附加Book:
到名称并返回它。
对象字面量表示法优于构造函数
JavaScript 语言有九个内置构造函数:Object()
, Array()
, String()
, Number()
, Boolean()
, Date()
, Function()
,Error()
和RegExp()
. 创建值时,我们可以自由使用对象字面量或构造函数。然而,对象字面量不仅更易于阅读,而且运行速度更快,因为它们可以在解析时进行优化。因此,对于简单的对象,最好坚持使用文字:
// a number object
// numbers have a toFixed() method
var obj = new Object(5);
obj.toFixed(2); // 5.00
// we can achieve the same result using literals
var num = 5;
num.toFixed(2); // 5.00
// a string object
// strings have a slice() method
var obj = new String("text");
obj.slice(0,2); // "te"
// same as above
var string = "text";
string.slice(0,2); // "te"
如您所见,对象字面量和构造函数之间几乎没有任何区别。更有趣的是,仍然可以在文字上调用方法。当对文字调用方法时,JavaScript 会自动将文字转换为临时对象,以便该方法可以执行操作。一旦不再需要临时对象,JavaScript 就会丢弃它。
使用 new 关键字是必不可少的
重要的是要记住new
在所有构造函数之前使用关键字。如果您不小心忘记了new
,您将修改全局对象而不是新创建的对象。考虑以下示例:
function Book(name, year) {
console.log(this);
this.name = name;
this.year = year;
}
var myBook = Book("js book", 2014);
console.log(myBook instanceof Book);
console.log(window.name, window.year);
var myBook = new Book("js book", 2014);
console.log(myBook instanceof Book);
console.log(myBook.name, myBook.year);
当我们在Book
没有 的情况下调用构造函数时new
,实际上是在调用没有返回语句的函数。结果,this
在构造函数内部指向Window
(而不是myBook
),并创建了两个全局变量。但是,当我们用 调用函数时new
,上下文会从全局(窗口)切换到实例。所以,this
正确地指向myBook
。
请注意,在严格模式下,此代码会引发错误,因为严格模式旨在防止程序员意外调用没有new
关键字的构造函数。
范围安全的构造函数
正如我们所见,构造函数只是一个函数,因此可以在没有new
关键字的情况下调用它。但是,对于没有经验的程序员来说,这可能是错误的来源。范围安全的构造函数旨在返回相同的结果,无论是否调用new
它,它都不会受到这些问题的影响。
大多数内置构造函数,例如和Object
,都是范围安全的。他们使用一种特殊的模式来确定如何调用构造函数。如果不使用,它们会通过再次调用构造函数来返回对象的正确实例。考虑以下代码:Regex
Array
new
new
function Fn(argument) {
// if "this" is not an instance of the constructor
// it means it was called without new
if (!(this instanceof Fn)) {
// call the constructor again with new
return new Fn(argument);
}
}
因此,我们的构造函数的作用域安全版本将如下所示:
function Book(name, year) {
if (!(this instanceof Book)) {
return new Book(name, year);
}
this.name = name;
this.year = year;
}
var person1 = new Book("js book", 2014);
var person2 = Book("js book", 2014);
console.log(person1 instanceof Book); // true
console.log(person2 instanceof Book); // true
结论
重要的是要理解 ES2015 中引入的类声明只是作为现有基于原型的继承的语法糖,并没有向 JavaScript 添加任何新内容。构造函数和原型是 JavaScript 定义相似和相关对象的主要方式。
在本文中,我们很好地了解了 JavaScript 构造函数的工作原理。我们了解到构造函数类似于常规函数,但它们与new
关键字一起使用。我们看到了构造函数如何使我们能够快速创建具有相同属性和方法的多个相似对象,以及为什么instanceof
操作符是确定实例类型的最安全方法。最后,我们查看了范围安全的构造函数,可以使用或不使用new
.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)