Skip to content

Commit a268863

Browse files
committed
修订JavaScript深入之从原型到原型链
1 parent bd24ef9 commit a268863

File tree

1 file changed

+64
-47
lines changed

1 file changed

+64
-47
lines changed

深入系列文章/JavaScript深入之从原型到原型链.md

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,52 @@
55
我们先使用构造函数创建一个对象:
66

77
```js
8-
98
function Person() {
109

1110
}
1211
var person = new Person();
13-
person.name = 'name';
14-
console.log(person.name) // name
15-
12+
person.name = 'Kevin';
13+
console.log(person.name) // Kevin
1614
```
1715

18-
在这个例子中,Person就是一个构造函数,我们使用new创建了一个实例对象person
16+
在这个例子中,Person 就是一个构造函数,我们使用 new 创建了一个实例对象 person
1917

2018
很简单吧,接下来进入正题:
2119

2220
## prototype
2321

24-
每个函数都有一个prototype属性,就是我们经常在各种例子中看到的那个prototype,比如:
22+
每个函数都有一个 prototype 属性,就是我们经常在各种例子中看到的那个 prototype ,比如:
2523

2624
```js
27-
2825
function Person() {
2926

3027
}
3128
// 虽然写在注释里,但是你要注意:
3229
// prototype是函数才会有的属性
33-
Person.prototype.name = 'name';
30+
Person.prototype.name = 'Kevin';
3431
var person1 = new Person();
3532
var person2 = new Person();
36-
console.log(person1.name) // name
37-
console.log(person2.name) // name
38-
33+
console.log(person1.name) // Kevin
34+
console.log(person2.name) // Kevin
3935
```
4036

41-
那这个函数的prototype属性到底指向的是什么呢?是这个函数的原型吗?
37+
那这个函数的 prototype 属性到底指向的是什么呢?是这个函数的原型吗?
4238

43-
其实,函数的prototype属性指向了一个对象,这个对象正是调用该构造函数而创建的**实例**的原型,也就是这个例子中的person1和person2的原型
39+
其实,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的**实例**的原型,也就是这个例子中的 person1 和 person2 的原型
4440

45-
那么什么是原型呢?你可以这样理解:每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
41+
那什么是原型呢?你可以这样理解:每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
4642

4743
让我们用一张图表示构造函数和实例原型之间的关系:
4844

49-
![原型图1](Images/prototype1.png)
45+
![构造函数和实例原型的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype1.png)
5046

51-
在这张图中我们用Object.prototype表示实例原型
47+
在这张图中我们用 Object.prototype 表示实例原型。
5248

53-
那么我们该怎么表示实例与实例原型,也就是person和Person.prototype之间的关系呢,这时候我们就要讲到第二个属性:
49+
那么我们该怎么表示实例与实例原型,也就是 person 和 Person.prototype 之间的关系呢,这时候我们就要讲到第二个属性:
5450

5551
## \_\_proto\_\_
5652

57-
这是每一个JavaScript对象(除了null)都具有的一个属性,叫\_\_proto\_\_,这个属性会指向该对象的原型。
53+
这是每一个JavaScript对象(除了 null )都具有的一个属性,叫\_\_proto\_\_,这个属性会指向该对象的原型。
5854

5955
为了证明这一点,我们可以在火狐或者谷歌中输入:
6056

@@ -63,45 +59,45 @@ function Person() {
6359

6460
}
6561
var person = new Person();
66-
console.log(person.__proto__ === Person.prototype); //true
62+
console.log(person.__proto__ === Person.prototype); // true
6763
```
6864

6965
于是我们更新下关系图:
7066

71-
![原型图2](Images/prototype2.png)
67+
![实例与实例原型的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype2.png)
7268

7369
既然实例对象和构造函数都可以指向原型,那么原型是否有属性指向构造函数或者实例呢?
7470

7571
## constructor
7672

77-
指向实例倒是没有,因为一个构造函数可以生成多个实例,但是原型指向构造函数倒是有的,这就要讲到第三个属性:construcotr,每个原型都有一个constructor属性指向关联的构造函数
73+
指向实例倒是没有,因为一个构造函数可以生成多个实例,但是原型指向构造函数倒是有的,这就要讲到第三个属性:construcotr,每个原型都有一个 constructor 属性指向关联的构造函数。
7874

7975
为了验证这一点,我们可以尝试:
8076

8177
```js
8278
function Person() {
8379

8480
}
85-
console.log(Person === Person.prototype.constructor); //true
81+
console.log(Person === Person.prototype.constructor); // true
8682
```
8783

8884
所以再更新下关系图:
8985

90-
![原型图3](Images/prototype3.png)
86+
![实例原型与构造函数的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype3.png)
9187

9288
综上我们已经得出:
9389

9490
```js
9591
function Person() {
92+
9693
}
9794

