天天看點

JavaScript進階面向對象ES61.JavaScript 面向對象2.構造函數和原型

1.JavaScript 面向對象

目标:

能夠說出什麼是面向對象

能夠說出類和對象的關系心

能夠使用class 建立自定義類

能夠說出什麼是繼承

1.1 面向對象程式設計介紹

1.1.1 兩大程式設計思想

  • 面向過程
  • 面向對象

1.1.2 面向過程程式設計POP(Process-oriented progrmming)

面向過程就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實作,使用的時候再一個一個的依次調用就可以了。

面向過程,就是按照分析好了的步驟,按照步驟解決問題。

1.1.3 面向對象程式設計OOP(Object Oriented Programming)

面向對象是把事務分解成為一個個對象,然後由對象之間分工與合作。

面向對象是以對象功能來劃分問題,而不是步驟。

在面向對象程式開發思想中,每一個對象都是功能中心,具有明确分工。

面向對象程式設計具有靈活、代碼可複用、容易維護和開發的優點,更适合多人合作的大型軟體項目。

面向對象的特性:

  • 封裝性
  • 繼承性
  • 多态性

1.1.4 面向過程和面向對象的對比

JavaScript進階面向對象ES61.JavaScript 面向對象2.構造函數和原型

1.2 ES6中的類和對象

面向對象

面向對象更貼近我們的實際生活,可以使用面向對象描述現實世界事物.但是事物分為具體的事物和抽象的事物

手機→ 抽象的(泛指的)

滑鼠指向的手機→ 具體的(特指的)

面向對象的思維特點:

1.抽取(抽象)對象共用的屬性和行為組織(封裝)成一個類(模闆)

2.對類進行執行個體化,擷取類的對象

面向對象程式設計我們考慮的是有哪些對象,按照面向對象的思維特點,不斷的建立對象,使用對象,指揮對象做事情.

1.2.1 對象

現實生活中∶萬物皆對象,對象是一個具體的事物,看得見摸得着的實物。例如,一本書、一輛汽車、一個人可以是“對象”,一個資料庫、一張網頁、一個與遠端伺服器的連接配接也可以是“對象”。

在JavaScript 中,對象是一組無序的相關屬性和方法的集合,所有的事物都是對象,例如字元串、數值、數組、函數等。

對象是由屬性和方法組成的:

  • 屬性:事物的特征,在對象中用屬性來表示(常用名詞)
  • 方法:事物的行為,在對象中用方法來表示(常用動詞)

1.2.2 類class

在ES6中新增加了類的概念,可以使用class關鍵字聲明一個類,之後以這個類來執行個體化對象。

類抽象了對象的公共部分,它泛指某一大類( class )

對象特指某一個,通過類執行個體化一個具體的對象

面向對象的思維特點:

1.抽取(抽象)對象共用的屬性和行為組織(封裝)成一個類(模闆)

2.對類進行執行個體化,擷取類的對象

1.2.3 建立類

文法:

// 類名習慣性定義首字母大寫
class ClassName {
	// class body
}
           

建立執行個體

注意:類必須使用 new 執行個體化對象。

1.2.4 類 constructor 構造函數

constructor()方法是類的構造函數(預設方法),用于傳遞參數,傳回執行個體對象,通過 new 指令生成對象執行個體時,自動調用該方法。如果沒有顯示定義,類内部會自動給我們建立一個constructor()

// 1. 通過 class 關鍵字建立類,類名習慣性定義首字母大寫
class Star {
	// 2.類裡面有個constructor函數,可以接受傳遞過來的參數,同時傳回執行個體對象
	// 3.constructor函數隻要new生成執行個體時,就會自動調用這個函數,如果我們不寫這個函數,類也會自動生成這個函數
	constructor(uname, age) {
		this.uname = uname;
		this.age = age;
	}
}
// 2.生成執行個體 new 不能省略
var ldh = new Star('劉德華', 18);
var zxy = new Star('張學友',20);
console.log(ldh);
console.log(zxy);
           

注意文法規範,建立類類名後面不要加小括号,生成執行個體類名後面加小括号,構造函數不需要加function

1.2.5 類添加方法

文法∶

class Person {
	constructor(name, age){  // constructor構造器或者構造函數
		this.name = name;
		this.age = age;
	}
	// 類裡面所有的函數不需要寫 function
	// 類裡面,多個函數方法之間不需要逗号分隔
	say () {
		console.log (this.name + '你好');
	}
}
           

1.3.類的繼承

1.3.1 繼承

繼承:子類可以繼承父類的一些屬性和方法。

文法:

class Father{   // 父類
}
class Son extends Father {  //子類繼承父類
}

           

1.3.2 super

super關鍵字用于通路和調用對象父類上的函數。可以調用父類的構造函數,也可以調用父類的普通函數。

1.調用父類的構造函數

