# 原型-原型链
在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作原型 prototype
每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),
function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );
结果如下:
{
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
当函数经过 new 调用时,这个函数就成为了构造函数,返回一个全新的实例对象,这个实例对象有一个 proto 属性,指向构造函数的原型对象。
因此 就有这么一条公式 :
实例的 proto 属性 等于 其构造函数的 prototype 属性
function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log( doSomeInstancing );
doSomeInstancing 的 proto 属性就是doSomething.prototype. 但是这又有什么用呢?
当你访问 doSomeInstancing 的一个属性,浏览器首先查找 doSomeInstancing 是否有这个属性, 如果 doSomeInstancing 没有这个属性,然后浏览器就会在 doSomeInstancing.proto (也就是 doSomething.prototype) . 如果 doSomeInstancing 的 proto 有这个属性, 那么 doSomeInstancing 的 proto 上的这个属性就会被使用 否则, 如果 doSomeInstancing 的 proto 没有这个属性, 浏览器就会去查找 doSomeInstancing 的 proto 的 proto ,看它是否有这个属性.
默认情况下所有函数的原型属性的 proto 就是 Object.prototype,如果在这个上面也没有找到这个属性,然后就得出结论,这个属性是 undefined
# constructor 属性
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
摘自 MDN
这个怎么理解呢
function A() {
}
var a = new A()
a.constructor===A //true
注意 函数的 prototype 的 constructor 指向函数本身
A.protopype.constructor===A
# 图解
JS 说,我好寂寞。因为 JS 的本源是空的,即:null。
JS 说,要有神。所以它通过万能术 proto 产生了 No1 这号神,即:No1.proto == null。
JS 说,神你要有自己的想法啊。所以神自己想了个方法,根据自己的原型 prototype 创建了对象 Object,即:Object.prototype == No1; No1.proto == null。于是我们把 prototype 叫做原型,就好比 Object 的原型是神,男人的原型是人类一样,同时 proto 叫做原型链,毕竟有了 proto,对象、神、JS 之间才有联系。这时候 Object.prototype.proto == null。
JS 说,神你要有更多的想法啊,我把万能术 proto 借你用了。所以神根据 Object,使用 proto 做了个机器 No2,即 No2.proto == No1,并规定所有的东西,通过 proto 可以连接机器,再找到自己,包括 Object 也是,于是 Object 成为所有对象的原型,Object.proto.proto == No1,然后 String、Number、Boolean、 Array 这些物种也是如此。
JS 说,神你的机器好厉害喔!你的机器能不能做出更多的机器啊?神咧嘴一笑:你通过万能术创造了我,我通过自己原型创造了对象。如此,那我造个机器 Function,Function.prototype == No2, Function.proto == No2,即 Function.prototype == Function.proto 吧!这样 No2 就成了造机器的机器,它负责管理 Object、Function、String、Number、Boolean、Array 这几个。
# 题目
- 题目1
var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
n: 2,
m: 3
}
var c = new A();
console.log(b.n);
console.log(b.m);
console.log(c.n);
console.log(c.m);
- 题目2
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
}
var f = new F();
f.a();
f.b();
F.a();
F.b();
- 题目3
var foo = {},
F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';
console.log(foo.a);
console.log(foo.b);
console.log(F.a);
console.log(F.b);
# 记住
Function.prototype === Function.__proto__
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
Function.prototype === Object.__proto__
# 练习1
function Foo() {
getName = function () {
console.log(1)
};
return this;
}
Foo.getName = function () {
console.log(2)
};
Foo.prototype.getName = function () {
console.log(3)
};
var getName = function () {
console.log(4)
};
function getName() {
console.log(5)
}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
# 练习2
Object.prototype.a = 'a';
Function.prototype.a = 'a1';
function Person() {}
var yideng = new Person();
console.log(Person.a);
console.log(yideng.a);
console.log((1..a);
console.log(
yideng.__proto__.__proto__.constructor.constructor.constructor.constructor
);
总结
所有构造函数都是Function的实例,所有原型对象都是Object的实例除了Object.prototype
推荐文章 JavaScript 世界万物诞生记