Prototype
撰写时间:2023-05-30
修订时间:2024-11-28
构造器
初识构造器
在JavaScript中,构造器(constructor)是用以创建类的实例的函数。
上面代码查看变量a的名为constructor的属性值。在终端将显示:
关系示意图:
即,使用了Number这个构造器来创建了一个值为25的整数的实例。
在其它编程语言中,我们从类创建对象实例。但在JavaScript中,函数是最重要的一等公民,我们从函数创建对象实例。能创建对象实例的函数,就称为构造器。
因此,像上面一样,以后当我们看到一个函数,其函数名是大写的JavaScript内置的数据类型,我们就应当清醒地认识到,这不是一个普通的函数,而是一个能创建对象实例的构造器。
再探构造器
显示:
Date是一个类,而date是Date这个类的一个实例。这是我们从其他编程语言中得到的普遍共识。
但在JavaScript中,Date是什么?
终端显示:
当我们使用console.log(Date)
来查看Date时,从C语言, Python语言一路走来的我们会想当然地认为,这是一个名为Date的类。但正如上面所见,Date不是一个类,而是构造器,一个可以创建对象实例的函数。
查看date这个实例的构造器:
终端显示:
两者是否为同一对象?
变量date的构造器就是Date,两者为同一对象。
综上,当我们编写如下代码:
时,我们所看到的,就是各种用以创建相应实例的构造器。
终极构造器
变量date的构造器为Date,后者呢?它有没有相应的构造器?
终端显示:
说明Date的构造器为Function。也即Function构造器先创建了Date构造器,然后再通过后者来创建各个相应与日期有关的实例。
查看Object的构造器:
就连根对象Object,其构造器也为Function。
Function呢?既然你这么牛,你的构造器又是哪个?
不用再找了,Function就是传说中的终极构造器,它甚至是它自己的构造器!
神通广大的孙悟空,绝非凡人所生,因此它得从一块仙石中蹦出来。你能想像,孙悟空将它自己生下来吗?孙悟空做不到,而Function却可以做得到!Function简直就是宇宙第一推动力啊!
通过字面符创建对象
当我们看到:
的代码时,很清楚,显式地使用了Date构造器来创建实例。
而对于以下代码:
则是通过字面符(literal notation)的方式来创建对象实例。在其内幕,通过调用了相应的构造器来创建对象实例。
Prototype
Prototype的作用
person有一个name属性,一个getName方法。但我们并未其声明toString方法,哪来的?
可以调用person的hasOwnProperty来查看自己是否有特定的属性。
说明person没有自己的属性toString。它是从哪里来的?
Object.prototype
每个构造器都是一个函数,都有一个prototype属性。该属性用于实现基于prototype的继承(protyotype-based inheritance)以及共享的属性(shared properties)。
prototype用以向下提供可以共享的属性(包括属性,以及方法)。__proto__用以向上链接。Object.prototype
是JavaScript中最顶层的,用以向一切对象提供共享的prototype.
显示:
所显示出来的正是Object.prototype
。定义在Object.prototype
之下的所有方法及其他属性,均可以被其他对象共享。因此,我们可以直接调用:
上面的代码console.dir(obj.__proto__)
与console.dir(Object.prototype)
所显示的结果是一样的。因此,下面的断言成立:
对象obj的__proto__属性指向了类Object的prototype属性。这个就叫做prototype链
。因此我们可以按照其他面向对象编程语言的说法来说:对象obj所属的类,继承于类Object
。
构造器中的prototype属性
构造器虽说函数,但与普通函数相比,多了一个prototype属性。
显示:
prototype的属性值是一个含有两个属性的object对象。第一个属性名称为constructor,指向该构造器自身。第二项属性集中列出了可供下级子对象所共享的属性与方法。从上面可以看出,这些可共享的属性与方法均来自于Object.prototype
。
然而,我们不能引用第二个属性名称,它由JavaScript内部管理。相反,我们可以像上节所述一样,通过该构造器所实例化的对象来引用它:
再看__proto__
无论是MyObj, 还是obj,都带有__proto__属性。
先看obj的__proto__属性:
显示:
它与上一节中的obj.constructor.prototype
是一样的。我们甚至也可验证:
也就是说,一个具体对象的__proto__属性链向了其构造器的prototype属性。
再来看MyObj的__proto__属性:
MyObj以标准的Function作为其构造器。
查看Function的构造器:
显示:
说明,Function这个内在对象也有其构造器,其构造器的名称为Function
。
再看Function的__proto__属性:
显示:
通过prototype属性提供共享数据
显示:
在构造器的prototype内的属性,均可被继承的对象访问。因此,上面代码中,add, address,以及Object的prototype属性内的所有属性及方法,均可被子对象访问。而name及age只能被obj自己访问。
这就是JavaScript判断对象是否具有相应属性名称的机制。简而言之,它只需要检查该对象构造器的prototype的属性,并按从近到远的方式来确定。
了解此点后,作为开发人员,只需把握一条:如果需要子对象共享属性,则把这些属性放在构造器的prototype的属性中即可。至于如何建立繁杂的prototype链
,这是JavaScript内部操作的问题,从而大大减轻了开发人员的工作量。
prototype内的属性不是自身的属性
Object类
Object
是JavaScript是顶层的类。它有以下属性:
- protoytpe
- __proto__
方法:
- assign
- create
- defineProperties
- defineProperty
- entries
- freeze
- fromEntries
- getOwnPropertyDescriptor
- getOwnPropertyDescriptors
- getOwnPropertyNames
- getOwnPropertySymbols
- getOwnPropertyKeys
- getPrototypeOf
- hasOwn
- is
- isExtensible
- isFrozen
- isSealed
- keys
- preventExtensions
- seal
- setPrototypeOf
- values
Object.prototype
有以下方法:
- hasOwnProperty
- isPrototypeOf
- propertyIsEnumerable
- toLocaleString
- toString
- valueOf
Object.__proto__
有getter及setter, 其中,getter调用了Object.getPrototypeOf
方法返回其原型。
Object.prototype
还有以下传统方法:
- __defineGetter
- __defineSetter
- __lookupGetter
- __lookupSetter