在《关于思维方式的思绪》那篇文章里提到了,
原型的本质就是一种 委托关系 。
即我这里没有,就到我的原型里去看看,一旦找到就当成我的用。
本文详细说一下这个事情。
比如某女买东西,钱都是她老公付款的。
用程序刻画是这样的:
程序中指明了girl的原型是boy,girl没pay方法,但是boy有,所以boy花钱了。
从这个例子来看那么,原型是一种委托关系,如果说是一种继承关系就不是那么贴切。
因为这段代码更等价于如下的代码:
用这种委托关系,而不是继承关系去理解原型,会感觉一切豁然开朗。
我们通过下面这个例子来看看什么是原型链?
上面的代码中,a是b的原型,b是c的原型,c又是d的原型。
那么d要找fn1方法,怎么找呢?
先去其原型c中找,没找到,
再去c的原型b中找,也没找到,
再去b的原型a中去找,找到了,
因此能调用fn1方法。
上面的过程就是原型链查找的过程。
讲到这里,原型链的原理想必是懂了。
此时我们再来看a,b,c,d四个对象是什么关系。
如果要看做是继承的话,那么就是父子关系。
如果用委托观点来看,那么每一个对象,都是后一个对象的智囊,也就是原型。
所以本文的观点是什么呢?
不要把原型当成亲爹,要当成智囊,要当成老公,要当成干爹,
本质是委托关系,说白了,就是“利用”。
其实讲到这儿原型是什么基本已经说完,后面我准备展开说说,跟构造函数扯上。
第一个问题,什么叫“一旦找到就当成我的用”?
其实指的是this问题。
比如:
第二个问题,克隆的观点?
假如一个对象是一个空对象的原型,
因为空对象什么也没有,所有的都来自其原型,
我们可以认为此对象是其原型的克隆。
当然,Object.create更适合描述克隆。
这里只使用了create的第一个参数,对此,这段代码很其上一段代码没什么区别。
第三,我们封装产生对象的过程?
我们希望上述的那个laoyao对象能通过函数产生。比如我会这样做:
我也换种方式来做:
我再继续变化,把proto拿出来
写到这里,你会发现其实跟我们平常写的代码很像:
我们来刻画一下这个new过程。
我们假设new是一个函数,类似call或bind的那样的函数
其中这new函数,不是完整模拟new的(考虑返回值是否是对象)。详细请看《js语言精粹》47页。其中this指向的是当前函数(Person)。
如果上面的代码看不习惯的话,我们也可以发明如下的api:
上述代码改编于《JS设计模式与开发实践》第20页。
如果读者没有其他什么面向对象语言基础,
那么看此文,会觉得 new是一个封装委托关系的过程 。
而不是什么模拟java,模拟不彻底啥的。
所以在我看来,那些号召不要使用new的文章,也未必正确。
最后说下:这个委托的观点,《你不知道的javascript》有更详细的介绍。
也欢迎看本文的 《续》 。
原型的本质就是一种 委托关系 。
即我这里没有,就到我的原型里去看看,一旦找到就当成我的用。
本文详细说一下这个事情。
比如某女买东西,钱都是她老公付款的。
用程序刻画是这样的:
-
1234567891011var girl = {name : '小美'} ;var boy = {name : '小帅' ,pay : function ( ) {console . log ( '花了一千元' ) ;}} ;Object . setPrototypeOf ( girl , boy ) ;girl . pay ( ) ;
程序中指明了girl的原型是boy,girl没pay方法,但是boy有,所以boy花钱了。
从这个例子来看那么,原型是一种委托关系,如果说是一种继承关系就不是那么贴切。
因为这段代码更等价于如下的代码:
-
12345678910111213var girl = {name : '小美' ,pay : function ( ) {boy . pay ( ) ;}} ;var boy = {name : '小帅' ,pay : function ( ) {console . log ( '花了一千元' ) ;}} ;girl . pay ( ) ;
用这种委托关系,而不是继承关系去理解原型,会感觉一切豁然开朗。
我们通过下面这个例子来看看什么是原型链?
-
123456789101112131415161718192021222324252627var a = {fn1 : function ( ) {console . log ( 1 ) ;}} ;var b = {fn2 : function ( ) {console . log ( 2 ) ;}} ;var c = {fn3 : function ( ) {console . log ( 3 ) ;}} ;var d = {fn4 : function ( ) {console . log ( 4 ) ;}} ;Object . setPrototypeOf ( d , c ) ;Object . setPrototypeOf ( c , b ) ;Object . setPrototypeOf ( b , a ) ;d . fn1 ( ) ;d . fn2 ( ) ;d . fn3 ( ) ;d . fn4 ( ) ;
上面的代码中,a是b的原型,b是c的原型,c又是d的原型。
那么d要找fn1方法,怎么找呢?
先去其原型c中找,没找到,
再去c的原型b中找,也没找到,
再去b的原型a中去找,找到了,
因此能调用fn1方法。
上面的过程就是原型链查找的过程。
讲到这里,原型链的原理想必是懂了。
此时我们再来看a,b,c,d四个对象是什么关系。
如果要看做是继承的话,那么就是父子关系。
如果用委托观点来看,那么每一个对象,都是后一个对象的智囊,也就是原型。
所以本文的观点是什么呢?
不要把原型当成亲爹,要当成智囊,要当成老公,要当成干爹,
本质是委托关系,说白了,就是“利用”。
其实讲到这儿原型是什么基本已经说完,后面我准备展开说说,跟构造函数扯上。
第一个问题,什么叫“一旦找到就当成我的用”?
其实指的是this问题。
比如:
-
12345678910var a = {sayName : function ( ) {alert ( this . name ) ;}} ;var laoyao = {name : 'laoyao'} ;Object . setPrototypeOf ( laoyao , a ) ;laoyao . sayName ( ) ;
第二个问题,克隆的观点?
假如一个对象是一个空对象的原型,
因为空对象什么也没有,所有的都来自其原型,
我们可以认为此对象是其原型的克隆。
-
12345678910var laoyao = {name : 'laoyao' ,sayName : function ( ) {alert ( this . name ) ;}} ;var fenshen = { } ;Object . setPrototypeOf ( fenshen , laoyao ) ;console . log ( fenshen . name ) ;fenshen . sayName ( ) ;
当然,Object.create更适合描述克隆。
-
123456789var laoyao = {name : 'laoyao' ,sayName : function ( ) {alert ( this . name ) ;}} ;var fenshen = Object . create ( laoyao ) ;console . log ( fenshen . name ) ;fenshen . sayName ( ) ;
这里只使用了create的第一个参数,对此,这段代码很其上一段代码没什么区别。
第三,我们封装产生对象的过程?
我们希望上述的那个laoyao对象能通过函数产生。比如我会这样做:
-
12345678910var createPerson = function ( name ) {return {name : name ,sayName : function ( ) {alert ( this . name )}} ;}var laoyao = createPerson ( 'laoyao' ) ;laoyao . sayName ( ) ;
我也换种方式来做:
-
12345678910111213141516var createPerson = function ( name ) {var o = { } ;o . name = name ;var proto = {sayName : function ( ) {alert ( this . name ) ;}} ;Object . setPrototypeOf ( o , proto ) ;return o ;}var laoyao = createPerson ( 'laoyao' ) ;laoyao . sayName ( ) ;
我再继续变化,把proto拿出来
-
12345678910111213var createPerson = function ( name ) {var o = { } ;o . name = name ;Object . setPrototypeOf ( o , createPerson . proto ) ;return o ;}createPerson . proto = {sayName : function ( ) {alert ( this . name ) ;}} ;var laoyao = createPerson ( 'laoyao' ) ;laoyao . sayName ( ) ;
写到这里,你会发现其实跟我们平常写的代码很像:
-
12345678910var Person = function ( name ) {this . name = name ;}Person . prototype = {sayName : function ( ) {alert ( this . name ) ;}} ;var laoyao = new Person ( 'laoyao' ) ;laoyao . sayName ( ) ;
我们来刻画一下这个new过程。
我们假设new是一个函数,类似call或bind的那样的函数
new Person('laoyao')我们换成
Person.new('laoyao')此函数定义如下:
-
12345678910111213141516Function . prototype . new = function ( ) {var that = Object . create ( this . prototype ) ;this . apply ( that , arguments ) ;return that ;} ;var Person = function ( name ) {this . name = name ;}Person . prototype = {sayName : function ( ) {alert ( this . name ) ;}} ;var laoyao = Person . new ( 'laoyao' ) ;laoyao . sayName ( ) ;
其中这new函数,不是完整模拟new的(考虑返回值是否是对象)。详细请看《js语言精粹》47页。其中this指向的是当前函数(Person)。
如果上面的代码看不习惯的话,我们也可以发明如下的api:
myNew(Person, 'laoyao')完整示例代码如下:
-
12345678910111213141516var myNew = function ( ) {var Constructor = [ ] . shift . call ( arguments ) ;var that = Object . create ( Constructor . prototype ) ;Constructor . apply ( that , arguments ) ;return that ;}var Person = function ( name ) {this . name = name ;}Person . prototype = {sayName : function ( ) {alert ( this . name ) ;}} ;var laoyao = myNew ( Person , 'laoyao' ) ;laoyao . sayName ( ) ;
上述代码改编于《JS设计模式与开发实践》第20页。
如果读者没有其他什么面向对象语言基础,
那么看此文,会觉得 new是一个封装委托关系的过程 。
而不是什么模拟java,模拟不彻底啥的。
所以在我看来,那些号召不要使用new的文章,也未必正确。
最后说下:这个委托的观点,《你不知道的javascript》有更详细的介绍。
也欢迎看本文的 《续》 。
本文完。
原文链接 http://www.qdfuns.com/notes/17398/35b250e9b392675c44f4f0cd833b72c8.html