js-函數(shù)及面向?qū)ο?/h2>

一.數(shù)據(jù)類型的操作原理

  • 數(shù)據(jù)類型

    • 基本數(shù)據(jù)類型(值類型)

      • number
      • string
      • boolean
      • null
      • undefined
    • 引用數(shù)據(jù)類型

      • 對象

        • {} 普通對象
        • /^$/正則
        • 一般類的實(shí)例都是對象數(shù)據(jù)類型的
        • 函數(shù)的prototype屬性
        • 實(shí)例的proto屬性
      • 函數(shù)

        • function 普通函數(shù)
        • 。。。

      console.log(typeof Object);//function

    • 值類型

      直接按值操作,例如:var a=12;直接把12這個賦值給變量a(讓a變量和12這個值建立了鏈接關(guān)系)

    • 對象數(shù)據(jù)類型

      在js中遇到對象,會嚴(yán)格按照如下的步驟操作

      1.瀏覽器為其開辟一個新的內(nèi)存空間,為了方便后期可以找到這個空間,瀏覽器給空間分配一個16進(jìn) 制的地址

      2.按照一定順序,分別的把對象鍵值對存儲到內(nèi)存空間中

      3.把開辟內(nèi)存的地址賦值給變量(或者其他的東西),以后變量就可以通過地址找到內(nèi)存空間,然后進(jìn)行一些操作

    • 分析例子

    js代碼運(yùn)行在瀏覽器中,是因?yàn)闉g覽器給我們提供了一個供js代碼執(zhí)行的環(huán)境->全局作用域(window/global)

    var a=12;
    var b=a;
    b=13;
    console.log(a);
    
    var o={name:"珠峰培訓(xùn)"};
    var p=o;
    p.name="周嘯天"
    console.log(o.name);
    
    var m={name:"珠峰培訓(xùn)"};
    var n=m;
    n={name:"中國最權(quán)威的前端培訓(xùn)機(jī)構(gòu)"};
    console.log(m.name);
    
    
    image.png
  • 函數(shù)的操作

    • 創(chuàng)建函數(shù)

      1.先開辟一個新的內(nèi)存空間(為其分配了一個16進(jìn)制的地址)

      2.把函數(shù)體中編寫的js代碼當(dāng)做字符串存儲到空間中(函數(shù)只創(chuàng)建不執(zhí)行沒有意義)

      3.把分配的地址賦值給聲明的函數(shù)名(function fn和var fn操作原理其實(shí)相同,都是在當(dāng)前作用域聲明了一個名字)

    • 執(zhí)行函數(shù)

      目的:執(zhí)行函數(shù)體中的代碼

      1.函數(shù)執(zhí)行的時候,瀏覽器會形成一個新的私有作用域(只能執(zhí)行函數(shù)體 中的代碼)供函數(shù)體中的代 碼執(zhí)行

      2.執(zhí)行代碼之前,先把創(chuàng)建函數(shù)存儲的那些字符串變?yōu)檎嬲膉s表達(dá)式, 按照從上到下的順序在私有作用域中執(zhí)行,一函數(shù)可以被執(zhí)行N次,
      每一次的執(zhí)行相互之間互不干擾

      形成的私有作用域把函數(shù)體中的私有變量都包裹起來了(保護(hù)起來了),在私有作用域中操作私有 變量和外界沒關(guān)系,外界也無法直接的操作私有變量

      3.函數(shù)執(zhí)行具體步驟

      • 形參賦值
      • 變量提升
      • 代碼自上而下執(zhí)行
      • 當(dāng)前棧內(nèi)存(私有作用域)銷毀 (也有例外:不銷毀的時候,往下看閉包)

      案例分析

      var a=12;
      var b=a;
      b=13;
      console.log(a);
      
      var o={name:"珠峰培訓(xùn)"};
      var p=o;
      p.name="周嘯天"
      console.log(o.name);
      
      var m={name:"珠峰培訓(xùn)"};
      var n=m;
      n={name:"中國最權(quán)威的前端培訓(xùn)機(jī)構(gòu)"};
      console.log(m.name);
      
      function fn(){
       var ary=Array.prototype.slice.call(arguments);
       return eval(ary.join('+'));
      }
      fn(12);
      fn(13);
      
      image.png

