面向?qū)ο?/h2>

面向?qū)ο?/h1>
  • 首先,我們要明確,面向?qū)ο蟛皇钦Z法,是一個思想,是一種 編程模式
  • 面向: 面(臉),向(朝著)
  • 面向過程: 臉朝著過程 =》 關(guān)注著過程的編程模式
  • 面向?qū)ο螅?臉朝著對象 =》 關(guān)注著對象的編程模式
  • 實(shí)現(xiàn)一個效果
    • 在面向過程的時候,我們要關(guān)注每一個元素,每一個元素之間的關(guān)系,順序,。。。
    • 在面向過程的時候,我們要關(guān)注的就是找到一個對象來幫我做這個事情,我等待結(jié)果
  • 例子 ??: 我要吃面條
    • 面向過程
      • 用多少面粉
      • 用多少水
      • 怎么和面
      • 怎么切面條
      • 做開水
      • 煮面
      • 吃面
    • 面向?qū)ο?
      • 找到一個面館
      • 叫一碗面
      • 等著吃
    • 面向?qū)ο缶褪菍γ嫦蜻^程的封裝
  • 我們以前的編程思想是,每一個功能,都按照需求一步一步的逐步完成
  • 我們以后的編程思想是,每一個功能,都先創(chuàng)造一個 面館,這個 面館 能幫我們作出一個 面(完成這個功能的對象),然后用 面館 創(chuàng)造出一個 ,我們只要等到結(jié)果就好了

創(chuàng)建對象的方式

  • 因?yàn)槊嫦驅(qū)ο缶褪且粋€找到對象的過程
  • 所以我們先要了解如何創(chuàng)建一個對象

調(diào)用系統(tǒng)內(nèi)置的構(gòu)造函數(shù)創(chuàng)建對象

  • js 給我們內(nèi)置了一個 Object 構(gòu)造函數(shù)

  • 這個構(gòu)造函數(shù)就是用來創(chuàng)造對象的

  • 當(dāng) 構(gòu)造函數(shù) 和 new 關(guān)鍵字連用的時候,就可以為我們創(chuàng)造出一個對象

  • 因?yàn)?js 是一個動態(tài)的語言,那么我們就可以動態(tài)的向?qū)ο笾刑砑映蓡T了

    // 就能得到一個空對象
    var o1 = new Object() 
    
    // 正常操作對象
    o1.name = 'Jack'
    o1.age = 18
    o1.gender = '男'
    

字面量的方式創(chuàng)建一個對象

  • 直接使用字面量的形式,也就是直接寫 {}

  • 可以在寫的時候就添加好成員,也可以動態(tài)的添加

    // 字面量方式創(chuàng)建對象
    var o1 = {
      name: 'Jack',
      age: 18,
      gender: '男'
    }
    
    // 再來一個
    var o2 = {}
    o2.name = 'Rose'
    o2.age = 20
    o2.gender = '女'
    

使用工廠函數(shù)的方式創(chuàng)建對象

  • 先書寫一個工廠函數(shù)

  • 這個工廠函數(shù)里面可以創(chuàng)造出一個對象,并且給對象添加一些屬性,還能把對象返回

  • 使用這個工廠函數(shù)創(chuàng)造對象

    // 1. 先創(chuàng)建一個工廠函數(shù)
    function createObj() {
      // 手動創(chuàng)建一個對象
      var obj = new Object()
    
      // 手動的向?qū)ο笾刑砑映蓡T
      obj.name = 'Jack'
      obj.age = 18
      obj.gender = '男'
    
      // 手動返回一個對象
      return obj
    }
    
    // 2. 使用這個工廠函數(shù)創(chuàng)建對象
    var o1 = createObj()
    var o2 = createObj()
    

