亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

函數(shù)以及函數(shù)作用域詳解

ShowerSun / 1145人閱讀

摘要:閉包是一個擁有自由訪問另一個函數(shù)作用域的表達式通常是一個函數(shù)。意味著內部函數(shù)擁有外部函數(shù)的作用域。所以通過函數(shù)構造方式分構造函數(shù)無論何時都是應該被避免的。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions_and_function_scope
Javascript 函數(shù) 函數(shù)作用域

概述

通常來說,函數(shù)就是一個可以被外部調用的(或者函數(shù)本身遞歸調用)的“子程序”。和程序本身一樣,一個函數(shù)的函數(shù)體是由一系列的語句組成的,函數(shù)可以接受函數(shù),也可以返回一個返回值。

在Javascript中函數(shù)也是對象,也可以像其他對象一樣,添加屬性和方法等。具體來講,它們是Function對象。

用法簡介

在Javascript中,每個函數(shù)其實都是一個Function對象,查看Function界面,了解Function屬性和方法。

要想返回一個返回值,函數(shù)必須使用return語句指定所要返回的值(使用new關鍵詞的構造函數(shù)除外)。如果一個函數(shù)沒有return語句,則它默認返回undefined。

調用函數(shù)時,傳入函數(shù)的值為函數(shù)的實參,對應位置的函數(shù)參數(shù)名為形參。如果實參是一個包含原始值(數(shù)字,字符串,布爾值)的變量,則就算函數(shù)在內部改變了形參的值,返回后該實參的值也不會變壞。如果實參是一個對象引用,則對應形參和實參會指向同一個對象,如果函數(shù)內部形參引用對象的值發(fā)生了改變,則實參指向的引用對象的值也會改變:

/*定義函數(shù) myFunc*/
function myFunc(theObject) {
    theObject.brand = "Toyota";
}

/*
 *定義變量 mycar;
 *創(chuàng)建并初始化一個對象;
 *將對象的引用賦值給變量mycar;
 */
var mycar = {
    brand : "Honda",
    model : "Accord",
    year : 1998
};

window.alert(mycar.brand);  //彈出“Honda”

myFunc(mycar);

window.alert(mycar.brand);  //彈出“Toyota”

函數(shù)執(zhí)行時,this不會指向正在運行的函數(shù)本身,而是指向調用該函數(shù)的對象。如果你想在函數(shù)內部獲取自身的引用,只能使用函數(shù)名或者arguments.callee屬性。

定義函數(shù)

定義函數(shù)一共有三種方法:

函數(shù)聲明(function語句)

有一個特別的語法來聲明函數(shù):

    function name([param,[param,[...param]]]) {
        //statements 
}

name
函數(shù)名

param
函數(shù)的參數(shù)名稱,一個函數(shù)最多有255個參數(shù)。

statements
函數(shù)體內執(zhí)行的語句

函數(shù)表達式(function操作符)

函數(shù)表達式很函數(shù)聲明有著很多相同之處,他們甚至有著相同的語法:

function[name]([param,[param,[...param]]]) {
        //statements
}

name
可以省略

param
函數(shù)的參數(shù)名稱,一個函數(shù)最多有255個參數(shù)。

statements
函數(shù)體內執(zhí)行的語句

Function構造函數(shù)

和其他類型一樣,F(xiàn)unction對象可以使用new操作符來創(chuàng)建:

[new] Function (arg1, arg2, ... argN, functionBody)

arg1, arg2, ... argN
一個或多個變量名稱,來作為函數(shù)的形參名.類型為字符串,值必須為一個合法的JavaScript標識符,例如Function("x", "y","alert(x+y)"),或者逗號連接的多個標識符,例如Function("x,y","alert(x+y)")

functionBody
一個字符串,包含了組成函數(shù)的函數(shù)體的一條或多條語句.
即使不使用new操作符,直接調用Function函數(shù),效果也是一樣的,仍然可以正常創(chuàng)建一個函數(shù).