二.堆棧內(nèi)存

  • 棧內(nèi)存

    俗稱叫做作用域(全局作用域/私有作用域)

    • 為js代碼提供執(zhí)行環(huán)境(執(zhí)行js代碼的地方)
    • 基本數(shù)據(jù)類型值是直接存放在棧內(nèi)存中的
  • 堆內(nèi)存

    存儲引用數(shù)據(jù)類型值的(相當(dāng)于一個存儲的倉庫)

    • 對象存儲的是鍵值對
    • 函數(shù)存儲的是代碼字符串
  • 內(nèi)存釋放

    在項(xiàng)目中,我們使用的內(nèi)存越少性能越好,我們需要把一些沒用的內(nèi)存處理掉

    • 堆內(nèi)存

      var o={};當(dāng)前對象對應(yīng)的堆內(nèi)存被變量o占用著呢,堆內(nèi)存是無法銷毀的

      o=null;null空對象指針(不指向任何的堆內(nèi)存),此時上一次的堆內(nèi)存就沒有被占用了,谷歌瀏覽器會在空閑時間把沒有被占用的堆內(nèi)存自動釋放(銷毀/回收)

    • 棧內(nèi)存

      一般情況下,函數(shù)執(zhí)行形成的棧內(nèi)存,函數(shù)執(zhí)行完,瀏覽器會把形成的棧內(nèi)存自動釋放;

      有的時候執(zhí)行完成,棧內(nèi)存不能被釋放?

      全局作用域在加載頁面的時候執(zhí)行,在關(guān)掉頁面的時候銷毀

三.變量提升

  • 定義及案例分析

    • 定義

    當(dāng)前作用域中,js代碼自上而下執(zhí)行之前,瀏覽器首先會把所有帶var/funciton關(guān)鍵字的進(jìn)行提前的聲明或者定義,

    也就是該變量不管是在當(dāng)前作用域的哪個地方聲明的,都會提升到所屬作用域的最頂上去

    聲明(declare):var num ;在當(dāng)前作用域中吼一嗓子我有num這個名了

    定義(defined): num=12;把聲明的名字賦一個值

    帶var關(guān)鍵字的只是提前的聲明一下;

    帶funciton關(guān)鍵字的在變量提升階段把聲明和定義都完成了

    同一個變量只會聲明一次,其他的會被忽略掉

    函數(shù)聲明的優(yōu)先級高于變量申明的優(yōu)先級

    • 分析這一段代碼
    console.log(num);
    console.log(fn);
    var num=13;
    function fn(){
     console.log(a);
     var a=10;
     console.log(a);
    }
    fn();
    console.log(num);
    
    image.png
  • 不管條件是否成立都要進(jìn)行變量提升

    不管條件是否成立,判斷體中出現(xiàn)的var/function,都會進(jìn)行變量提升;

    但是在最新版瀏覽器中,function聲明的變量只能提前聲明不能定義了(前提:函數(shù)是在判斷體中)

    console.log(num);//undefined
    console.log(fn);//undefined
    if(1==1){
     console.log(num);//undefined
     console.log(fn);//fn函數(shù)本身
     var num =12;
     function fn(){
    
     }
    }
    
    console.log(fn);//fn函數(shù)本身
    

    代碼執(zhí)行到條件判斷的地方

    • 條件不成立

      進(jìn)入不到判斷體中,賦值的代碼執(zhí)行不了,此時之前的聲明的變量或者函數(shù)以然是undefined

    • 條件成立

      進(jìn)入到條件判斷體中的第一件事情不是代碼執(zhí)行,而是把之前的變量提升沒有定義的函數(shù)首先定義了(進(jìn)入到判斷體中的函數(shù)就有定義了)

      老版本瀏覽器不是這樣處理的:不管條件是否成立,function都要進(jìn)行變量提升(聲明+定義)

      新版瀏覽器function只是聲明

  • 關(guān)于重名的處理

            //重名的只會聲明一次,函數(shù)在提升階段就可以定義了就是賦值,可以多次賦值    
       //=>變量提升:fn=aaafff111 (=aaafff222) (=aaafff333) (=aaafff444)
       fn();//4
       function fn(){console.log(1);}
       fn();//4
       function fn(){console.log(2);}
       fn();//4
       var fn=13;   
       fn();//Uncaught TypeError: fn is not a function
    
       function fn(){console.log(3);}
       fn();
       function fn(){console.log(4);}
    
  • 變量提升,函數(shù)優(yōu)先

    //函數(shù)聲明和變量聲明都會被提升,但是需要注意的是函數(shù)會先被提升,然后才是變量
    var fn1; //只定義沒有賦值,意味著原有棧中fn1的結(jié)果沒有改變
    function fn1(){
    
    }
    console.log(typeof fn1);//function
    理由:var fn1;盡管出現(xiàn)在function fn1()之前,但它是重復(fù)的聲明,會被忽略,因?yàn)楹瘮?shù)聲明會被提升到普通變量之前
    
    (
     其實(shí)也可以從另外一個角度去理解,提升變量聲明fn1,
     但是函數(shù)聲明和定義都被提升上去的,最總fn1賦值是函數(shù)體
     )
    
    
    
    var fn1=4;//定義賦值,覆蓋函數(shù)
    function fn1(){
    
    }
    console.log(typeof fn1);//number
    

