试图理解JavaScript中原型和构造函数之间的区别

时间:2015-02-19 06:50:57

标签: javascript prototype

我是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
  1. prototype是JS实现继承的一种方式,因为Emp(name)是基函数原型被引用到同一个函数本身。那是怎么回事?

  2. employee.constructoremployee.prototype.constructor有何不同?

  3. 为什么jack.prototypeundefined,即如果它是从函数Emp(name)继承的,为什么它没有引用该函数?

  4. 如何在没有在控制台中输入原型或构造函数或者prototype.constructor ......产生的内容的情况下清楚地预测自己

6 个答案:

答案 0 :(得分:30)

如果你已经习惯了在其他OOP语言中扩展对象的简易性,那么你可以很好地解决这个问题,但是我会尽力解释它们的用途以及它们的用途。我假设您熟悉其他OOP语言。如果我错了,请纠正我。

所有函数都有原型Function()。它们继承了函数的所有基本功能,如toString()和valueOf()。

然后有一个构造函数。这就是你用来初始化对象的方法。

p = new Foo();

所以在这种情况下我们有两件事。

  • function Foo为原型(Foo)
  • Function
  • Function对象,Foo()作为构造函数(p)

(跟我来?)

Foo()构造函数可以覆盖Function构造函数的某些基本功能,但也保留原样并充分利用它。

如果您熟悉OOP原则,那么原型是基类,构造函数是您当前的类。在OOP中,上面将是class Foo extends Function

您还可以使用整个原型和构造函数的设置开始继承,在共享功能的同时制作更复杂的对象。

例如:

// make a 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和getters。

因此我们需要扩展它以使用OOP术语。在OOp中,这将是期望的最终结果class Title extends Foo(){}。所以我们来看看如何到达那里。

function Title(what) {
    this.message = what;
}

此时Title函数如下所示:

  • 原型功能
  • 构造函数标题

因此,为了使它扩展到Foo,我们需要更改原型。

Title.prototype = new Foo();
  • 原型Foo
  • 构造函数Foo

这是通过针对原型初始化新的Foo()对象来完成的。 现在它基本上是一个名为Title的Foo对象。这不是我们想要的,因为现在我们无法访问Title中的消息部分。 我们可以通过将构造函数重置为Title

来正确扩展Foo()
Title.prototype.constructor = Title;
  • 原型Foo
  • 构造函数标题

现在我们又遇到了一个问题。 Foo的构造函数没有初始化,所以最终得到一个未定义的this.baz

要解决这个问题,我们需要调用父级。在java中,您可以使用super(vars)中的$parent->__construct($vars)执行此操作。

在javascript中,我们必须修改Title类构造函数以调用父对象的构造函数。

所以Title类构造函数将成为

function Title(what) {
    Foo.call(this,what);
    this.message = what;
}

通过使用函数对象属性Foo继承,我们可以初始化Title对象中的Foo对象。

现在你有一个正确继承的对象。

因此,与其他OOP语言一样使用extend之类的关键字时,它使用prototypeconstructor

答案 1 :(得分:8)

  

employee.constructor //给出Function()

在JavaScript中,函数也是对象,可以使用自己的构造函数Function来构造。因此,您可以编写以下代码来获取Function的实例。

var employee2 = new Function('a', 'b', 'return a+b');

使用函数文字创建函数时也会发生同样的情况。此对象的构造函数属性也引用相同的本机Function对象/类。

  

employee.prototype //给出了Emp {}

JavaScript中的每个对象都有一个与之关联的原型。虽然只有.prototype可以直接访问函数对象原型。当您使用new关键字创建对象时,会将相同的原型复制到其对象原型上。这种复制主要负责继承/扩展。虽然复制了原型,但它不像Function对象那样可以直接组合。它以.__proto__的非标准方式提供。以下代码将返回true。

jack.__proto__==employee.prototype
  

employee.prototype.constructor //给出Emp(名称)

