欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript高級程序設計(第3版)學習筆記8 js函數(shù)(中)

 更新時間:2012年10月11日 14:48:44   作者:  
接著看函數(shù)——這個具有魔幻色彩的對象。在上篇文章中說函數(shù)內(nèi)部屬性時,還遺留了一個this內(nèi)部屬性沒有解釋,不過在說this之前,我想先說一說執(zhí)行環(huán)境和作用域的概念
6、執(zhí)行環(huán)境和作用域

(1)執(zhí)行環(huán)境(execution context):所有的JavaScript代碼都運行在一個執(zhí)行環(huán)境中,當控制權轉(zhuǎn)移至JavaScript的可執(zhí)行代碼時,就進入了一個執(zhí)行環(huán)境?;顒拥膱?zhí)行環(huán)境從邏輯上形成了一個棧,全局執(zhí)行環(huán)境永遠是這個棧的棧底元素,棧頂元素就是當前正在運行的執(zhí)行環(huán)境。每一個函數(shù)都有自己的執(zhí)行環(huán)境,當執(zhí)行流進入一個函數(shù)時,會將這個函數(shù)的執(zhí)行環(huán)境壓入棧頂,函數(shù)執(zhí)行完之后再將這個執(zhí)行環(huán)境彈出,控制權返回給之前的執(zhí)行環(huán)境。

(2)變量對象(variable object):每一個執(zhí)行環(huán)境都有一個與之對應的變量對象,執(zhí)行環(huán)境中定義的所有變量和函數(shù)就是保存在這個變量對象中。這個變量對象是后臺實現(xiàn)中的一個對象,我們無法在代碼中訪問,但是這有助于我們理解執(zhí)行環(huán)境和作用域相關概念。

(3)作用域鏈(scope chain):當代碼在一個執(zhí)行環(huán)境中運行時,會創(chuàng)建由變量對象組成的一個作用域鏈。這個鏈的前端,就是當前代碼所在環(huán)境的變量對象,鏈的最末端,就是全局環(huán)境的變量對象。在一個執(zhí)行環(huán)境中解析標識符時,會在當前執(zhí)行環(huán)境相應的變量對象中搜索,找到就返回,沒有找到就沿著作用域鏈一級一級往上搜索直至全局環(huán)境的變量對象,如果一直未找到,就拋出引用異常。

(4)活動對象(activation object):如果一個執(zhí)行環(huán)境是函數(shù)執(zhí)行環(huán)境,也將變量對象稱為活動對象?;顒訉ο笤谧铋_始只包含一個變量,即arguments對象(這個對象在全局環(huán)境的變量對象中不存在)。

  這四個概念雖然有些抽象,但還是比較自然的,可以結合《JavaScript高級程序設計(第3版)》中的一個例子來細細體會一下:
復制代碼 代碼如下:

// 進入到全局作用域,創(chuàng)建全局變量對象
var color = "blue";

function changeColor(){
// 進入到changeColor作用域,創(chuàng)建changeColor相應變量對象
var anotherColor = "red";

function swapColors(color1, color2){
// 進入到swapColors作用域,創(chuàng)建swapColors相應變量對象
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
/*
* swapColors作用域內(nèi)可以訪問的對象有:
* 全局變量對象的color,changeColor
* changeColor函數(shù)相應變量對象的anotherColor、swapColors
* swapColors函數(shù)相應變量對象的tempColor
*/
}
swapColors('white');
/*
* changeColor作用域內(nèi)可以訪問的對象有:
* 全局變量對象的color,changeColor
* changeColor函數(shù)相應變量對象的anotherColor、swapColors
*/
}

changeColor();
/*
* 全局作用域內(nèi)可以訪問的對象有:
* 全局變量對象的color,changeColor
*/

這里的整個過程是:

(1)進入全局環(huán)境,創(chuàng)建全局變量對象,將全局環(huán)境壓入棧頂(這里也是棧底)。根據(jù)前面的關于聲明提升的結論,這里創(chuàng)建全局變量對象可能的一個過程是,先創(chuàng)建全局變量對象,然后處理函數(shù)聲明設置屬性changeColor為相應函數(shù),再處理變量聲明設置屬性color為undefined。

