javascript 中的原型
javascript 中,每个函数都可以有属性,prototype 就是每个函数都有的一个特殊属性,我们定义一个函数,然后查看其 prototype
属性:
> function dosomething() {}
> console.log(dosomething.prototype)
我们得到这样一个 prototype
对象:
{constructor: ƒ}
将这个查看这个对象详细内容:
[图片上传失败...(image-4985bb-1544192076037)]
其中,这两个成员又都可以详细展开:
[图片上传失败...(image-e996a7-1544192076037)]
现在,向 dosomething
添加一些属性和方法:
> dosomething.prototype.foo = "bar"
> dosomething.prototype.log = function() {console.log("this is a logging.")}
此时 dosomething
的 prototype
属性已与原先有所不同:
[图片上传失败...(image-d98f12-1544192076037)]
至此,我们大概了解的函数的 prototype
属性。
首先,会显示其包含的所有属性和方法;
其次是 constructor
,是构造函数的原始定义,
最后是 __proto__
,这个 __proto__
到底是什么呢?请往下看
使用 new
创建对象实例:
var dosomeInstancing = new dosomething()
然后往上面添加一些属性,
dosomeInstancing.prop = "some value"
再看这个对象实例:
[图片上传失败...(image-80ce55-1544192076037)]
是否发现了一些端倪,这个对象实例有两个属性,其中一个是我们刚刚添加进去的 prop
,另一个是其自带的 __proto__
,而且这个 __proto__
竟然就是 dosomething.prototype
到这里,我们可以基本说一下所谓原型链是什么东西了:
正如我们上面看到的,当我们访问 dosomeInstancing
这个对象实例的某个属性 prop
。
首先,浏览器会在这个对象本身查找是否有这个属性(比如我们刚刚添加的 prop
),如果有,返回属性值
如果没有(即假如我们没有给 dosomeInstacing
添加 prop
属性),浏览器会在 dosomeInstancing
的 __proto__
属性(也就是 dosomething.prototype
)中查找有没有这个 prop
属性,如果有,返回属性值
如果没有,浏览器则会继续在 dosomeInstancing
的属性 __proto__
的 __proto__
属性(dosomeInstancing.__proto__.__proto__
,即 dosomething.prototype.__proto__
),看看它有没有这个属性,默认情况下,所有函数的 prototype
的 __proto__
就是 windows.Object.prototype
。如果有,返回属性值。
如果没有,则继续在 dosomeInstancing.__proto__.__proto__.__proto__
寻找有没有这个属性,然而这里有一个问题,dosomeInstancing.__proto__.__proto__
就是 Object.prototype
,而其并没有 __proto__
属性,代表原型链上所有 __proto__
已找完,没有这个属性,会返回 undefined
。
[图片上传失败...(image-cc356f-1544192076037)]
prototype属性 继承成员被定义的地方
之前我们已看到,对象 dosomeInstancing
继承的属性和方法都是在 dosomething.prototype
中定义的,如果在实例本身没有属性和方法的情况下,访问是要从 dosomething.prototype
中寻找的。由此,我们可以说, prototype
属性是继承成员被定义的地方。
假如我给 dosomething
(注意不是 dosomething.prototype
)添加一个属性或方法,那么这个方法能否被通过 dosomething
实例化的对象所访问呢?
dosomething.outprop = "test"
[图片上传失败...(image-c70ae6-1544192076037)]
其实根据之前所述,我们已经能够想到问题的答案了,不能。
因为其不是定义在 dosomething
的 prototype
中的,而prototype
属性是继承成员被定义的地方。
这些定义在 prototype
之外的属性和方法只能被构造函数本身所访问。
constructor
prototype
里有一个属性 constructor
,是构造函数的原始定义:
[图片上传失败...(image-e579cc-1544192076037)]
既然其包含在构造函数的 prototype
属性内,那么通过构造函数实例化的对象也能继承这个属性,用以指向构造此实例的构造函数。
[图片上传失败...(image-985761-1544192076037)]
那么,就存在“另一种”创建对象实例的方法:
var person3 = new person1.constructor()
应用场景:当我们忘记或丢失了对象的原始构造函数的引用时,可以直接通过此种方式构建新的对象,或者通过:
[图片上传失败...(image-9d7ee-1544192076037)]
获得构造函数名
属性和方法定义的最佳位置
属性和方法可以定义在三个地方:
//FIXME:下面这句话有待商榷,outprop 也会出现在 dosomething.prototype.constructor 内
1 构造函数的定义中,这会直接包含在构造函数的 prototype
的 constructor
内,如:
function Person(first, last) {
this.name = [first, last],
this.country = "China"
}
2 构造函数的 prototype
内,如之前前文定义的 foo
属性:
[图片上传失败...(image-1e9feb-1544192076037)]
3 构造函数 prototype 外
//TODO: 把三种方法定义之后属性出现的位置截图放上
[图片上传失败...(image-734832-1544192076037)]
由于前两者皆是定义在 prototype
上的,所以由此实例化的对象均能够继承这些属性和方法,而最后一种定义方法不是定义在 prototype
上,不能够被继承,只能由构造函数本身访问。
那么,如何选择属性和方法的定义位置呢?
常见的做法是:
在构造器中定义属性,在 prototype
中定义方法。
理由:# javascript 中的原型
javascript 中,每个函数都可以有属性,prototype 就是每个函数都有的一个特殊属性,我们定义一个函数,然后查看其 prototype
属性:
> function dosomething() {}
> console.log(dosomething.prototype)
我们得到这样一个 prototype
对象:
{constructor: ƒ}
将这个查看这个对象详细内容:
[图片上传失败...(image-acd5fe-1544192076037)]
其中,这两个成员又都可以详细展开:
[图片上传失败...(image-277dc7-1544192076037)]
现在,向 dosomething
添加一些属性和方法:
> dosomething.prototype.foo = "bar"
> dosomething.prototype.log = function() {console.log("this is a logging.")}
此时 dosomething
的 prototype
属性已与原先有所不同:
[图片上传失败...(image-1ecacf-1544192076037)]
至此,我们大概了解的函数的 prototype
属性。
首先,会显示其包含的所有属性和方法;
其次是 constructor
,是构造函数的原始定义,
最后是 __proto__
,这个 __proto__
到底是什么呢?请往下看
使用 new
创建对象实例:
var dosomeInstancing = new dosomething()
然后往上面添加一些属性,
dosomeInstancing.prop = "some value"
再看这个对象实例:
[图片上传失败...(image-bfde0-1544192076037)]
是否发现了一些端倪,这个对象实例有两个属性,其中一个是我们刚刚添加进去的 prop
,另一个是其自带的 __proto__
,而且这个 __proto__
竟然就是 dosomething.prototype
到这里,我们可以基本说一下所谓原型链是什么东西了:
正如我们上面看到的,当我们访问 dosomeInstancing
这个对象实例的某个属性 prop
。
首先,浏览器会在这个对象本身查找是否有这个属性(比如我们刚刚添加的 prop
),如果有,返回属性值
如果没有(即假如我们没有给 dosomeInstacing
添加 prop
属性),浏览器会在 dosomeInstancing
的 __proto__
属性(也就是 dosomething.prototype
)中查找有没有这个 prop
属性,如果有,返回属性值
如果没有,浏览器则会继续在 dosomeInstancing
的属性 __proto__
的 __proto__
属性(dosomeInstancing.__proto__.__proto__
,即 dosomething.prototype.__proto__
),看看它有没有这个属性,默认情况下,所有函数的 prototype
的 __proto__
就是 windows.Object.prototype
。如果有,返回属性值。
如果没有,则继续在 dosomeInstancing.__proto__.__proto__.__proto__
寻找有没有这个属性,然而这里有一个问题,dosomeInstancing.__proto__.__proto__
就是 Object.prototype
,而其并没有 __proto__
属性,代表原型链上所有 __proto__
已找完,没有这个属性,会返回 undefined
。
[图片上传失败...(image-a7a845-1544192076037)]
prototype属性 继承成员被定义的地方
之前我们已看到,对象 dosomeInstancing
继承的属性和方法都是在 dosomething.prototype
中定义的,如果在实例本身没有属性和方法的情况下,访问是要从 dosomething.prototype
中寻找的。由此,我们可以说, prototype
属性是继承成员被定义的地方。
假如我给 dosomething
(注意不是 dosomething.prototype
)添加一个属性或方法,那么这个方法能否被通过 dosomething
实例化的对象所访问呢?
dosomething.outprop = "test"
[图片上传失败...(image-ef4755-1544192076037)]
其实根据之前所述,我们已经能够想到问题的答案了,不能。
因为其不是定义在 dosomething
的 prototype
中的,而prototype
属性是继承成员被定义的地方。
这些定义在 prototype
之外的属性和方法只能被构造函数本身所访问。
constructor
prototype
里有一个属性 constructor
,是构造函数的原始定义:
[图片上传失败...(image-5de1cd-1544192076037)]
既然其包含在构造函数的 prototype
属性内,那么通过构造函数实例化的对象也能继承这个属性,用以指向构造此实例的构造函数。
[图片上传失败...(image-1793f8-1544192076037)]
那么,就存在“另一种”创建对象实例的方法:
var person3 = new person1.constructor()
应用场景:当我们忘记或丢失了对象的原始构造函数的引用时,可以直接通过此种方式构建新的对象,或者通过:
[图片上传失败...(image-d2d82a-1544192076037)]
获得构造函数名
属性和方法定义的最佳位置
属性和方法可以定义在三个地方:
//FIXME:下面这句话有待商榷,outprop 也会出现在 dosomething.prototype.constructor 内
1 构造函数的定义中,这会直接包含在构造函数的 prototype
的 constructor
内,如:
function Person(first, last) {
this.name = [first, last],
this.country = "China"
}
2 构造函数的 prototype
内,如之前前文定义的 foo
属性:
[图片上传失败...(image-d31e37-1544192076037)]
3 构造函数 prototype 外
//TODO: 把三种方法定义之后属性出现的位置截图放上
[图片上传失败...(image-3494bb-1544192076037)]
由于前两者皆是定义在 prototype
上的,所以由此实例化的对象均能够继承这些属性和方法,而最后一种定义方法不是定义在 prototype
上,不能够被继承,只能由构造函数本身访问。
那么,如何选择属性和方法的定义位置呢?
常见的做法是:
在构造器中定义属性,在 prototype
中定义方法。
理由:
在构造器中定义属性,可以方便使用 this
关键字,因为在函数域内,如 this.name = this.first + this.last
,如果放在 prototype
很难做到
在 prototype
中定义方法,之后可以方便地进行更新,添加,一致性更好,同时不同方法定义在不同代码块,可读性也更高。
两点说明
一、Object.create()
上一篇文章Javascript对象中提到过 Object.create()
var person2 = Object.create(person1);
create()
实际做的是从指定原型对象创建一个新的对象。这里以 person1
为原型对象创建了 person2
对象。在控制台输入:
person2.__proto__
返回对象 person1
。
二、构造函数的 proto
dosomething.proto 与 dosomething.prototype.proto 是不一样的,
当我们访问 dosomething.__proto__
时:
[图片上传失败...(image-a80a88-1544192076037)]
发现也有内容返回,而根据前文所述,__proto__
应该是对象实例所拥有的,或者是构造函数的 prototype
属性所拥有的属性。
那这里返回的究竟是什么呢?
说实话,在这块内容之前,我觉得自己整的挺明白的,但在这想了半天,我有点懵,我看看其他资料研究下。
__proto__
是 Object 构造函数的自有属性,不会被继承,在 Object.prototype.constructor 中显示的都是自有属性,__proto__
而 Object.__proto__
属性指向的是 Function.prototype
,即 Function
是 Object
的原型
Object.prototype
无 __proto__
属性
Object
有 __proto__
属性
参考链接
- 对象原型, by MDN
在构造器中定义属性,可以方便使用 this
关键字,因为在函数域内,如 this.name = this.first + this.last
,如果放在 prototype
很难做到
在 prototype
中定义方法,之后可以方便地进行更新,添加,一致性更好,同时不同方法定义在不同代码块,可读性也更高。
两点说明
一、Object.create()
上一篇文章Javascript对象中提到过 Object.create()
var person2 = Object.create(person1);
create()
实际做的是从指定原型对象创建一个新的对象。这里以 person1
为原型对象创建了 person2
对象。在控制台输入:
person2.__proto__
返回对象 person1
。
二、构造函数的 proto
dosomething.proto 与 dosomething.prototype.proto 是不一样的,
当我们访问 dosomething.__proto__
时:
[图片上传失败...(image-2a936-1544192076037)]
发现也有内容返回,而根据前文所述,__proto__
应该是对象实例所拥有的,或者是构造函数的 prototype
属性所拥有的属性。
那这里返回的究竟是什么呢?
说实话,在这块内容之前,我觉得自己整的挺明白的,但在这想了半天,我有点懵,我看看其他资料研究下。
__proto__
是 Object 构造函数的自有属性,不会被继承,在 Object.prototype.constructor 中显示的都是自有属性,__proto__
而 Object.__proto__
属性指向的是 Function.prototype
,即 Function
是 Object
的原型
Object.prototype
无 __proto__
属性
Object
有 __proto__
属性
参考链接
- 对象原型, by MDN