注意: 不推薦使用Function構造函數(shù)來創(chuàng)建函數(shù).因為在這種情況下,函數(shù)體必須由一個字符串來指定.這樣會阻止一些JS引擎做程序優(yōu)化,還會引起一些其他問題.

arguments對象

在函數(shù)內部,你可以使用arguments對象獲取到該函數(shù)的所有傳入?yún)?shù). 查看 arguments.

作用域和函數(shù)堆棧

調用其他函數(shù)時的作用域部分和函數(shù)部分

遞歸

函數(shù)能指向并引用它自己,有三種引用方法引用函數(shù)它自己:

通過函數(shù)名

arguments.callee

指向該函數(shù)的變量

考慮到一下的函數(shù)定義:

var foo = function bar() {
    //statements
}

在這個函數(shù)內部,下面就是上面所說的三種:

bar()

arguments.callee

foo()

一個函數(shù)調用它自己叫做遞歸函數(shù)。在某些方面,遞歸函數(shù)像一個循環(huán)(loop),同樣的代碼多次執(zhí)行同時有一個控制語句(為了避免無限循環(huán)),比如下面的循環(huán):

var x = 0;
while (x < 10){
    x++
};
console.log(x);    //輸出10

能變換為迭代函數(shù)并調用該函數(shù):

function loop(x) {
    if(x>=10){console.log(x);}
    else{loop(x+1);}
}
loop(0);    //返回10

然而,一些算法并不是簡單的迭代循環(huán)。比如,獲得DOM節(jié)點使用遞歸就更方便:

function walkTree(node) {
    if (node == null) return;
    for(var i = 0; i < node.childNodes.length; i++){
        walkTree(node.childNodes[i]);
    }
}

比較這些循環(huán)方程,每一個遞歸調用都會調用更多的調用。
將遞歸算法轉換成非遞歸算法大多是能實現(xiàn)的,但是經常邏輯會變得更加復雜并且要使用更多的堆棧,事實上,遞歸本身就使用堆棧:函數(shù)堆棧。
這種類似堆棧的行為能在下圖中看到:

function foo(i){
    if(i<0) return;
    console.log("begin: " + i);
    foo(i - 1);
    console.log("end: " + i);
}
foo(3);
嵌套函數(shù)和閉包

你可以將一個函數(shù)嵌套在另一個函數(shù)內部,被嵌套函數(shù)只是他外部函數(shù)的一個私有函數(shù),這種情況我們將它叫做閉包。

閉包是一個擁有自由訪問另一個函數(shù)作用域的表達式(通常是一個函數(shù))。

當一個被嵌套函數(shù)是一個閉包時,意味著一個被嵌套函數(shù)能繼承它的外部函數(shù)的參數(shù)(arguments)和變量(variables)。意味著內部函數(shù)擁有外部函數(shù)的作用域。
總而言之:

內部函數(shù)能擁有訪問外部函數(shù)的語句

內部的函數(shù)定義了一個閉包:內部函數(shù)能使用外部函數(shù)的參數(shù)(arguments)和變量(variables),但是外部函數(shù)不能使用內部函數(shù)的參數(shù)(arguments)和變量(variables)。
下例演示一個函數(shù)閉包:

function addSquares(a,b){
    function square(x){
        return x*x;
        }
    return square(a)+square(b);
}
console.log(addSquares(1,2));      //返回5

由于內部函數(shù)形成了一個閉包,你可以把內部函數(shù)當作返回值返回:

function outSide(x){
    function inSide(y){
        return x + y;
    }
    return inSide;
}
fnInside = outSide(1);
console.log(fnInside(2));       //返回3
console.log(outSide(2)(10));       //返回12
變量的保存

注意,x變量是如何在inside函數(shù)返回后被保存的。一個閉包必須保存它的作用域中的參數(shù)和變量。每一次調用有可能提供不同的參數(shù),這樣一個新的閉包都會在外層調用時被創(chuàng)建,只有當要返回的內部函數(shù)不再被訪問時內存才會被清空。
這和保存來自其他對象的引用沒有區(qū)別,但是后一種情況更加少見因為一個函數(shù)不會直接設置引用并且不能檢查它們。