使用自定義構(gòu)造函數(shù)創(chuàng)建對象

  • 工廠函數(shù)需要經(jīng)歷三個步驟

    • 手動創(chuàng)建對象
    • 手動添加成員
    • 手動返回對象
  • 構(gòu)造函數(shù)會比工廠函數(shù)簡單一下

    • 自動創(chuàng)建對象
    • 手動添加成員
    • 自動返回對象
  • 先書寫一個構(gòu)造函數(shù)

  • 在構(gòu)造函數(shù)內(nèi)向?qū)ο筇砑右恍┏蓡T

  • 使用這個構(gòu)造函數(shù)創(chuàng)造一個對象(和 new 連用)

  • 構(gòu)造函數(shù)可以創(chuàng)建對象,并且創(chuàng)建一個帶有屬性和方法的對象

  • 面向?qū)ο缶褪且朕k法找到一個有屬性和方法的對象

  • 面向?qū)ο缶褪俏覀冏约褐圃?構(gòu)造函數(shù) 的過程

    // 1. 先創(chuàng)造一個構(gòu)造函數(shù)
    function Person(name, gender) {
      this.age = 18
      this.name = name
      this.gender = gender
    }
    
    // 2. 使用構(gòu)造函數(shù)創(chuàng)建對象
    var p1 = new Person('Jack', 'man')
    var p2 = new Person('Rose', 'woman')
    

構(gòu)造函數(shù)詳解

  • 我們了解了對象的創(chuàng)建方式
  • 我們的面向?qū)ο缶褪且茨苤苯拥玫揭粋€對象
  • 要么就弄出一個能創(chuàng)造對象的東西,我們自己創(chuàng)造對象
  • 我們的構(gòu)造函數(shù)就能創(chuàng)造對象,所以接下來我們就詳細(xì)聊聊 構(gòu)造函數(shù)

構(gòu)造函數(shù)的基本使用

  • 和普通函數(shù)一樣,只不過 調(diào)用的時候要和 new 連用,不然就是一個普通函數(shù)調(diào)用

    function Person() {}
    var o1 = new Person()  // 能得到一個空對象
    var o2 = Person()      // 什么也得不到,這個就是普通函數(shù)調(diào)用
    
    • 注意: 不寫 new 的時候就是普通函數(shù)調(diào)用,沒有創(chuàng)造對象的能力
  • 首字母大寫

    function person() {}
    var o1 = new person() // 能得到一個對象
    
    function Person() {}
    var o2 = new Person() // 能得到一個對象
    
    • 注意: 首字母不大寫,只要和 new 連用,就有創(chuàng)造對象的能力
  • 當(dāng)調(diào)用的時候如果不需要傳遞參數(shù)可以不寫 (),建議都寫上

    function Person() {}
    var o1 = new Person()  // 能得到一個空對象
    var o2 = new Person    // 能得到一個空對象 
    
    • 注意: 如果不需要傳遞參數(shù),那么可以不寫 (),如果傳遞參數(shù)就必須寫
  • 構(gòu)造函數(shù)內(nèi)部的 this,由于和 new 連用的關(guān)系,是指向當(dāng)前實(shí)例對象的

    function Person() {
      console.log(this)
    }
    var o1 = new Person()  // 本次調(diào)用的時候,this => o1
    var o2 = new Person()  // 本次調(diào)用的時候,this => o2
    
    • 注意: 每次 new 的時候,函數(shù)內(nèi)部的 this 都是指向當(dāng)前這次的實(shí)例化對象
  • 因?yàn)闃?gòu)造函數(shù)會自動返回一個對象,所以構(gòu)造函數(shù)內(nèi)部不要寫 return

    • 你如果 return 一個基本數(shù)據(jù)類型,那么寫了沒有意義
    • 如果你 return 一個引用數(shù)據(jù)類型,那么構(gòu)造函數(shù)本身的意義就沒有了

