javascript面向對象編程(OOP)——匯總


目錄

  • 一、JS的解析與執行過程

    • 預處理階段
    • 執行階段
  • 二、作用域

    • 塊作用域
    • 函數作用域
    • 動態作用域
    • 詞法作用域
  • 三、閉包

    • 什么是閉包
    • 閉包的好處
  • 四、函數與對象

    • 對象
    • 函數

    •  

      原型(prototype)

    •  

      this

    •  

      new的理解

  • 五、封裝

  • 六、繼承

  • 七、多態

  • 八、項目實戰minijQuery

一、JS的解析與執行過程

1.1、預處理階段

注意:js預處理階段會掃描所有var聲明的變量,把var聲明的變量或函數存放到詞法作用域里,如果是變量初始值為“undefined”,如果是函數則指向函數;

全局(window)

  詞法作用域(Lexical Environment)頂級的Lexical Environment是window;

  1、先掃描函數聲明后掃描變量(var聲明);

  2、處理函數聲明有沖突,會覆蓋;處理變量聲明時有沖突,會忽略。

函數

  詞法作用域(Lexical Environment):每調用一次,產生一個Lexical Environment;

  1、先函數的參數:比如arguments(函數內部對象,代表函數實參,可通過下標獲取調用函數時傳的實參)

  2、先掃描函數聲明后掃描變量(var聲明);

  3、處理函數聲明有沖突,會覆蓋;處理變量聲明時有沖突,會忽略。

 

1.2、執行階段

  1、給預處理階段的成員賦值

  2、如果沒有用var聲明的變量,會成為最外部LexicalEnvironment的成員(即window對象的變量)

 

函數內部對象:arguments

<script>
    /*提示:*/
    // arguments是每一個函數內部的一個對象
    // 可以訪問實際傳遞給函數的參數的信息。
    // 聲明的時候參數的個數與實際調用時無關
    
    function add(a,b){
    console.log(add.length);// 形參的個數
    console.log(arguments.length);// 實際傳過來的參數
    var total = 0;
    for(var i = 0;i< arguments.length;i++){
        total += arguments[i];
    }
    return total;// 返回實參的總和
    }
    
    // 調用時傳的實參
    var result = add(1,2,3,4,5);
    var result2 = add(1,2);
    
    console.log(result);// 15
    console.log(result2);// 3
</script>
View Code

 

二、作用域

提示:js的作用域不是塊級別的;js的作用域是函數級別的。

2.1、塊作用域

2.2、函數作用域

2.3、動態作用域

2.4、詞法作用域

代碼示例:

<script>
            //js作用域
            // 定義:用來查找變量的值的規則集;決定一個變量的范圍
            // 提示:js的作用域不是塊級別的;js的作用域是函數級別的。
            // javascript使用的是詞法作用域,它的最重要的特征是它的定義過程發生在代碼的書寫階段
            
            /*以下js代碼用立即調用寫法(私有化),避免變量沖突*/
            
            //1、塊作用域:代碼在花括號里面有效(js沒有塊作用域)
            (function(){
                for(var i=0;i<5;i++){
                    var a = i ;
                }
                // 在花括號外面可以訪問到i,a
                console.log(i);// 5
                console.log(a);// 4
            })();
            
            
            //2、函數作用域:代碼在function()函數的花括號里面有效
            (function(){
                var message = "函數外部的";
                function fn(){
                    var message = "函數內部的";
                    console.log(message);// 函數內部的
                }
                console.log(message);// 函數外部的
            })();
            
            
            //3、動態作用域:在運行時決定(是this指向的表現;誰調用,this指向誰);動態作用域其實是指this的詞法作用域
                // 動態作用域並不關心函數和作用域是如何聲明以及在任何處聲明的,只關心它們從何處調用。
                // 換句話說,作用域鏈是基於調用棧的,而不是代碼中的作用域嵌套
            (function(){
                var a = 2;
                function foo() {
                    console.log( a );
                }
                function bar() {
                    var a = 3;
                    foo();// 此時this===window
                }
                bar();// 2
            })();
            /*
            var a = 2;
            bar = {
                a:3,
                foo:function(){
                    console.log(this.a);
                }
            }
            bar.foo();//3
            */
            

            //4、詞法作用域:詞法作用域(也稱為靜態作用域或閉包)
                // js的作用域解析,用new Function創建函數
            (function(){
                // 閉包
                var a = 2;
                function bar() {
                    var a = 3;
                    return function(){
                        console.log(a);// 此時捕獲a=3
                    };
                }
                var foo = bar();
                foo();// 3
            })();
            
            
            
            // 如果處於詞法作用域,也就是現在的javascript環境。變量a首先在foo()函數中查找,沒有找到。於是順着作用域鏈到全局作用域中查找,找到並賦值為2。所以控制台輸出2
          // 如果處於動態作用域,同樣地,變量a首先在foo()中查找,沒有找到。這里會順着調用棧在調用foo()函數的地方,也就是bar()函數中查找,找到並賦值為3。所以控制台輸出3
            
            //小結:兩種作用域的區別,簡而言之,詞法作用域是在定義時確定的,而動態作用域是在運行時確定的
            
        </script>