多層嵌套

函數(shù)能多層嵌套,A函數(shù)中B函數(shù),B函數(shù)中包含C函數(shù)。在這個函數(shù)里B和C都是閉包。因此B函數(shù)能訪問A函數(shù),C函數(shù)能訪問B函數(shù)。并且,C函數(shù)也能訪問A函數(shù)。這樣看,閉包能包含多層嵌套。它們遞歸的包含外層的作用域,這種情況我們稱之為作用域鏈。
例子如下:

function A(x){
    function B(y){
        function C(z){
            console.log(x + y + z);
        }
        C(2);
    }
    B(6);
}
A(1);

此例中C能訪問B中y和A中的x,這些之所以能成立是因為:

1.B為A的閉包,B能訪問A的變量和參數(shù)。
2.C為B的閉包
3.因為B包含A的作用域,所以C也包含A的作用域。換言之,C包含B和A的作用域鏈。

然而反過來卻不行,A不能訪問B,B也不能訪問C

命名沖突

當閉包中兩個變量或者參數(shù)擁有相同的名字,這種情況叫做命名沖突。越內層的函數(shù)擁有越高的優(yōu)先權,最內層的作用域擁有最高的優(yōu)先權,最外層的作用域的優(yōu)先權最低。作用域鏈的原理如此。作用域最前端是最內層的作用域,最后是最外層??紤]如下例子

function outside(){
    var x = 10;
    function inside(x){
    return x;
    }
    return inside;
}
result = outside()(20);       //返回20

上例中的命名沖突發(fā)生在return的x是返回inside里的參數(shù)x還是外層代碼的x,此處的作用域鏈為(inside,outside,全局),然而內層的x優(yōu)先級比外層的x優(yōu)先級高,于是返回的是內層的x。

函數(shù)構造器vs.函數(shù)聲明vs.函數(shù)表達式

比較如下:
1.用函數(shù)構造器構造函數(shù)并添加參數(shù):

var multiply = new Function("x", "y", "return x*y;");

2.函數(shù)聲明:

function multiply(x, y) {
    return x*y; 
}

3.函數(shù)表達式:

var multiply = function(x, y) {
    return x*y;
}

4.函數(shù)命名表達式

var multiply = function func_name(x, y) {
    return x*y;
}

上面的代碼大約都是干的同樣的事情,除了一些微妙的區(qū)別:

函數(shù)名和函數(shù)指定的變量名是有區(qū)別的:

函數(shù)名是不能改變的,函數(shù)指定變量名是可以改變的

函數(shù)名的使用只能和函數(shù)體一起使用,如果嘗試在函數(shù)體外使用函數(shù)名的話會報錯error或者返回undefined。比如:

y = function x(){};
alert(x);

另一方面,函數(shù)指定變量名(比如最后一例中的multiply)只被它自身(變量名)的作用域所約束,這個作用域一定包含函數(shù)聲明的作用域。

如同以上四個例子所示,函數(shù)名與函數(shù)指定變量名不同。它們彼此之間沒有關聯(lián)。

函數(shù)聲明同樣會創(chuàng)建一個和函數(shù)名一樣的變量。不像那些在函數(shù)表達式中定義的那樣,在函數(shù)聲明中定義的函數(shù)能在它們定義的作用域中通過它們的命名被訪問:

function x(){
}
console.log(x);   //返回function(){};

接下來的例子會展示函數(shù)名和函數(shù)指定變量是如何無關的。即使函數(shù)名被指定給了其他的變量名,返回的仍然是函數(shù)名:

function foo(){};
console.log(foo);
var bar = foo;
console.log(bar);

一個由“new Function()”創(chuàng)建的新函數(shù)是沒有函數(shù)名的。在spiderMonkey的javascript引擎中,如果函數(shù)名為“anonymous”序列化的函數(shù)將會顯示,比如下例中使用“console.log(new Function())”的輸出:

function anonymous(){};
console.log(new Function());  //返回“function anonymous(){}” 