使用構(gòu)造函數(shù)創(chuàng)建一個對象

  • 我們在使用構(gòu)造函數(shù)的時候,可以通過一些代碼和內(nèi)容來向當(dāng)前的對象中添加一些內(nèi)容

    function Person() {
      this.name = 'Jack'
      this.age = 18
    }
    
    var o1 = new Person()
    var o2 = new Person()
    
    • 我們得到的兩個對象里面都有自己的成員 nameage
  • 我們在寫構(gòu)造函數(shù)的時候,是不是也可以添加一些方法進(jìn)去呢?

    function Person() {
      this.name = 'Jack'
      this.age = 18
      this.sayHi = function () {
        console.log('hello constructor')
      }
    }
    
    var o1 = new Person()
    var o2 = new Person()
    
    • 顯然是可以的,我們得到的兩個對象中都有 sayHi 這個函數(shù)
    • 也都可以正常調(diào)用
  • 但是這樣好不好呢?缺點(diǎn)在哪里?

    function Person() {
      this.name = 'Jack'
      this.age = 18
      this.sayHi = function () {
        console.log('hello constructor')
      }
    }
    
    // 第一次 new 的時候, Person 這個函數(shù)要執(zhí)行一遍
    // 執(zhí)行一遍就會創(chuàng)造一個新的函數(shù),并且把函數(shù)地址賦值給 this.sayHi
    var o1 = new Person()
    
    // 第二次 new 的時候, Person 這個函數(shù)要執(zhí)行一遍
    // 執(zhí)行一遍就會創(chuàng)造一個新的函數(shù),并且把函數(shù)地址賦值給 this.sayHi
    var o2 = new Person()
    
    • 這樣的話,那么我們兩個對象內(nèi)的 sayHi 函數(shù)就是一個代碼一模一樣,功能一模一樣
    • 但是是兩個空間函數(shù),占用兩個內(nèi)存空間
    • 也就是說 o1.sayHi 是一個地址,o2.sayHi 是一個地址
    • 所以我們執(zhí)行 console.log(o1.sayhi === o2.sayHi) 的到的結(jié)果是 false
    • 缺點(diǎn): 一模一樣的函數(shù)出現(xiàn)了兩次,占用了兩個空間地址
  • 怎么解決這個問題呢?

    • 就需要用到一個東西,叫做 原型,后面講

es6新增的類和繼承

class的寫法及繼承JavaScript 語言中,生成實(shí)例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù)。下面是一個例子

function Point(x, y) {
  this.x = x;
  this.y = y;}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';};

var p = new Point(1, 2);

上面這種寫法跟傳統(tǒng)的面向?qū)ο笳Z言(比如 C++ 和 Java)差異很大,很容易讓新學(xué)習(xí)這門語言的程序員感到困惑。

定義類

ES6 提供了更接近傳統(tǒng)語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過class關(guān)鍵字,可以定義類。
基本上,ES6 的class可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已。上面的代碼用 ES6 的class改寫,就是下面這樣。

//定義類
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }}

上面代碼定義了一個“類”,可以看到里面有一個constructor方法,這就是構(gòu)造方法,而this關(guān)鍵字則代表實(shí)例對象。也就是說,ES5 的構(gòu)造函數(shù)Point,對應(yīng) ES6 的Point類的構(gòu)造方法
point類除了構(gòu)造方法,還定義了一個toString方法。注意,定義“類”的方法的時候,前面不需要加上function這個關(guān)鍵字,直接把函數(shù)定義放進(jìn)去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。

//hasOwnProperty 可以用來判斷對象是否有每一個屬性

point.hasOwnProperty('x') // true

point.hasOwnProperty('y') // true

