【JavaScriptOPP基礎】【成員屬性與成員方法】【原型與原型鏈】【原型屬性與原型方法】【for-in迴圈】【JS OOP 中的繼承】【JS模式實現繼承的三種方式】【閉包】 ...
今天和大家分享一下近期整理的有關JavaScriptOPP的基礎知識~~~我們一起來學習一下……
JavaScriptOPP基礎
一、面向對象編程OOP
1、語言的分類:
面向機器:彙編語言
面向過程:C語言
面向對象:C++,Java,PHP等
2、面向過程和麵向對象
①面向過程:專註於如何去解決一個問題的過程。
編程特點:用一個個函數去實現過程操作,沒有類和對象的概念;
②面向對象:專註於有哪一個對象實體去解決這個問題。
編程特點是:出現了一個個的類,由類去生成對象。
二、面向對象的三大特征
繼承,封裝,多態
三、類&對象的關係
1、類:一群有相同特征(屬性)和行為(方法)的集合
eg: 人類
屬性:"身高,體重,年齡"
方法:"吃飯,睡覺,敲代碼"
2、對象:從類中,new出的具有確定屬性值和方法的個體稱為對象。
eg:張三:屬性:身高:身高178,體重150 方法:說話---我叫張三
3、類和對象的關係:
類是抽象的,對象是具體的。
(!類是對象的抽象化,對象是類的具體化!)
通俗的來講:
類是一個抽象概念,表示具有相同屬性和行為的集合。
但是類僅僅表示表明這類群體具有相同的屬性,但是沒有具體的屬性值。
而對象是對類的屬性進行具體賦值後,而得到的一個具體的個體;
eg:人類有身高體重年齡,但不能具體說人類的身高體重具體是多少;而張三是人類的一個具體個體,身高體重等有具體值,則張三就是人類的一個對象。
4、使用類和對象的步驟:
①創建一個類(構造函數):
類名,必須要每個單詞首字母都大寫
function 類名(屬性一){
this.屬性 = 屬性一;
this.方法= function(){}
//this指向誰?即將調用當前構造函數的對象。
}
②通過類,實例化出一個新的對象;
var obj = new 類名(屬性一Value);
//原構造函數中this,指向新創建的obj對象
obj.方法();調用方法
obj.屬性();調用屬性
實例化對象的時候,會執行構造函數
5、兩個重要屬性:
constructor:返回當前對象的構造函數。(只有對象才有,返回的是構造函數-類)
>>>zhangsan.constructor == Person
>>>對象的constructor,位於_proto_原型對象上(後續講解)
instanceof:判斷對象是否是某個類的實例
>>>zhangsan instanceof Person true
>>>zhangsan instanceof Object true
>>>Person instanceof Object true(函數也是對象)
6、廣義對象與狹義對象:
廣義對象:除了用字面量聲明的基本數據類型之外,外物皆對象,換句話說,能夠添加屬性和方法的變數,就是對象;
var s = "1" ; 不是對象
var s = new String("1") ; 就是對象
狹義對象:只有屬性,方法,除此之外沒有任何東西。
var obj = {}; var obj = new Object();
成員屬性與成員方法
一、成員屬性與成員方法
1、在構造函數中通過this.屬性聲明;或者實例化出對象後,通過"對象.屬性"追加的。
都屬於成員屬性,或成員方法;也叫實例屬性與實例方法。
成員屬性/方法,是屬於實例化出的這個對象。
通過"對象.屬性"調用
二、靜態屬性與靜態方法
2、通過"類名.屬性名","類名.方法名"聲明的變數,稱為靜態屬性,靜態方法;也叫類屬性、類方法。
類屬性/類方法,是屬於類的(屬於構造函數的)
通過"類名.屬性名"調用
3、成員屬性,是屬於實例化出的對象的,會出現在新對象的屬性上
靜態屬性,是屬於類(構造函數)自己的,不會出現在新對象的屬性上
4、JS對象屬性的刪除:
①對象無需手動刪除,JS
②刪除對象的屬性:delete 對象名.屬性名
5、對象是引用數據類型
也就是說,當new出一個對象時,這個obj變數存儲的實際上是對象的地址,在對象賦值時,賦的也是地址。
function Person(){}
var xiaoxi = new Person();//xiaoxi對象實際上存的是地址
var ashu = xiaoxi;//賦值時,實際是將xiaoxi存的地址給了ashu
ashu.name = "阿樹";//阿樹通過地址,修改了地址
console.log(xiaoxi.name);//xiaoxi再通過地址打開對象,實際已經變了
引用數據類型,賦值時傳遞的是引用(地址)---快捷方式
基本數據類型,賦值時傳遞的是數據(值)
三、私有屬性和私有方法
6、在構造函數中,通過var聲明的屬性,成為私有屬性:
function Person(){var num=1;}
私有屬性的作用域,僅在當前函數有效。對外不公開,即通過對象/類都無法調用到。
原型與原型鏈
【__proto__與prototype】
1、prototype(函數的原型):函數才有prototype。
prototype是一個對象,指向了當前構造函數的引用地址。
2、__proto__(對象的原型對象):所有對象都有__proto__屬性。當用構造函數實例化(new)一個對象時,
會將新對象的__proto__屬性,指向構造函數的prototype。
eg:zhangsan.__proto__==Person.prototype √
所有對象最終都會指向Object()的prototype。
【原型鏈】
1、通過構造函數,new出的對象,新對象的__proto__指向構造函數的prototype。
2、所有函數的__proto__指向Function()的prototype。
3、非構造函數new出的對象({} new Object() 對象的prototype)的__proto__指向Object的prototype。
4、Object的__proto__指向Null。
原型屬性與原型方法
【類中屬性與方法的聲明方式】
1、成員屬性、成員方法:
this.name = ""; this.func = function(){}
>>>屬於實例化出的對象的。通過"對象.屬性"調用。
2、靜態屬性、靜態方法:
Person.name = ""; Person.func = function(){}
>>>屬於類(構造函數)的。通過"類名.屬性"調用。
3、私有屬性、私有方法:
在構造函數中,使用var num = 1;聲明
>>>只在構造函數內部能用,在外部無法通過任何方式訪問。
4、原型屬性、原型方法:
Person.prototype.name = "";
Person.prototype.func = function(){};
>>>寫在了構造函數的prototype上,當使用構造函數實例化對象時,該屬性方法會進入新對象的__proto__上。
也就是說,1/4使用對象可訪問,2使用類名可訪問,3只能在函數的{}內使用。
5、習慣上,我們會將屬性寫為成員屬性,而方法寫為原型方法;
eg:
1 function Person(){ 2 this.name = "zhangsan"; 3 } 4 Person.prototype.say = function(){}
原因:
①實例化出對象後,所有屬性直接在對象上,所有方法都在__proto__上,非常直觀清晰。
②方法寫到prototype上,要更加節省記憶體。
③使用for in 迴圈時,會將對象以及對象原型鏈上的所有屬性和方法列印出來,而方法往往是不需要展示的。
將方法寫到__proto__上,可以使用hasOwnProperty將原型上的方法更好的過濾。
④官方都這麼寫。
6、當訪問對象的屬性/方法時,會優先使用對象自有的屬性和方法。
如果沒有找到,便使用__proto__屬性在原型上查找,如果找到即可使用。
但是,當對象自身,以及__proto__上有同名方法,執行對象自身的。
7、可以通過prototype擴展內置函數的相關方法。
代碼演示:
1 function Person(){ 2 this.name1 = "name1"; //成員屬性 3 this.age = "age1"; 4 this.sex = "男"; 5 this.say = function(){ 6 alert("我是大帥哥!"); 7 } 8 var name2 = "name2";//私有屬性 9 } 10 Person.name3 = "name3";//靜態屬性 11 Person.prototype.name4 = "name4";//原型屬性 12 13 14 Person.prototype.say = function(){ 15 alert("我叫"+this.name1); 16 } 17 18 var zhangsan = new Person(); 19 // zhangsan.say = function(){ 20 // alert("我是大帥哥!"); 21 // } 22 zhangsan.say(); 23 zhangsan.toString(); 24 25 console.log(zhangsan); 26 27 // console.log(zhangsan.name1); 28 // console.log(zhangsan.name2); 29 // console.log(zhangsan.name3); 30 // console.log(zhangsan.name4); 31 // 32 // console.log(s);
可以通過prototype擴展內置函數的相關方法
代碼演示:
1 Number.prototype.add = function(a){ 2 return this+a 3 } 4 5 var num = new Number(10); 6 console.log(num); 7 alert(num.add(3)); 8 9 /* 10 * 為Array類添加一個find(val)方法,當一個Array對象調用該方法的時候,如果能找到val值,則返回其下標,否則返回-1。 11 */ 12 Array.prototype.find = function(val){ 13 for (var i = 0; i<this.length;i++) { 14 if(this[i]==val){ 15 return i; 16 } 17 } 18 return -1; 19 } 20 var arr = new Array(1,2,3,4,5); 21 alert(arr.find(1));
位String類添加一個字元串反轉方法
1 String.prototype.fanzhuan = function(){ 2 var arr = this.split(""); 3 arr.reverse(); 4 return arr.join("") 5 } 6 7 8 console.log(str.fanzhuan()); 9 10 11 12 for-in迴圈 13 14 15 【for-in迴圈】 16 for-in迴圈,主要用於遍歷對象。 17 for()中格式:for(keys in obj){} 18 19 keys表示obj對象的每一個鍵值對的鍵,所以{}中,使用obj[keys]讀取每個值; 20 但是,使用for-in迴圈,不但能遍歷對象本身的屬性和方法。還能夠遍歷對象原型鏈上的所有屬性和方法。 21 22 可以使用hasOwnProperty判斷一個屬性,是否是對象自身的屬性。 23 obj.hasOwnProperty(keys)==true 表示:keys是對象自身的一個屬性 24 25 代碼演示: 26 // foreach 27 function Person(){ 28 this.name1 = "name11111"; //成員屬性 29 this.age = "age11111"; 30 this.func2 = function(){ 31 console.log("thisFun") 32 } 33 } 34 Person.name3 = "name333333"; 35 Person.prototype.name4 = "name44444"; 36 Person.prototype.func1 = function(){ 37 console.log("prototypeFun") 38 } 39 var zhangsan = new Person(); 40 for(keys in zhangsan){ 41 if(zhangsan.hasOwnProperty(keys)){ //過濾掉原型上的屬性,只列印對象自身屬性 42 console.log(zhangsan[keys]) 43 } 44 }
JS OOP 中的繼承【JS模式實現繼承的三種方式】
【文檔註釋】第一行兩個星號
/**
* ……
*/
調用函數時,可以看到註釋內容
【JS OOP 中的繼承】
1、使用一個子類繼承另一個父類,子類可以自動擁有父類的屬性和方法。
>>>繼承的兩方,發生在兩個類之間。
2、JS模式實現繼承的三種方式:
1)擴展Object實現繼承
①定義父類:function Parent(){}
②定義子類:function Son(){}
③通過原型給Object對象添加一個擴展方法。
1 Object.prototype.customExtend = function(parObj){ 2 for(var i in parObj){ 3 //通過for-in迴圈,把父類的所有屬性方法,賦值給自己。 4 this[i] = ParObj[i]; 5 } 6 }
④子類對象調用擴展方法
son.customExtend(Parent);
2)使用call和apply
首先,瞭解一下call和apply:通過函數名調用方法,強行將函數中的this指向某個對象;
call寫法:func.call(func的this指向的obj,參數1,參數2...);
apply寫法:func.apply(func的this指向的obj,[參數1,參數2...]);
call與apply的唯一區別:在於接收func函數的參數方式不同。call採用直接寫多個參數的方式,而apply採用是一個數組封裝所有參數。
①定義父類 funtion Parent(){}
②定義子類 function Son(){}
③在子類中通過call方法或者apply方法去調用父類。
function Son(){
Parent.call(this,....);
}
3)使用原型繼承
①定義父類function Parent(){}
②定義子類function Son(){}
③把在子類對象的原型對象聲明為父類的實例。
Son.prototype = new Parent();
閉包
【JS中的作用域】
1、全局變數:函數外聲明的變數
局部變數:函數內聲明的變數
在JS中,函數為唯一的局部作用域,而if、for等其他{}沒有自己的作用域
所以,函數外不能訪問局部變數。
其實,變數在函數執行完畢以後,占用的記憶體就被釋放。
2、如何訪問函數私有變數?
JS中,提供了一種"閉包"的概念:在函數內部,定義一個子函數,可以用子函數訪問父函數的私有變數。執行完操作以後,將子函數通過return返回。
1 function func2(){ 2 var num = 1; 3 function func3(){ 4 var sum = num+10; 5 alert(sum); 6 } 7 return func3; 8 } 9 var f = func2(); 10 f();
3、閉包的作用:
① 訪問函數的私有變數;
② 讓函數的變數始終存在於記憶體中,而不被釋放。
代碼演示:
1 function func1(){ 2 var n = 1; 3 } 4 if(true){ 5 var m = 3; 6 } 7 //alert(m); // 3 8 func1(); 9 //alert(n);// 報錯。函數外不能訪問局部變數。其實,n變數在func1函數執行完畢以後,占用的記憶體就被釋放。 10 11 12 /*閉包*/ 13 function func2(){ 14 var num = 1; 15 function func3(){ 16 var sum = num+10; 17 alert(sum); 18 } 19 return func3; 20 } 21 var f = func2(); 22 f(); 23 24 // 迴圈轉一次,創建一個lis[i].onclick函數。但是,當點擊li執行函數的時候,i已經變為6,所以無論點擊哪一個,i都是6 25 // 迴圈創建lis[i].onclick的時候, 迴圈一次,li[i]裡面的i都被賦為不同值;創建了 li[1] li[2] li[3]... 26 // 但是,創建過程中,並沒有執行onlick後面的函數,所以函數裡面的i並沒有賦值,還是字母i。 27 // 最終,我們點擊li[1] li[2]...時,onclick函數裡面的i才會賦值。(但此時,全局變數的i ,已經成為6 了) 28 var lis = document.getElementsByTagName("li"); 29 for (var i=0;i<lis.length;i++) { 30 lis[i].onclick = function(){ 31 //alert("您/點擊了第"+i+"個li!"); 32 //console.log(lis[i]); 33 //console.log(this); 34 } 35 } 36 37 // 迴圈轉一次,創建一個自執行函數。 38 // 而自執行函數擁有自己的作用域,所以用函數局部作用域j保存當前的全局變數i值。這樣,創建第一個自執行函數,它的j=1;創建第二個,它的j=2...不同函數的j互不幹擾。 39 // 這樣,迴圈轉完以後,相當於創建了6個自執行函數,每個函數中都有一個不同的j變數 40 var lis = document.getElementsByTagName("li"); 41 for (var i=0;i<lis.length;i++) { 42 !function(){ 43 var j = i; 44 lis[j].onclick = function(){ 45 alert("您/點擊了第"+j+"個li!"); 46 //console.log(lis[i]); 47 //console.log(this); 48 } 49 }(); 50 } 51 52 var lis = document.getElementsByTagName("li"); 53 for (var i=0;i<lis.length;i++) { 54 !function(j){ 55 lis[j].onclick = function(){ 56 alert("您/點擊了第"+j+"個li!"); 57 //console.log(lis[i]); 58 //console.log(this); 59 } 60 }(i); 61 }