9895
var person = new Person();
9996

10097
console.log(person.__proto__ == Person.prototype) // true
10198
console.log(Person.prototype.constructor == Person) // true
10299
// 顺便学习一个ES5的方法,可以获得对象的原型
103-
console.log(Object.getPrototypeOf(person) === Person.prototype) //true
104-
100+
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
105101
```
106102

107103
了解了构造函数、实例原型、和实例之间的关系,接下来我们讲讲实例和原型的关系:
@@ -113,56 +109,67 @@ console.log(Object.getPrototypeOf(person) === Person.prototype) //true
113109
举个例子:
114110

115111
```js
116-
117112
function Person() {
118113

119114
}
120115

121-
Person.prototype.name = 'name';
116+
Person.prototype.name = 'Kevin';
122117

123118
var person = new Person();
124119

125-
person.name = 'name of this person';
126-
console.log(person.name) // name of this person
120+
person.name = 'Dasiy';
121+
console.log(person.name) // Dasiy
127122

128123
delete person.name;
129-
console.log(person.name) // name
130-
124+
console.log(person.name) // Kevin
131125
```
132126

133-
在这个例子中,我们设置了person的name属性,所以我们可以读取到为'name of this person',当我们删除了person的name属性时,读取person.name,从person中找不到就会从person的原型也就是person.\_\_proto\_\_ == Person.prototype中查找,幸运的是我们找到了为'name',但是万一还没有找到呢?原型的原型又是什么呢?
127+
在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Dasiy。
134128

135-
在前面,我们已经讲了原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是
129+
但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.\_\_proto\_\_ ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。
136130

137-
```js
131+
但是万一还没有找到呢?原型的原型又是什么呢?
138132

139-
var obj = new Object();
140-
obj.name = 'name'
141-
console.log(obj.name) // name
133+
## 原型的原型
134+
135+
在前面,我们已经讲了原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是:
142136

137+
```js
138+
var obj = new Object();
139+
obj.name = 'Kevin'
140+
console.log(obj.name) // Kevin
143141
```
144142

145-
所以原型对象是通过Object构造函数生成的,结合之前所讲,实例的\_\_proto\_\_指向构造函数的prototype,所以我们再更新下关系图:
143+
所以原型对象是通过 Object 构造函数生成的,结合之前所讲实例的 \_\_proto\_\_ 指向构造函数的 prototype ,所以我们再更新下关系图:
146144

147-
![关系图4](Images/prototype4.png)
145+
![原型的原型关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype4.png)
148146

149147
## 原型链
150148

151-
那Object.prototype的原型呢?
149+
那 Object.prototype 的原型呢?
150+
151+
null,不信我们可以打印:
152+
153+
```js
154+
console.log(Object.prototype.__proto__ === null) // true
155+
```
152156

153-
null,嗯,就是null,所以查到Object.prototype就可以停止查找了
157+
所以查到属性的时候查到 Object.prototype 就可以停止查找了。
154158

155159
所以最后一张关系图就是
156160

157-
![关系图5](Images/prototype5.png)
161+
![原型链示意图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype5.png)
158162

159163
顺便还要说一下,图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
160164

161165
## 补充
162166

163-
最后,补充和纠正本文中一些不严谨的地方:
167+
最后,补充三点大家可能不会注意的地方:
168+
169+
### constructor
170+
171+
首先是 constructor 属性,我们看个例子:
164172

165-
首先是constructor,
166173
```js
167174
function Person() {
168175

@@ -171,15 +178,25 @@ var person = new Person();
171178
console.log(person.constructor === Person); // true
172179
```
173180

174-
当获取person.constructor时,其实person中并没有constructor属性,当不能读取到constructor属性时,会从person的原型也就是Person.prototype中读取,正好原型中有该属性,所以
181+
当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以
175182

176183
```js
177184
person.constructor === Person.prototype.constructor
178185
```
179186

180-
其次是\_\_proto\_\_, 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在与Person.prototype中,实际上,它是来自于Object.prototype,与其说是一个属性,不如说是一个getter/setter,当使用obj.\_\_proto\_\_时,可以理解成返回了Object.getPrototypeOf(obj)
187+
### \_\_proto\_\_
188+
189+
其次是 \_\_proto\_\_ ,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.\_\_proto\_\_ 时,可以理解成返回了 Object.getPrototypeOf(obj)。
190+
191+
### 真的是继承吗?
192+
193+
最后是关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:
194+
195+
继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
196+
197+
## 下一篇文章
181198

182-
最后是关于继承,前面我们讲到“每一个对象都会从原型"继承"属性”,实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:继承意味着复制操作,然而JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
199+
[JavaScript深入之词法作用域和动态作用域](https://github.com/mqyqingfeng/Blog/issues/3)
183200

184201
## 深入系列
185202

0 commit comments

Comments
 (0)