ES6 的類,完全可以看作構(gòu)造函數(shù)的另一種寫法
class Point {// ...}
typeof Point // "function"上面代碼表明,類的數(shù)據(jù)類型就是函數(shù),類本身就指向構(gòu)造函數(shù)

let methodName = 'getArea';

class Square {
   //構(gòu)造函數(shù)
  constructor(length) {
  // ...
  }
  //實(shí)例方法 對象方法
  [methodName]() {
  // ...
  }}
  //實(shí)例方法 對象方法
  wang(){
  
  }

上面代碼中,Square類的方法名getArea,是從表達(dá)式得到的。

類內(nèi)部,默認(rèn)就是嚴(yán)格模式,所以不需要使用use strict指定運(yùn)行模式。只要你的代碼寫在類或模塊之中,就只有嚴(yán)格模式可用。
考慮到未來所有的代碼,其實(shí)都是運(yùn)行在模塊之中,所以 ES6 實(shí)際上把整個語言升級到了嚴(yán)格模式。

constructor 方法``

constructor方法是類的默認(rèn)方法,通過new命令生成對象實(shí)例時,自動調(diào)用該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認(rèn)添加。
class Point {}// 等同于class Point { constructor() {}}上面代碼中,定義了一個空的類Point,JavaScript 引擎會自動為它添加一個空的constructor方法。
constructor方法默認(rèn)返回實(shí)例對象(即this

類必須使用new調(diào)用,否則會報錯。這是它跟普通構(gòu)造函數(shù)的一個主要區(qū)別,后者不用new也可以執(zhí)行。

class Foo { constructor() { }}
Foo()// TypeError: Class constructor Foo cannot be invoked without 'new'

生成類的實(shí)例對象的寫法,與 ES5 完全一樣,也是使用new命令。前面說過,如果忘記加上new,像函數(shù)那樣調(diào)用Class,將會報錯。
class Point {// ...}// 報錯var point = Point(2, 3);// 正確var point = new Point(2, 3);

類不存在變量提升(hoist),這一點(diǎn)與 ES5 完全不同。
new Foo(); // ReferenceErrorclass Foo {}

類方法

加上static關(guān)鍵字,就表示該方法不會被實(shí)例繼承,而是直接通過類來調(diào)用,這就稱為“靜態(tài)方法”。class Foo { static classMethod() { return 'hello'; }}
Foo.classMethod() // 'hello'var foo = new Foo();foo.classMethod()// TypeError: foo.classMethod is not a function
上面代碼中,F(xiàn)oo類的classMethod方法前有static關(guān)鍵字,表明該方法是一個靜態(tài)方法,可以直接在Foo類上調(diào)用(Foo.classMethod()),而不是在Foo類的實(shí)例上調(diào)用。如果在實(shí)例上調(diào)用靜態(tài)方法,會拋出一個錯誤,表示不存在該方法。

繼承

類的繼承Class 可以通過extends關(guān)鍵字實(shí)現(xiàn)繼承 這比 ES5 的通過修改原型鏈(在后面章節(jié)會講解)實(shí)現(xiàn)繼承,要清晰和方便很多。

class Point {}

class ColorPoint extends Point {}

上面代碼定義了一個ColorPoint類,該類通過extends關(guān)鍵字,繼承了Point類的所有屬性和方法。但是由于沒有部署任何代碼,所以這兩個類完全一樣,等于復(fù)制了一個Point類。下面,我們在ColorPoint內(nèi)部加上代碼。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調(diào)用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
  }}

上面代碼中,constructor方法和toString方法之中,都出現(xiàn)了super關(guān)鍵字,它在這里表示父類的構(gòu)造函數(shù),用來新建父類的this對象。
ES6 要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)

子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例時會報錯。這是因?yàn)樽宇愖约旱?code>this對象,必須先通過父類的構(gòu)造函數(shù)完成塑造,得到與父類同樣的實(shí)例屬性和方法,然后再對其進(jìn)行加工,加上子類自己的實(shí)例屬性和方法。如果不調(diào)用super方法,子類就得不到this對象。

class Point { /* ... */ }

class ColorPoint extends Point {
  constructor() {
  }}

let cp = new ColorPoint(); // ReferenceEr

上面代碼中,ColorPoint繼承了父類Point,但是它的構(gòu)造函數(shù)沒有調(diào)用super方法,導(dǎo)致新建實(shí)例時報錯。

需要注意的地方是,在子類的構(gòu)造函數(shù)中,只有調(diào)用super之后,才可以使用this關(guān)鍵字,否則會報錯。這是因?yàn)樽宇悓?shí)例的構(gòu)建,是基于對父類實(shí)例加工,只有super方法才能返回父類實(shí)例

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color; // ReferenceError
    super(x, y);
    this.color = color; // 正確
  }}

上面代碼中,子類的constructor方法沒有調(diào)用super之前,就使用this關(guān)鍵字,結(jié)果報錯,而放在super方法之后就是正確的。

總結(jié)

  • 到了這里,我們就發(fā)現(xiàn)了面向?qū)ο蟮乃枷肽J搅?/p>

    • 當(dāng)我想完成一個功能的時候
    • 先看看內(nèi)置構(gòu)造函數(shù)有沒有能給我提供一個完成功能對象的能力
    • 如果沒有,我們就自己寫一個構(gòu)造函數(shù),能創(chuàng)造出一個完成功能的對象
    • 然后在用我們寫的構(gòu)造函數(shù) new 一個對象出來,幫助我們完成功能就行了
  • 比如: tab選項(xiàng)卡

    • 我們要一個對象
    • 對象包含一個屬性:是每一個點(diǎn)擊的按鈕
    • 對象包含一個屬性:是每一個切換的盒子
    • 對象包含一個方法:是點(diǎn)擊按鈕能切換盒子的能力
    • 那么我們就需要自己寫一個構(gòu)造函數(shù),要求 new 出來的對象有這些內(nèi)容就好了
    • 然后在用構(gòu)造函數(shù) new 一個對象就行了
最后編輯于
?著作權(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ù)。

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