1. 程式人生 > >1.1 js 面向對象的基本概念和基本使用方法

1.1 js 面向對象的基本概念和基本使用方法

朝向 排序 讀取 只需要 try catch 傳遞 個數 存在 創建表

js 面向對象的基本概念和基本使用方法

  1. -> js 是不是一個面向對象的語言?
    • 說不是:因為與傳統面向對象的理論語言有矛盾。C#,Java。
    • 說是:因為js裏面到處都是對象,數組,時間,正則… 和 DOM。也可以像傳統面向對象的語言那樣用 new 的方式創建對象
  2. -> 其實js是一個基於對象的多範式編程語言。
    • 面向過程的方式
    • 面向對象的方式
    • 函數式 遞歸與鏈式 例: Jquery 鏈式編程

面向對象的概念

對象的定義:無序屬性的集合,其屬性可以包含基本值,對象或是函數
1. 面向:將臉朝向… -> 關註,用
2. 面向過程 -> 使用過程的方式開發
關註過程,步驟,細節,順序。 就是自己動手豐衣足食
3. 面向對象開發 -> 使用對象的方式開發
要做什麽,找到對象,告訴他做,等結果
4. 面向對象不是面向過程的替代,是面向過程的封裝,可以理解為面向對象編程就是面向編程的一種簡化

萬物皆對象

  • 在實際開發中,對象是一個抽象的概念,可以簡單理解為: 數據集或功能集。可以混合使用

    • 數據集: 很多數據打包到一起。 { name: ‘張三’, age:19, gender:’男’ }
      • 假設展示 10 個商品數據,每個商品有名字,描述,價格,圖片等
      • 對象可以用來保存數據,每一個商品可以變成對象 { name: ‘123’, desc:’123’, price: 0 }
      • 引入數組,將多個對象數據存儲在數組中(數組也是對象)
    • 功能集(函數集,方法集)
    • 在 js 中,所謂的對象就是 鍵值對 的集合
  • 給頁面中所有的div和p添加邊框,設置寬高

<body>
    <!-- 給頁面中所有的div和p添加邊框,設置寬高 -->
    <div>div1</div>
    <p>p標簽</p>
    <div>div2</div>
</body>
<script>
    /* 獲得所有div */
    var divs = document.getElementsByTagName(‘div‘);
    var ps = document.getElementsByTagName(‘p‘);
    /* 設置樣式 */
    /* 由於 getby 獲取的是 偽數組 */
    for ( var i = 0; i < divs.length; i++ ) {
        divs[ i ].style.border = ‘1px solid red‘;
        divs[ i ].style.width = ‘300px‘;
        divs[ i ].style.height = ‘200px‘;
    }
    for ( var i = 0; i < ps.length; i++ ) {
        ps[ i ].style.border = ‘1px solid blue‘;
        ps[ i ].style.width = ‘300px‘;
        ps[ i ].style.height = ‘200px‘;
    }
</script>
  • 利用函數封裝較長的功能
<script>
   /* 利用函數封裝較長的功能 */
    function tag ( tagName ) {
        return document.getElementsByTagName( tagName );
    }
    var divs = tag(‘div‘);
    var ps = tag(‘p‘);

    for ( var i = 0; i < divs.length; i++ ) {
        divs[i].style.border = ‘1px solid red‘;
        divs[i].style.width = ‘300px‘;
        divs[i].style.height = ‘200px‘;
    }
    for ( var i = 0; i < ps.length; i++ ) {
        ps[i].style.border = ‘1px solid blue‘;
        ps[i].style.width = ‘300px‘;
        ps[i].style.height = ‘200px‘;
    }
<script>
  • 封裝函數進行循環