四.作用域/作用域鏈

  • 定義

    • 作用域
      【棧內(nèi)存】

      • 全局作用域:window
      • 私有作用域:函數(shù)的執(zhí)行
      • 塊級作用域:使用let創(chuàng)建變量存在塊級作用域
    • 作用域鏈

      當(dāng)前作用域代碼執(zhí)行的時候遇到一個變量,首先看一下它是否屬于私有變量,

      如果不是私有變量,向其上級作用域查找,也不是上級作用域私有變量,繼續(xù)向上查找,

      一直到window全局作用域?yàn)橹梗?/p>

      我們把這種向上一級級查找機(jī)制叫做作用域鏈

  • 查找私有變量

    js中的私有變量有且只有兩種

    • 在私有作用域變量提升階段,聲明過的變量(或者函數(shù))

    • 形參也是私有變量

  • 案例分析

    • 案例1
    image.png
    • 案例2
    function fn(b){
      //=>私有作用域
      //=>形參賦值:b=1(私有變量)
      //=>變量提升:b=aaaff111 (此處賦值操作替換了形參賦值的內(nèi)容)
      console.log(b);//=>函數(shù)
      function b(){
        //=>私有作用域
        //=>形參賦值和變量提升都沒有
        console.log(b);//=>函數(shù),不是私有變量,去上一級作用域查找,
      }
      b();
    }
    
    fn(1);
    
  • 如何查找上級作用域

    函數(shù)執(zhí)行形成的一個私有的作用域(A),A的上級作用域是誰,和它在哪執(zhí)行的沒有關(guān)系,主要看他在哪定義的,在哪個作用域下定義的,當(dāng)前A的上級作用域就是誰;

    • 例1
    var n=10;
    function sum(){
     console.log(n);//10
    }
    
    (function(){
     var n=100;
     sum();
    })();
    
    • 例2
    var n=10;
    var obj={
     n:20,
     fn:(function(){
       var n=30;
       return function(){
         console.log(n);//30
       }
     })(),
    };
    obj.fn();
    
    image.png
    • 例3
    var n=10;
    var obj={
     n:20,
     fn:(function(){
       //上級作用域:全局作用域
       return function(){
         //上級作用域:自執(zhí)行函數(shù)
         console.log(n);//10
       }
     })(),
    };
    obj.fn();
    
    • 例4
    var n=10;
    var obj={
     n:20,
     fn:(function(n){
         return function(){
             console.log(n);//10
       }
     })(obj.n) //Uncaught TypeError: Cannot read property 'n' of undefined
     //因?yàn)檫@時候obj還沒有呢
    
    };
    obj.fn();
    

五.閉包

  • 閉包作用

    • 保護(hù)作用

      形成私有作用域,保護(hù)里面的私有變量不受外界的干擾

      例如jqery代碼

      (function(){
       var jQuery=function(){
         。。。
       }
       window.jQuery=window.$=jQuery;
      })();
      
      jQuery();
      
    • 保存作用

      函數(shù)執(zhí)行形成一個私有作用域,函數(shù)執(zhí)行完成,形成的這個棧內(nèi)存一般情況下都會自動釋放

      但是還有二般情況:函數(shù)執(zhí)行完成,當(dāng)前私有作用域(棧內(nèi)存)中的某一部分內(nèi)容被棧內(nèi)存以外的其他東西占用了,當(dāng)前的棧內(nèi)存就不能釋放掉,也就形成了不銷毀的私有作用域(里面得到私有變量也不會銷毀)

      【形式】

      ? 函數(shù)執(zhí)行返回了一個引用數(shù)據(jù)類型堆內(nèi)存的地址,在外面有一個變量接收了這個返回值,此時 當(dāng)前作用域就不能銷毀,(想要銷毀,只需要讓外面的變量賦值為null,也就是不占用當(dāng)前 作用域的內(nèi)容了)

       function fn(){
              var i=1;
              return function(n){
                  console.log(n+ i++);
              }
          }
        var f=fn(); //只要f不銷毀,fn函數(shù)形成的私有作用域不會銷毀
        f(10);//11
        fn()(10);//11 //這種執(zhí)行完,fn函數(shù)形成的私有作用域就銷毀了
        f(20);//22
        fn()(20);//21
      
      image.png

