(1)執行環境(execution context):所有的JavaScript程式碼都運作在一個執行環境中,當控制權轉移到JavaScript的可執行程式碼時,就進入了一個執行環境。活動的執行環境從邏輯上形成了一個棧,全域執行環境永遠是這個棧的棧底元素,棧頂元素就是目前正在運作的執行環境。每一個函數都有自己的執行環境,當執行流進入函數時,會將這個函數的執行環境壓入棧頂,函數執行完之後再將這個執行環境彈出,控制權回傳給先前的執行環境。
(2)變數物件(variable object):每一個執行環境都有一個與之對應的變數對象,而執行環境中定義的所有變數和函數就是保存在這個變數物件中。這個變數對像是後台實作中的一個對象,我們無法在程式碼中訪問,但這有助於我們理解執行環境和作用域相關概念。
(3)作用域鏈(scope chain):當程式碼在一個執行環境中運作時,會建立一個由變數物件組成的一個作用域鏈。這個鏈的前端,就是目前程式碼所在環境的變數對象,鏈的最末端,就是全域環境的變數對象。在一個執行環境中解析標識符時,會在當前執行環境相應的變量對像中搜索,找到就返回,沒有找到就沿著作用域鏈一級一級往上搜索直至全局環境的變量對象,如果一直未找到,就拋出引用異常。
(4)活動物件(activation object):如果一個執行環境是函數執行環境,也將變數物件稱為活動物件。活動物件在最開始只包含一個變量,即arguments物件(這個物件在全域環境的變數物件中不存在)。
這四個概念雖然有些抽象,但還是比較自然的,可以結合《JavaScript高級程式設計(第3版)》中的一個例子來細細體會一下:
// 進入全域作用域,建立全域變數物件
var color = " blue";
function changeColor(){
// 進入changeColor作用域,建立changeColor對應變數物件
var anotherColor = "red";
function swapors(111 , color2){
// 進入到swapColors作用域,創建swapColors相應變量對象
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
/*
* swapColors作用域內可以存取的物件有:
* 全域變數物件的color,changeColor
* changeColor函數對應變數物件的anotherColor、swapColors
* swapColors函數對應變數物件的tempColor
*/
}
swapColors('white');
/*
* changeColor作用域內可以存取的物件有:
* 全域變數物件的color,changeColor
* changeColor函數函數變數物件的anotherColor、swapColors
*/
}
changeColor();
/*
* 全域作用域內可存取的物件有:
* 全域變數的color,changeColor
*/
這裡的整個過程是:
(1)進入全域環境,建立全域變數對象,將全域環境壓入堆疊頂(這裡也是堆疊底)。根據前面的關於宣告提升的結論,這裡建立全域變數物件可能的一個過程是,先建立全域變數對象,然後處理函數宣告設定屬性changeColor為對應函數,再處理變數宣告設定屬性color為undefined。
(2)執行全域環境中的程式碼。先執行color變數初始化,賦值為'blue',再呼叫changeColor()函數。
(3)呼叫changeColor()函數,進入到changeColor函數執行環境,建立這個環境對應的變數物件(也就是活動物件),將這個環境壓入堆疊頂部。建立活動對象可能的一個過程是,先建立活動對象,處理內部函數宣告設定屬性swapColors為對應函數,處理函數參數建立活動對象的屬性arguments對象,處理內部變數宣告設定屬性anotherColor為undefined。
(4)執行changeColor()函數程式碼。先執行anotherColor初始化為'red',再呼叫swapColors()函數。
(5)呼叫swapColors()函數,進入到swapColors函數執行環境,建立對應的變數物件(活動物件),將swapColors執行環境壓入堆疊頂。這裡創建活動對象可能的一個過程是,先創建活動對象,處理函數參數,將形式參數作為活動對象的屬性並賦值為undefined,創建活動對象的屬性arguments對象,並根據實際參數初始化形式參數和arguments對應的值和屬性(將屬性color1和arguments[0]初始化為'white',由於沒有第二個實際參數,所以color2的值為undefined,而arguments的長度只為1了),處理完函數參數之後,再處理函數內部變數聲明,將tempColor作為活動物件的屬性並賦值為undefined。
(6)執行swapColors()函數程式碼。先將tempColor初始化賦值,然後實作值交換功能(這裡color和anotherColor的值都是沿著作用域鏈才讀取到的)。
(7)swapColors()函數程式碼執行完之後,返回undefined,將對應的執行環境彈出堆疊並銷毀(注意,這裡會銷毀執行環境,但是執行環境對應的活動物件不一定會被銷毀),目前執行環境恢復成changeColor()函數的執行環境。隨著swapColor()函數執行完並返回,changeColor()也就執行完了,同樣返回undefined,並將changeColor()函數的執行環境彈出堆疊並銷毀,當前執行環境恢復成全域環境。整個處理過程結束,全域環境直到頁面退出再銷毀。
作用域鏈也解釋了為什麼函數可以在內部遞歸呼叫自身:函數名稱是函數定義所在執行環境對應變數物件的屬性,然後在函數內部執行環境中,就可以沿著作用域鏈向外上溯一層訪問函數名指向的函數物件了。如果在函數內部將函數名稱指向了一個新函數,遞歸呼叫時就會不正確了:
function fn(num){
if(1 == num){
return 1;
}else{
fn = function(){
return 0;
};
return num * fn(num - 1);
}
}
console.info(fn(5));//0
關於作用域和宣告提升,再看一個例子:
程式碼如下:
程式碼如下:
這裡最不直觀的可能是第3行輸出undefined,因為在全域中已經定義過name了,不過按照上面解析的步驟去解析一次,就可以得到正確的結果了。另外強調一下,在ECMAScript中只有全域執行環境和函數執行環境,對應的也只有全域作用域和函數作用域,沒有區塊作用域-雖然有區塊語句。
複製程式碼
程式碼如下:
function fn(){
{
對於作用域鏈,也可以使用with、try-catch語句的catch區塊來延長:
•使用with(obj){}語句時,將obj物件加入目前作用域鏈的最前端。
•使用try{}catch(error){}語句時,將error物件加入目前作用域鏈的最前端。
插了一段較為抽象的概念,希望不至於影響整個閱讀的流暢,事實上,我在這裡還悄悄的繞過了一個稱為“閉包”的概念,關於函數與閉包,在下篇文章中再詳細敘述。
7、函數內部物件與this
對於物件導向語言的使用者來說,this實在是再熟悉不過了,不就是指向建構子新創建的對象嗎!不過,在ECMAScript中,且別掉以輕心,事情沒有那麼簡單,雖然在使用new操作符調用函數的情況下,this也的確是指向新創建的對象,但這只是指定this對象值的一種方式而已,還有更多的方式可以指定this物件的值,換句話說,this是動態的,是可以由我們自己自由指定的。
(1)全域環境中的this
在全域環境中,this指向全域物件本身,在瀏覽器中也就是window,這裡也可以把全域環境中的this理解為全域執行環境對應的變數對象,在全域環境定義的變數和函數都是這個變數物件的屬性:
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
如果在自訂函數中要引用全域對象,雖然可以直接使用window,但更好的方式則是將全域物件作為參數傳入函數,這是在JS函式庫中非常通用的一種方式:
(function(global){
console.info(global === window);//在內部可以用global取代window了
})(this);
這種方式相容性更好(ECMAScript的實作中全域物件未必都是window),在壓縮時,也可以將global簡化為g,而不用使用window了。
(2)函數內部屬性this
在函數環境中,this是一個內部屬性對象,可以理解成函數對應的活動對象的一個屬性,而這個內部屬性的值是動態的。那this值是怎麼動態決定的呢?
•使用new呼叫時,函數也稱為建構函數,這個時候函數內部的this被指定為新建立的物件。
function fn(){
function fn(){
varname = ' oulinhai';//函數對應的活動對象的屬性
this.name = 'linjisong';//當使用new呼叫函數時,將this指定為新創建對象,也就是給新創建對象添加屬性
}
var person = new fn();
console.info(person.name);//linjisong
console.info(arr[ 0]());//undefined
需要注意區分函數執行環境中定義的屬性(也即活動物件的屬性)和this物件的屬性,在使用陣列元素方式呼叫函數時,函數內部this指向數組本身,因此上例最後輸出undefined。
•作為一般函數呼叫時,this指向全域物件。
程式碼如下:
var🎜>
name:'linjisong',
getName:function(){
return this.name;
}
};
console. info(person.getName());//linjisong
這裡函數物件本身是匿名的,是作為person對象的一個屬性,當作為對象屬性調用時,this指向了對象,當把這個函數賦給另一個函數然後調用時,是作為一般函數調用的,this指向了全域物件。這個例子充分說明了“函數作為對象的方法調用時內部屬性this指向這個調用對象,函數作為一般函數調用時內部屬性this指向全局對象”,也說明了this的指定是動態的,是在調用時指定的,而不管函數是單獨定義的還是作為物件方法定義的。也正是因為函數作為對象的方法呼叫時this指向這個呼叫對象,所以在函數內部返回this時才能夠延續呼叫對象的下一個方法-也就是鍊式操作(jQuery的一大特色)。
•使用apply()、call()或bind()呼叫函數時,this指向第一個參數物件。如果沒有傳入參數或傳入的是null和undefined,this指向全域物件(在ES5的嚴格模式下會設為null)。如果傳入的第一個參數是一個簡單類型,會將this設定為對應的簡單類型包裝物件。
var name = 'function; (){
return this.name;
}
var person = {
name:'oulinhai',
getName:fn
};
var person2 = {name :'hujinxing'};
var person3 = {name:'huanglanxue'};
console.info(fn());//linjisong,一般函數調用,內部屬性this指向全域對象,因此this. name返回linjisong
console.info(person.getName());//oulinhai,作為對象方法調用,this指向這個對象,因此這裡返回person.name
console.info(fn.apply(person2) );//hujinxing,使用apply、call或bind呼叫函數,執行傳入的第一個參數對象,因此返回person2.name
console.info(fn.call(person2));//hujinxing
var newFn = fn.bind(person3);//ES5中新增方法,會建立一個新函數實例返回,內部this值被指定為傳入的參數物件
console.info(newFn()); //huanglanxue
var object = {
name : 'My Object',
getName:function(){
return this.name;
},
getNameFunc:function(){
return function(){
return this.name;
}
}
};
console.info(object.Name ());//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)最终返回的是函数对象本身,也就是说第3个会作为一般函数来调用,第4个则先是调用getNameFunc这个方法,返回一个函数,然后再调用这个函数,也是作为一般函数来调用。
8、函数属性和方法
函数是一个对象,因此也可以有自己的属性和方法。不过函数属性和方法与函数内部属性很容易混淆,既然容易混淆,就把它们放一起对照着看,就好比一对双胞胎,不对照着看,不熟悉的人是区分不了的。
先从概念上来区分一下:
(1)函数内部属性:可以理解为函数相应的活动对象的属性,是只能从函数体内部访问的属性,函数每一次被调用,都会被重新指定,具有动态性。
(2)函数属性和方法:这是函数作为对象所具有的特性,只要函数一定义,函数对象就被创建,相应的属性和方法就可以访问,并且除非你在代码中明确赋为另一个值,否则它们的值不会改变,因而具有静态性。有一个例外属性caller,表示调用当前函数的函数,也是在函数被调用时动态指定,在《JavaScript高级程序设计(第3版)》中也因此将caller属性和函数内部属性arguments、this一起讲解,事实上,在ES5的严格模式下,不能对具有动态特性的函数属性caller赋值。
光从概念上区分是非常抽象的,也不是那么容易理解,再把这些属性列在一起比较一下(没有列入一些非标准的属性,如name):
类别 | 名称 | 继承性 | 说明 | 备注 |
函数内部属性 | this | - | 函数据以执行的环境对象 | 和一般面向对象语言有很大区别 |
arguments | - |
表示函数实际参数的类数组对象 arguments本身也有自己的属性:length、callee和caller |
1、length属性表示实际接收到的参数个数 2、callee属性指向函数对象本身,即有: fn.arguments.callee === fn 3、caller属性主要和函数的caller相区分,值永远都是undefined |
|
函数属性 | caller | 否 | 调用当前函数的函数 | 虽然函数一定义就可访问,但是不在函数体内访问时永远为null,在函数体内访问时返回调用当前函数的函数,在全局作用域中调用函数也会返回null |
length | 否 | 函数形式参数的长度 | 就是定义函数时命名的参数个数 | |
prototype | 否 | 函数原型对象 | 原型对象是ECMAScript实现继承的基础 | |
constructor | 是 | 继承自Object,表示创建函数实例的函数,也就是Function() | 值永远是Function,也就是内置的函数Function() | |
函数方法 | apply | 否 | 调用函数自身,以(类)数组方式接受参数 |
这三个方法主要作用是动态绑定函数内部属性this 1、apply和call在绑定之后会马上执行 2、bind在绑定之后可以在需要的时候再调用执行 |
call | 否 | 调用函数自身,以列举方式接受参数 | ||
bind | 否 | 绑定函数作用域,ES5中新增 | ||
toLocalString | 覆盖 |
覆盖了Object类型中的方法,返回函数体 不同浏览器实现返回可能不同,可能返回原始代码,也可能返回去掉注释后的代码 |
||
toString | 覆盖 | |||
valueOf | 覆盖 | |||
hasOwnProperty | 是 | 直接继承自Object类型的方法,用法同Object | ||
propertyIsEnumerable | 是 | |||
isPropertyOf | 是 |
函數屬性和方法,除了從Object繼承而來的屬性和方法,也包括函數本身特有的屬性和方法,用的最多的方法自然就是上一小節說的apply()、call(),這兩個方法都是用來設定函數內部屬性this從而擴展函數作用域的,只不過apply()擴展函數作用域時是以(類)數組方式接受函數的參數,而call()擴展函數作用域時需要將函數參數一一列舉出來傳遞,看下面的範例:
function sum(){
var total = 0,
l = arguments.length ;
for(l) l-- --){ -1];
}
return total;
}
console.info(sum.apply(null,[1,2,3,4]));//10
var that = this;
return function(){
that.apply(scope, arguments);
}
}
Function.prototype.bind = bind;

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器