Javascript的函數調用javascript
使用return語句時,函數會中止執行,並返回指定的值。html
在 JavaScript 函數內部聲明的變量(使用 var)是局部變量,因此只能在函數內部訪問它。(該變量的做用域是局部的)。當函數被執行完以後就會被刪除,自動被銷燬。java
您能夠在不一樣的函數中使用名稱相同的局部變量,由於只有聲明過該變量的函數才能識別出該變量。編程
函數的參數也是局部變量。數組
只要函數運行完畢,本地變量就會被刪除。瀏覽器
在函數外聲明的變量是全局變量,全局變量會在頁面關閉後被刪除,被銷燬。app
向未聲明的 JavaScript 變量分配值函數式編程
若是您把值賦給還沒有聲明的變量,該變量將被自動做爲全局變量聲明。函數
這條語句:學習
carname="Volvo";
將聲明一個全局變量 carname,即便它在函數內執行。
函數表達式
JavaScript 函數能夠經過一個表達式定義,須要以分號結束。
函數表達式能夠存儲在變量中:
在函數表達式存儲在變量後,變量也可做爲一個函數使用:
var x = function (a, b) {return a * b};
var z = x(4, 3);
以上函數其實是一個 匿名函數 (函數沒有名稱)。
函數存儲在變量中,不須要函數名稱,一般經過變量名來調用。
Function() 構造函數
在以上實例中,咱們瞭解到函數經過關鍵字 function 定義。
函數一樣能夠經過內置的 JavaScript 函數構造器(Function())定義。
實例
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
實際上,你沒必要使用構造函數。上面實例能夠寫成:
實例
var myFunction = function (a, b) {return a * b}
var x = myFunction(4, 3);
在 JavaScript 中,不少時候,你須要避免使用 new 關鍵字。
而methods這一律念在JavaScript中的表現就是,一個對象的屬性是一個function:一樣的是函數,將其賦值給一個對象的成員之後,就不同了。將函數賦值給對象的成員後,那麼這個就不在稱爲函數,而應該叫作方法
函數提高?
1、var 變量預編譯
JavaScript 的語法和 C 、Java、C# 相似,統稱爲 C 類語法。有過 C 或 Java 編程經驗的同窗應該對「先聲明、後使用」的規則很熟悉,若是使用未經聲明的變量或函數,在編譯階段就會報錯。然而,JavaScript 卻可以在變量和函數被聲明以前使用它們。下面咱們就深刻了解一下其中的玄機。
先來看一段代碼:
(function() {
console.log(noSuchVariable);//ReferenceError: noSuchVariable is not defined
})();
運行上面代碼立馬就報錯,不過,這也正是咱們指望的,由於 noSuchVariable 變量根本就沒有定義過嘛!再來看看下面的代碼:
(function() {
console.log(declaredLater); //undefined
var declaredLater = "Now it's defined!";
console.log(declaredLater);// "Now it's defined!"
})();
首先,上面這段代碼是正確的,沒有任何問題。可是,爲何不報錯了?declaredLater 變量是在調用語句後面定義的啊?爲何竟然輸出的是 undefined?
這實際上是 JavaScript 解析器搞的鬼,解析器將當前做用域內聲明的全部變量和函數都會放到做用域的開始處,可是,只有變量的聲明被提早到做用域的開始處了,而賦值操做被保留在原處。上述代碼對於解析器來講實際上是以下這個樣子滴:
(function() {
var declaredLater; //聲明被提早到做用域開始處了!
console.log(declaredLater); // undefined
declaredLater = "Now it's defined!"; //賦值操做還在原地!
console.log(declaredLater);//"Now it's defined!"
})();
這就是爲何上述代碼不報異常的緣由!變量和函數通過「被提早」以後,declaredLater 變量其實就被放在了調用函數的前面,根據 JavaScript 語法的定義,已聲明而未被賦值的變量會被自動賦值爲 undefined ,因此,第一次打印 declaredLater 變量的值就是 undefined,後面咱們對 declaredLater 變量進行了賦值操做,因此,第二次再打印變量就會輸出Now it's defined!。
再來看一個例子:
var name = "Baggins";
(function () {
console.log("Original name was " + name);// "Original name was undefined"
var name = "Underhill";
console.log("New name is " + name);// "New name is Underhill"
})();
上述代碼中,咱們先聲明瞭一個變量 name ,咱們的本意是但願在第一次打印 name 變量時可以輸出全局範圍內定義的 name 變量,而後再在函數中定義一個局部 name 變量覆蓋全局變量,最後輸出局部變量的值。但是第一次輸出的結果和咱們的預期徹底不一致,緣由就是咱們定義的局部變量在其做用域內被「提早」了,也就是變成了以下形式:
var name = "Baggins";
(function () {
var name; //注意:name 變量被提早了!
console.log("Original name was " + name);// "Original name was undefined"
name = "Underhill";
console.log("New name is " + name);//"New name is Underhill"
})();
因爲 JavaScript 具備這樣的「怪癖」,因此建議你們將變量聲明放在做用域的最上方,這樣就能時刻提醒本身注意了。
2、函數聲明「被提早」
前邊說的是變量,接下來咱們說說函數。
函數的「被提早」還要分兩種狀況,一種是函數聲明,第二種是函數做爲值賦值給變量,也即函數表達式。
先說第一種狀況,上代碼:
isItHoisted();//"Yes!"
function isItHoisted() {
console.log("Yes!");
}
如上所示,JavaScript 解釋器容許你在函數聲明以前使用,也就是說,函數聲明並不只僅是函數名「被提早」了,整個函數的定義也「被提早」了!因此上述代碼可以正確執行。
再來看第二種狀況:函數表達式形式。仍是先上代碼:
definitionHoisted();// "Definition hoisted!"
definitionNotHoisted();// TypeError: undefined is not a function
function definitionHoisted() {
console.log("Definition hoisted!");
}
var definitionNotHoisted = function () {
console.log("Definition not hoisted!");
};
咱們作了一個對比,definitionHoisted 函數被妥妥的執行了,符合第一種類型;definitionNotHoisted 變量「被提早」了,可是他的賦值(也就是函數)並無被提早,從這一點上來講,和前面咱們所講的變量「被提早」是徹底一致的,而且,因爲「被提早」的變量的默認值是 undefined ,因此報的錯誤屬於「類型不匹配」,由於 undefined 不是函數,固然不能被調用。
總結
經過上面的講解能夠總結以下:
var的做用域是函數做用域由var定義的變量,它做用域在一個函數體內,而不是咱們其餘語言理解的大括號{ }內。
使用var聲明的變量,其做用域爲該語句所在的函數內,且存在變量提高現象;
變量的聲明被提早到做用域頂部,賦值保留在原地
函數聲明整個「被提早」
函數表達式時,只有變量「被提早」了,函數沒有「被提早」
3、var的反作用
隱式全局變量和明肯定義的全局變量間有些小的差別,就是經過delete操做符讓變量未定義的能力。
經過var建立的全局變量(任何函數以外的程序中建立)是不能被刪除的。
無var建立的隱式全局變量(無視是否在函數中建立)是能被刪除的。
這代表,在技術上,隱式全局變量並非真正的全局變量,但它們是全局對象的屬性。屬性是能夠經過delete操做符刪除的,而變量是不能的:
// 定義三個全局變量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {
global_fromfunc = 3; // 反面教材
}());
// 試圖刪除
delete global_var; // false ,聲明的變量不能夠被刪除
delete global_novar; // true,非嚴格模式下,未聲明的變量能夠被當作是屬性能夠刪除
delete global_fromfunc; // true
// 測試該刪除
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"
在ES5嚴格模式下,無論是已聲明的全局變量,仍是未聲明的隱示全局變量,執行刪除操做上會拋出一個錯誤。
使用 "use strict" 指令,開啓嚴格模式。嚴格模式經過在腳本或函數的頭部添加 "use strict"; 表達式來聲明。在函數內部聲明是局部做用域 (只在函數內使用嚴格模式)
嚴格模式下不能使用未聲明的變量!
嚴格模式的限制有哪些??
4、單var形式聲明變量
在函數頂部使用單var語句是比較有用的一種形式,其好處在於:
提供了一個單一的地方去尋找功能所須要的全部局部變量
防止變量在定義以前使用的邏輯錯誤
少代碼(類型啊傳值啊單線完成)
單var形式長得就像下面這個樣子:
function func() {
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body...
}
您可使用一個var語句聲明多個變量,並以逗號分隔。像這種初始化變量同時初始化值的作法是很好的。這樣子能夠防止邏輯錯誤(全部未初始化但聲明的變量的初始值是undefined)和增長代碼的可讀性。在你看到代碼後,你能夠根據初始化的值知道這些變量大體的用途。
以上就是針對javascript的var預解析與函數聲明提高的學習內容,但願對你們的學習有所幫助。
自調用函數?(4)
1 ( function(){alert(1);}() );
2 (function(){alert(1);}) ();
3 !function(){alert(1);}();
4 void function(){alert(2);}();
函數是對象
在 JavaScript 中使用 typeof 操做符判斷函數類型將返回 "function" 。
可是JavaScript 函數描述爲一個對象更加準確。
JavaScript 函數有 屬性 和 方法。(arguments.length;??)
arguments.length 屬性返回函數調用過程接收到的參數個數:
實例
function myFunction(a, b) {
return arguments.length;
}
toString() 方法將函數做爲一個字符串返回:
實例
function myFunction(a, b) {
return a * b;
}
var txt = myFunction.toString();//function myFunction(a, b) { return a * b; }
函數參數?
js函數參數有顯式參數(Parameters)即形參和隱式參數(Arguments)實參
1、顯式參數(Parameters)即形參在函數定義時列出。
function functionName(parameter1, parameter2, parameter3){
//要執行的代碼
}
2、隱式參數(Arguments)即實參在函數調用時傳遞給函數的真正的值
function add(){
console.log(arguments[0], arguments[1], arguments[2]);
}
add(1,2,3);//1 2 3
參數的個數
(1)當實參比比函數聲明指定的形參個數少時,剩下的形參都將設置爲undefined。
例:
function add(x, y){
console.log(x, y);
}
add(1);//1, undefined
(2)當實參比形參個數要多時,剩下的實參沒辦法直接得到,能夠經過arguments對象來訪問。在javascript中函數的參數在函數內部是用一個數組表示的。函數接收的始終都是這個數組,並不關心數組中包含哪些參數。而arguments是一個類數組對象,可使用方括號語法來訪問它的每個元素。
例:
function add(x, y){
console.log(arguments[0], arguments[1], arguments[2]);
}
add(1, 2, 3);//1 2 3
arguments對象的length屬性能夠顯示實參的個數,函數的length屬性則顯示形參的個數。
例:
function add(x, y){
console.log(arguments.length);//3}
add(1, 2, 3);
console.log(add.length);//2
形參能夠提供便利,但不是必須的。
function add(){
console.log(arguments[0] + arguments[1]);
}
add(1, 2);//3
實參與形參同步
當形參與實參個數相同時,arguments對象的值和對應形參的值保持同步,但命名空間獨立。
例:
function test(num1, num2){
console.log(num1, arguments[0]);//1 1
arguments[0] = 2;
console.log(num1, arguments[0]);//2 2
num1 = 10;
console.log(num1, arguments[0]);//10 10}
test(1);
在嚴格模式下,arguments對象的值和形參都是獨立的。
例:
function test(num1, num2){
'use strict';
console.log(num1, arguments[0]);//1 1
argument[0] = 2;
comnsole.log(num1, arguments[0]);//1 2
num1 = 10;
console.log(num1, arguments[0]);//10 2}
test(1);
當形參並無對應的實參時,arguments對象的值與形參的值並不對應。
例:
function test(num1, num2){
console.log(num1, arguments[0]);//undefined, undefined
num1 = 10;
arguments[0] = 5;
console.log(num1, arguments[0]);//10, 5}
test();
對象參數:當一個函數包含超過3個形參時,要記住調用函數中實參的正確順序是一件很繁瑣的事情,能夠經過值對的形式來傳入參數。
參數的傳遞
javascript中全部函數的參數都是按值傳遞的。也就是說,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣,當傳遞的值是基本類型時,複製的是值自己。而參數爲引用類型時,會把這個值在內存中的地址複製給一個局部變量。
(1)基本類型值的傳遞(傳遞的值是變量值的副本。)
在向參數傳遞基本類型的值時,被傳遞的值會被複制給一個局部變量(命名參數或arguments對象的一個元素)。
function add(num){
num += 10;
return num;
}var count = 20;
console.log(add(count));//30
console.log(count);//20
(2)引用類型值的傳遞(傳遞的值是一個指向對象的指針。)
在向參數傳遞引用類型的值時,會把存儲在變量對象中的值複製一份放到爲新變量分配的空間中,這個新變量的值是原對象的地址,也指向原對象。不一樣的是,這個值的副本其實是一個指針,而這個指針指向存儲在堆中的一個對象。複製操做結束後,兩個變量實際上引用同一個對象,所以改變其中一個變量,就會影響另一個變量。
var person = {
name:'Tom'
};function obj(peo){
peo.name = 'Jerry';
return peo;
}var result = obj(person);
console.log(result.name);//Jerry
console.log(person.name);//Jerry
在上面的例子中,把person(person是一個指向{name:‘Tom’}這個對象的指針)複製傳入obj()中並賦值給了局部變量peo(按值傳遞),peo和person指向了同一個對象,而在obj()函數中peo指針經過引用修改了對象中的name屬性,其實修改了person與peo共同指向的對象的name屬性,相對應的外部person引用的name屬性也就改變了,因此打印出來的爲Jerry。
咱們再看一個例子:
(3)共享類型值的傳遞
var person = {
name:'Tom'
};function obj(peo){
peo = {
name:'Jerry'
};
return peo;
}var result = obj(person);
console.log(result.name);//Jerry
console.log(person.name);//Tom
在上述例子中,與前一個並無什麼不一樣,咱們只是在函數內對peo變量進行了從新賦值,將一個新對象的引用地址賦值給了peo,因此peo指向的是函數內部建立的新對象,和所有變量person指向老的對象是不同的。
注:
(1)引用傳遞:指在調用函數時,即傳遞的是原對象引用的一個副本(原對象的地址),可是這個副本跟原對象的引用指向的都是內存中的對象!
(2)共享傳遞:在共享傳遞中對函數形參的直接賦值,也就是給形參直接賦值後,形參的值再也不是原對象的地址了,指向了另一個對象,所以不會影響實參的值,即原對象的值。
Arguments 對象?
JavaScript 函數有個內置的對象 arguments 對象。
javascript中的arguments對象
在js中一切都是對象,連函數也是對象,函數名實際上是引用函數定義對象的變量。
1、什麼是arguments?
這個函數體內的arguments很是特殊,其實是所在函數的一個內置類數組對象,能夠用數組的[i]和.length。arguments對象是比較特別的一個對象,其實是當前函數的一個內置屬性。arguments很是相似Array,但實際上又不是一個Array實例。
arguments.length是有實參決定,即函數調用時候裏面的參數個數決定!
arguments對象的長度是由實參個數而不是形參個數決定的。形參是函數內部從新開闢內存空間存儲的變量,可是其與arguments對象內存空間並不重疊。對於arguments和值都存在的狀況下,二者值是同步的,可是針對其中一個無值的狀況下,對於此無值的情形值不會得以同步。以下代碼能夠得以驗證。
1 function f(a, b, c){
2 alert(arguments.length); // result: "2"
3 a = 100;
4 alert(arguments[0]); // result: "100"
5 arguments[0] = "qqyumidi";
6 alert(a); // result: "qqyumidi"
7 alert(c); // result: "undefined"
8 c = 2012;
9 alert(arguments[2]); // result: "undefined"
10 }
11
12 f(1, 2);
由JavaScript中函數的聲明和調用特性,能夠看出JavaScript中函數是不能重載的。
根據其餘語言中重載的依據:"函數返回值不一樣或形參個數不一樣",咱們能夠得出上述結論:
第一:Javascript函數的聲明是沒有返回值類型這一說法的;
第二:JavaScript中形參的個數嚴格意義上來說只是爲了方便在函數中的變量操做,實際上實參已經存儲在arguments對象中了。
另外,從JavaScript函數自己深刻理解爲何JavaScript中函數是不能重載的:在JavaScript中,函數其實也是對象,函數名是關於函數的引用,或者說函數名自己就是變量。對於以下所示的函數聲明與函數表達式,其實含義上是同樣的(在不考慮函數聲明與函數表達式區別的前提下),很是有利於咱們理解JavaScript中函數是不能重載的這一特性。
function f(a){
return a + 10;
}
function f(a){
return a - 10;}
// 在不考慮函數聲明與函數表達式區別的前提下,其等價於以下
var f = function(a){
eturn a + 10;}
var f = function(a){
return a - 10;}
2、有什麼做用?
js語法不支持重載!但可用arguments對象模擬重載效果。
arguments對象:函數對象內,自動建立的專門接收全部參數值得類數組對象。
arguments[i]: 得到傳入的下標爲i的參數值
arguments.length: 得到傳入的參數個數!
重載:
程序中可定義多個相同函數名,不一樣參數列表的函數,
調用者沒必要區分每一個函數的參數,
執行時,程序根據傳入的參數個數,自動判斷選擇哪一個函數執行。
例子以下:
// 1、若是用戶傳入一個參數,求平方
function sum(a){
console.log(a*a);
}
//若是用戶傳入兩個參數,就求和
function sum(a,b){
console.log(a+b);
}
sum(4); //?
sum(4,5); //?
上述例子中本意是想讓同名函數sum()根據參數不一樣輸出不一樣結果,可是sum是函數名字,本質也是個變量,
第二個會覆蓋第一個,因此上述的正確輸出答案是:NaN,9.因此這樣顯然不能夠。
若是用arguments,就簡單多了。
以下2個例子:
//2、
function calc(){
//若是用戶傳入一個參數,求平方
if(arguments.length==1){
console.log(arguments[0]*arguments[0]);
}else if(arguments.length==2){
//若是用戶傳入兩個參數,就求和
console.log(arguments[0]+arguments[1]);
}
}
calc(4); //16
calc(4,5); //9
/*3、不管用戶傳入幾個數字,均可以求和*/
function add(){
//arguments:[]
//遍歷arguments中每一個元素,並累加
for(var i=0,sum=0;i<arguments.length;sum+=arguments[i++]);
return sum;//返回和 }
console.log(add(1,2,3)); //6
console.log(add(1,2,3,4,5,6)); //21
這就是JS利用arguments重載的效果,簡單理解就是一個函數重複利用.
arguments.length是有實參決定,即函數調用時候裏面的參數個數決定!
4、arguments對象中有一個很是有用的屬性:callee。arguments.callee返回此arguments對象所在的當前函數引用。在使用函數遞歸調用時推薦使用arguments.callee代替函數名自己。
以下:
function count(a){
if(a==1){
return 1;}
return a + arguments.callee(--a);
}
var mm = count(10);
alert(mm);
函數式編程的特性,??
函數的調用?
javascript的執行函數的四種方式
javascript的函數調用和構造函數調用
1 函數調用
Function絕對是JavaScript中的重中之重。在JavaScript中,Function承擔了procedures, methods, constructors甚至是classes以及modules的功能。
在面向對象程序設計中,functions,methods以及class constructor每每是三件不一樣的事情,由不一樣的語法來實現。可是在JavaScript中,這三個概念都由function來實現,經過三種不一樣的模式。
最簡單的使用模式就是function 調用:
function hello(username) {
return "hello, " + username;
}
hello("Keyser Söze"); // "hello, Keyser Söze"
/*函數調用模式*/
var add=function(a,b){
alert(this)//this被綁頂到window
return a+b;
}
var sum=add(3,4);
alert(sum)
2 方法調用
而methods這一律念在JavaScript中的表現就是,一個對象的屬性是一個function,一樣的是函數,將其賦值給一個對象的成員之後,就不同了。將函數賦值給對象的成員後,那麼這個就不在稱爲函數,而應該叫作方法。
var obj = {
hello: function() {
return "hello, " + this.username;
},
username: "Hans Gruber"
};
obj.hello(); // "hello, Hans Gruber"
/*方法調用模式*/
var myobject={
value:0,
inc:function(){
alert(this.value)
}
}
myobject.inc()
真正的行爲是,調用自己纔會決定this會綁定到哪一個對象,即:
obj1.hello()會將this綁定到obj1,obj2.hello()則會將this綁定到obj2。記住一句話,誰是最後調用方法的,this就指向誰。
正由於this綁定的這種規則,在下面的用法也是可行的:
function hello() {
return "hello, " + this.username;
}
var obj1 = {
hello: hello,
username: "Gordon Gekko"
};
obj1.hello(); // "hello, Gordon Gekko"
var obj2 = {
hello: hello,
username: "Biff Tannen"
};_
obj2.hello(); // "hello, Biff Tannen"
可是,在一個普通的函數中,如上面的hello函數,使用this關鍵字是不太好的方式,當它被直接調用的時候,this的指向就成了問題。在這種狀況下,this每每被指向全局對象(GlobalObject),在瀏覽器上通常就是window對象。
而這種行爲是不肯定和沒有意義的。
因此在ES5標準中,若是使用了strict mode,那麼this會被設置爲undefined:
function hello() {
"use strict";
return "hello, " + this.username;
}
hello(); // error: cannot read property "username" of undefined
以上這種作法是爲了讓潛在的錯誤更快的暴露出來,避免了誤操做和難以找到的bug。
區別普通函數調用和方法調用,直接看這個例子就明確了。
var func = function() {
alert(this);
};
var o = {};
o.fn = func;//」=」號給o對象自身添加一個屬性,並把一個函數賦值給該屬性,也便是方法。
// 比較
alert(o.fn === func);//true
// 調用
func();//[object Window]
o.fn();//[object Object]
這裏的運行結果是,兩個函數是相同的,所以打印結果是 true。可是因爲兩個函數的調用是不同的,func 的調用,打印的是 [object Window],而o.fn 的打印結果是 [object Object]。
這裏即是函數調用與方法調用的區別,函數調用中,this 專指全局對象 window,而在方法中 this 專指當前對象,即 o.fn 中的 this 指的就是對象o。
3 構造函數
function的第三種使用模式就是講它做爲constructor:
構造器中的this
咱們須要分析建立對象的過程,方能知道this的意義. 以下面代碼:
/*構造器調用模式 摒棄*/
var quo=function(string){
this.status=string;
}
quo.prototype.get_status=function(){
return this.status;
}
var qq=new quo("aaa");
alert(qq.get_status());
var Person = function() {
this.name = "小平果";
};
var p = new Person();
這裏首先定義了函數Person,下面分析一下整個執行:
程序在執行到第一句的時候,不會執行函數體,所以 JavaScript的解釋器並不知道這個函數的內容.
接下來執行new關鍵字,建立對象,解釋器開闢內存,獲得對象的引用,將新對象的引用交給函數.
緊接着執行函數,將傳過來的對象引用交給this. 也就是說,在構造方法中,this就是剛剛被new建立出來的對象.
而後爲this 添加成員,也就是爲對象添加成員.
最後函數結束,返回this,將this交給左邊的變量.
分析過構造函數的執行之後,能夠獲得,構造函數中的this就是當前對象.
構造器中的return
在構造函數中return的意義發生了變化,首先若是在構造函數中,若是返回的是一個對象,那麼就保留原意. 若是返回的是非對象,好比數字、布爾和字符串,那麼就返回this,若是沒有return語句,那麼也返回this. 看下面代碼:
// 返回一個對象的 return
var ctr = function() {
this.name = "趙曉虎";
return {
name:"牛亮亮"
};
};
// 建立對象
var p = new ctr();
// 訪問name屬性
alert(p.name);
//執行代碼,這裏打印的結果是」牛亮亮」. 由於構造方法中返回的是一個對象,那麼保留return的意義,返回內容爲return後面的對象. 再看下面代碼:
// 定義返回非對象數據的構造器
var ctr = function() {
this.name = "趙曉虎";
return "牛亮亮";
};
// 建立對象
var p = new ctr();
// 使用
alert(p);
alert(p.name);
代碼運行結果是,先彈窗打印[object Object],而後打印」趙曉虎」. 由於這裏 return 的是一個字符串,屬於基本類型,那麼這裏的return語句無效,返回的是this對象. 所以第一個打印的是[object Object]而第二個不會打印undefined.
function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
var u = new User("sfalken",
"0ef33ae791068ec64b502d6cb0191387");
u.name; // "sfalken"
使用new關鍵將function做爲constructor進行調用。和function以及method調用不同的是,constructor會傳入一個新的對象並將它綁定到this,而後返回該對象做爲constructor的返回值。而constructor function自己的做用就是爲了初始化該對象。
構造函數調用常犯的一個錯誤
興致勃勃地定義了下面這麼個構造函數:
var Coder = function( nick ){
this.nick = nick;
};
定義構造函數結束後呢?沒錯,趕忙實例化:
var coder = Coder( 'casper' );
- 1
這個coder兄弟叫什麼名字?趕忙打印下:
console.log( coder.nick ); //undefined
居然是undefined!!再回過頭看看實例化的那個語句,不難發現問題出在哪裏:少了個new
var coder = Coder( 'casper' ); //看成普通的函數來調用,故內部的this指針其實指向window對象
console.log( window.nick); //輸出:casper
var coder = new Coder( 'casper' ); //加上new,一切皆不一樣,this正確地指向了當前建立的實例
console.log( coder.nick ); //輸出:casper
這樣的錯誤貌似挺低級的,但出現的機率挺高的,腫麼去避免或減小這種狀況的發生呢?
能夠在內部實現裏面動下手腳:
var Coder = function( nick ){
if( !(this instanceof Coder) ){
return new Coder( nick );
}
this.nick = nick;
};
其實很簡單,實例化的時候,內部判斷下,當前this指向的對象的類型便可,若是非當前構造函數的類型,強制從新調用一遍構造函數。
忽然以爲Coder這名字不夠洋氣?想用Hacker,好吧,我改。。。數了下,一共有三處要改,這不科學,有沒有辦法只把構造函數的名字改了就行?
固然有:
var Coder = function( nick ){
if( !(this instanceof arguments.callee) ){
return new arguments.callee( nick );
}
this.nick = nick;
};
4 間接調用
在 JavaScript 中, 函數是對象。JavaScript 函數有它的屬性和方法。
call() 和 apply() 是預約義的函數方法。 兩個方法可用於調用函數,兩個方法的第一個參數必須是對象自己。
區分apply,call就一句話,
foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
1
例如:
A, B類都有一個message屬性(面向對象中所說的成員),
A有獲取消息的getMessage方法,
B有設置消息的setMessage方法,
var b = new B();
//給對象a動態指派b的setMessage方法,注意,a自己是沒有這方法的!
b.setMessage.call(a, "a的消息");
//下面將顯示"a的消息"
alert(a.getMessage());
//給對象b動態指派a的getMessage方法,注意,b自己也是沒有這方法的!
alert(b.setMessage());
這就是動態語言 JavaScript call的威力所在!
簡直是」無中生有」,對象的方法能夠任意指派,而對象自己一直都是沒有這方法的,注意是指派,通俗點就是,方法是借給另外一個對象的調用去完成任務,原理上是方法執行時上下文對象改變了.
因此 b.setMessage.call(a, 「a的消息」); 就等於用a做執行時上下文對象調用b對象的setMessage方法,而這過程當中與b一點關係都沒有, 做用等效於a.setMessage( 「a的消息」);
call, apply做用就是借用別人的方法來調用,就像調用本身的同樣.
好,理解了call, apply相同處—–做用後,再來看看它們的區別,看過上面例子,相信您大概知道了.
從 b.setMessage.call(a, 「a的消息」) 等效於 a.setMessage( 「a的消息」) 能夠看出, 「a的消息」在call中做爲一個參數傳遞,
call, apply方法區別是,從第二個參數起, call方法參數將依次傳遞給借用的方法做參數, 而apply直接將這些參數放到一個數組中再傳遞, 最後借用方法的參數列表是同樣的.當參數明確時可用call, 當參數不明確時可用apply給合arguments