天天看點

JS函數與面向對象

        函數是一組可以随時随地運作的語句。

一.JS函數

        建立函數:

function fnOne() {//具有名稱的函數,函數名必須符合變量名命名規範
	//可以沒有符何語句
}
var fnTwo = function () {//匿名函數
};
function () {//建立匿名函數而不立即建立其引用,那麼之後就沒辦法調用此函數
}
(function fnThree() {
})();//建立函數并立即執行一次
(function () {})();//建立匿名函數并立即執行一次      

        匿名函數與命名函數的差別:

fnOne();//不會出錯,使用function建立的具有名稱的函數在任何與之相同作用域的地方都能調用
fnTwo();//出錯
var fnTwo =function () {};//因為隻有執行了這個指派語句後,fnTwo才會被建立
function fnOne() {}      

        函數傳回值:

function fnTest() {
	return "value";//使用return來傳回值
	alert("Hello!!!");//執行了return之後,函數就會退出 
}      

        函數參數:

function fnTest(arg1,arg2,arg3) {
alert(arg1+"\n"+arg2+"\n"+arg3);
}
fnTest(1,2,3);
fnTest(1,2);//沒有傳值過去時,就會是undefined      

        arguments對象:在函數執行時函數内部就會有arguments對象,它包含了所有的參數,arguments的length屬性報告了傳入參數個數。

function fnTest() {
for (var i=0;i< arguments.length;i++) {
alert(arguments[i]);
}
}
fnTest(1,2,3);
fnTest(45);      

        使用arguments對象模拟函數重載:

function fnTest() {
var args = arguments;
switch (arguments.length) {
    case 0 :
        return "沒有輸入!!!";
    case 1 :
        return "一個參數:"+args[0];
    case 2 :
        return "二個參數:"+args[0]+" 和 "+ args[1];
    }
}
alert(fnTest());
alert(fnTest(1));
alert(fnTest(2));      

        arguments對象補充:arguments對象的callee屬性指向它所在的函數。

function fnTest() {alert(arguments.callee);}      

二.閉包

        "閉包,指的是詞法表示包括不被計算的變量的函數,也就是說,函數可以使用函數之外定義的變量。" 請了解透并牢牢記住這句話,否則你就可能永遠不知道什麼是閉包,了解好,其實閉包還是蠻簡單的。-----摘者注。

        在 ECMAScript 中使用全局變量是一個簡單的閉包執行個體。請思考下面這段代碼:

var msg = "我是全局變量!!!";
function say() {
  alert(msg);
}
say();      

        在ECMAScript中,在函數聲明處向函數外部看到的聲明的所有變量,在函數内部都能通路到它們的最終值!

var g = "全局變量!!!";
function fnA() {
	var a="A";
	function fnB() {
		var b="B";
		alert(a);//可以通路到a
		alert(c);//但不以通路c
		function fnC() {
			var c = "C";
			alert(a+"\n"+b);//隻要遵循從裡向外看的原則,看到的變量都可以通路到
		}
	}
}
//更複雜的閉包
function fnTest(num1,num2) {
	var num3 = num1+num2;
	return function () {
		alert("num1+num2結果為"+num3);
	};
}
var result = fnTest(23,56);
result();      

        閉包函數隻能通路變量的最終值!!!

function fnTest(arr) {
    for (var i=0;i < arr.length;i++) {
        arr[i]=function () {
            alert(i+" | "+arr[i]);
        };
    }
}
var arr = [0,1,2,3,4,5];
fnTest(arr);
for (var i=0;i < arr.length;i++) {
    arr[i]();//始終輸出6還有一個undefined
    //因為函數退出後,i值為6,是以通路到的值隻有6
}
//我開始一看感覺應該不怎麼複雜,不過隻細看過後,再運作過後,感覺還真不簡單呢!很意思的一段代碼。      

        不但在閉包中可以通路閉包外的變量值,而且還可以設定它的值。

function fnTest() {
    var a=123;
    return {
        set:function (param) {a = param},
        get:function () {return a}
    };
}
var obj = fnTest();
alert(obj.get());//123
obj.set(4);
alert(obj.get());//4      

三.對象,構造函數

        建立一個對象:

var obj = new Object();
alert(obj);
alert(Object);//一個函數
Object();//可以直接執行
//構造函數也是一個普通的函數
function Demo() {
}
var d = new Demo();//不會出錯,使用new運算符來建立對象執行個體
alert(d);//object      

        this關鍵字的用法:

function Demo() {
this.property = "屬性!!!";
}
d = new Demo();
alert(d.property);//屬性!!!      

        不使用new而直接執行構造函數時,this指向window。

function Demo() {
this.property = "屬性!!!";
}
var d = Demo();
alert(d.property);//undefined
alert(window.property);//屬性!!!      

        可以給構造函數傳遞參數,然後可以将參數指派給對象的屬性。

function Person(name,age) {
this.name = name;
this.age = age;
}
var p1 = new Person("CJ",18);      

        instanceof 運算符,用來判斷對象是否是某個類(雖然ECMAScript中并不存在類,但我們在這裡依然使用這一術語)的執行個體。

var str = new String("string");
alert(str instanceof String);//true
var arr = new Array();
alert(arr instanceof Array);//true
function Demo() {}
var d = new Demo();
alert(d instanceof Demo);//true      

四.面向對象術語

        一種面向對象語言需要向開發者提供四種基本能力:

        封裝——把相關的資訊(無論資料或方法)存儲在對象中的能力。

        聚集——把一個對象存儲在另一個對象内的能力。

        繼承——由另一個類(或多個類)得來類的屬性和方法的能力。

        多态——編寫能以多種方法運作的函數或方法的能力。

        ECMAScript支援這些要求,是以可被看作面向對象的。

五.封裝與私有屬性:封裝并不要求私有!

function Person(name,age) {
	this.name = name;//将值存儲為對象的屬性即是封裝
	this.age = age;
}
var p1 = new Person("CJ",18);      

        ECMAScript目前版本并不支援私有屬性,但可以通過閉包來模拟。

function Person(name,age) {
	this.getName = function () {return name};
	this.setName = function (param) {name=param};
	this.getAge = function () {return age};
	this.setAge = function (param) {age=param};
}
var p1 = new Person("CJ",18);
alert(p1.name);//undefined
alert(p1.getName());//CJ
p1.setName("XXX");
alert(p1.getName());//XXX      

        繼承:prototype屬性

        ECMAScript中,繼承是通過構造函數的prototype屬性實作的。

function Person(name,age) {
    this.name=name;
    this.age = name;
}
alert(Person.prototype);//object
Person.prototype.sort = "人";
var p1 = new Person("CJ",18);
alert(p1.sort);//所有的Person對象的執行個體繼承了sort這個屬性      

        所有對象都有一個方法isPrototypeOf(),用來判斷它是不是另一個對象的原型。

function Person() {}
var p1 = new Person();
alert(Person.prototype.isPrototypeOf(p1));//true    P1的原型是不是Person      

        在ECMAScript中讓一個類繼承另一個類的方式比較特殊。

function ClassA() {
this.a = "A";
}
function ClassB() {
this.b = "B";
}
ClassB.prototype = new ClassA(); //讓ClassB繼承ClassA
var b = new ClassB();
alert(b.a);//"A",繼承了屬性a
alert(b instanceof ClassB);//true
alert(b instanceof ClassA);//true,因為繼承,b也是ClassA的後代
alert(ClassB.prototype.isPrototypeOf(b));//true
alert(ClassA.prototype.isPrototypeOf(b));//true,ClassA.prototype也是b的原型      

        然而這樣的繼承有個注意點——

function ClassA() {
this.a = "A";
}
function ClassB() {
this.b = "B";
}
var b = new ClassB();//先執行個體化ClassB
ClassB.prototype = new ClassA();//再去繼承ClassA,将prototype重置為另一個對象
alert(b instanceof ClassB);//false
alert(b instanceof ClassA);//false
alert(ClassB.prototype.isPrototypeOf(b));//false
alert(ClassA.prototype.isprototypeOf(b));//false      

        當構造函數需要參數時

function Person(name,age) {
    this.name = name;
    this.age = age;
}
function Teacher(name,age,lesson) {
    this.tempMethod = Person;//對象冒充
    this.tempMethod(name,age);
    //當執行Person時,由于是在Teacher某個執行個體上調用的,是以在Person函數中的this指向了Teacher的執行個體
    delete this.tempMethod;//删除臨時方法
    this.lesson = lesson;
}
ClassB.prototype =new ClassA();//始終不應在繼承時放參數
var t1 = new Teacher("HUXP",18,"C#");
alert(t1.name+" | "+ this.age+ " | "+this.lesson);      

        事實上,對于對象冒充,ECMAScript提供了更簡潔的内置方法call,在每個函數上調用,第一個參數即為要冒充的對象,剩下的是函數需要的其它參數。

function Demo(arg) {
    this.name = arg;
}
var obj = new Object();
Demo.call(obj,"name");
alert(obj.name);//"name"
//使用call重寫上面繼承的例子
function Person(name,age) {
    this.name = name;
    this.age = age;
}
function Teacher(name,age,lesson) {
    Person.call(this,name,age);//對象冒充
    this.lesson = lesson;
}      

        靜态屬性與Function類

        在ECMAScript裡有個有趣的地方是,函數本身也是對象(和數組也一樣),也可使用new來建立.Function構造函數至少要傳兩個字元串參數,可以是空字元串。除了最後一個字元串會被當做函數體語句去執行,其它參數都會作為函數參數變量名! 

var fn = new Function();//一個空函數
//建立有内容的函數
fn = new Function("arg1","alert(arg1)");//最後一個參數為執行語句的字元串,前面參數全是函數要用到的參數
//上面的代碼等效于
fn = function (arg1) {alert(arg1)};//同樣,由于都是指派語句,是以要注意出現次序
fn.property ="既然是對象,那麼就要以添加屬性"      

        事實上,在構造函數上添加的屬性就被稱之為靜态屬性,它不會被執行個體所繼承。

function ClassDemo() {
}
ClassDemo.property = new Array();
var d =new ClassDemo();
alert(d.property);//undefined
alert(ClassDemo.property);//object      

        Function類的執行個體還有其它一些方法和屬性(當然,使用function關鍵字聲明的函數也是一樣的) 

function outer() {
	inner();
	function inner() {
		alert(inner.caller);
		//函數的caller屬性指向調用自身的上下文函數,這裡指向outer
	}
}
function applyTest() {
	var args = arguments;
	this.name = args[0];
	this.age=args[1];
}
var obj = new Object();
applyTest.apply(obj,["name",18]);
alert(obj.name);
alert(obj.age);
//apply方法與call方法類似,也用于對象冒充,隻是apply方法隻有兩個參數
//第一個是要冒充的對象,第二個是所有參數組成的數組      

文章來源:http://www.cnblogs.com/koeltp/archive/2012/09/08/2676028.html

繼續閱讀