[英]Trying to understand the difference between prototype and constructor in JavaScript
我是 JavaScript 新手,并试图理解这个概念。 我已经阅读了很多关于原型和构造函数的文章,但无论我走到哪里,我都会感到困惑。
当人们同时谈论构造函数和原型时,就会产生混淆。
在下面的例子中
var employee = function Emp(name) {
this.name = name;
}
var jack = new employee("Jack Dwain");
employee.constructor // gives Function()
employee.prototype // gives Emp {}
employee.prototype.constructor // gives Emp(name)
jack.constructor // gives Emp(name)
jack.prototype // gives undefined
原型是 JS 实现继承的一种方式,因为Emp(name)
是基函数,原型被引用到同一个函数本身。 是这样的吗?
employee.constructor
和employee.prototype.constructor
有什么不同?
为什么jack.prototype
undefined
? 即,如果它是从函数Emp(name)
继承的,为什么它不引用该函数?
我怎样才能清楚地预测(无需在控制台中输入)原型或构造函数或prototype.constructor ......会产生什么?
如果您习惯于在其他 OOP 语言中轻松扩展对象,那么很难围绕这个概念来思考,但我会尽我所能解释这些的用途和什么是什么。 我假设您熟悉其他 OOP 语言。 如我错了请纠正我。
所有函数都有原型Function()
。 它们继承了Function
的所有基本功能,例如toString()
和valueOf()
。
然后是构造函数。 这就是你用来初始化对象的东西。
p = new Foo();
所以在这种情况下,我们有两件事。
Function
为原型的function Foo
(Foo)Foo()
作为构造函数的Function
对象(p)(还跟着我?)
Foo()
构造函数可以覆盖Function
构造函数的一些基本功能,或者保持原样并充分利用它。
如果你熟悉 OOP 原理,原型是基类,构造函数是你当前的类。 在 OOP 中,上面将是class Foo extends Function
您还可以从原型和构造函数的整个设置开始继承,在共享功能的同时制作更复杂的对象。
例如这个:
// Make an object initialiser extending Function. In OOP `class Foo extends Function`
function Foo(bar) {
this.baz = bar;
}
Foo.prototype.append = function(what) {
this.baz += " " + what;
};
Foo.prototype.get() {
return this.baz
}
现在让我们说我们想要不同的方法来让baz
离开那里。 一种用于控制台日志记录,另一种用于将其放在标题栏上。 我们可以为我们的类Foo
做一件大事,但我们不这样做,因为我们需要用为不同实现而制作的新类做完全不同的事情。 他们唯一需要分享的是baz
项目和 setter 和 getter。
所以我们需要扩展它以使用 OOP 术语。 在 OOO 中,这将是所需的最终结果class Title extends Foo(){}
。 那么让我们来看看如何到达那里。
function Title(what) {
this.message = what;
}
此时Title
函数如下所示:
因此,要使其扩展Foo
,我们需要更改原型。
Title.prototype = new Foo();
这是通过针对原型初始化一个新的Foo()
对象来完成的。 现在它基本上是一个名为Title
的Foo
对象。 这不是我们想要的,因为现在我们无法访问Title
中的消息部分。 我们可以通过将构造函数重置为Title
来使其正确扩展Foo()
Title.prototype.constructor = Title;
现在我们又面临一个问题。 Foo
的构造函数没有被初始化,所以我们最终得到一个未定义的this.baz
为了解决这个问题,我们需要打电话给父母。 在 Java 中,您可以在 PHP $parent->__construct($vars)
中使用super(vars)
来做到这一点。
在Javascript中我们要修改Title
类的构造函数来调用父对象的构造函数。
所以Title
类的构造函数会变成
function Title(what) {
Foo.call(this,what);
this.message = what;
}
通过使用Function
对象属性Foo
继承,我们可以在Titl
对象中初始化Foo
对象。
现在你有了一个正确继承的对象。
因此,它不像其他 OOP 语言那样使用像extend
之类的关键字,而是使用prototype
和constructor
。
如果您想创建一个 javascript对象,您可以简单地声明一个新对象并为其赋予属性(我选择将自己对象化):
var myself= {
name:"Niddro",
age:32
};
此方法允许您制作一个对象。 如果你想要的是一个描述一个人的原型,你可以在其中声明几个具有相同设置的人。 要创建原型,您可以使用构造函数,如下所示:
//Constructor
function generalNameForObject(param1, param2,...) {
//Give the object some properties...
}
我有一个原型(配方),我想调用 person ,它应该包含属性名称和年龄,我将使用构造函数来制作它:
function person(name,age) {
this.name=name;
this.age=age;
}
上面的构造函数描述了我的人员对象的原型。
通过调用构造函数创建一个新人:
var myself = new person("Niddro",31);
var OP = new person("rajashekar thirumala",23);
一段时间过去了,我意识到我已经过生日了,所以我需要更改原型的属性:
myself.age=32;
如果要在构造函数中添加属性,则需要手动将其添加到构造函数中:
function person(name,age,rep) {
this.name=name;
this.age=age;
this.reputation=rep;
}
相反,您可以通过执行以下操作向原型添加属性(这里“原型”是一个实际命令,而不仅仅是一个名称):
function person(name,age,rep) {
this.name=name;
this.age=age;
}
person.prototype.reputation=105;
请注意,这将为创建的所有对象添加 105 的声誉。
我希望这能让您对构造函数和原型之间的关系有更多的了解。
employee.constructor //给出 Function()
在 JavaScript 中,函数也是对象,可以使用它自己的构造函数来构造,即Function 。 因此,您可以编写以下代码来获取 Function 的实例。
var employee2 = new Function('a', 'b', 'return a+b');
当您使用函数文字创建函数时也会发生同样的情况,就像您的情况一样。 并且这个对象的构造函数属性也引用了相同的原生函数对象/类。
employee.prototype // 给 Emp {}
JavaScript 中的每个对象都有一个与之关联的原型。 虽然只有函数对象原型可以通过.prototype
直接访问。 当您使用new
关键字创建对象时,相同的原型会复制到其对象原型上。 这种复制主要负责继承/扩展。 尽管原型被复制了,但它不像 Function 对象那样直接可访问。 它可以通过.__proto__
以非标准方式使用。 以下代码将返回 true。
jack.__proto__==employee.prototype
employee.prototype.constructor //给出 Emp(name)
正如Object.prototype.constructor的文档中所说。 这将返回对创建实例原型的 Object 函数的引用。 这里引用的对象是employee.prototype 而not employee
。 这有点复杂,但对象employee.prototype 的原型是由函数 Emp(name) 创建的
jack.constructor //给出 Emp(name)
如上一点所述,这个对象原型是由函数 Emp(name) 在您使用 new Emp() 创建对象时创建的,
jack.prototype //给出未定义的
jack 不是一个函数对象,所以你不能像那样访问它的原型。 您可以访问(不是标准方式)jack 的原型,如下所示。
jack.__proto__
构造函数:
function Foo(x) {
this.x =x;
}
Foo
是构造函数。 构造函数是一个函数。
有两种方法可以使用这个构造函数Foo
。
“对象是通过在 new 表达式中使用构造函数创建的;例如,new Date(2009,11) 创建一个新的 Date 对象。在不使用 new 的情况下调用构造函数会产生依赖于构造函数的结果。例如,Date() 会生成一个字符串表示当前日期和时间,而不是对象。”
来源ECMA-262
这意味着如果Foo
返回一些东西(通过return "somevalue";
),那么typeof Foo()
是返回值的类型。
另一方面,当你打电话
var o = new Foo();
JavaScript 实际上只是
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
原型:
当您调用oa
时,javascript 首先检查a
是否是对象o
自己的属性。 如果不是,javascript 将查找属性链以a
.
有关属性链的更多信息,请查看mdn 。
构造函数的prototype
属性有一个非常强大的特性,它在类中不可用。 如果它有用,那就另当别论了。 构造函数的prototype
属性可以更改在原型链中链接到该原型的每个实例的属性。
长话短说:
注意:这不是一个精确的定义,总结的目的只是为了让你对构造函数和原型有一个感觉。
如果您使用带有new
关键字的构造函数,则构造函数和原型具有相似的目的,即使它们完全不同。 构造函数初始化对象的属性,因此它提供属性。 原型还通过属性链(基于原型的继承)提供属性。
原型只是一个对象,而构造函数是指向创建该对象的函数的指针。
构造函数是一个指针。 它指向创建您从中检索构造函数的点的 Function()。 (即构造函数只是对 Function() 的引用,我们可以根据需要多次调用它。)
构造函数的用途之一是帮助您创建对象的复制副本。 由于构造函数属性是对创建对象的函数的引用,因此只要您拥有该对象的副本,它将始终指向原始构造函数。 https://coderwall.com/p/qjzbig/understanding-constructor-and-prototype
使用对象构造器:通常,单独创建的对象在许多情况下都是有限的。 它只创建一个对象。
有时我们喜欢有一个“对象类型”,它可以用来创建一个类型的许多对象。
创建“对象类型”的标准方法是使用对象构造函数:
function person(first, last, email ) {
this.first_name = first;
this.last_name = last;
this.e_mail = email;
}
var myFather = new person("Ibm", "Muh", "ibm@gmail.com");
上面的函数(person)是一个对象构造函数。 一旦有了对象构造函数,就可以创建相同类型的新对象:
var myFather = new person("Sul", "Ahm", "sul@gmail.com");
每个 JavaScript 对象都有一个原型。 原型也是一个对象。
所有 JavaScript 对象都从它们的原型继承它们的属性和方法。
使用 2 种创建对象的方法来创建对象,即(1)对象字面量,或(2)使用 new Object(),从名为 Object.prototype 的原型继承。 使用 new Date() 创建的对象继承 Date.prototype。
Object.prototype 位于原型链的顶端。
所有 JavaScript 对象(日期、数组、正则表达式、函数......)都继承自 Object.prototype。 https://www.w3schools.com/js/js_object_prototypes.asp
关键字原型是 Function() 对象的属性。
原型的值是创建该特定对象的对象构造函数。 让我们看几个原型:
Boolean.prototype // returns Object Boolean
String.prototype // returns Object String with methods such as "toUpperCase"
Function.prototype // returns function() {} or function Empty() {}
创建原型:
创建对象原型的标准方法是使用对象构造函数:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
}
var myFather = new Person("John", "Doe", 50);
使用构造函数,您可以使用 new 关键字从相同的原型创建新对象,如上所示:
构造函数是 Person 对象的原型。 使用大写首字母命名构造函数被认为是一种很好的做法。
向原型添加属性
不能像向现有对象添加新属性一样向原型添加新属性,因为原型不是现有对象。
示例:Person.nationality = "English";
要将新属性添加到原型,您必须将其添加到构造函数:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.nationality = "English";
}
所有本机和复杂对象都检索到它们的原始构造函数,在这种情况下就是它们自己。 唯一的例外是 Function 原型,它返回创建它的 Function() 函数。 不要将它与构造函数混淆,因为它不一样。
Function.prototype === Function.constructor // returns false, Function.constructor is function Function(){}
还有一个额外的属性__proto__
,它引用实例对象的内部 [[proto]] 属性。 与 Function() 对象不同,每个 Object 都有一个__proto__
。 不建议更新实例对象的原型,因为原型并不意味着在运行时更改(您应该能够看到谁是谁的原型,否则您需要花费额外的计算来确保没有循环引用)。
然而事实是,这种方法在许多情况下可能是错误的。 在 Javascript 中,当您将方法绑定到 this 关键字时,您只是将该方法提供给该特定实例,并且它与该构造函数的对象实例实际上没有任何关系,就像静态方法一样。 请记住,函数是 Javascript 中的一等公民,我们可以像处理对象一样处理它们,在这种情况下,我们只是向函数对象的实例添加属性。 这只是故事的一部分,您还必须知道,通过 this 附加的任何方法都会为我们创建的每个新实例重新声明,如果我们希望创建如此多的实例,这可能会对应用程序的内存使用产生负面影响。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.