(2)執(zhí)行全局環(huán)境中的代碼。先執(zhí)行color變量初始化,賦值為'blue',再調(diào)用changeColor()函數(shù)。

(3)調(diào)用changeColor()函數(shù),進入到changeColor函數(shù)執(zhí)行環(huán)境,創(chuàng)建這個環(huán)境相應的變量對象(也就是活動對象),將這個環(huán)境壓入棧頂。創(chuàng)建活動對象可能的一個過程是,先創(chuàng)建活動對象,處理內(nèi)部函數(shù)聲明設置屬性swapColors為相應函數(shù),處理函數(shù)參數(shù)創(chuàng)建活動對象的屬性arguments對象,處理內(nèi)部變量聲明設置屬性anotherColor為undefined。

(4)執(zhí)行changeColor()函數(shù)代碼。先執(zhí)行anotherColor初始化為'red',再調(diào)用swapColors()函數(shù)。

(5)調(diào)用swapColors()函數(shù),進入到swapColors函數(shù)執(zhí)行環(huán)境,創(chuàng)建相應的變量對象(活動對象),將swapColors執(zhí)行環(huán)境壓入棧頂。這里創(chuàng)建活動對象可能的一個過程是,先創(chuàng)建活動對象,處理函數(shù)參數(shù),將形式參數(shù)作為活動對象的屬性并賦值為undefined,創(chuàng)建活動對象的屬性arguments對象,并根據(jù)實際參數(shù)初始化形式參數(shù)和arguments對應的值和屬性(將屬性color1和arguments[0]初始化為'white',由于沒有第二個實際參數(shù),所以color2的值為undefined,而arguments的長度只為1了),處理完函數(shù)參數(shù)之后,再處理函數(shù)內(nèi)部變量聲明,將tempColor作為活動對象的屬性并賦值為undefined。

(6)執(zhí)行swapColors()函數(shù)代碼。先給tempColor初始化賦值,然后實現(xiàn)值交換功能(這里color和anotherColor的值都是沿著作用域鏈才讀取到的)。

(7)swapColors()函數(shù)代碼執(zhí)行完之后,返回undefined,將相應的執(zhí)行環(huán)境彈出棧并銷毀(注意,這里會銷毀執(zhí)行環(huán)境,但是執(zhí)行環(huán)境相應的活動對象并不一定會被銷毀),當前執(zhí)行環(huán)境恢復成changeColor()函數(shù)的執(zhí)行環(huán)境。隨著swapColor()函數(shù)執(zhí)行完并返回,changeColor()也就執(zhí)行完了,同樣返回undefined,并將changeColor()函數(shù)的執(zhí)行環(huán)境彈出棧并銷毀,當前執(zhí)行環(huán)境恢復成全局環(huán)境。整個處理過程結束,全局環(huán)境直至頁面退出再銷毀。

  作用域鏈也解釋了為什么函數(shù)可以在內(nèi)部遞歸調(diào)用自身:函數(shù)名是函數(shù)定義所在執(zhí)行環(huán)境相應變量對象的一個屬性,然后在函數(shù)內(nèi)部執(zhí)行環(huán)境中,就可以沿著作用域鏈向外上溯一層訪問函數(shù)名指向的函數(shù)對象了。如果在函數(shù)內(nèi)部將函數(shù)名指向了一個新函數(shù),遞歸調(diào)用時就會不正確了:
復制代碼 代碼如下:

function fn(num){
if(1 == num){
return 1;
}else{
fn = function(){
return 0;
};
return num * fn(num - 1);
}
}
console.info(fn(5));//0

關于作用域和聲明提升,再看一個例子:
復制代碼 代碼如下:

var name = 'linjisong';
function fn(){
console.info(name);//undefined
var name = 'oulinhai';
console.info(name);//oulinhai
}
fn();
console.info(name);//linjisong

