JavaScript中的原型和构造函数(普通英语)?

HIR*_*KUR 15 javascript constructor prototype prototypal-inheritance prototype-programming

"JavaScript是世界上最容易被误解的语言"-D.Crockford

我的问题:

  1. 构造函数和原型用简单的英语?
  2. 使用原型需要什么?使用Prototypes和构造函数的目的是什么?我的意思是他们提供更多的灵活性.我问这个,因为我过去六个月一直在使用这种语言,从来没有使用过原型和构造函数的情况.

我不是在寻找任何语法以及如何进行解释,因为我理解它们的某些部分,只是想以更简单的方式了解这些事情.类比(非技术)或例子会很棒.*

详细说明我问这个问题的原因(如果你愿意,请忽略):

我在过去的六个月里一直在使用JavaScript,当我发现JavaScript是一种基于原型的语言时,我感到非常震惊.

我经历了一些关于如何使用JavaScript并遇到原型和构造函数的Stack Overflow问题.

我学会了它,现在我可以说在构造函数和原型方面我不是一个菜鸟.我熟悉语法.但是我仍然觉得我错过了一些东西而没有深入到这种语言的核心,我有时会感到困惑.

我希望我很清楚.

T.J*_*der 19

用简单的英语构造和原型?

构造函数创建对象并为其分配原型.原型是具有各种属性的对象,对象可以通过原型链继承这些属性.一如既往,示例有助于:

function Foo() {
}
Foo.prototype.answer = 42;

var f = new Foo();
console.log(f.answer); // "42"
Run Code Online (Sandbox Code Playgroud)

Foo是一个构造函数.使用时new Foo,Foo.prototype指向的对象将成为创建对象的原型.当你这样做时f.answer,由于f没有自己的名称属性answer,JavaScript引擎会查看f原型以查看是否有一个.既然如此,它使用原型中的值,我们在控制台中看到"42".这就是如何解析属性:通过查看一个对象来查看它是否具有给定名称的属性,如果没有,则转到它的原型以查看是否具有该属性,如果没有它的原型,依此类推.

请注意,上述结果是在使用该原型创建对象向原型添加属性可以正常工作; 您可以通过对象使用这些新属性:

function Foo() {
}
Foo.prototype.answer = 42;

var f = new Foo();
console.log(f.question); // "undefined", neither `f`, nor `Foo.prototype`, nor
                         // `Object.prototype` has a `question` property

Foo.prototype.question = "Life, the Universe, and Everything";
console.log(f.question); // "Life, the Universe, and Everything"
Run Code Online (Sandbox Code Playgroud)

从ES5开始,构造函数不再是可以将原型分配给对象的唯一方法.现在你也可以通过Object.create.以上大致相当于:

var fooProto = {
    answer: 42
};
var f = Object.create(fooProto);
console.log(f.answer); // "42"
Run Code Online (Sandbox Code Playgroud)

使用Prototypes和构造函数的目的是什么?

分享对象之间的特征.原型的属性可以是函数或数据,使用该原型的对象都可以访问并且可以重用.

你的评论如下:

我理解了关于共享特征的部分,但是我可以更详细地了解它

好吧,考虑一个Circle构造函数:

function Circle(radius) {
    this.r = radius;
}
Circle.prototype.radius = function() {
    return this.r;
};
Circle.prototype.diameter = function() {
    return this.r * 2;
};
Circle.prototype.circumference = function() {
    return 2 * Math.PI * this.r;
};
Circle.prototype.area = function() {
    return Math.PI * this.r * this.r;
};
Run Code Online (Sandbox Code Playgroud)

通过构建的所有对象Circle都会得到Circle.prototype他们的原型,所以它们都具有方便diameter,circumference等.人.功能.

var c1 = new Circle(3);
console.log(c1.area());          // 28.274333882308138
console.log(c1.circumference()); // 18.84955592153876

var c2 = new Circle(5);
console.log(c2.area());          // 78.53981633974483
console.log(c2.circumference()); // 31.41592653589793
Run Code Online (Sandbox Code Playgroud)