<script>
    function tag ( tagName ) {
        return document.getElementsByTagName( tagName );
    }
    function addCss( array, border, width, height ) {
        for( var i = 0; i < array.length; i++ ) {
            array[i].style.border = border;
            array[i].style.width = width;
            array[i].style.height = height;
        }
    }
    var divs = tag( ‘div‘ );
    var ps = tag(‘p‘);
    addCss( divs, ‘1px solid red‘, ‘400px‘, ‘50px‘ );
    addCss( ps, ‘1px solid blue‘, ‘300px‘, ‘50px‘ );
</script>
  • addCss的改良
<script>
    function tag ( tagName ) {
        return document.getElementsByTagName( tagName );
    }
    function addCss( array, styles ) {
        for( var i = 0; i < array.length; i++ ) {
            for( var k in styles ) {
                array[i].style[k] = styles[k];
            }
        }
    }
    var divs = tag(‘div‘);
    var ps = tag(‘p‘);
    addCss( divs, {
        ‘border‘: ‘1px solid red‘,
        ‘width‘: ‘400px‘,
        ‘height‘: ‘50px‘
    });
    addCss( ps, {
        ‘border‘: ‘1px solid red‘,
        ‘width‘: ‘400px‘,
        ‘height‘: ‘50px‘
    });
</script>
  • 自己定義的函數越多,那麽開發的時候就越方便,但是,也有隱患,自己定義的越多,那麽與引入的框架出現沖突的幾率就越大,一旦出現名字沖突了,就會造成框架中的功能無法使用。
  • 怎麽辦? 定義的名字越多,問題的幾率越大,最簡單的方法就是盡可能少定義名字
  • 定義一個對象,然後所有的函數,變成這個對象的方法即可
<script>
    var jespon = {
        tag: function( tagName ) {
            return document.getElementsByTagName( tagName );
        },
        addCss: function( array, styles ) {
            for( var i = 0; i < array.length; i++ ) {
                for( var k in styles ) {
                    array[i].style[k] = styles[k];
                }
            }
        }
    }
    var divs = jespon.tag(‘div‘);
    var ps = jespon.tag(‘p‘);
    jespon.addCss( divs, {
        ‘border‘: ‘1px solid red‘,
        ‘width‘: ‘400px‘,
        ‘height‘: ‘50px‘,
        ‘background‘: ‘green‘
    });
    jespon.addCss( ps, {
        ‘border‘: ‘1px solid red‘,
        ‘width‘: ‘400px‘,
        ‘height‘: ‘50px‘
    });
</script>

面向對象的特性

  • 抽象性:抽取我們所需要的數據信息等
  • 封裝性
  • 繼承性
  • (多態性)

名詞提煉法找對象

  • 做一個表格排序
    • 面向過程: 1創建表格, 2添加點擊事件, 3排序,更新數據
    • 面向對象: 1創建表格對象, 2添加數據

實際開發的時候如果需要完成一個功能

  • 首先考慮系統是否提供了對象
    • 例如: 創建圖片: var img = new Image(); img.src = ‘…png’;
    • 例如: 獲取頁面元素:document
    • 用 document對象 獲取導航欄裏面的所有li 標簽
      var nav = document.getElementById( ‘nav’ );
      var lis = document.getElementsByTagName( ‘li’ );
  • 如果系統沒有提供,可以自己定義,或第三方
<script>
    var jespon = {
        tag: function( tagName ) {
            return document.getElementsByTagName( tagName );
        },
        addCss: function( array, styles ) {
            for( var i = 0; i < array.length; i++ ) {
                for( var k in styles ) {
                    array[i].style[k] = styles[k];
                }
            }
        }
        attr: ...,
        getId: ...
    }
<script>

js 的數據類型

  • 基本數據類型(值類型):數字 number,字符串 string,布爾 boolean
    • 基本數據類型的存儲模型就是一個方格裏面放一個數據
  • 復合數據類型(引用類型):對象(數組,時間類型,函數類型)
    • 復合類型,除了函數其他的數據類型 無法使用 typeof 來獲得數據類型名,typeof arr 等 拿到的都是object
    • 需要使用 Object.prototype.toString.apply( )
  • 空類型: null undefined
    var arr = [1, 2, 3];
    console.log( typeof arr );       //object
    console.log( Object.prototype.toString.apply(arr) );  //  [object Array]

