面向對象的三大特性
- 封裝: 在JS中使用對象封裝一些變量和函數
- 好處: 信息隱蔽, 方便擴展維護, 提高代碼的復用性
- 繼承: 一個類(對象)獲取另一個類(對象)的屬性和方法的一種形式
- 面向對象語言定義: 是否有類的概念(c++ Java)
- JS沒有類的概念, JS從這個角度說不是面向對象語言. 但面向對象具有繼承特性, 可以說JS支持面向對象語言
- 多態: 同一操作, 作用于不同對象, 會產生不同行為(解釋)
- 實現: JS天生就具有多態的特性(弱類型語言)
創建對象的方式
-
字面量的方式
var student = { name: 'zs', age: 16, class: '一分鐘精通JS' } //一個班級有N個學生, 通過字面量形式, 需要創建N個學生對象 //問題: 創建多個同類型的對象時, 大量代碼冗余(重復代碼) //價值: 只需要創建簡單的幾個對象
-
內置的構造函數
-
系統內置的構造函數: Object | Array | Math | Date | Function | String | Number | Boolean
var stu = new Object(); stu.name= 'zs'; stu.age= 16; stu.class= '一分鐘精通JS'; //跟字面量的方式差不多, 問題也是一樣-->創建對象的相同部分進行函數封裝(簡單工廠)
-
-
簡單的工廠方式: 類似函數封裝, 相同的不變, 不同的作為參數傳進來
function person(){ var stu = new Object(); stu.name= 'zs'; stu.age= 16; stu.class= '一分鐘精通JS'; //返回該對象 return stu; } var stu1 = person(); var stu2 = person(); //問題: 兩個學生信息都一樣 //優化如下: 不同的信息當做參數傳進來 function person(name, age, classes){ var stu = new Object(); stu.name= name; stu.age= age; stu.class= classes; return stu; } function dog(name, age, classes){ var dog = new Object(); dog.name= name; dog.age= age; dog.class= classes; return dog; } var stu3 = person('zs', 16, '一分鐘精通JS' ); var dog = dog('wangCai', 5 , '騎單車'); console.log(stu3 == dog); //false //問題: 創建不同類型的對象時, 無法分辨其類型
-
自定義構造函數-參照上一章構造函數創建對象
-
定義
- 自己定義的構造函數(首字母大寫)-通過this設置屬性/方法-使用new調用構造函數創建對象
-
執行過程
- 通過new關鍵字調用構造函數,構造函數內部默認創建一個新對象
- 將這個新對象賦值給this
- 通過this設置屬性和方法
- 默認返回新創建對象
-
返回值
- 如果沒有顯示return, 默認返回構造函數內部新創建的對象
- 若寫了返回值, 要看具體情況
- 如果返回值類型的數據, 就直接忽略, 返回內部新創建的對象
- 若返回引用類型的數據, 直接返回該數據(會覆蓋內部新創建的對象)
-
自定義構造函數(區別于簡單的工廠函數)
- 函數名首字母大寫
- 自定義構造函數內部會默認創建一個對象-默認將這個對象賦值給this
- 默認會返回新創建的對象
- 創建不同類型的對象時, 可以分辨其類型
-
構造函數注意事項
- 函數傳值: 函數當做參數傳進構造函數
- 判斷對象的類型
-
對象 instanceof 構造函數
: 判斷指定對象是否由某個構造函數創建出來
-
- 獲取對象類型: 構造器屬性
obj.constructor
- 構造函數調用-區別于函數調用
new
關鍵字: 默認創建/返回對象-
構造函數: 對這個對象進行一些初始化的操作(將創建對象賦值給this, 通過this綁定屬性/方法)
function Person(name) { // this = obj; console.log(this); this.name = name; // return obj; } var p1 = new Person('zs'); // 構造函數也是一個函數 var p2 = Person('ls'); // 可以這樣寫,但是不建議這樣做 console.log(p2); // undefined, 直接調用構造函數,this指向window //分析打印結果 <script> function fn(){ var t = 1; this.x = 3; document.write(t); document.write(this.x); } var obj = new fn(); document.write(obj.x); document.write(fn()); </script>
構造函數的原型對象
-
原型對象定義
-
在構造函數創建出來時, 系統默認會創建一個對象與這個構造函數相關聯, 這個對象稱為該構造函數的原型對象
構造函數的原型對象
-
-
作用
- 使用構造函數創建出來的對象, 默認就可以使用該構造函數原型對象的屬性和方法
訪問原型對象:
構造函數.prototype
-
設置原型對象:
構造函數.prototype.name = 'name1';
- 原型對象本質也是一個對象, 可以利用對象的動態特性設置原型對象
-
約定
- 該對象構造函數的原型對象
- 構造函數的原型對象(原型) | 對象的原型對象(原型)
實例與實例化
- 實例: 通過構造函數創建出來的對象.
- 一般說: XX是構造函數的實例
- 實例成員: 實例屬性+實例方法
- 實例化: 通過構造函數創建對象的過程
- 注意點
- 實例對象可以訪問實例成員和原型成員, 但是不能夠訪問靜態成員
- 構造函數在訪問靜態方法時, 內部的this指向的是構造函數自己, 不能訪問到實例對象的成員
function Person() {
this.name = "張三"; //實例屬性
var age = 18; //私有變量
this.showName = function () { //實例方法
console.log(this.name);
}
this.showAge = function () { //特權方法
console.log(age);
}
}
Person.prototype.show = function () { //原型方法
console.log(this.name);
}
Person.des = function () { //靜態方法
console.log(this.name);
}
var p1 = new Person();
p1.showName(); //張三
p1.showAge(); //18
p1.show(); //張三
//p1.des(); //undefined ? 報錯(Y)
Person.des(); //undefined ? 報錯 ? 張三 ?Person(Y)
原型的使用方法
- 利用對象的動態特性設置原型對象
- 提供一個構造函數
- 設置原型對象的成員(原型屬性+原型方法)
- 添加成員:
構造函數.prototype.name = "name1";
- 修改成員:
構造函數.prototype.name = "name2";
- 刪除成員:
delete 構造函數.prototype.name;
- 添加成員:
- 替換原型對象: 自己定義的對象賦值給原型對象
- 替換原型對象之前創建的對象和替換之后創建的對象,他們的原型對象不是同一個對象
* 注意點: 獲取替換原型對象的屬性與替換之前原型對象的屬性值不相等 * 建議: 最好在替換原型對象之后, 再創建對象
- 修正構造器屬性
使用原型對象的注意事項
- 訪問原型對象的屬性-方式
-
對象.屬性
(就近原則)- 首先查找自身是否有該屬性, 若有就直接使用
- 若沒有就查找該對象原型對象的屬性, 若有就直接使用
- 再沒有就返回
undefined
或報錯
構造函數.prototype.屬性
-
- 設置原型對象
- 通過
對象.屬性
設置屬性- 如果有這個屬性就是修改, 沒有這個屬性就是添加屬性, 不會修改原型對象的屬性
- 設置原型對象的屬性
- 通過
構造函數.prototype.屬性
或者替換原型 - 如果原型屬性是引用類型的數據, 可以通過
對象.屬性.屬性
的方式修改
- 通過
- 通過
構造器屬性相關
-
constructor
屬性: 指向與之對應的構造函數 -
對象.constructor
: 訪問的是原型對象的constructor
屬性
__proto__
屬性
- 定義
- 構造函數創建出來的對象, 默認就有一個
__proto__
屬性, 該屬性指向該對象對應的構造函數的原型對象
- 構造函數創建出來的對象, 默認就有一個
- 訪問原型對象-方式
構造函數.prototype
-
對象.__proto__
-
注意點: 在正式開發中, 不建議使用. 該屬性不是ES標準, 是部分瀏覽器廠商方便調試程序
__proto__和constructor結構關系圖
-
hasOwnProperty()
- 語法:
對象.hasOwnProperty("屬性名")
- 定義: 判斷對象中是否存在指定的屬性(實例屬性)/ES5 中判斷對象的自有屬性(hasOwnProperty)
- 與
in
區別:in
判斷對象中是否存在指定的屬性(不區分實例屬性/原型屬性)
isPrototypeOf()
- 語法:
構造函數.protoType.isPrototypeOf(對象)
- 定義: 判斷一個對象是否是指定對象的原型對象(判斷原型對象是否在整條原型鏈上)
- 與
instanceof
區別:instanceof
判斷一個對象是否是指定構造函數的實例對象