内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用
“JavaScript是世界上最容易被误解的语言”-D.Crockford
我的问题:
我不是在寻找任何语法和如何去解释事物,因为我理解了它们的某些部分,只是想以更简单的方式了解这些事情。一个类比(非技术)或例子会很棒。*
详细说明为什么我问这个问题(如果你想要忽略):
过去六个月来我一直在使用JavaScript,当我知道JavaScript是一种基于原型的语言时,我感到非常震惊。
我经历了一些Stack Overflow的问题,关于如何使用JavaScript并碰到原型和构造函数。
我知道了,现在我可以说,在构造和原型方面,我不是一个noob。我熟悉语法。但我仍然认为我错过了一些东西,并没有深入到这种语言的核心,有时候我会感到困惑。
简单英语中的构造函数和原型?
构造函数创建对象并为其分配原型。原型是具有各种属性的对象,对象可以通过原型链继承。像往常一样,例子有助于:
function Foo() {
}
Foo.prototype.answer = 42;
var f = new Foo();
console.log(f.answer); // "42"
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"
从ES5开始,构造函数不再是将原型分配给对象的唯一方法。现在你也可以通过Object.create
。以上大致等同于:
var fooProto = {
answer: 42
};
var f = Object.create(fooProto);
console.log(f.answer); // "42"
使用原型和构造函数的目的是什么?
共享对象之间的特征。原型的属性可以是函数或数据,使用该原型的对象都可以访问并重用。
请在下面重新评论:
我了解有关共享特征的部分,但是我能否更详细地了解它
那么,考虑一个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;
};
通过构建的所有对象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
它们以一种高效的内存方式共享这些属性:每个实例都没有自己的这些属性副本(这意味着每个属性名称及其值在每个对象中); 相反,他们只是参考了他们共享的原型,这些原型具有这些属性。
我将通过描述其他传统的面向对象编程语言中的对象如何描述来开始回答这个问题,因为我还想要针对您在问题开始时发布的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;
}
}
然后,我会使用上面的“blueprint” 创建一个对象,如下所示:
Human Joe = new Human(90, 180, "Joe");
现在,我们说的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;
};
};
然后,如果您想要Joe
像上面那样定义,您可以执行以下操作:
var Joe = new Human("Joe", 180, 90);
您可以看到显示的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";
}
那么,如果你打电话Joe.toString()
或当你做这样的事情时alert(Joe)
自动打电话toString()
,返回的价值将是“乔身高190厘米,体重80公斤”。
关于OOP和JavaScript的更多细节可以在您的问题的背景下讨论,但我认为我的答案足够长!我希望这回答了你的问题。