super 關鍵字可以調用父類的構造方法,可以寫在子類的構造方法中,進而給父類的屬性指派。

避免出現通過子類構造方法,指派給的是子類的屬性,導緻父類的方法無法使用的情況。

以下是這種情況的說明:

父類 Father 如下:

class Father {
	constructor(×,y) {
		this.x =X;
		this.y = y;
	}
	sum() {
		console.log(this.x + this.y);
	}
}
           

想要子類 Son 繼承它的 sum 方法。按照如下代碼寫的話,會出現問題。

class Son extends Father {
	// 這個地方這樣寫的話,子類執行個體化的時候是指派給了子類的屬性 x和y
	constructor(×,y) {
		this.x = X;
		this.y = y;
	}
}
// 這個Son類的執行個體的屬性x和y分别是1和2
var son = new Son(1,2);
// son 類繼承的sun 方法,算的是父類的屬性x和y的和,無法得到結果。
son.sum();
           

正确的寫法應該是:

class Son {
	constructor(x,y) {
		super(x,y);//調用了父類的構造函數
	}
}
// new 指令生成子類的執行個體時,自動調用構造函數,把 1和2 傳給了子類的構造函數的 x和y
// 執行super(x,y)語句,調用了父類的構造函數
// 父類中的x和y 賦了值,分别是 1和2
var son = new Son(1,2); 
// 計算父類的屬性:x和y 的和。結果是3.
son.sum();
           

2.super關鍵字調用父類的普通函數

class Father {
	say() {
		return "我是爸爸';
	}
}
class Son extends Father {
	say() {
		console.log('我是兒子');
		// super.say()就是調用父類中的普通函數say()
		console.log(super.say() + '的兒子');
	}
}
var son = new Son();
son.say();

           

☆ 繼承中的屬性或者方法查找原則(就近原則):

  • 如果執行個體化子類輸出一個方法,先看子類有沒有這個方法,如果有就先執行子類的。
  • 如果子類裡面沒有,就去查找父類有沒有這個方法,如果有,就執行父類的這個方法(就近原則)

3.子類繼承父類方法,同時擴充自己方法

在子類的構造函數中:

  • 使用 super關鍵字,在子類執行個體化時給父類的構造函數傳值
  • 使用 this 關鍵字,在子類執行個體化時給子類的屬性指派

注意:子類在構造函數中使用super,必須放到this 前面(必須先調用父類的構造方法,再使用子類構造方法)

class Father {
	constructor(×,y){
	// 構造函數裡面的 this 指的是 建立的執行個體對象
	this.x = X;
	this.y = y;
}
	sum() {
		console.log(this.× + this.y);
	}
}
//子類繼承父類加法方法同時擴充減法方法
class Son extends Father {
	constructor(x,y) {
		// 利用super 調用父類的構造函數
		// super 必須在子類this 之前
		super(x,y);
		
		this.x = x;
		this.y = y;
		
	}
	// subtract 中的this,指的是 執行個體對象son,因為son 調用了這個函數
	subtract() {
		console.log(this.x - this.y);
	}
}
var son = new Son(5,3);
son.subtract();
           

☆ 三個注意點;

  1. 在ES6中類沒有變量提升,是以必須先定義類,才能通過類執行個體化對象
  2. 類裡面的共有的屬性和方法,一定要加 this 使用
  3. 類裡面的this指向問題.
  4. constructor裡面的this指向執行個體對象,方法裡面的this指向這個方法的調用者

1.4.面向對象案例

案例:面向對象版tab欄切換

功能需求:

1.點選 tab欄,可以切換效果.

2.點選+号,可以添加tab項和内容項.

3.點選x号,可以删除目前的tab項和内容項.

4.輕按兩下tab項文字或者内容項文字可以修改裡面的文字内容.

抽象對象:

Tab 對象

1.該對象具有切換功能

2.該對象具有添加功能

3.該對象具有删除功能

4.該對象具有修改功能

2.構造函數和原型

目标:
  • 能夠使用構造函數建立對象
  • 能夠說出原型的作用
  • 能夠說出通路對象成員的規則
  • 能夠使用ES5新增的一些方法

2.1 構造函數和原型

2.1.1 概述

在典型的OOP的語言中( 如Java ),都存在類的概念,類就是對象的模闆,對象就是類的執行個體,但在ES6之前,JS中并沒有引入類的概念。

ES6,全稱ECMAScript 6.0 ,2015.06發版。但是目前浏覽器的JavaScript是 ES5 版本,大多數高版本的浏覽器也支援ES6,不過隻實作了5S6的部分特性和功能。

在ES6之前,對象不是基于類建立的,而是用一種稱為建構函數的特殊函數來定義對象和它們的特征。

建立對象可以通過以下三種方式:

1.對象字面量

2.new Object()

3.自定義構造函數