六.原型

  • 1.原理

    • 所有的函數(shù)都天生自帶一個屬性:prototype(原型),它是一個對象數(shù)據(jù)類型的值,在當(dāng)前prototype對象中,存儲了類需要給其實(shí)例使用的公有的屬性和方法

    • prototype這個對象,瀏覽器會默認(rèn)為其開一個堆內(nèi)存,這個堆內(nèi)存中天生自帶一個屬性:constructor(構(gòu)造函數(shù)),這個屬性存儲的的值就是當(dāng)前函數(shù)本身

    • 每一個類的實(shí)例(每一個對象)都天生自帶一個屬性:proto,屬性值是當(dāng)前對象所屬類的原型(prototype)

    分析個例子

    function Fn(name,age){
     //這里面的屬性都是私有的
      this.name=name;
      this.age=age;
      this.say=function(){
          console.log(this.name);
      }
    }
    
    //原型上的都是公有的
    Fn.prototype.say=function(){
      console.log("hello world");
    }
    Fn.prototype.eat=function(){
      console.log("I like food");
    }
    
    var f1=new Fn("王燕燕",19);
    var f2=new Fn("王雪超",69);
    
    f1.eat();
    
    image.png
  • 2.內(nèi)置類的原型分析

    image.png
    /*
    判斷是否是對象的對象屬性
    hasOwnProperty
    
    私有和公有是一個相對論,我們需要看相對于哪個對象而言:
    1.相對于實(shí)例ary1來說push是公有的
    2.相對于Array.prototype來說,push就是自己的私有的
    3.凡是通過__proto__找到的屬性都是公有的,反之都是私有的
    */ 
    var ary1=[12,23];
    console.log(ary1.hasOwnProperty("push"));//false
    console.log(Array.prototype.hasOwnProperty("push"));
    
  • 3重新構(gòu)造原型

    function Fn(name){
     this.name=name;
     this.say=function(){
       console.log(this.name);
     }
    }
    
    Fn.prototype={
            /*讓原型指向自己開辟的堆內(nèi)存有一個問題:自己開辟的堆內(nèi)存中沒有constructor這個屬性,
           所以實(shí)例在調(diào)取constructor的時候找到的是Object,這樣不好,
           此時我們應(yīng)該重新設(shè)置一下constructor,保證機(jī)制的完整性
           */
           constructor:Fn,
           aa:function(){},
           bb:function(){}
    }
     var f =new Fn("wanglei");
     console.log(f.constructor)//Fn函數(shù)
    

7.繼承

  • 原型繼承

    讓父類中的屬性和方法在子類實(shí)例的原型鏈上

    • 特點(diǎn)

      • 它是把父類的原型放到子類實(shí)例的原型鏈上,實(shí)例想調(diào)取這些方法,是基于__proto__ 原型鏈查找機(jī)制完成的

      • 子類可以重寫父類上的方法(這樣會導(dǎo)致父類其它的實(shí)例也會收到影響)

      • 父類中私有或者公有的屬性方法,最后都會變?yōu)樽宇愔泄械膶傩院头椒?/p>

    • 代碼分析

     function A(x){
                this.x=x;
            }
      A.prototype.getX=function(){
        console.log(this.x);
      }
    
      function B(y){
        this.y=y;
      }
    
       //這2行代碼就實(shí)現(xiàn)了原型繼承
      B.prototype =new A(200);
      B.prototype.constructor=B;
    
      B.prototype.getY=function(){
        console.log(this.y);
      }
    
      var b=new B(100);
      console.log(b);
    
    image.png
  • call繼承

    • 特點(diǎn)

      • child方法中把parent當(dāng)做普通函數(shù)執(zhí)行,讓parent中的this指向child的實(shí)例,相當(dāng)于給child的實(shí)例設(shè)置了很多私有的屬性或者方法

      • 只能繼承父類私有的屬性或者方法(因?yàn)槭前裵arent當(dāng)做普通函數(shù)執(zhí)行,和其原型上的屬性和方法沒有關(guān)系

      • 父類私有的變?yōu)樽宇愃接械?/p>

    • 代碼

     function A(x){
                this.x=x;
            }
      A.prototype.getX=function(){
        console.log(this.x);
      }
    
      function B(y){
        //這行代碼實(shí)現(xiàn)了call繼承
        A.call(this,200);//=>b.x=200
       
        this.y=y;
      }
      B.prototype.getY=function(){
        console.log(this.y);
      }
    
      var b=new B(100);
      console.log(b);
    
  • 寄生組合繼承:call繼承+類似原型繼承 (推薦)

    • 特點(diǎn)
      父類私有和公有的分別是子類實(shí)例的私有和公有屬性方法

    • 代碼分析

    function A(){
        this.x=10;
    }
    A.prototype.getX=function(){
        console.log(this.x);
    }
    
    function B(){
        //這行代碼繼承父類私有屬性
        A.call(this);
        this.y=10;
    }
    
    //這2行代碼繼承父類原型上的屬性(公有屬性)
    B.prototype = Object.create(A.prototype);
    B.prototype.constructor=B;
    
    B.prototype.getY=function(){
        console.log(this.y);
    }
    
    var b=new B();
    console.log(b);
    
    image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容