第二節:函數和對象

一、函數


JavaScript中的函數定義分爲兩種:聲明式函數和賦值式函數。而且,函數的名稱就是函數的內存地址,和變量一樣,指向函數代碼所在的內存區域。


// 聲明式函數

function fn(name){

    var msg = "你好,"+name+",我是一個函數.";

    alert(msg);

}


// 賦值式函數

var fn = function(name){

    var msg = "你好,"+name+",我是一個函數.";

    alert(msg);

}


以上兩種表達式中的fn是一樣的,都是指向一個函數的指針。alert(fn)的運行結果都是:


wKiom1msq2LSmQqoAAAbXCncD1M659.png-wh_50


alert(fn)之所以能夠打印函數的聲明語句,是因爲toString()方法:


function fn(){

    var msg = "你好,"+name+",我是一個函數.";

    alert(msg);

       

}

alert(fn.toString());


運行結果是一樣的。


那麼toString()方法是怎樣來的呢?


對於JavaScript來說,函數其實是Function類的對象。比如,我們可以用Function類直接創建上面的fn函數:


var fn = new Function("name","var msg = \"你好,\"+name+\",我是一個函數.\";\nalert(msg);");

alert(fn);


執行結果如下:


wKioL1msq4eSZP-EAAAbKaOGWzw323.png-wh_50


函數的執行結果也是一樣的:


var fn = new Function("name","var msg = \"你好,\"+name+\",我是一個函數.\";\nalert(msg);");

fn("張三");


wKiom1msq7iBwcu_AAAUFaKPmto861.png-wh_50


Function對象有length屬性,可以查看函數的參數,有valueOf()方法和toString()方法,這兩個方法返回的都是函數的源代碼。因此,alert(fn)顯示的就是函數的聲明語句了。


既然函數名是指向函數的變量,那麼就可以把函數作爲參數傳遞給另一個函數。



function fn(name){

    var msg = "你好,"+name+",我是一個函數.";

    alert(msg);

}

function call_fn(func,name){

    func(name);

}

call_fn(fn,"張三");


wKioL1msq9PidgmqAAAUFaKPmto358.png-wh_50


JavaScript的函數,使用特殊對象arguments封裝了函數的參數,我們無需明確指出參數名,就能訪問它們。通過屬性 arguments.length可以獲得函數的參數個數 。用arguments對象判斷傳遞給函數的參數個數,即可模擬函數重載。下面是官方的一個例子。


function doAdd() {

  if(arguments.length == 1) {

    alert(arguments[0] + 5);

  } else if(arguments.length == 2) {

    alert(arguments[0] + arguments[1]);

  }

}

doAdd(10);       //輸出 "15"

doAdd(40, 20);    //輸出 "60"


JavaScript函數中,變量的作用域,要特別注意的一點是:加var關鍵字的變量是函數內部的局部變量,函數執行完後,變量將銷燬;但沒有加var關鍵字的變量,將成爲全局變量,函數外可以訪問。


function fn(){

    var x = 10;

    y = 20;

    alert("函數內部訪問x:"+x+"\n函數內部訪問y:"+y);

}

fn();

alert("函數外部訪問y:"+y);

alert("函數外部訪問x:"+x);


wKiom1msrNqy4JBAAAAVStlR_YE999.png-wh_50  wKiom1msrR2DloumAAATqf0MxrE669.png-wh_50  wKiom1msrZ2yJNymAAAY2LreAcI252.png-wh_50      


二、對象


JavaScript沒有類的概念,只有對象定義。對象是由 new 運算符加上要實例化的對象的名字創建的。其中,要實例化的對象其實就是函數。那麼,函數是怎樣變成對象的呢?


先看下面這段代碼。


function myobj(){

  return this;

}

var f = myobj();

var o= new myobj();

alert(f);

alert(o);


我們發現,加了new運算符後,this才指向由myobj創建的對象。

wKiom1msrfDBDluUAAARy7U4EPA714.png-wh_50

而var f = myobj(),這就不是創建對象,其中的this不是指myobj創建的對象,而是代碼當前上下文環境的當前對象,比如是window。

wKioL1msrfPCIVCnAAASLEUnfd0395.png-wh_50

也就是說,用new運算符調用函數就創建了對象。this代表實例化之後的對象,可以顯示的return,但並不需要這麼做。以下代碼效果是一樣的。


function myobj(){

}

var o = new myobj();

alert(o);

wKioL1msrhGTVwkUAAARpT3MR3M563.png-wh_50

JavaScript的Object對象與Java中的Object相似,JavaScript中的所有對象都由這個對象繼承而來,Object對象中的所有屬性和方法都會出現在其他對象中。


Object 對象具有下列屬性:

  1. constructor:創建對象的函數(構造函數)的引用(指針)。

  2. Prototype:對該對象的對象原型的引用。對於所有的對象,它默認返回Object對象的一個實例。