View Code

 

三、閉包(Closure)

3.1、什么是閉包

由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成“定義在一個函數內部的函數,內部函數並訪問父函數的局部變量”。

理解閉包:

  1、閉包可以理解為一個對象,里面包含函數以及被函數捕獲的變量 , 一個圈里包含函數與捕獲的變量 

  2、也可以只把函數捕獲的變量稱之為閉包。 

<script>
    //如何寫會產生閉包
    function P(){
    var a = 5;
    var b = 6;
    return function C(){
        console.log(b);//此時捕獲變量b,值為6
    }
        
    }
    var result = P();
    result();// 6
</script>

產生閉包的條件:

  1、函數內部包含子函數;

  2、子函數訪問父函數的變量;

 

3.2、閉包的好處

  用途:一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

<script>
    // 閉包實例
    function Person(){
        var age = 1;
        this.getAge = function(){
            return age ;
        }
        
        this.setAge = function(val){
               age = val;
        }
    }
    var p = new Person();
    p.setAge(20);
    console.log(p.getAge());
</script>

代碼示例:

<script type="text/javascript">
    /*閉包--理解*/
    // 提示:this由運行時決定!

    // 題目一:理解r1與r2的輸出
    function addFactory(){
        var adder = 5;
        return function(data){
            adder += data;// 此時adder變量是閉包捕獲到的值
            return adder;
        }
    }
    var adder1 = addFactory();
    var r1 = adder1(1);//6
    r1 = adder1(1);//7
    
    var adder2 = addFactory();
    var r2 = adder2(2);//7
    r2 = adder2(2);//9
    
    
    // 題目二:下面的代碼輸出什么
     var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;// 輸出"The Window";this = window;
                //return object.name;// 輸出"My object"
      };
    }
  };
  //alert(object.getNameFunc()());// The Window
    
    // 理解二:
    var fun = object.getNameFunc();// 返回一個函數,此時函數this指向window;window.fun()
    alert(fun());// 所以,輸出是:"The Window"
    
    
    // 題目三:
    var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;//this = object;
      return function(){
        return that.name;//閉包捕獲父函數的that,that = object;
      };
    }
  };
  alert(object.getNameFunc()());// My Object
    
    // 理解三:
    // var obj = object.getNameFunc();
    // alert(obj());// 此時函數由於內部的name是object調用
    
</script>
View Code

四、函數與對象

4.1、對象

4.2、函數

4.3、原型(prototype)

 javascript對象部署:

 

JavaScript是一種通過原型實現繼承的語言;在JavaScript中所有都是對象,原型(prototype)也是一個對象,通過原型可以實現對象的屬性繼承;

   prototype:在js中是函數特有的屬性;指向該函數的原型(this.prototype)

  __proto__:在js中是所有對象都有的屬性;指向此對象的構造器(函數)的原型對象(prototype)

<!-- 
    對象都有這屬性(找對象的父類對象):__proto__;
    只有函數有的屬性(找函數的原型,是個對象):prototype;
    對象(或函數)的構造器(頂級構造器時Function()):constructor;
 -->
<script>
    function Aaa(){}
    //undefined
    var aaa = new Aaa();
    //undefined
    aaa.__proto__;
    //{constructor: ƒ}
    Aaa.prototype;
    //{constructor: ƒ}
    aaa.__proto__ === Aaa.prototype;
    //true
    aaa.constructor;
    //ƒ Aaa(){}
    Aaa.constructor;
    //ƒ Function() { [native code] }
    aaa.constructor.constructor
    //ƒ Function() { [native code] }
    
</script>

原型理解:

1. 函數Foo的__proto的值等於Foo.prototype,對嗎?
    錯,函數Foo.__proto__===Function.prototype,函數Foo的實例的__proto__屬性的值等於函數Foo.prototype
    
2.Object的prototype可以修改嗎?能與不能原因是什么?
    //可以,函數(對象)的原型可以任意修改或繼承
    不可以,因為Object.prototype是只讀,所以不能賦值;但可以修改或添加
    
3. 頂級constructor是誰?
    Function()
    
4.頂級原型對象是誰?
    Object
    
5.對象的construtor成員是個屬性還是個方法?
    可以是屬性,也可以是方法(一般不建議這么寫,耗資源,在每次new是都要執行很多代碼)
    
6.Function有沒有__proto__,為什么?值等於Object.prototype嗎?
    1.有(是對象都有),與prototype相等,因為Function是頂級構造器,所以,函數的__proto__屬性指向的構造器原型是與Function.prototype相等;
    2.不等於,Function.prototype與Function.__proto__指向function.prototype

    
