<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從問題入手,深入了解JavaScript中原型與原型鏈

          共 7757字,需瀏覽 16分鐘

           ·

          2021-02-09 23:51

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          ? 作者?|? _Fatman

          來源 |? urlify.cn/VjUJ3e

          76套java從入門到精通實戰(zhàn)課程分享

          前言

          開篇之前,我想提出3個問題:

          1. 新建一個不添加任何屬性的對象為何能調(diào)用toString方法?

          2. 如何讓擁有相同構(gòu)造函數(shù)的不同對象都具備相同的行為?

          3. instanceof關(guān)鍵字判斷對象類型的依據(jù)是什么?

          要是這3個問題都能回答上來,那么接下來的內(nèi)容不看也罷。但若是對這些問題還存在疑慮和不解,相信我,下面的內(nèi)容將正是你所需要的。

          正文

          新建一個不添加任何屬性的對象為何能調(diào)用toString方法?

          我在深入了解JavaScript中基于原型(prototype)的繼承機制一文中提到過,JavaScript使用的是基于原型的繼承機制,它的引用類型與其對應(yīng)的值將都存在著__proto__[1]屬性,指向繼承的原型對象[2]。當訪問對象屬性無果時,便會在其原型對象中繼續(xù)查找,倘若其原型對象中還是查詢無果,那便接著去其原型對象的原型中去查找,直到查找成功或原型為null時[3]才會停止查找。

          let?obj?=?{
          }
          obj.toString();//"[object?Object]"

          這段代碼就是在obj對象中查找toString方法,查詢無果,繼而在其原型[4]中查找toString方法,正好其原型中含有toString方法,故而得以輸出"[object Object]"

          如何讓擁有相同構(gòu)造函數(shù)的不同對象都具備相同的行為?

          下面是一段實現(xiàn)了發(fā)布訂閱模式的代碼:

          let?_indexOf?=?Array.prototype.indexOf;
          let?_push?=?Array.prototype.push;
          let?_slice?=?Array.prototype.slice;
          let?_concat?=?Array.prototype.concat;
          let?_forEach?=?Array.prototype.forEach;

          function?Publish(){
          ????this.subList;
          ????
          ????this.indexOf?=?function(sub){
          ????????let?index?=?-1;
          ????????if(typeof?this.subList?===?'undefined'?||?this.subList?===?null){
          ????????????this.subList?=?[];
          ????????}
          ????????if(typeof?sub?!==?'undefined'?&&?sub?!==?null){
          ????????????index?=?_indexOf.call(this.subList,sub);
          ????????}
          ????????return?index;
          ????}

          ????this.addSub?=?function(sub){
          ????????let?index?=?this.indexOf(sub);
          ????????index?>?-1??
          ????????????''?:?
          ????????????_push.call(this.subList,sub);
          ????};

          ????this.removeSub?=?function(sub){
          ????????let?index?=?this.indexOf(sub);
          ????????index?>?-1??
          ????????????index?===?0????
          ????????????????this.subList?=?_slice.call(this.subList,1)?:
          ????????????????this.subList?=?_concat.call(_slice.call(this.subList,0,index),_slice.call(this.subList,index?+?1))?:??
          ????????????'';
          ????};

          ????this.notifySingle?=?function(sub,msg){
          ????????let?index?=?this.indexOf(sub);
          ????????index?>?-1??
          ????????????(typeof?sub.onReceive?===?'function'???
          ????????????????sub.onReceive(msg)?:?
          ????????????????'')?:?
          ????????????'';
          ????};

          ????this.notifyAll?=?function(msg){
          ????????if(typeof?this.subList?!==?'undefined'?&&?this.subList?!==?null){
          ????????????_forEach.call(this.subList,(sub)=>{
          ????????????????if(typeof?sub?!==?'undefined'?&&?sub?!==?null){
          ????????????????????typeof?sub.onReceive?===?'function'???
          ????????????????????????sub.onReceive(msg)?:?
          ????????????????????????'';
          ????????????????}
          ????????????})
          ????????}
          ????};
          }

          function?Subscription(name){
          ????this.name?=?name;
          ????this.onReceive?=?function(msg){
          ????????console.log(this.name?+?'?收到消息?:?'?+?msg);
          ????};
          }

          let?pub?=?new?Publish();
          let?sub1?=?new?Subscription('sub1');
          let?sub2?=?new?Subscription('sub2');
          let?sub3?=?new?Subscription('sub3');
          let?sub4?=?new?Subscription('sub4');

          pub.addSub(sub1);
          pub.addSub(sub1);
          pub.addSub(sub2);
          pub.addSub(sub3);
          pub.addSub(sub4);

          pub.notifyAll('這是一條全部推送的消息');
          //?sub1?收到消息?:?這是一條全部推送的消息
          //?sub2?收到消息?:?這是一條全部推送的消息
          //?sub3?收到消息?:?這是一條全部推送的消息
          //?sub4?收到消息?:?這是一條全部推送的消息

          pub.notifySingle(sub2,"這是一條單獨推送的消息");
          //?sub2?收到消息?:?這是一條單獨推送的消息

          pub.removeSub(sub3);

          pub.notifyAll('這是一條全部推送的消息');
          //?sub1?收到消息?:?這是一條全部推送的消息
          //?sub2?收到消息?:?這是一條全部推送的消息
          //?sub4?收到消息?:?這是一條全部推送的消息

          此代碼中擁有同一構(gòu)造函數(shù)的所有對象都含有不同的方法。

          sub1.onReceive?===?sub2.onReceive;//false
          sub1.onReceive?===?sub3.onReceive;//false
          sub1.onReceive?===?sub4.onReceive;//false
          sub2.onReceive?===?sub3.onReceive;//false
          sub2.onReceive?===?sub4.onReceive;//false
          sub3.onReceive?===?sub4.onReceive;//false

          這樣會導(dǎo)致:
          1.浪費內(nèi)存;
          2.不易于對方法進行批量操作。

          接下來是改進版本,使用原型達到代碼復(fù)用的效果

          let?_indexOf?=?Array.prototype.indexOf;
          let?_push?=?Array.prototype.push;
          let?_slice?=?Array.prototype.slice;
          let?_concat?=?Array.prototype.concat;
          let?_forEach?=?Array.prototype.forEach;

          function?Publish(){
          ????this.subList;
          }

          Publish.prototype.indexOf?=?function(sub){
          ????let?index?=?-1;
          ????if(typeof?this.subList?===?'undefined'?||?this.subList?===?null){
          ????????this.subList?=?[];
          ????}
          ????if(typeof?sub?!==?'undefined'?&&?sub?!==?null){
          ????????index?=?_indexOf.call(this.subList,sub);
          ????}
          ????return?index;
          }

          Publish.prototype.addSub?=?function(sub){
          ????let?index?=?this.indexOf(sub);
          ????index?>?-1??
          ????????''?:?
          ????????_push.call(this.subList,sub);
          };

          Publish.prototype.removeSub?=?function(sub){
          ????let?index?=?this.indexOf(sub);
          ????index?>?-1??
          ????????index?===?0????
          ????????????this.subList?=?_slice.call(this.subList,1)?:
          ????????????this.subList?=?_concat.call(_slice.call(this.subList,0,index),_slice.call(this.subList,index?+?1))?:??
          ????????'';
          };

          Publish.prototype.notifySingle?=?function(sub,msg){
          ????let?index?=?this.indexOf(sub);
          ????index?>?-1??
          ????????(typeof?sub.onReceive?===?'function'???
          ????????????sub.onReceive(msg)?:?
          ????????????'')?:?
          ????????'';
          };

          Publish.prototype.notifyAll?=?function(msg){
          ????if(typeof?this.subList?!==?'undefined'?&&?this.subList?!==?null){
          ????????_forEach.call(this.subList,(sub)=>{
          ????????????if(typeof?sub?!==?'undefined'?&&?sub?!==?null){
          ????????????????typeof?sub.onReceive?===?'function'???
          ????????????????????sub.onReceive(msg)?:?
          ????????????????????'';
          ????????????}
          ????????})
          ????}
          };

          function?Subscription(name){
          ????this.name?=?name;
          ????
          }

          Subscription.prototype.onReceive?=?function(msg){
          ????console.log(this.name?+?'?收到消息?:?'?+?msg);
          };

          let?pub?=?new?Publish();
          let?sub1?=?new?Subscription('sub1');
          let?sub2?=?new?Subscription('sub2');
          let?sub3?=?new?Subscription('sub3');
          let?sub4?=?new?Subscription('sub4');

          pub.addSub(sub1);
          pub.addSub(sub1);
          pub.addSub(sub2);
          pub.addSub(sub3);
          pub.addSub(sub4);

          pub.notifyAll('這是一條全部推送的消息');
          //?sub1?收到消息?:?這是一條全部推送的消息
          //?sub2?收到消息?:?這是一條全部推送的消息
          //?sub3?收到消息?:?這是一條全部推送的消息
          //?sub4?收到消息?:?這是一條全部推送的消息

          pub.notifySingle(sub2,"這是一條單獨推送的消息");
          //?sub2?收到消息?:?這是一條單獨推送的消息

          pub.removeSub(sub3);

          pub.notifyAll('這是一條全部推送的消息');
          //?sub1?收到消息?:?這是一條全部推送的消息
          //?sub2?收到消息?:?這是一條全部推送的消息
          //?sub4?收到消息?:?這是一條全部推送的消息
          sub1.onReceive?===?sub2.onReceive;//true
          sub1.onReceive?===?sub3.onReceive;//true
          sub1.onReceive?===?sub4.onReceive;//true
          sub2.onReceive?===?sub3.onReceive;//true
          sub2.onReceive?===?sub4.onReceive;//true
          sub3.onReceive?===?sub4.onReceive;//true

          改進版本與之前的版本相比有一個特點:擁有同一構(gòu)造函數(shù)的對象,屬性是唯一的,行為是一致的[5]。所有對象都擁有獨立于其它對象的屬性,卻存在相同的行為。這正是因為在改進版本中,方法存在于構(gòu)造函數(shù)的prototype屬性值上,其將被其創(chuàng)建的對象所繼承。也正是因為如此,盡管此時的sub1、sub2、sub3、sub4中都不包含onReceive方法,但也可以通過繼承的原型對象Subscription.prototype去達到調(diào)用onReceive的目的。而且修改Subscription.prototype上的onReceive方法是可以馬上作用到sub1、sub2、sub3、sub4上的將方法定義到構(gòu)造函數(shù)的prototype屬性值上,就可以讓擁有相同構(gòu)造函數(shù)的不同對象都具備相同的行為以達到代碼復(fù)用目的

          instanceof關(guān)鍵字判斷對象類型的依據(jù)是什么?

          我在深入了解JavaScript中基于原型(prototype)的繼承機制中聲明了函數(shù)Person,并以它為構(gòu)造函數(shù)創(chuàng)建了person對象

          function?Person(){
          ?
          }
          let?person?=?new?Person();

          person對象的繼承Person函數(shù)的prototype屬性值,而Person函數(shù)的prototype屬性值又繼承Object函數(shù)的prototype屬性值,這種一層一層繼承的關(guān)系構(gòu)成了原型鏈。

          instanceof關(guān)鍵字判斷對象類型的依據(jù)便是判斷函數(shù)的prototype屬性值是否存在于對象的原型鏈上。

          正如Person函數(shù)的prototype屬性值和Object函數(shù)的prototype屬性值都存在于person對象的原型鏈上,所以使用instanceof判斷兩者都為true。

          person?instanceof?Person;//true
          person?instanceof?Object;//true

          而Function函數(shù)的prototype屬性值不存在于person對象的原型鏈上,所以使用instanceof判斷Function函數(shù)為false。

          person?instanceof?Function;//false

          最后,完成一個instanceof。

          /**
          *?obj?變量
          *?fn?構(gòu)造函數(shù)
          */
          function?myInstanceof(obj,fn){
          ????let?_prototype?=?Object.getPrototypeOf(obj);
          ????if(null?===?_prototype){
          ????????return?false;
          ????}
          ????let?_constructor?=?_prototype.constructor;
          ????if(_constructor?===?fn){
          ????????return?true;
          ????}
          ????return?myInstanceof(_prototype,fn);
          }

          //測試代碼
          myInstanceof({},Object);//true
          myInstanceof([],Array);//true
          myInstanceof(window,Window);//true
          myInstanceof(new?Map(),Map);//true
          myInstanceof({},Array);//false
          myInstanceof({},Function);//false

          大功告成。

          結(jié)尾

          這3個問題的解答分別對原型和原型鏈的含義以及它們在JavaScript中起到了什么作用進行了闡述。不過由于本人才疏學(xué)淺,難免會遇到一些我個人理解亦或是表達存在錯誤的地方,還望各位遇到之時,能不吝指出。


          1. 雖然__proto__已經(jīng)被不推薦使用,但是為了更直觀,我在此文中獲取對象原型的方法都將通過對象的__proto__屬性,還望悉知。???

          2. Object.prototype繼承的原型指向null。???

          3. Object.prototype的原型為null,它是原型鏈的頂點,查到Object.prototype的原型時還找不到便會報找不到了。???

          4. 對象obj的原型為obj的構(gòu)造函數(shù)的prototype屬性,也就是Object.prototype。???

          5. 這里的屬性意指除方法外的屬性,行為意指方法。???





          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ??????

          ??長按上方微信二維碼?2 秒


          感謝點贊支持下哈?

          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色播在线观看 | 欧美大香蕉免费看 | 性感美女香蕉视频 | 最新一区二区在线 | 男女抽插网站 |