這里最不直觀的可能是第3行輸出undefined,因為在全局中已經(jīng)定義過name了,不過按照上面解析的步驟去解析一次,就可以得出正確的結果了。另外強調(diào)一下,在ECMAScript中只有全局執(zhí)行環(huán)境和函數(shù)執(zhí)行環(huán)境,相應的也只有全局作用域和函數(shù)作用域,沒有塊作用域——雖然有塊語句。
復制代碼 代碼如下:

function fn(){
var fnScope = 'a';

{
var blockScope = 'b';
blockScope += fnScope;
}
console.info(blockScope);//沒有塊作用域,所以可以在整個函數(shù)作用域內(nèi)訪問blockScope
console.info(fnScope);
}
fn();//ba,a

console.info(blockScope);//ReferenceError,函數(shù)作用域外,不能訪問內(nèi)部定義的變量
console.info(fnScope);//ReferenceError

對于作用域鏈,還可以使用with、try-catch語句的catch塊來延長:

•使用with(obj){}語句時,將obj對象添加到當前作用域鏈的最前端。
•使用try{}catch(error){}語句時,將error對象添加到當前作用域鏈的最前端。
  插了一段較為抽象的概念,希望不至于影響整個閱讀的流暢,事實上,我在這里還悄悄的繞過了一個稱為“閉包”的概念,關于函數(shù)與閉包,在下篇文章中再詳細敘述。

7、函數(shù)內(nèi)部對象與this

  對于面向?qū)ο笳Z言的使用者來說,this實在是再熟悉不過了,不就是指向構造函數(shù)新創(chuàng)建的對象嗎!不過,在ECMAScript中,且別掉以輕心,事情沒有那么簡單,雖然在使用new操作符調(diào)用函數(shù)的情況下,this也的確是指向新創(chuàng)建的對象,但這只是指定this對象值的一種方式而已,還有更多的方式可以指定this對象的值,換句話說,this是動態(tài)的,是可以由我們自己自由指定的。

(1)全局環(huán)境中的this

  在全局環(huán)境中,this指向全局對象本身,在瀏覽器中也就是window,這里也可以把全局環(huán)境中的this理解為全局執(zhí)行環(huán)境相應的變量對象,在全局環(huán)境中定義的變量和函數(shù)都是這個變量對象的屬性:
復制代碼 代碼如下:

var vo = 'a';
vo2 = 'b';
function fn(){
return 'fn';
}
console.info(this === window);//true
console.info(this.vo);//a
console.info(this.vo2);//b
console.info(this.fn());//fn

如果在自定義函數(shù)中要引用全局對象,雖然可以直接使用window,但更好的方式則是將全局對象作為參數(shù)傳入函數(shù),這是在JS庫中非常通用的一種方式:
復制代碼 代碼如下:

(function(global){
console.info(global === window);//在內(nèi)部可以使用global代替window了
})(this);  

這種方式兼容性更好(ECMAScript的實現(xiàn)中全局對象未必都是window),在壓縮時,也可以將global簡化為g,而不用使用window了。

(2)函數(shù)內(nèi)部屬性this

  在函數(shù)環(huán)境中,this是一個內(nèi)部屬性對象,可以理解成函數(shù)對應的活動對象的一個屬性,而這個內(nèi)部屬性的值是動態(tài)的。那this值是怎么動態(tài)確定的呢?

•使用new調(diào)用時,函數(shù)也稱為構造函數(shù),這個時候函數(shù)內(nèi)部的this被指定為新創(chuàng)建的對象。
復制代碼 代碼如下:

function fn(){
var name = 'oulinhai';//函數(shù)對應的活動對象的屬性
this.name = 'linjisong';//當使用new調(diào)用函數(shù)時,將this指定為新創(chuàng)建對象,也就是給新創(chuàng)建對象添加屬性
}
var person = new fn();
console.info(person.name);//linjisong

var arr = [fn];
console.info(arr[0]());//undefined

需要注意區(qū)分一下函數(shù)執(zhí)行環(huán)境中定義的屬性(也即活動對象的屬性)和this對象的屬性,在使用數(shù)組元素方式調(diào)用函數(shù)時,函數(shù)內(nèi)部this指向數(shù)組本身,因此上例最后輸出undefined。