7.所有的構造器的__proto__都等於其對應的prototype
    錯,等於Function.prototype;因為對象的__proto__屬性指向的是對象的構造器的prototype(函數的構造器是Function())
    
8.創建類形式的繼承的四部曲是什么?
    1.創建父類
    2.創建子類
    3.確定繼承關系:A.prototype = Object.create(B.prototype);
    4.修改構造器(因為繼承后的構造器指向是父類的原型指向的構造器,也就是說,子類的原型指向的構造器===父類的原型指向的構造器)
    
9.Function的constructor於prototype值可以修改嗎?
    不可以,Function是頂級構造器,Function.__proto__指向Function.prototype
    
10.Object.prototype === Object.__proto__嗎?
    不相等,Object.prototype是object.prototype;Object.__proto__是function.prototype
    
11. Function.prototype === Function.__proto__嗎?
    相等,(因為Function是頂級構造器,__proto__指向Function.prototype)Function.prototype===Function.__proto__
    
12. function F(){}; var f1 = new F();
   f1.__proto__ === Object.prototype嗎?
   不對,f1.__proto__ === F.prototype;f1.__proto__.__proto__ === Object.prototype;
   
View Code

 

4.5、this

原則:

  1、this由運行時決定!

  2、函數中 this 到底指向誰 , 由調用此函數時的對象決定 , 而不是由定義函數所在的對象決定。

  在JavaScript中this表示:誰調用它,this就是誰。

 如何改變this指向:

  call:

  apply:

<script>
    /*
    var data = {};
    Array.prototype.push.call(data,100,200);
    Array.prototype.push.apply(data,[1,2,3,8,10]);
    console.log(data);
    */
</script>

 

4.6、new的理解

  簡單的可以理解為:new改變了this指向的對象;

 

五、封裝

六、繼承

七、多態

八、項目實戰minijQuery

<script type="text/javascript">
    /*
        // 提示:
        // 暴露外部使用的一個接口
        var jQuery = window.jQuery = window.$ = function(selector){
            return new jQuery.fn.init(selector); 
        }
        
        // 處理原型對象
        jQuery.fn = jQuery.prototype = {}
        jQuery.fn.init.prototype = jQuery.fn;
        
        // 實現繼承,並且只處理只有一個參數,也就是插件的擴展
        jQuery.extend = jQuery.fn.extend = function(){}
        
        // 添加靜態方法
        jQuery.extend({});
        
        // 添加實例方法
        jQuery.fn.extend({});
        
        // 1.獲取節點對象
        var jq1 = jQuery(".pp");
        或
        var jq1 = jQuery.fn.init(".pp");
    */
    
    // 提供全局訪問接口($()、jQuery())
    (function  () {
        /// 暫時把window的全局變量存起來,用做處理變量沖突
        var _$ = window.$;
        var _jQuery = window.jQuery;
        
        //暴露外部使用的一個接口(獲取節點對象)
        var jQuery = window.jQuery = window.$ = function(selector){

            return new jQuery.fn.init(selector);// init.prototype;
        };

    //處理原型對象
        jQuery.fn = jQuery.prototype = {
            init:function(selector){
                var elements = document.querySelectorAll(selector);
                Array.prototype.push.apply(this,elements);
                return this;
            },
            version:"1.0.0",
            length:0,
            size:function(){
                return this.length;
            }

        };
        // jQuery.fn.init.prototype === init.prototype;
        // jQuery.prototype;
        jQuery.fn.init.prototype = jQuery.fn;
    //實現繼承,並且只處理只有一個參數,也就是插件的擴展
        jQuery.extend = jQuery.fn.extend = function(){
            var o = arguments[0];
            for(var p in o){
                this[p] = o[p];
            }
        };
        /// 測試:(繼承方法)
            // var obj = {name:"張三三"}
            // var jq = $(".pp");
            // jq.extend(obj);

    //添加靜態方法
        jQuery.extend({
            trim:function(text){
                return (text||"").replace(/^\s+|\s+$/g,"");// 替換text字符串的開頭和結尾匹配任何空白字符為空(即,替換開頭和結尾的空格字符為空)
            },
            noConflict:function(){
                window.$ = _$;
                window.jQuery = _jQuery;
                return jQuery;
            }
        });
        
        /// 測試:(命名沖突)
            // var jq = jQuery.noConflict();//返回一個jQuery函數,解決與全局的jQuery屬性沖突
            // var obj = jq(".pp");
            
        
    //添加實例方法
        jQuery.fn.extend({
            get:function(num){
                return this[num];
            },
            each:function(fn){
                for(var i = 0 ;i< this.length; i++){
                    fn(i,this[i]);
                }
                return this;
            },
            css:function(){
                var l = arguments.length;
                if(l == 1){
                    return this[0].style[arguments[0]];
                } else {
                    var name = arguments[0];
                    var value = arguments[1];
                    this.each(function(index,ele) {
                        ele.style[name] = value;

                    });
                }
                return this;
            }

        });

    })();
</script>
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM