函式
函式是一組可以隨時隨地執行的語句。
函式是 ECMAScript 的核心。
建立函式
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
在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方法只有兩個引數 //第一個是要冒充的物件,第二個是所有引數組成的陣列