JavaScript中的对象原型介绍。
JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是
__proto__
属性,指向这个对象的构造函数的原型对象,是从构造函数的prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法 。
在此感谢https://www.cnblogs.com/gulei/p/6733707.html这篇文章给予我的启迪
(这篇文章解释的很棒_(:з」∠)_
)。
在JavaScript中,每个函数都有一个特殊的属性叫做原型(prototype)
,如下图示例:
function doSomething(){}
console.log( doSomething.prototype );
// It does not matter how you declare the function, a
// function in javascript will always have a default
// prototype property.
var doSomething = function(){};
console.log( doSomething.prototype );//原型为:Object()
//会出现以下结果:
{
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
//也可以添加一些属性
function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );
//结果如下:
{
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {……}
}
如果你new了一个对象,创建一个doSomething的实例,也就是如下图所示:
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log(doSomeInstancing);
//会出现以下结果:
{
prop: "some value",
__proto__: {
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}
注释:当你访问doSomeInstancing的一个属性的时候,浏览器会先在doSomeInstancing中是否有这个属性。如果没有,就会在doSomething的__proto__
中(即:doSomething.prototype)寻找这个属性。如果没有,就会继续再往上找,直到找到或者没找到返回undefined。
//一个很不正经的理解:我个人是觉得原型对象和构造器函数应该是:原型对象拥有一个构造函数,构造函数用来创建对象并且赋予对象私有的能力,而原型对象给这些对象提供了一些共有的能力(这个能力其实还是由原型对象来代劳)。
prototype 属性
我们创建的对象并不能完全继承Object();因为继承的属性和方法是定义在prototype属性上的,即那些以
Object.prototype.
开头的属性,而非仅仅以Object.
开头的属性。 于是Object.prototype.watch()、Object.prototype.valueOf()
等等成员,适用于任何继承自Object()
的对象类型。而Object.is()
、Object.keys()
,以及其他不在prototype
对象内的成员,不会被“对象实例”或“继承自Object()
的对象类型”所继承。这些方法/属性仅能被Object()
构造器自身使用。
var person2 = Object.create(person1);
//这个person2是以person1为原型对象创建的,所以person2.__proto__会返回person1
constructor 属性
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
person1.constructor //Person()
可以在
constructor
属性的末尾添加一对圆括号(括号中包含所需的参数),从而用这个构造器创建另一个对象实例。毕竟构造器是一个函数,故可以通过圆括号调用;只需在前面添加new
关键字,便能将此函数作为构造器使用。
//如:
var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
//和以下代码一样:
var person3 = new Person('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
此外,constructor
属性还有其他用途。想要获得某个对象实例的构造器的名字可以这么用:
person1.constructor.name
修改原型
我们可以通过构造函数来修改构造器的 prototype
属性 。
比如:
//我们用Person()构造函数为prototype添加了一个farewell方法
Person.prototype.farewell = function() {
alert(this.name.first + ' has left the building. Bye for now!');
}
//用我们创建的对象来调用,会发现可以运行
person1.farewell();
//整条继承链动态地更新了,任何由此构造器创建的对象实例都自动获得了这个方法。
旧有对象实例的可用功能被自动更新了。这证明了先前描述的原型链模型。这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。
Note:你可能会在 prototype
上定义常属性 (constant property) (指那些你永远无需改变的属性),但一般来说,在构造器内定义属性更好。
一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 prototype 属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。
即如下代码所示:
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }
// 等等……
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!