55我们先使用构造函数创建一个对象:
66
77``` js
8-
98function Person () {
109
1110}
1211var 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-
2825function Person () {
2926
3027}
3128// 虽然写在注释里,但是你要注意:
3229// prototype是函数才会有的属性
33- Person .prototype .name = ' name ' ;
30+ Person .prototype .name = ' Kevin ' ;
3431var person1 = new Person ();
3532var 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}
6561var 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
8278function 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
9591function Person () {
92+
9693}
9794
9895var person = new Person ();
9996
10097console .log (person .__proto__ == Person .prototype ) // true
10198console .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-
117112function Person () {
118113
119114}
120115
121- Person .prototype .name = ' name ' ;
116+ Person .prototype .name = ' Kevin ' ;
122117
123118var 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
128123delete 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
167174function Person () {
168175
@@ -171,15 +178,25 @@ var person = new Person();
171178console .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
177184person .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