原型和原型链

原型和原型链

https://juejin.im/post/5835853f570c35005e413b19

https://juejin.im/post/5c218e4c5188254caf18bb1a

引用类型都是对象

原型与原型链

对象都有原型,原型也是对象,也有自己的原型,就组成了一条原型链。在对象上找某个属性没找到就会往原型链上找。

第一个🌰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = {};
console.log(obj.__proto__ == Object.prototype);//true

var string = "string";
console.log(string.__proto__ == String.prototype);//true

var boolean = true;
console.log(boolean.__proto__ == Boolean.prototype);//true

var arr = [];
console.log(arr.__proto__ == Array.prototype);//true

var num = 1;
console.log(num.__proto__ == Number.prototype);//true

Object、String、Boolean、Array、Number是构造函数,而声明的变量是它们的实例。

第二个🌰

1
2
3
4
5
6
7
8
9
10
function test(name){
this.name = name;
}
test.prototype.print = function(){
console.log(this.name);
}
let test1 = new test('qinhanwen');
test1.print();//qinhanwen
console.log(test1.__proto__ === test.prototype);//true
console.log(test1.__proto__.constructor === test);//true

new的过程,从这里可以看出test1对象实例的__proto__指向test的prototype。constructor指向构造函数。

第三个🌰

1
2
3
4
5
6
7
8
9
10
function test(name){
this.name = name;
}
test.prototype.print = function(){
console.log(this.name);
}
let test1 = new test('qinhanwen');
console.log(test1.__proto__);//也就是test.prototype
console.log(test1.__proto__.__proto__);//也就是test.prototype.__proto__,也是Object.prototype
console.log(test1.__proto__.__proto__.__proto__);//null

这里没打算贴原型链那张很复杂的图片

贴几个面试题

1.
1
2
3
4
5
6
Function.prototype.a = 'a';
Object.prototype.b = 'b';
var test = function(){}
let test1 = new test();
console.log(test1.a);//undefined
console.log(test1.b);//b
2.实现个简陋版instanceof方法
1
2
3
4
5
6
7
8
9
10
11
12
function instanceOf(data, constructor) {
if (data.__proto__ === constructor.prototype) {
return true;
} else if (data.__proto__ === null) {
return false;
}
return instanceOf(data.__proto__, constructor);
}
console.log({} instanceof Object);//true
console.log(instanceOf({}, Object));//true
console.log([] instanceof Object);//true
console.log(instanceOf([], Object));//true

这里可以知道[].__proto__.__proto__ === Object.prototype{}.__proto__ === Object.prototype

也就是说instanceof其实没办法区分对象与数组,那么怎么区分

1
2
3
4
let arr = [];
console.log(Object.prototype.toString.call(arr));//[object Array]
let obj = {};
console.log(Object.prototype.toString.call(obj));//[object Object]
3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Parent() {
this.a = 1;
this.b = [1, 2, this.a];
this.c = { demo: 5 };
this.show = function () {
console.log(this.a , this.b , this.c.demo );
}
}
function Child() {
this.a = 2;
this.change = function () {
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = new Parent();
var parent = new Parent();
var child1 = new Child();
var child2 = new Child();
child1.a = 11;
child2.a = 12;
parent.show();
child1.show();
child2.show();
child1.change();
child2.change();
parent.show();
child1.show();
child2.show();

解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
function Parent() {
this.a = 1;
this.b = [1, 2, this.a];
this.c = { demo: 5 };
this.show = function () {
console.log(this.a, this.b, this.c.demo);
}
}
function Child() {
this.a = 2;
this.change = function () {
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = new Parent();
/*
Child.prototype = {
a:1,
b:[1,2,1],
c:{
demo:5
},
show:function(){
console.log(this.a, this.b, this.c.demo);
}
}
*/

var parent = new Parent();
/*
parent = {
a:1,
b:[1,2,1],
c:{
demo:5
},
show:function(){
console.log(this.a, this.b, this.c.demo);
}
}
*/

var child1 = new Child();
//child1.__ptoto__ === Child.prototype
/*
child1 = {
a:2,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
*/

var child2 = new Child();
//child2.__ptoto__ === Child.prototype
/*
child2 = {
a:2,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
*/

child1.a = 11;
/*
child1 = {
a:11,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
*/

child2.a = 12;
/*
child2 = {
a:12,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
*/

parent.show();//1,[1,2,1],5
child1.show();//11,[1,2,1],5
//没有show方法,a,b,c没有就都从child1.__proto__上找,也就是Child.prototype,this指向child1,

child2.show();//12,[1,2,1],5
//同上

child1.change();
//没有的属性就从child1.__proto__上找,this指向child1
/*
child1 = {
a:5,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = {
a:1,
b:[1,2,1,11],
c:{
demo:4
},
show:function(){
console.log(this.a, this.b, this.c.demo);
}
}
*/

child2.change();
//没有的属性就从child2.__proto__上找,this指向child2
/*
child2 = {
a:6,
change:function(){
this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = {
a:1,
b:[1,2,1,11,12],
c:{
demo:5
},
show:function(){
console.log(this.a, this.b, this.c.demo);
}
}
*/

parent.show();//1,[1,2,1],5

child1.show();//5,[1,2,1,11,12],5
child2.show();//6,[1,2,1,11,12],5

//打印顺序
//1,[1,2,1],5
//11,[1,2,1],5
//12,[1,2,1],5
//1,[1,2,1],5
//5,[1,2,1,11,12],5
//6,[1,2,1,11,12],5

总结

1.函数在创建的时候,会同时创建prototype对象,这个对象会自动获得constructor属性。

2.构造函数的实例的[[Prototype]]会指向构造函数的prototype属性,它们传递的是引用,而不是副本。

3.如果需要取不到对象上的属性和方法,就会往原型链上找。

4.原型链的顶部是null。