復合類型的存儲模型

  • 復合類型的對象是一個單獨的內存區域, 對象有什麽屬性, 那麽內存區域中就有什麽數據.
    變量 p 只存儲該對象的 ‘地址’. 所以 p 不是真正存儲數據的區域.
    // 聯想內存邏輯圖
    var arr1 = [ 1, 2, 3, 4 ];
    var arr2 = [
        { name: ‘張三‘, age: 19, gender: ‘男‘ },
        { name: ‘李四‘, age: 18, gender: ‘男‘ },
        { name: ‘小李‘, age: 17, gender: ‘女‘ }
    ];

值類型和引用類型的存儲特征

  • 值類型的數據,只需要開辟一段內存存儲數據即可
var a = 123; 
var b = ‘abc‘;
var c = true;
  • 對象類型(引用類型),對象才是真正的數據,需要占據單獨的內存。而變量名只是存儲著對象的內存地址(引用)
    • 即創建一個對象,並賦值,需要兩塊內存空間,一個存儲數據(對象)。另一個存儲變量以引用對象
    • var o = { name: “張三”, age: 19 },前後各占一塊空間,前者於棧,後者於堆

值類型與引用類型的賦值與傳參的特性

  • 賦值:將原變量中的數據拷貝一份,然後存儲到給定變量中

    1. 值類型

      var a = 123; // 有個盒子a,裏面存了123
      var b ;     // 有個盒子 b, 裏面什麽都沒有 (undenfied)
      b = a;  // 將 a 中存儲的東西賦值一份,然後賦值為 b,即存儲在 b 中
      • b 和 a 是兩個獨立的變量, 兩者之間不再有關系。
      • 改變其中一個數據,另一個不變。
    2. 引用類型

      var o1 = { num:123 };
      var o2;
      // 賦值
      o2 = o1; //o1 中存儲的是引用,或‘地址’
      • 將 o1 中存儲的內容拷貝一份,存儲到 o2 中,o1 和 o2 雖然是不相同的兩個變量, 即兩個獨立的盒子. 但是由於拷貝的內容是地址,存儲的地址相同。
      • 在訪問數據的時候, o1 與 o2 也是訪問同一個數據, o1 將數據修改了, o2 再讀取,讀取的就是修改後的數據。

函數參數傳遞

  • 什麽是函數參數傳遞
    函數要調用,一般會給函數傳遞參數
    在調用函數的時候,會將參數中表示的數據拷貝一份,然後給參數賦值

  • 值類型傳遞

    function foo ( num ) {
        console.log( ‘num:‘ + num );    // 123
        num++;
        console.log( ‘num:‘ + num );    // 124
    }
    // 調用
    var a = 123;
    console.log( a );   // 123
    foo( a ); // 調用就是執行,將 a 中存的值,拷貝一份
              // 然後進入 函數 foo
              // 給參數賦值,相當於 num = a;
              // 進入函數體,開始執行函數
    console.log( a );   // 123
  • 引用類型傳遞
    function foo ( num ) {
        console.log( ‘nu====m:‘ + num[ 0 ] );   // 123
        num[ 0 ]++;
        console.log( ‘num:‘ + num[ 0 ] );   // 124
    }
    // 調用
    var a = [ 123 ];
    console.log( a[ 0 ] );  // 123
    foo( a );
    console.log( a[ 0 ] );  // 124
此時的賦值特性與前面介紹的值類型引用類型的賦值是一個特點

深拷貝與淺拷貝

什麽是拷貝: 就是創建一個與目標數據一模一樣的數據

var p = { name: ‘張三‘ };
var p1 = p;                 // 是否存在拷貝
// 一般描述拷貝是指拷貝對象
p1 = {};
p1.name = p.name;// 才是拷貝

