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