它们以一种以内存效率的方式共享这些属性:每个实例都没有自己的属性副本(这意味着在每个对象中保留每个属性名称及其值); 相反,他们只是引用了他们共享的原型,这些原型具有这些属性.


nas*_*-sh 7

首先,我建议你看看这个男人自己的播放列表(Crockford).它可能很旧,但它确实很好地解释了JavaScript"逻辑",你的问题在第三个视频中得到了特别的回答.

我将通过描述如何在其他传统的面向对象编程语言中描述对象来开始回答这个问题,因为我还想针对您在问题开头发布的Crockford评论.

为了理解构造函数,首先必须对对象有一个很好的理解.在传统的OOP语言中,Object是描述对象状态的变量(称为属性或字段)的集合,以及描述其行为的函数(称为方法).在那些(非JavaScript)语言中,这些对象的"蓝图"称为类.

所以,如果我用Java创建一个Human类,那么非常简单的描述将如下所示:

class Human {
    String name;
    int weight; // kg
    int height; // cm

    void eat(int foodWeight) {
        this.weight += foodWeight;
    }

    Human(int weight, int height, int name) {
        this.weight = weight; 
        this.height = height;
        this.name   = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我将使用上面的"蓝图" 创建一个Object,如下所示:

Human Joe = new Human(90, 180, "Joe");
Run Code Online (Sandbox Code Playgroud)

现在,我们说的Joe 是一个例子 Human,其重量为90千克,高度为180厘米.

在上面的类中,您注意到我有一个Human()用于创建对象的函数,并在创建时定义它的状态.这基本上就是构造函数的作用.

那么JavaScript的不同之处是什么?

为了在大众创作时吸引大众(正如您将在我发布的视频系列中看到的那样),JavaScript结合了一些类似Java的语法.根据Crockford的说法,这样做的目的是让程序员知道,因为他们已经知道/学习了一些Java,然后他们可以学习一些新的命令,然后继续用JavaScript编程,而实际上,他们之间存在差异.这两者远远超过了它们的相似之处.

在JavaScript中,为了以看起来像Java类的方式创建对象,您将使用如下函数语法:

var Human = function(name, height, weight) {
    this.name   = name;
    this.height = height;
    this.weight = weight;

    this.eat    = function(foodWeight) {
        this.weight += foodWeight;
    };
};
Run Code Online (Sandbox Code Playgroud)

然后,如果您想像Joe上面那样定义,您将执行以下操作:

var Joe = new Human("Joe", 180, 90);
Run Code Online (Sandbox Code Playgroud)

您可以看到显示的Java和JavaScript语法之间的相似之处.所以,回答你的第一个问题:JavaScript构造函数是一个函数,当被调用时new,创建并返回一个隐式创建的对象,指向this.

那么Prototype来自哪里?好吧,在JavaScript中,函数本身也是JS对象,它们有一个名为的属性prototype.因此,Human()我们上面创建的构造函数有一个名为的属性prototype,此属性引用其属性和方法继承的对象Joe,以及所有其他实例Human,并且可以扩展此对象以创建将继承的属性在所有这些情况下.

例如,其中一种方法Function.prototype是着名的toString方法.你可以定义

Human.prototype.toString = function() {
    return this.name + " is " + this.height + " cm tall and weighs " + this.weight + " kg";
}
Run Code Online (Sandbox Code Playgroud)

然后,如果你打电话Joe.toString()或当你做这样的事情alert(Joe)自动呼叫时toString(),返回的值将是"乔是190厘米高,重80公斤".

关于OOP和JavaScript的更多细节可以在您的问题的上下文中介绍,但我认为我的答案足够长!我希望这回答了你的问题.

  • 在你的Human JS例子中,函数eat更适合Human.prototype,因为它不会在实例之间发生变化(对于Joe.eat和Jane.eat也会这样做).因此,每次创建Human实例时都不需要启动进食. (5认同)