•作為一般函數(shù)調(diào)用時,this指向全局對象。
•作為對象的方法調(diào)用時,this指向調(diào)用這個方法的對象。
  看下面的例子:
復制代碼 代碼如下:

var name = 'oulinhai';
var person = {
name:'linjisong',
getName:function(){
return this.name;
}
};
console.info(person.getName());//linjisong
var getName = person.getName;
console.info(getName());//oulinhai

這里函數(shù)對象本身是匿名的,是作為person對象的一個屬性,當作為對象屬性調(diào)用時,this指向了對象,當把這個函數(shù)賦給另一個函數(shù)然后調(diào)用時,是作為一般函數(shù)調(diào)用的,this指向了全局對象。這個例子充分說明了“函數(shù)作為對象的方法調(diào)用時內(nèi)部屬性this指向這個調(diào)用對象,函數(shù)作為一般函數(shù)調(diào)用時內(nèi)部屬性this指向全局對象”,也說明了this的指定是動態(tài)的,是在調(diào)用時指定的,而不管函數(shù)是單獨定義的還是作為對象方法定義的。也正是因為函數(shù)作為對象的方法調(diào)用時this指向這個調(diào)用對象,所以在函數(shù)內(nèi)部返回this時才能夠延續(xù)調(diào)用對象的下一個方法——也就是鏈式操作(jQuery的一大特色)。

•使用apply()、call()或bind()調(diào)用函數(shù)時,this指向第一個參數(shù)對象。如果沒有傳入?yún)?shù)或傳入的是null和undefined,this指向全局對象(在ES5的嚴格模式下會設為null)。如果傳入的第一個參數(shù)是一個簡單類型,會將this設置為相應的簡單類型包裝對象。
復制代碼 代碼如下:

var name = 'linjisong';
function fn(){
return this.name;
}
var person = {
name:'oulinhai',
getName:fn
};
var person2 = {name:'hujinxing'};
var person3 = {name:'huanglanxue'};
console.info(fn());//linjisong,一般函數(shù)調(diào)用,內(nèi)部屬性this指向全局對象,因此this.name返回linjisong
console.info(person.getName());//oulinhai,作為對象方法調(diào)用,this指向這個對象,因此這里返回person.name
console.info(fn.apply(person2));//hujinxing,使用apply、call或bind調(diào)用函數(shù),執(zhí)行傳入的第一個參數(shù)對象,因此返回person2.name
console.info(fn.call(person2));//hujinxing
var newFn = fn.bind(person3);//ES5中新增方法,會創(chuàng)建一個新函數(shù)實例返回,內(nèi)部this值被指定為傳入的參數(shù)對象
console.info(newFn());//huanglanxue

上面示例中列出的都是一些常見情況,沒有列出第一個參數(shù)為null或undefined的情況,有興趣的朋友可以自行測試。關于this值的確定,在原書中還有一個例子:
復制代碼 代碼如下:

var name = 'The Window';
var object = {
name : 'My Object',
getName:function(){
return this.name;
},
getNameFunc:function(){
return function(){
return this.name;
}
}
};

console.info(object.getName());//My Object
console.info((object.getName)());//My Object
console.info((object.getName = object.getName)());//The Window
console.info(object.getNameFunc()());//The Window

第1個是正常輸出,第2個(object.getName)與object.getName的效果是相同的,而第3個(object.getName=object.getName)最終返回的是函數(shù)對象本身,也就是說第3個會作為一般函數(shù)來調(diào)用,第4個則先是調(diào)用getNameFunc這個方法,返回一個函數(shù),然后再調(diào)用這個函數(shù),也是作為一般函數(shù)來調(diào)用。

8、函數(shù)屬性和方法

  函數(shù)是一個對象,因此也可以有自己的屬性和方法。不過函數(shù)屬性和方法與函數(shù)內(nèi)部屬性很容易混淆,既然容易混淆,就把它們放一起對照著看,就好比一對雙胞胎,不對照著看,不熟悉的人是區(qū)分不了的。

  先從概念上來區(qū)分一下:

(1)函數(shù)內(nèi)部屬性:可以理解為函數(shù)相應的活動對象的屬性,是只能從函數(shù)體內(nèi)部訪問的屬性,函數(shù)每一次被調(diào)用,都會被重新指定,具有動態(tài)性。

(2)函數(shù)屬性和方法:這是函數(shù)作為對象所具有的特性,只要函數(shù)一定義,函數(shù)對象就被創(chuàng)建,相應的屬性和方法就可以訪問,并且除非你在代碼中明確賦為另一個值,否則它們的值不會改變,因而具有靜態(tài)性。有一個例外屬性caller,表示調(diào)用當前函數(shù)的函數(shù),也是在函數(shù)被調(diào)用時動態(tài)指定,在《JavaScript高級程序設計(第3版)》中也因此將caller屬性和函數(shù)內(nèi)部屬性arguments、this一起講解,事實上,在ES5的嚴格模式下,不能對具有動態(tài)特性的函數(shù)屬性caller賦值。

  光從概念上區(qū)分是非常抽象的,也不是那么容易理解,再把這些屬性列在一起比較一下(沒有列入一些非標準的屬性,如name):

類別 名稱 繼承性 說明 備注
函數(shù)內(nèi)部屬性 this - 函數(shù)據(jù)以執(zhí)行的環(huán)境對象 和一般面向?qū)ο笳Z言有很大區(qū)別
arguments -

表示函數(shù)實際參數(shù)的類數(shù)組對象

arguments本身也有自己的屬性:length、callee和caller

1、length屬性表示實際接收到的參數(shù)個數(shù)

2、callee屬性指向函數(shù)對象本身,即有:

  fn.arguments.callee === fn

3、caller屬性主要和函數(shù)的caller相區(qū)分,值永遠都是undefined

函數(shù)屬性 caller 調(diào)用當前函數(shù)的函數(shù) 雖然函數(shù)一定義就可訪問,但是不在函數(shù)體內(nèi)訪問時永遠為null,在函數(shù)體內(nèi)訪問時返回調(diào)用當前函數(shù)的函數(shù),在全局作用域中調(diào)用函數(shù)也會返回null
length 函數(shù)形式參數(shù)的長度 就是定義函數(shù)時命名的參數(shù)個數(shù)
prototype 函數(shù)原型對象 原型對象是ECMAScript實現(xiàn)繼承的基礎
constructor 繼承自Object,表示創(chuàng)建函數(shù)實例的函數(shù),也就是Function() 值永遠是Function,也就是內(nèi)置的函數(shù)Function()
函數(shù)方法 apply 調(diào)用函數(shù)自身,以(類)數(shù)組方式接受參數(shù)

這三個方法主要作用是動態(tài)綁定函數(shù)內(nèi)部屬性this

1、apply和call在綁定之后會馬上執(zhí)行

2、bind在綁定之后可以在需要的時候再調(diào)用執(zhí)行

call 調(diào)用函數(shù)自身,以列舉方式接受參數(shù)
bind 綁定函數(shù)作用域,ES5中新增
toLocalString 覆蓋

覆蓋了Object類型中的方法,返回函數(shù)體

不同瀏覽器實現(xiàn)返回可能不同,可能返回原始代碼,也可能返回去掉注釋后的代碼

toString 覆蓋
valueOf 覆蓋
hasOwnProperty 直接繼承自Object類型的方法,用法同Object
propertyIsEnumerable
isPropertyOf

  函數(shù)屬性和方法,除了從Object繼承而來的屬性和方法,也包括函數(shù)本身特有的屬性和方法,用的最多的方法自然就是上一小節(jié)說的apply()、call(),這兩個方法都是用來設置函數(shù)內(nèi)部屬性this從而擴展函數(shù)作用域的,只不過apply()擴展函數(shù)作用域時是以(類)數(shù)組方式接受函數(shù)的參數(shù),而call()擴展函數(shù)作用域時需要將函數(shù)參數(shù)一一列舉出來傳遞,看下面的例子:

復制代碼 代碼如下:

function sum(){
  var total = 0,
  l = arguments.length ;

  for(; l; l--){
  total += arguments[l-1];
  }
  return total;
}

console.info(sum.apply(null,[1,2,3,4]));//10
console.info(sum.call(null,1,2,3,4));//10

不過需要強調(diào)的是:apply和call的主要作用還是在于擴展函數(shù)作用域。apply和call在擴展作用域時會馬上調(diào)用函數(shù),這使得應用中有了很大限制,因此在ES5中新增加了一個bind()函數(shù),這個函數(shù)也用于擴展作用域,但是可以不用馬上執(zhí)行函數(shù),它返回一個函數(shù)實例,將傳入給它的第一個參數(shù)作為原函數(shù)的作用域。它的一個可能的實現(xiàn)如下:
復制代碼 代碼如下:

function bind(scope){
var that = this;
return function(){
that.apply(scope, arguments);
}
}
Function.prototype.bind = bind;

這里涉及了一個閉包的概念,明天再繼續(xù)。

相關文章

  • JavaScript中的eval()函數(shù)詳解

    JavaScript中的eval()函數(shù)詳解

    和其他很多解釋性語言一樣,JavaScript同樣可以解釋運行由JavaScript源代碼組成的字符串,并產(chǎn)生一個值。JavaScript通過全局函數(shù)eval()來完成這個工作
    2013-08-08
  • JS 全屏和退出全屏詳解及實例代碼

    JS 全屏和退出全屏詳解及實例代碼

    退出全屏效果與全屏效果我們可能在看視頻網(wǎng)站時看到多,這里來為各位介紹利用js全屏和退出全屏代碼范例吧,,需要的朋友可以參考下
    2016-11-11
  • 使用JS CSS去除IE鏈接虛線框的三種方法

    使用JS CSS去除IE鏈接虛線框的三種方法

    本文使用JS、CSS、標簽屬性等方式去除IE鏈接上的虛線框,方法很簡單,大家可以選擇使用
    2013-11-11
  • JavaScript編程中布爾對象的基本使用

    JavaScript編程中布爾對象的基本使用

    這篇文章主要介紹了JavaScript編程中布爾對象的基本使用,是JavaScript入門學習中的基礎知識,需要的朋友可以參考下
    2015-10-10
  • JavaScript多線程運行庫Nexus.js詳解

    JavaScript多線程運行庫Nexus.js詳解

    這篇文章主要介紹了JavaScript多線程運行庫Nexus.js的學習心得以及代碼分享,有需要的朋友一起參考學習下吧。
    2017-12-12
  • 老生常談onBlur事件與onfocus事件(js)

    老生常談onBlur事件與onfocus事件(js)

    下面小編就為大家?guī)硪黄仙U刼nBlur事件與onfocus事件(js)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • php與js的區(qū)別是什么

    php與js的區(qū)別是什么

    php與js的區(qū)別包括:類型轉(zhuǎn)換的不同、實形參的不同、數(shù)據(jù)類型的不同等等,更多參考下文,希望對大家有所幫助
    2013-08-08
  • JavaScript初學者應注意的七個細節(jié)詳細介紹

    JavaScript初學者應注意的七個細節(jié)詳細介紹

    種種語言都有它特別的地方,對于JavaScript來說,使用var就可以聲明任意類型的變量,這門腳本語言看起來很簡單,然而想要寫出優(yōu)雅的代碼卻是需要不斷積累經(jīng)驗的,接下來介紹初學者應注意
    2012-12-12
  • WEB前端設計師常用工具集錦

    WEB前端設計師常用工具集錦

    這篇文章主要介紹了WEB前端設計師常用工具集錦,非常的全面,想成為一個優(yōu)秀的WEB前端設計師嗎?那本文你要好好研究研究了。
    2014-12-12
  • 分析Node.js connect ECONNREFUSED錯誤

    分析Node.js connect ECONNREFUSED錯誤

    最近在準備Angularjs +node.js demo的時候在我的mac開發(fā)中 遇見此錯誤
    2013-04-04

最新評論