面向對象與原型模式
1.1. js的對象:
定義:是"無序屬性的集合,其屬性可以包含基本值,對象,和函數",沒有類的概念(其他面向對象的語言都有類的概念)
面向對象思維:把解決問題的關注點放在解決問題的所需對象上.
1.2. 面向對象的三大特性:
1.2.1. 封裝
就是講一系列屬性和方法,也就是功能 ,封裝在對象里面,對象對外界暴露一些接口,外界在使用的時候,不需要關心對象內部的具體功能.
1.2.2. 繼承
# 其他面向對象語言中的繼承:有父子關系.
# JS中的繼承:自己沒有的東西,別人有,拿過來用就是繼承.
* 例:
var wangsicong = {}; var wangjianlin = { money:9999, manageCompany:function(){ console.log('我的公司'); } };
console.log(wangsicong);
* 混入式繼承(mix-in)
for(var k in wangjianlin){ wangsicong[k] = wangjianlin[k]; } console.log(wangsicong);
* 原型的繼承
1通過混入的方式為原型新增成員,以實現繼承
2直接替換原型
var obj1 = {屬性與方法}; function Person(){}; //構造函數 Person.prototype = obj1; var P1 = Person();
*經典繼承 : Object.create() //存在兼容問題
語法: var 對象1 = Object.create(對象2); //將對象2作為對象1的原型
兼容封裝:
function = myCreate(obj){ if(Object.create){ return Object.create(obj); }else{ var Create = function(){} Create.prototype = obj; return new Create(); } }
1.2.3. 多態
多態就是父類指針指向子類對象.
JS中不支持多態
1.3. 創建對象的方式
1.3.1. 1字面量
* 優點:簡單易用
* 缺陷:復用性比較差
var obj = { name:'小紅', sayHi:function(){ consol.log('我愛吃'); } }
1.3.2. 2使用內置構造函數object
* 缺陷:1創建出的是空對象; 2復用性差
var obj = new object();
1.3.3. 3自定義構造函數
例:
function Person(形參){ this.name = 形參, this.sarHi = function(){ console.log('你好'); } } var p1 = new Person(實參); var p2 = new Person(實參); console.log(p1.sarHi == p2.sarHi); //false 不在同一個地址,但用的相同的方法
# 什么是構造函數??
構造函數一般是用來初始化對象的函數(不是創建對象:是new創建的)!
# 構造函數的特點:
* 首字母大寫(規范)
* 一般和new關鍵字一起使用
* 不需要手動寫返回語句,會默認返回新創建出來的對象.
# 構造函數執行的過程:
* 使用new關鍵字構造函數
* 調用構造函數
* 將構造函數內部的this指針指向創建出的對象
* 使用this指針在構造函數內部對對象進行初始化操作(新增成員)
* 默認的將創建出來的對象返回
# 構造函數使用注意事項:
* 如果沒return,返回的是構造出的對象
* 如果手動返回的是值類型的數據,不會對默認返回值有任何影響
* 如果手動返回的是引用類型的數據,使用new關鍵字創建的對象被拋棄
* 將構造函數當做普通函數調用的時候,this指向window,如果返回值不寫,返回undefined
## 傳統構造函數存在的問題:
# 問題每次進來都創建相同的方法,造成資源浪費!
* 解決:將函數聲明放在構造函數外面,將構造函數的地址賦值給對象的方法.
* 這樣解決的問題:
1全局變量污染
2代碼結構混亂,不利於維護
3不安全 ,容易被人更改
* 解決方案:原型
1.4. 原型模式
1.4.1. 原型?:在構造函數創建出來的時候系統會默認的幫構造函數創建且關聯一個空對象,這個對象就是原型
1.4.2. 原型的作用?
在原型中的所有屬性和方法,都可以被其關聯的構造函數創建出來的對象所共享(與其他編程語言中的類可以相對應理解,但不相同)
1.4.3. 如何訪問原型?
1 構造函數.prototype;
2 對象.__proto屬性__(兩個下划線)
* 非標准屬性:ECMA中沒有的屬性,不推薦使用,只適合在調試過程中使用!(一般兩個下划線開頭的屬性都是非標准的屬性,單下划線開頭的是私有屬性)
* 例:
function Obj(){} var man = new Obj(); console.log(Obj.prototype === man.__proto__); //true
1.4.4. 原型的使用?
例1使用動態特性: 添加的成員少的時候
function Obj(){} Obj.prototype.sayHi = function(){ console.log('我是原型中的sayHi'); }; var p1 = new Obj(); var p2 = new Obj(); console.log(p1.sayHi() == p2.sayHi()) //true
例2:替換原型: 添加的成員多的時候
Obj.prototype = { 屬性, sayHi:function(){ console.log('我是原型中的sayHi'); } var p1 = new Obj(); var p2 = new Obj();
1.4.5. 原型使用注意事項:
*對象只有訪問不到本身屬性的時候才會去原型中查找.設置屬性不會去原型中查找
*一般情況下,只會將方法放在原型中,屬性放在對象中(方法是一樣的,每個對象的屬性是不一樣的)
*需要將原型放在上面,否則有可能訪問不到
*替換原型的時候,替換之前的原型與替換之后的原型不一致
*對象的原型,就是創建對象的那一刻,構造函數所關聯的原型
1.4.6. 實例化:通過構造函數實例化一個對象,(也就是所謂的創建對象)
1.4.7. 原型鏈:
js中所有的對象都有原型,原型也有對象,所以原型對象也有原型.
# 構造函數.prototype的成員介紹:
* constructor屬性,從原型指向關聯的構造函數(原型的屬性)
console.log(Obj.prototype.constructor);
* 對象.hasOwnProperty(屬性); //判斷對象有沒有這個屬性
* 當前對象.isPrototypeOf(另一個對象); //判斷當前對象是不是另一個對象的原型
* obj.propertyIsEnumerable(屬性); //判斷屬性是否屬於此對象並且可以被遍歷
* __proto屬性__
* toString() 與 toLocaleString()
都是將對象轉換成字符串的方法
但是toLocaleString()會將對象轉換成本地格式的字符串
* valueOf()
獲取屬性得值
當引用類型與值類型的數據參與運算的時候會調用valueOf,如果不能運算再調用toString()
# 對象 instanceof 構造函數
判斷此構造函數的原型有沒有在此對象的原型鏈上
原型鏈實例:
下面是建立了一個preson對象:
function Preson(){} var preson = new Preson();
這是一條preson對象的完整的原型鏈:

1.4.8. 屬性搜索原則:
*1當使用屬性訪問成員的時候,會先在對象自身查找
*2如果沒有找到,就去對象的原型中查找
*3如果沒有找到,就沿着原型鏈繼續往上查找,直到null
1.5. 如何安全的擴展內置對象?
// 例1:避免這么做!! var arr = new Array(); //array構造函數 Array.prototype.max = function(){ return 1; } arr.max(); //應當避免對內置對象進行直接修改,會影響別人代碼!!
// 例2:應當這么做!! function MyArray(){} MyArray.prototype = []; MyArray.prototype.max = function(){ return 1; } var myarr = new MyArray(); arr.max(); //MyArray繼承了數組的原型並進行擴展,並不會修改內置數組對象的原型