// 方法一:對象字面量
var obj1 = {};

// 方法二: new Object()
var obj2 = new Object();

// 方法三:自定義構造函數
function Star(uname, age) {
	this.uname = uname;
	this.age = age;
	this.sing = function(){
		console.log('會唱歌')
	}
}
var ldh = new Star('劉德華',18);
ldh.sing();
           

2.1.2 構造函數

構造函數是一種特殊的函數,主要用來初始化對象,即為對象成員變量賦初始值,它總與new一起使用。我們可以把對象中一些公共的屬性和方法抽取出來,然後封裝到這個函數裡面。

在JS中,使用構造函數時要注意以下兩點︰

1.構造函數用于建立某一類對象,其首字母要大寫

2.構造函數要和 new 一起使用才有意義

new 在執行時會做四件事情:

  1. 在記憶體中建立一個新的空對象。
  2. 讓 this指向這個新的對象。
  3. 執行構造函數裡面的代碼,給這個新對象添加屬性和方法。
  4. 傳回這個新對象(是以構造函數裡面不需要return ).

JavaScript的構造函數中可以添加一些成員,可以在構造函數本身上添加,也可以在構造函數内部的this上添加。通過這兩種方式添加的成員,就分别稱為靜态成員和執行個體成員。

靜态成員︰在構造函數本身上添加的成員稱為靜态成員,隻能由構造函數本身來通路。如sex。

執行個體成員∶在構造函數内部通過 this 添加的對象成員稱為執行個體成員,隻能由執行個體化的對象來通路。如uname、age、sing。

function Star(uname, age) {
	// 1.在構造函數内部通過 this 添加的對象成員稱為執行個體成員
	this.uname = uname;
	this.age = age;
	this.sing = function(){
		console.log('會唱歌')
	}
}
var ldh = new Star('劉德華',18);
// 執行個體成員 隻能由執行個體化的對象來通路
ldh.sing();
// 2.在構造函數本身上添加的成員稱為靜态成員
Star.sex = '男';
// 靜态成員 隻能由構造函數本身來通路
console.log(Star.sex)
           

2.1.3 構造函數的問題

構造函數方法很好用,但是存在浪費記憶體的問題。

JavaScript進階面向對象ES61.JavaScript 面向對象2.構造函數和原型

我們希望所有的對象使用同一個函數,這樣就比較節省記憶體,那麼我們就要使用到原型。

2.1.4 構造函數原型 prototype

構造函數通過原型配置設定的函數是所有對象所共享的。

JavaScript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。注意這個prototype就是一個對象,這個對象的所有屬性和方法,都會被構造函數所據有。

我們可以把那些不變的方法,直接定義在prototype對象上,這樣所有對象的執行個體就可以共享這些方法。

Star.prototype.sing = function(){
	console.log('會唱歌')
}
           
  1. Q:原型是什麼?

    A:一個對象,我們也稱prototype 為原型對象

  2. Q:原型的作用是什麼?

    A:共享方法。

一般情況下,公共屬性定義到構造函數裡面,公共的方法放到原型對象上。

2.1.5 對象原型 __proto__

對象都會有一個屬性__proto__,指向構造函數的prototype原型對象,之是以我們對象可以使用構造函數prototype原型對象的屬性和方法,就是因為對象有__proto__原型的存在。

  • proto 對象原型和原型對象 prototype是等價的
  • __proto__對象原型的意義就在于,為對象的查找機制提供一個方向,或者說一條路線,但是它是一個非标準屬性,是以實際開發中,不可以使用這個屬性,它隻是内部指向原型對象prototype
    JavaScript進階面向對象ES61.JavaScript 面向對象2.構造函數和原型

    方法的查找規則:

    首先看ldh對象身上是否有sing方法,如果有就執行這個對象上的sing

    如果沒有sing 這個方法,因為有__proto__的存在,就去構造函數原型對象prototype身上去查找sing這個方法

2.1.6 constructor 屬性

對象原型(__proto__)和構造函數( prototype ) 原型對象裡面都有一個屬性constructor屬性,constructor我們稱為構浩函數,因為它指回構造函教本身。

constructor主要用于記錄該對象引用于哪個構造函數,它可以讓原型對象重新指向原來的構造函數。

很多情況下,我們需要手動的利用constructor這個屬性指回原來的構造函數

Star.prototype = {
	// 如果我們修改了原來的原型對象,給原型對象指派的是一個對象,則必須手動的利用constructor指回原來的構造函數
	constructor: star;
	sing: function() f
	console.log('我會唱歌');
	},
	movie: function() {
	console.log('我會演電影');
	}
}
           

2.1.7構造函數、執行個體、原型對象三者之間的關系

JavaScript進階面向對象ES61.JavaScript 面向對象2.構造函數和原型

2.1.8原型鍊

2.2 繼承

2.3 ES5 中的新增方法