不像函數(shù)表達式定義的函數(shù)調用必須在函數(shù)表達式后面,函數(shù)聲明的函數(shù)調用可以在函數(shù)的前面,如下:

foo();
function foo(){
    alert("FOOO");
}

函數(shù)表達式中定義的函數(shù)會繼承當前的作用域。就是說該函數(shù)是一個閉包。而使用Function創(chuàng)建的函數(shù)不會繼承任何的作用域除了全局作用域(所有的函數(shù)都會繼承)。

函數(shù)表達式和函數(shù)聲明定義的函數(shù)只會解析一次,但那些使用Function構造的卻不是。就是說,通過new Function方式構建函數(shù)時內部的字符每一次都會重解析,函數(shù)本身不會重分析,于是函數(shù)表達式比“new Function(...)”這種方式要快。所以通過函數(shù)構造方式分構造函數(shù)無論何時都是應該被避免的。這應該被記下來,然而,函數(shù)表達式和函數(shù)聲明嵌套在通過函數(shù)構造器構造的中且立即執(zhí)行就不會重復解析多次,如下例:

var foo = (new Function("var bar="fooo!";
return(function(){
	alert("bar");
})"))();
foo();

函數(shù)聲明通常會很輕易的變成函數(shù)表達式,函數(shù)聲明不再是函數(shù)聲明的原因可能是因為它滿足以下兩個條件:
1.變成了表達式的一部分。
2.它不再是函數(shù)的“資源元素”,一個“資源元素”是腳本或者函數(shù)中的一段非嵌套語句:

var x = 0;                  //資源元素
if (x == 0) {               //資源元素
    x = 10;                 //不是資源元素
    function boo() {}       //不是資源元素
}
function foo(){              //資源元素
    var y = 20;              //資源元素
    function bar() {}           //資源元素
    while(y == 10){             //資源元素
        function blah(){}      //不是資源元素
        y++;                   //不是資源元素
    }
}

例如:

//函數(shù)聲明
function foo(){}

//函數(shù)表達式
(function bar(){})

//函數(shù)表達式
var x = function hello(){}
if(x){
    //函數(shù)表達式
    function hello(){}
}
//函數(shù)聲明
function a(){
    //函數(shù)聲明
    function b(){}
    if(0) {
        //函數(shù)表達式
        function c(){}
    }
}
條件執(zhí)行函數(shù)

函數(shù)可以定義在條件語句里通過普通的function語句和new Function語句。但請注意以下這種情況ECMAScript5中不再允許出現(xiàn)函數(shù)語句,所以這個特性在跨瀏覽器中并不能表現(xiàn)的很好,你不能再編程中完全依賴它。
在下面的代碼中,這個zero函數(shù)永遠都不能定義和執(zhí)行。因為if(0)永遠都返回false;

if(0) {
    function zero(){
        document.writeln("This is a zero.");
    }
}

如果這個條件發(fā)生改變,if(1)那么zero就會被定義。

注意:盡管這一類函數(shù)看上去就像是函數(shù)聲明,但是它實際上是函數(shù)表達式。因為它被包含在其他的條件語句中。看函數(shù)表達式和函數(shù)聲明有何不同.

注意:一些javascript的解析器,不包括SpiderMonkey,錯誤的把命名函數(shù)表達式當成了函數(shù)定義。這樣會導致zero會被定義即使if返回的是false。安全的方法是將匿名函數(shù)指定給變量:

if(0) {
    var zero = function(){
        document.writeln("This is zero.");
    }
}   
函數(shù)和事件處理程序

在JavaScript中, DOM事件處理程序只能是函數(shù)(相反的,其他DOM規(guī)范的綁定語言還可以使用一個包含handleEvent方法的對象做為事件處理程序)(譯者注:這句話已經過時,目前在大部分非IE[6,7,8]的瀏覽器中,都已經支持一個對象作為事件處理程序). 在事件被觸發(fā)時,該函數(shù)會被調用,一個 event 對象會作為第一個也是唯一的一個參數(shù)傳入該函數(shù).像其他參數(shù)一樣,如果不需要使用該event對象, 則對應的形參可以省略.

通常的HTML事件對象包括:window(Window對象,包括frames),document(HTMLdocument對象和elements(各種元素對象)。在HTMLDOM中,事件對象擁有時間處理程序屬性,這個屬性通常是小寫的有on前綴的,比如:onfocus。一個更加靈活的添加事件對象的方法有DOM2級事件。

注意:事件是DOM的一部分,而不是javascript的一部分。

下例會個window對象的onfocus事件綁定一個函數(shù):

window.onfocus = function() {
    document.body.style.backgroundColor = "blue";
}

如果函數(shù)綁定到了變量上,那么可以將事件指向該變量。如下:

var setBgColor = new Function("document.body.style.backgroundColor = "while" ");

你可以像如下的方法那樣使用它們:
1.給DOM的事件屬性指向變量:

document.form1.colorbutton.onclick = setBgColor;

2.HTML標簽屬性:

上例在執(zhí)行過程中的效果如下代碼:

document.form1.colorbutton.onclick = function onclick(){
    setBgColor();
}

注意:怎樣在HTML中調用一個onclick返回事件的屬性。可以像如下這樣使用:


就像其他的參數(shù)參考的函數(shù)一樣,事件處理程序同樣是方法,在函數(shù)中返回的this對象會指向調用該方法的對象。比如:

window.onfocus

上例中在onfocus上綁定的事件處理程序的this對象就會指向window對象。
給事件綁定的有傳參的事件處理程序,必須被包含在其他的函數(shù)中:

document.form1.button1.onclick = function(){
    setBgColor("some color");
}
函數(shù)的局部變量

arguments:一個"類數(shù)組"的對象,包含了傳入當前函數(shù)的所有實參;
arguments.callee:指向當前函數(shù);
arguments.caller:指向調用當前函數(shù)的函數(shù),請使用arguments.callee.caller代替;
arguments.length:arguments對象的中元素的個數(shù)。

同步于個人博客:http://penouc.com

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/78097.html

相關文章

  • 閉包詳解

    摘要:函數(shù)中的閉包閉包是指有權訪問另一個函數(shù)作用域中的變量的函數(shù)。理解閉包預與變量此時返回注意觀察下面的輸出內容,理解函數(shù)的調用時刻和把的賦值給變量時刻這個函數(shù)會返回長度為的函數(shù)數(shù)組。 Javascript函數(shù)中的閉包 閉包是指有權訪問另一個函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式就是,在一個函數(shù)的內部創(chuàng)建另一個函數(shù)。 有關創(chuàng)建作用域鏈以及作用域鏈有什么作用的細節(jié)對于徹底理解閉包至關重...

    lunaticf 評論0 收藏0
  • JavaScript的作用詳解

    摘要:而閉包的神奇之處正是可以阻止這件事情的發(fā)生。事實上內部作用域依然存在,因此沒有被回收頻繁使用閉包可能導致內存泄漏。拜所聲明的位置所賜,它擁有涵蓋內部作用域的閉包,使得該作用域能夠一直存活,以供在之后任何時間進行引用。 作用域 作用域(scope),程序設計概念,通常來說,一段程序代碼中所用到的變量并不總是有效/可用的,而限定這個變量的可用性的代碼范圍就是這個變量的作用域。通俗一點就是我...

    cnio 評論0 收藏0
  • 詳解js中的閉包

    摘要:定義函數(shù)的時候,為什么的值重新從開始了因為又一次運行了函數(shù),生成一個新的的活動對象,所以的作用域鏈引用的是一個新的值。 前言 在js中,閉包是一個很重要又相當不容易完全理解的要點,網(wǎng)上關于講解閉包的文章非常多,但是并不是非常容易讀懂,在這里以《javascript高級程序設計》里面的理論為基礎。用拆分的方式,深入講解一下對于閉包的理解,如果有不對請指正。 寫在閉包之前 閉包的內部細節(jié),...

    chaosx110 評論0 收藏0
  • JavaScript深入淺出

    摘要:理解的函數(shù)基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統(tǒng)的類繼承還要強大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...

    blair 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<