相關鏈接:
JS面向對象(1) -- 簡介,入門,系統常用類,自定義類,constructor,typeof,instanceof,對象在內存中的表現形式
JS面向對象(2) -- this的使用,對象之間的賦值,for...in語句,delete使用,成員方法,json對象的使用,prototype的使用,原型繼承與原型鏈
JS面向對象(3) -- Object類,靜態屬性,閉包,私有屬性, call和apply的使用,繼承的三種實現方法
1.Object類
在JS中,Object是所有類的基類,使用Object類來創建自定義對象時,可以無需定義構造函數(constructor,prototype,hasOwnProperty(property))
1 var per = new Object(); 2 per.name = 'zhangsan'; 3 per.age = 30; 4 alert(per.name + per.age);
我們想在程序中得到一個對象變量,只要能存儲大量數據即可,這個時候,我們可以考慮使用Object類。Object類避免了對構造器的定義。 Object類下另一個常用的屬性:hasOwnProperty
1 var per = new Object(); 2 per.name = 'zhangsan'; 3 per.age = 30; 4 if per.hasOwnProperty('email'){ 5 alert('具有email'); 6 }else{ 7 alert('無email'); 8 }
2.靜態屬性
在有些面向對象的語言當中,可以使用static關鍵字定義類的靜態屬性或者靜態方法,在JS中,可以進行模擬。
語法:
類名.屬性名
類名.屬性=function(){}
1 function Person(){ 2 } 3 Person.count = 0; 4 var p1 = new Person(); 5 Person.count++; 6 var p2 = new Person(); 7 Person.count++; 8 var p3 = new Person(); 9 Person.count++; 10 alert(Person.count);
添加靜態屬性和靜態方法:
1 function Person(){ 2 Person.count++; //靜態屬性 3 Person.getCount=function(){ //靜態方法 4 alert('當前共有' + Person.count + '個人'); 5 } 6 } 7 Person.count = 0; 8 var p1 = new Person(); 9 var p2 = new Person(); 10 var p3 = new Person(); 11 Person.getCount();
3.閉包
概念:所謂閉包,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因此這些變量也是該表達式的一部分。
提出一個問題:
1 function display(){ 2 var i=10; 3 } 4 display(); 5 //在這裡,想訪問局部變量i
在全局中,不能訪問局部變量i,因為作用域不同,而且,在display函數執行完畢後,局部變量i會被回收。 閉包的功能:“訪問局部變量”和“使變量所占的內存不被釋放”
1 //例1 2 function fn1(){ 3 function fn2(){ 4 alert('hello'); 5 } 6 return fn2; //返回fn2函數首地址 7 } 8 var test=fn1(); //test也指向了fn2函數的首地址 9 test();
通過例1我們知道:變量是可以指向函數的首地址的,函數也可以返回另一個函數的首地址。
1 //例2 2 function fn1(){ 3 var i = 10; 4 function fn2(){ 5 alert(i); 6 } 7 return fn2; //返回fn2函數首地址 8 } 9 var test=fn1(); //test也指向了fn2函數的首地址 10 test();
通過例2我們知道:使用一個拒不函數包含變量i,這樣局部變量i的內存不會被回收。
1 //例3 2 function fn1(){ 3 var i = 10; 4 function fn2(){ 5 alert(i++); 6 } 7 return fn2; //返回fn2函數首地址 8 } 9 var test=fn1(); //test也指向了fn2函數的首地址 10 test(); 11 test(); 12 test();
在例3中,因為i的內存永遠不會被回收,所以每次調用fn2,i的值會+1。運行的結果是彈出10,彈出11,彈出12。
閉包的原理:在例3中,共有三個作用域:全局作用域,fn1的作用域,fn2的作用域。在全局作用域裡有test=fn1(),其實這句話就相當於test=fn2。在fn1作用域裡有 var i=10和return fn2,在fn2作用域例有alert(i++)。當全局作用域下的test=fn1()執行時,test指向了fn2的作用域,這個時候fn2作用域下的i被全局作用域鉤住,根據作用域鏈的法則,fn2下並沒有定義i,所以在fn2下的i往上一層作用域上找,找到了fn1作用域下的var i=10。所以全局的test鉤住了fn2的i,fn2的i鉤住了fn1的i,所以fn1運行完畢後,不會被回收。
4.私有屬性
在面向對象思想中,對於有些敏感的,不想公開的成員可以定義為私有的,在JavaScript中可以模擬這個功能。
語法:
function Person(p_name){
var name = p_name;
this.age
}
var :私有
this :公有
function Person(p_name,p_age){ this.name = p_name; var age = p_age; } var p1 = new Person('zhangsan',30); alert(p1.name); alert(p1.age);
在上面這個例子中,我們想用var來表示私有成員屬性,但Person構造函數執行完畢後,age會被回收,不能當做成員屬性來使用。
function Person(p_name,p_age){ this.name = p_name; var age = p_age; this.setAge=function(a){ age = a; } this.getAge=function(){ return(age); } } var p1 = new Person('zhangsan',30); p1.setAge(20); alert(p1.getAge());
this.setAge和this.getAge兩個方法使用到了局部變量age,所以age不會被回收。
如果只有set方法,說明該屬性是只寫屬性。
如果只有get方法,說明該屬性是只讀屬性。
5.call和apply的使用
call和apply的功能:使用指定的對象調用當前函數。call和apply的功能完全相同,只是在語法上略有不同。
語法:
call([thisObj[,arg1[,arg2[,argN]]]])
第一個參數:函數執行時,this指向誰
後面的參數:根據需要順序指定
apply([thisObj[,argArray]])
第一個參數:函數執行時,this指向誰
第二個參數:數組,表示參數集合
在js中,函數有幾種調用形式:
Person(); //Person內的this指向window
var p1=new Person(); //Person內的this指向p1
per.Person(); //Person內的this指向per
function Person(p_name,p_age){ this.name = p_name; this.age = p_age; } function speak(){ alert(this.name + this.age); } var p1 = new Person('zhangsan',30); //speak(); 這樣調用this指向window //p1.speak(); p1對象沒有speak屬性
使用call和apply來調用
function Person(p_name,p_age){ this.name = p_name; this.age = p_age; } function speak(){ alert(this.name + this.age); } var p1 = new Person('zhangsan',30); speak.call(p1); speak.apply(p1);
call和apply在執行時做了兩件事:1)將函數內部this指向了第一個參數 2)調用函數
另外:還可以這樣解決問題:
P1.say=speak;
P1.say();
這樣解決和上面解決方法有本質上的區別:
上面的解決辦法是直接調用speak函數,只不過函數內部this的指向發生改變。
下面的解決辦法會為p1對象增加屬性,p1對象的“體積”會變大。
舉例說明:
<script> function fn1(){ this.style.color='red'; } function fn2(){ this.style.fontSize='50px'; } window.onload=function(){ document.getElementById('btn').onclick=function(){ var div1 = document.getElementById('div1'); fn1.call(div1); fn2.apply(div1); }; }; </script> <div id='div1'>hello javascript</div> <input type='button' id='btn' value='確定'>
6.繼承的三種實現方法
概念:在有些面向對象語言中,可以使用一個類(子類)繼承另一個類(父類),子類可以擁有父類的屬性和方法,這個功能可以在js中進行模擬。
三種方法:
第一種:擴展Object方法
Object.prototype.方法=function(父類對象){ for(var i in 父類對象){ this[i] = 父類對象[i]; } };
舉例說明:
Object.prototype.ext=function(parObject){ //循環遍歷父類對象所有屬性 for(var i in parObject){ //為子類對象添加這個遍歷到的屬性 //它的值是父類對象這個屬性的屬性值 this[i] = parObject[i]; } } function Person(p_name,p_age){ this.name=p_name; this.age=p_age; this.speak=function(){ alert(this.name+this.age); } } function Student(p_no){ this.no=p_no; this.say=function(){ alert(this.no+this.name_this.age); } } var stu = new Student(101); stu.ext(new Person('xiaoqiang',20)); stu.speak(); stu.say();
第二種:使用call和apply方法
語法:
父類構造器.call(this,.......);
function Person(p_name,p_age){ this.name=p_name; this.age=p_age; this.speak=function(){ alert(this.name+this.age); } } function Student(p_no,p_name,p_age){ this.no=p_no; this.say=function(){ alert(this.name+this.age+this.no); } Person.call(this,p_name,p_age); } var stu = new Student(8,'zhagsan',18); stu.speak(); stu.say();
第三種:原型繼承
語法:
子類.prototype = new 父類();
function Person(p_name,p_age){ this.name=p_name; this.age=p_age; this.speak=function(){ alert(this.name+this.age); } } function Student(p_no){ this.no=p_no; this.say=function(){ alert(this.name+this.age+this.no); } } Student.prototype = new Person('wangwu',21); var stu = new Student(10); stu.speak(); stu.say();