本文翻译自微软的牛人Scott Allen Prototypes and Inheritance in JavaScript ,本文对到底什么是Prototype和为什么通过Prototype能实现继承做了详细的分析和阐述,是理解JS OO 的佳作之一。翻译不好的地方望大家修改补充。
二、正文
JavaScript中的面向对象不同于其他语言,在学习前最好忘掉你所熟知的面向对象的概念。JS中的OO更强大、更值得讨论(arguably)、更灵活。
1.类和对象
JS从传统观点来说是面向对象的语言。属性、行为组合成一个对象。比如说,JS中的array就是由属性和方法(如push、reverse、pop 等)组合成的对象。
var myArray = [1, 2];
myArray.push(3);
myArray.reverse();
myArray.pop();
var length = myArray.length;
问题是:这些方法(如push)从哪里来的?一些静态语言(比如JAVA)用class来定义一个对象的结构。但是JS是没有”class"(classless)的语言,没有一个叫做“Array"的类定义了这些方法给每个array去继承。因为JS是动态的,我们可以在需要的时候随意的往对象中添加方法。例如,下面的代码定义了一个对象,该对象表示二维空间中的坐标,里面有一个add方法。
var point = {
x : 10,
y : 5,
add: function(otherPoint)
{
this.x = otherPoint.x;
this.y = otherPoint.y;
}
};
我们想让每一个point对象都有一个add方法。我们也希望所有的poin对象共享一个add方法,而不必把add方法加到所有的point对象当中。这就需要让prototype登场了。
2.关于Prototypes
JS中每一个对象都有一个隐含的属性(state)——对另一个对象的引用,称为对象的prototype.我们上面创建的array和point当然也都含有各自prototype的引用。prototype引用是隐含的,但是它是ECMAScript已实现的,允许我们使用对象的_proto_(在Chrome中)属性来获取它。从概念上理解我们可以认为对象和prototype的关系就像下图所表示的:
作为开发者,我们将用Object.getPrototypeOf 函数来替代_proto_属性来查看对象的prototype引用。在写这篇文章的时候,Object.getPrototypeOf这个函数已经在Chrome,firefox,还有IE9中提供了支持。在未来还会有更多的浏览器来支持这一特性,这已经是ECMAScript的标准之一。我们可以用以下代码来证明myArray和我们之前创建的point对象确实引用了两个不同的prototype对象。
Object.getPrototypeOf(point) != Object.getPrototypeOf(myArray);
在文章接下来的部分,我还将使用到_proto_,主要是因为_proto_在图示和句子里里比较直观。但要记住这不是规范的,Object.getPrototypeOf才是用来获取对象prototype的推荐方法。
2.1是什么使Prototypes如此特别?
我们已经知道了array的push方法来自myArray的prototype对象。图2是Chrome中的一个截图,我们调用Object.getPrototypeOf方法来取得myArray的prototype对象。
图 2
注意到myArray的prototype对象里包含了很多方法,比如push、pop还有reverse这些我们在开头代码里使用过的。prototype对象才是push方法的唯一拥有者,但这个方法是如何通过myArray调用到的呢?
myArray.push(3);
要想明白它是怎样实现的,第一步是认清Protytype一点儿不特殊。Prototype就是一些对象。我们可以给这些对象添加方法、属性,像其他任何的JS对象一样。但同时Prototype也是一个特殊的对象。
Prototype的特殊是因为下列规则:当我们通知JS我们想要在一个对象上调用push方法或是读取某个属性的时候,解释器(runtime)首先去寻找这个对象本身的方法或属性。如果解释器没有找到该方法(或属性)就会沿着_proto_引用去寻找对象的prototype中的各个成员。当我们在myArray中调用push方法,JS没有在myArray对象中找到push,但是在myArray的prototype对象中找到了push,即调用了该push方法(图3)。
图 3
我所描述的这种行为本质上就是对象本身继承了 它的prototype中的所有方法和属性。我们在JS中不需要用class来实现这种继承关系。即,一个JS对象从它的prototype中继承特性。
图3还告诉我们每个array对象都能维护自己的状态(state)和成员。如果我们需要myArray的length属性,JS将从myArray中找到length的值而不会去prototype中去寻找。我们能运用这个特性来“override"一个方法,即,将需覆盖的方法(像push)放到myArray自己的对象中。这样做就可以有效的将prototype中的push方法隐藏掉。
3.共享Prototype
Prototype在JS中真正神奇的地方是多个对象能引用同一个prototype对象。比如,我们创建两个数组:
var myArray = [1, 2];var yourArray = [4, 5, 6];
这两个数组将共享一个相同的prototype对象,下面的代码将返回true
Object.getPrototypeOf(myArray) === Object.getPrototypeOf(yourArray);
如果我们在两个数组中调用push方法,JS将调用他们共同的prototype中的push。
Prototype对象在JS中给我们这种继承的特性,它们也允许我们共享方法的实现。Prototype也是链式的。换句话说,prototype是一个对象,那么prototype对象也可以拥有一个指向别的prototype对象的引用。从图2中可以看到prototype的_proto_属性是一个不为null的值也指向另外一个prototype.当JS开始寻找成员变量的时候,比如push方法,它将沿着这些prototype的引用检查每一个对象直到找到这个对象或达到链的尾部为止。这种链的方式更增加了JS中继承和共享的灵活性。
接下来你也许会问:我怎样设置自定义对象的prototype引用?比如,我们之前创建过的对象point,我们怎样加入一个add方法到prototype对象中,让所有的point对象都能继承它?在我们回答这个问题之前,我们先了解一下JS中的函数.
4.关于Funciton
函数在JS中同样也是对象。函数在JS中有很多重要的特性,在此文章中我们不能一一列举。但像把一个函数赋值给一个变量或是将一个函数当做另外一个函数的参数在当今的JS编程中是很基础的方式。
我们需要关注的是:因为函数是对象,所以它拥有方法、属性和一个prototype对象的引用。我们一起讨论一下下面代码的含义:
// this will return true:
typeof (Array) === "function"
// and so will this:
Object.getPrototypeOf(Array) === Object.getPrototypeOf(function () { })
// and this, too:
Array.prototype != null
第一行代码证明Array在JS中是一个函数。待会儿我们将看到怎样调用Array函数来创建一个新的array对象。
第二行代码证明Array对象和function对象引用相同的prototype,就像我们之前看到的所有的array对象共享一个prototype。
最后一行证明Array函数有一个prototype属性。千万不要将这个prototype属性和_proto_属性混淆了。它们的使用目的和指向的对象都不相同。
// true
Array.prototype == Object.getPrototypeOf(myArray)
// also true
Array.prototype == Object.getPrototypeOf(yourArray);
我们用新学的知识重画之前的图片:
图 5
现在我们要创建一个array对象。其中一种方法就是:// create a new, empty object
var o = {};
// inherit from the same prototype as an array object
o.__proto__ = Array.prototype;
// now we can invoke any of the array methods ...
o.push(3);
尽管上面的代码看起来不错,但问题是不是每一个JS的环境都支持对象的_proto_属性。幸运的是,JS内置一个标准的机制用来创建新对象同时设置对象的_proto_属性,这就是“new”操作符。
var o = new Array();
o.push(3);
“new”操作符在JS中有三个重要的任务:首先,它创建一个新的空对象。接着,它设置这个新对象的_proto_属性指向调用函数的prototype属性。最后,执行调用函数同时把“this”指针指向新的对象。如果我们把上面的两行代码展开,将得到以下的代码:
var o = {};
o.__proto__ = Array.prototype;
Array.call(o);
o.push(3);
函数的“call”方法允许你调用一个函数同时指定这个函数里面的"this"指向传入的新对象。当然,我们也想通过上面的方法来创建我们自己的对象来实现对象的继承,这种函数就是我们所熟知的——构造函数。
5.构造函数
构造函数是一个有两个独特标识的普通JS函数对象:
1.首字母大写(容易识别)。
2.用new操作符连接来构造新对象。
Array就是一个构造函数——Array函数用new连接、首字母大写。JS中的Array函数是内置的,但任何人都可以创建自己的构造函数。事实上,我们终于到了该为point对象来创建一个构造函数的时候了。
var Point = function (x, y) {
this.x = x;
this.y = y;
this.add = function (otherPoint) {
this.x = otherPoint.x;
this.y = otherPoint.y;
}
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
p1.add(p2);
上面的代码中我们使用new操作符和Point函数来来构造一个point对象。在内存中你可以把最终的结果想成图6所表示的样子。
图 6
现在的问题是,add方法存在于每一个point对象中。鉴于我们对prototype的了解,把add方法加到Point.prototype中是一个更好的选择(不必把add方法的代码拷贝到每个对象中)。为了实现这个目的,我们需要在Point.prototype对象上做些修改。
var Point = function (x, y) {
this.x = x;
this.y = y;
}
Point.prototype.add = function (otherPoint) {
this.x = otherPoint.x;
this.y = otherPoint.y;
}
var p1 = new Point(3, 4);
var p2 = new Point(8, 6);
p1.add(p2);
好了!我们已经用prototype实现了JS中的继承!
6.总结
希望能通过这篇文章让你能拨开prototype的迷雾。当然这只是功能强大又灵活的prototype的入门。更多的关于prototype的知识还是希望读者能够自己去探索和发现。

解决PHP报错:继承父类时遇到的问题在PHP中,继承是一种重要的面向对象编程的特性。通过继承,我们能够重用已有的代码,并且能够在不修改原有代码的情况下,对其进行扩展和改进。尽管继承在开发中应用广泛,但有时候在继承父类时可能会遇到一些报错问题,本文将围绕解决继承父类时遇到的常见问题进行讨论,并提供相应的代码示例。问题一:未找到父类在继承父类的过程中,如果系统无

如何在PHP中使用多态和继承来处理数据类型引言:在PHP中,多态和继承是两个重要的面向对象编程(OOP)概念。通过使用多态和继承,我们可以更加灵活地处理不同的数据类型。本文将介绍如何在PHP中使用多态和继承来处理数据类型,并通过代码示例展示它们的实际应用。一、继承的基本概念继承是面向对象编程中的一种重要概念,它允许我们创建一个类,该类可以继承父类的属性和方法

继承是一个概念,它允许我们从一个类访问另一个类的属性和行为。被继承方法和成员变量的类被称为超类或父类,而继承这些方法和成员变量的类被称为子类或子类。在Java中,我们使用“extends”关键字来继承一个类。在本文中,我们将讨论使用继承来计算定期存款和定期存款的利息的Java程序。首先,在您的本地机器IDE中创建这四个Java文件-Acnt.java−这个文件将包含一个抽象类‘Acnt’,用于存储账户详情,如利率和金额。它还将具有一个带有参数‘amnt’的抽象方法‘calcIntrst’,用于计

PHP中的封装技术及应用封装是面向对象编程中的一个重要概念,它指的是将数据和对数据的操作封装在一起,以便提供对外部程序的统一访问接口。在PHP中,封装可以通过访问控制修饰符和类的定义来实现。本文将介绍PHP中的封装技术及其应用场景,并提供一些具体的代码示例。一、封装的访问控制修饰符在PHP中,封装主要通过访问控制修饰符来实现。PHP提供了三个访问控制修饰符,

继承:继承是面向对象编程(OOP)中的一个基本概念,它允许类从其他类继承属性和行为。它是一种基于现有类创建新类的机制,促进代码重用并建立类之间的层次关系。继承基于"父子"或"超类-子类"关系的概念。从中继承的类被称为超类或基类,而继承超类的类被称为子类或派生类。子类继承其超类的所有属性(变量)和方法(函数),还可以添加自己独特的属性和方法或覆盖继承的属性和方法继承的类型在面向对象编程(OOP)中,继承是一个基本概念,它允许类从其他类中继承属性和行为。它促进

如何使用Java强制继承代理final类?在Java中,final关键字用于修饰类、方法和变量,表示它们不可被继承、重写和修改。然而,在某些情况下,我们可能需要强制继承一个final类,以实现特定的需求。本文将讨论如何使用代理模式来实现这样的功能。代理模式是一种结构型设计模式,它允许我们创建一个中间对象(代理对象),该对象可以控制对另一个对象(被代理对象)的

PHP是一种服务器端编程语言,自PHP5之后开始支持面向对象编程(OOP)。OOP的核心思想是将数据和行为封装在对象中,以提高程序的可维护性和可扩展性。在PHP中,面向对象编程具有三大特性:继承、多态与接口。一、继承继承是指一个类可以从另一个类中继承属性和方法。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以通过继承获得父类中的属性和方法,并且可

如何在Go语言中实现封装和继承封装和继承是面向对象编程中的两个重要概念,它们可以使代码更加模块化和可维护,同时也为代码的复用提供了便利。本文将介绍在Go语言中如何实现封装和继承,并提供相应的代码示例。封装封装是将数据和功能进行封装,隐藏实现的细节,只暴露必要的接口给外部使用。在Go语言中,封装是通过导出和非导出标识符来实现的。首字母大写的标识符可以被其他包访


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Pelayar Peperiksaan Selamat
Pelayar Peperiksaan Selamat ialah persekitaran pelayar selamat untuk mengambil peperiksaan dalam talian dengan selamat. Perisian ini menukar mana-mana komputer menjadi stesen kerja yang selamat. Ia mengawal akses kepada mana-mana utiliti dan menghalang pelajar daripada menggunakan sumber yang tidak dibenarkan.

DVWA
Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

SublimeText3 versi Inggeris
Disyorkan: Versi Win, menyokong gesaan kod!

EditPlus versi Cina retak
Saiz kecil, penyerlahan sintaks, tidak menyokong fungsi gesaan kod

SublimeText3 Linux versi baharu
SublimeText3 Linux versi terkini
