
这张图应该能够很好的解释 Function,Object,prototype和实例之间的关系了。
想起来以前整理的一些简单的问题:
prototype
是函数才有的一个属性。- 函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型。
- 原型可以理解成:每一个 JS 对象(null 除外),在创建的时候,就会与之关联另一个对象,这个对象就是我们说的原型。每个对象都会从原型继承属性。
- 每一个 JS 对象(null 除外)都有一个
_proto_
的属性,该属性指向该对象的原型。 - 每一个原型都会有一个
constructor
属性,指向关联的构造函数。 - 原型也是一个对象,是通过
Object()
构造函数生成的。 Object.prototype
没有原型(null)
今天看到一个问题,理解了一下Function,
- Function 构造函数 创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个Function对象。
- 全局的Function对象没有自己的属性和方法, 但是, 因为它本身也是函数,所以它也会通过原型链从Function.prototype上继承部分属性和方法。
- 每个函数都是Function的实例,构造函数也是Function的实例
来看几个问题
eg1:
Function.prototype.a = () => console.log(1);
Object.prototype.b = () => console.log(2);
function A () {};
var a = new A();
A.a(); // 1
a.a(); // Uncaught TypeError: a.a is not a function at <anonymous>:1:3
a.b(); // 2
为什么会出现这个情况呢?可以从下面几个方面来理解:
- A.a() 在A中找,A里面没有,就根据原型链往上找,A.prototype上没有,但A是Function的实例,就会找到Function.prototype上面,Function.prototype中有a属性,所以A.a() 就会执行Function.prototype中a方法,打印1.
- new 关键字,除了Function这个内置的构造器之外构建的实例的类型都是对象;对象a里没有a属性,只能往上找,a的原型链中到Object.prototype没有a属性,只有b属性,所以a.a()就会报错,a.b() 就会打印2。
eg2:
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); // 1
console.log(b.m); // undefined
console.log(c.n); // 2
console.log(c.m); // 3
这个很容易解答出答案:
这个题看起来是一个原型的问题,感觉像一个内存的问题。
- b和c的构造函数都是A,但是b和c的原型却是不一样的。
- 在new一个b的实例之前,给A.prototype里添加了一个n属性,属性值是1。所以b的原型是一个包含constructor和n的一个对象
- 然后给A的prototype的换了个指针,指向了{n:2,m:3};同时b.proto.constructor.prototype也指向了{n:2,m:3};
- 所以c的原型就变成了{n:2,m:3}
所以,b就不能找到m属性咯
eg3:
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
}
var f = new F();
f.a(); // a
f.b(); // Uncaught TypeError: f.b is not a function at <anonymous>:1:3
F.a(); // a
F.b(); // b
这个和第一个题一样。就是F.a() 需要找到Function.prototype.__proto__里,也就是Object.prototype对象里。
eg4:
function A() {
B = function() {
console.log(10)
}
return this
}
A.B = function() {
console.log(20)
}
A.prototype.B = function() {
console.log(30)
}
var B = function() {
console.log(40)
}
function B() {
console.log(50)
}
A.B() // 20
B() // 40
A().B() // 10
B() // 10
new A.B() // 20
new A().B() // 30
前三个比较好理解,
- 第四个是因为A()运行完了之后,改变了全局的B的值,B变成了打印10的函数。所以第四个B()就是10.
- 第五个也好理解。
- 第六个执行A() 返回this,this里没有B属性,只能从A的prototype里找,所以打印30
eg5:
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a); // 1
console.log(new B().a); // undefined
console.log(new C(2).a); // 2
- A里没有a,所以得去原型里找,所以第一个打印1
- B,C里都有a属性,所以不用去原型里找,B传了undefined,C传了2,于是第二个打印undefined,第三个打印2