還具有幾個方法:

  1. hasOwnProperty(property) :判斷對象是否有某個特定的屬性。必須用字符串指定該屬性。(例如,o.hasOwnProperty("name"))。

  2. isPrototypeOf(object) :判斷該對象是否爲另一個對象的原型。

  3. propertyIsEnumerable :判斷給定的屬性是否可以用 for...in 語句進行枚舉。

  4. toString() :返回對象的原始字符串表示。對於 Object 對象,ECMA-262 沒有定義這個值,所以不同的 ECMAScript 實現具有不同的值。

  5. valueOf() :返回最適合該對象的原始值。對於許多對象,該方法返回的值都與 ToString() 的返回值相同。


我們在瀏覽器的開發者工具的控制檯輸入new object()回車,可以查看上面那些Object對象的屬性和方法。


wKioL1msri_ydrXRAAB7C8xobI0298.png-wh_50


Object對象也可以使用對象字面量語法來創建:var o = {};

同樣,我們可以在瀏覽器的開發者工具中進行驗證。

wKioL1msrobxEdAsAABwgmQ2I74458.png-wh_50


javascript支持後綁定,比如這樣:

function obj()

{

}

var o = new obj();

o.fn = function(){

  alert("hello");

}

o.fn();


wKiom1mss5yDSa-4AAAQH82TWPY611.png-wh_50


所以,可以通過函數創建對象,這叫工廠方法:


function createCar(color,doors,mpg) {

  var car = new Object();

  car.color = color;

  car.doors = doors;

  car.mpg = mpg;

  car.showColor = function() {

    alert(this.color);

  };

  return car;

}

var car1 = createCar("red",4,23);

var car2 = createCar("blue",3,25);

car1.showColor();

car2.showColor();

     


  wKioL1msr8GDEIDDAAAP_ymWBU0296.png-wh_50  wKiom1mss7Swv0oUAAAQGsKqv4c881.png-wh_50



比較正規一點的創建對象的方法,用構造函數法,用this代表當前對象了:


function Car(color,doors,mpg) {

  this.color = color;

  this.doors = doors;

  this.mpg = mpg;

  this.showColor = function() {

    alert(this.color);

  };

}

var car1 = new Car("red",4,23);

var car2 = new Car("blue",3,25);

car1.showColor();

car2.showColor();

 

 

  wKioL1mssK7yOr6DAAAQGsKqv4c428.png-wh_50 wKioL1mssWeg5EQDAAAQGsKqv4c799.png-wh_50



構造函數法的問題是,其中的對象方法,比如例子中的showColor方法,每次new的時候都會創建一次,顯然浪費內存。


可以用原型鏈定義對象的方法。這樣,所有對象共享一個對象方法,既保證了對象屬性的唯一性,也節約了內存:


function Car(color,doors,mpg) {

    this.color = color;

    this.doors = doors;

    this.mpg = mpg;

    this.drivers = new Array("Mike","John");

}

Car.prototype.showDrivers = function() {

  alert(this.drivers);

};

var car1 = new Car("red",4,23);

var car2 = new Car("blue",3,25);

car1.drivers.push("Bill");

car1.showDrivers();        //輸出 "Mike,John,Bill"

car2.showDrivers();        //輸出 "Mike,John"


  wKioL1msscPT6razAAARQ-UiY9g025.png-wh_50  wKioL1mstEDzGCmVAAARcxELLxg794.png-wh_50



對象方法,只能是new之後的對象可以訪問:


function obj(){

}

obj.prototype.fn = function(){

    alert("hello");

}

var o = new obj();

o.fn();


wKioL1mstGmRQkzcAAAQH82TWPY005.png-wh_50


但是,如果這樣訪問obj.fn()就會報錯:

function obj(){

}

obj.prototype.fn = function(){

    alert("hello");

}

obj.fn();


 wKiom1mstLnBq4uQAAAaZEaHa1E290.png-wh_50


用函數名字直接訪問的方法,我們叫函數方法,或者靜態方法。


function obj(){

}

obj.fn = function(){

    alert("hello");

}

obj.fn();


wKioL1mstMKzD6ZgAAAQH82TWPY061.png-wh_50


但javascript的靜態方法,對象並不能訪問,如果這樣就會報錯:


function obj(){

}

obj.fn = function(){

    alert("hello");

}

var o = new obj();

o.fn();


wKiom1mstSDjp2AsAAAabDTKJ1Y156.png-wh_50


還有很重要的一點,原型屬性是所有對象共享,如果屬性是引用類型,那麼就會出現,一個對象修改了數值,其他對象相應的屬性數值都會改變的尷尬局面。


function obj(){

}

obj.prototype.names = ["張三","李四"];

var o1 = new obj();

var o2 = new obj();

o1.names.push("o1添加的新的名字");

alert("o1.names:"+o1.names+"\no2.names:"+o2.names);


wKioL1mstWDCYWvkAAAZ7tOG5Ok030.png-wh_50


其實,javascript並沒有真正的靜態方法,只是利用javascript靈活的特性模擬實現的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章