Object.prototype.constructor的文件中所述。这将返回对创建实例原型的Object函数的引用。这里引用的对象是employee.prototype和not employee。这有点复杂,但对象employee.prototype的原型是由函数Emp(name)

创建的
  

jack.constructor //给出Emp(名字)

正如前面所述,当你使用新的Emp()创建对象时,这个对象原型是由函数Emp(name)创建的,

  

jack.prototype //给出未定义的

jack不是一个函数对象,所以你无法像这样访问它的原型。您可以访问(不是标准方式)插孔原型,如下所示。

jack.__proto__

答案 2 :(得分:5)

如果你想创建一个javascript 对象,你可以简单地声明一个新对象并给它属性(我已选择自我客观化):

var myself= {
    name:"Niddro",
    age:32
};

此方法允许您创建一个对象。如果你想拥有一个原型来描述一般人,你可以用相同的设置声明几个人。要创建原型,您可以使用构造函数,如下所示:

//Constructor
function generalNameForObject(param1, param2,...) {
    //Give the object some properties...
}

我有一个原型(一个recepie),我想要打电话给人,它应该包含属性名称和年龄,我将使用构造函数来实现它:

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的声誉。

我希望这能让你更深入地了解构造函数和原型之间的关系。

答案 3 :(得分:3)

构造

function Foo(x) {
    this.x =x;
}

Foo是构造函数。构造函数是一个函数。

有两种方法可以使用此构造函数Foo

  

“通过在新表达式中使用构造函数来创建对象;对于   例如,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);

<强>原型:

当您致电o.a时,javascript会首先检查a是否为对象o的属性。如果不是,javascript将查找属性链以查找a

有关属性链的更多信息,请查看mdn

构造函数的prototype属性具有非常强大的功能,在类中不可用。如果它有用是另一场辩论。构造函数的prototype属性可以改变在原型链中链接到该原型的每个实例的属性。

<强>要点:

注意:这不是一个确切的定义,摘要的目的只是为了让您对控制器和原型有所了解。

如果你使用带有new关键字的构造函数,那么构造函数和原型具有类似的目的,即使它们完全不同也很难。构造函数初始化对象的属性,因此它提供属性。原型还通过属性链(基于原型的继承)提供属性。

答案 4 :(得分:1)

原型只是对象, 构造函数是指向创建对象的函数的指针。

构造函数是指针。它指向创建要从中检索构造函数的点的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");

以上函数(人)是一个对象构造函数。一旦有了对象构造函数,就可以创建相同类型的新对象:

var myFather = new person("Sul", "Ahm", "sul@gmail.com");

每个JavaScript对象都有一个原型。 原型也是一个对象。

所有JavaScript对象都从其原型继承其属性和方法。

使用两种创建对象的方法来创建对象,即(1)对象文字,或(2)和new Object(),它们是从称为Object的原型继承而来的。原型。使用new Date()创建的对象继承了Date.prototype。

Object.prototype在原型链的顶部。

所有JavaScript对象(日期,数组,RegExp,函数等)都继承自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 =“英语”;

要将新属性添加到原型,必须将其添加到构造函数:

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__。 不建议更新实例对象的原型,因为不应在运行时更改原型(您应该能够看到谁是谁的原型,否则您需要花费额外的计算来确保没有循环引用)。 / p>

答案 5 :(得分:0)

实际上,这种方法在许多情况下可能是错误的。在Javascript中,当您将方法绑定到this关键字时,您仅将该方法提供给该特定实例,并且它实际上与该构造函数的对象实例没有任何关系,非常类似于静态方法。请记住,函数是Javascript中的一等公民,我们可以像处理对象一样处理它们,在这种情况下,我们仅向函数对象的实例添加属性。那只是故事的一部分,您还必须知道,对于我们创建的每个新实例,通过此方法附加的任何方法都将被重新声明,如果我们希望创建这么多实例,这可能会对应用程序的内存使用产生负面影响。