給 對象 p 提供一個 clone 方法, 完成拷貝
- 淺拷貝:如果對象的屬性也是一個引用類型, 拷貝的時候不重新創建一個新的對象來實現該屬性的拷貝, 那麽就是淺拷貝.即任何不完全的拷貝都是淺拷貝
- 深拷貝:將兩個對象完全從內存中隔離開, 就是深拷貝. 即每一個引用屬性, 以及引用屬性的引用屬性, … 全部拷貝出來.

構造函數( 構造器 constructor )的作用

js 中對象的動態特性, 即 想要什麽屬性就可以給什麽屬性

    var o = {};     // 這時 o 對象沒有 name 屬性
    o.name = "aaa"; // 這時 o 對象擁有了 name 屬性且值為 “aaa”

在 js 中 對象如果沒有指定的屬性, 只需要利用賦值就 可以給對象 動態 提供該屬性.
- 點語法與關聯數組語法

    o.name = ‘jim‘;             // 點語法賦值
    console.log( o.name );      // 點語法取值
    o[ ‘name‘ ] = ‘tom‘;        // 關聯數組語法賦值
    console.log( o[ ‘name‘ ] ); // 關聯數組語法取值
  • 創建一個 Person 對象
    var p = {};     // 什麽都沒有的對象
    // 根據需要添加成員
    p.name = ‘張三‘;
    p.age = 30;
    p.gender = ‘男‘;
  • 簡化,提供一個創建 Person 對象的函數
    function createPerson( name, age, gender ) {
        var p = {};
        p.name = name;
        p.age = age;
        p.gender = gender;
        return p;
    }

    var p1 = createPerson( ‘jepson‘, 19, ‘男‘ );
    var p2 = createPerson( ‘lucy‘, 18, ‘女‘ );
這個( 這種類型 )的函數就是用來創建對象的, 即生產對象. 常常將這類函數, 稱為 **‘工廠函數‘**。

構造方法創建對象

構造器中不需要 return 語句. 一般也可以不寫
調用構造器的時候, 使用 new 運算符引導
在構造器中 this 表示當前對象. 給對象提供成員使用 this.xxx 的 方式

  • 將 createPerson 改造成構造器
   function createPerson( name, age, gender ) {
       this.name = name;
       this.age = age;
       this.gender = gender;
   }
   // 調用構造器創建對象
   var p = new createPerson( ‘jepson‘, 19, ‘男‘ ); 
  • 構造器創建對象的本質: 還是使用對象的動態特性
    -> 首先執行 new 運算符. 即創建對象. 可以簡單看做為 {} 的簡寫, var p = new … ‘相當於’ var p = {}
    -> 調用構造器. 並隱含一個參數, 即剛剛創建的對象.
    -> 在構造器中使用 this 引用剛剛創建出來的對象.
    -> 構造器結束是 默認返回 this
  • 補充說明
    -> 構造器的名字, 一般使用 Pascal 命名規則( 首字母大寫的 )
    -> 一般為了與其他面向對象的編程語言( C++, C#, Java )保持一致. 稱構造函數名為類名
   function Person( name, age, gender ) {
       this.name = name;
       this.age = age;
       this.gender = gender;
   }
   // 調用構造器創建對象
   var p = new Person( ‘jepson‘, 19, ‘男‘ );

異常與捕獲

    • 異常:簡單的說, 就是代碼在執行過程中出現的錯誤. 並不是代碼的語法錯誤,一旦出現異常, 其後的代碼, 不再執行。
    • try catch 語法
      1) try-catch 形態

      try {
          代碼
      } catch ( e ) {
          代碼
      }

      2) try-catch-finally 形態

    • 自定義拋出異常
      一般可以封裝函數完成特定的功能

    • 拋出異常的語法
      throw 對象

1.1 js 面向對象的基本概念和基本使用方法