您好,登錄后才能下訂單哦!
??在介紹作用域特性之前,我們先來回顧一下js的執行上下文(詳細介紹:https://www.jianshu.com/p/8f19e45fd1f1)
??一段<script>或者一個函數之內,都會去生成一個執行環境(execution context,EC)或稱之為執行上下文。當一段JS代碼執行的時候,JS解釋器會通過兩個階段去產生一個EC。
1.創建階段
o創建變量對象VO
o設置[[Scope]]屬性的值: (指向作用域鏈)
o設置this的值: (指向一個EC,默認undefined)
2.初始化變量對象(即設置變量的值、函數的引用),然后解釋/執行代碼。
注意事項:
??全局:針對一段<script>,它會生成一個全局的執行上下文,在執行之前會先去把“變量定義”和“函數聲明”拿出來裝在對象VO。
??函數:針對一個函數,它會生成一個函數執行上下文,在函數執行之前會先把“變量定義”、“函數聲明”、“arguments”拿出來封裝在對象VO里邊。
先執行變量定義后,變量值默認為undefined;
函數聲明后不會立即執行,如果輸出一下函數,能看到函數體所有的代碼;而需要調用函數后(例如:fn();),函數體中的代碼才會執行.
arguments 是JavaScript里的一個內置對象,所有的函數都有屬于自己的一個arguments對象,它包括了函所要調用的參數。
函數申明和函數表達式的區別:
function fn(name){} 這是一個函數聲明,而var a = function(){}這是函數表達式;
如果是函數表達式的寫法,在執行之前先拿出來處理的就是var a;了,這時候a只是一個普遍變量(值為undefined)不是函數,這點要注意。
作用域指的是變量的適用范圍。(詳細介紹:https://www.w3cschool.cn/javascript_prototype/y2cjfozt.html)
js無塊級作用域(不過es6中let定義的變量只在代碼塊中有效,也就有了塊級作用域)
if (true){
var name = 'zhao';
}
console.log(name); //zhao,這里依然可以獲取到上面代碼塊里邊的變量。
只有函數和全局作用域
var a = 100
function fn(){
var a =200;
console.log('fn',a); // fn 200
}
console.log('global',a); // global 100,獲取不到函數內部的變量。
var a = 100;
function fn1(){
var b =200;
function fn2(){
var c =300;
// 當前作用域沒有定義的變量,即“自由變量”,a和b都是
console.log(a); // 100
console.log(b); // 200
console.log(c); // 300
}
fn2();
}
fn1();
PS:自由變量因為在當前作用域沒有定義,所以只能去父級作用域查找.
(注意:父級作用域是在函數“定義”的時候就已經確定了的,自由變量這種一層層往父級查找的鏈式結構也就是“作用域鏈”)
閉包實際上是對js作用域鏈的一種應用形式;主要利用了作用域鏈從父級函數獲取變量的特性,從外部調用父級函數局部變量并且互不污染,或者子函數循環利用父級函數的變量達到某種計算用途。
閉包特性一:調用函數內部的變量,利用作用域鏈原理,能獲取函數fn1的父級函數的局部變量進行計算。
閉包特性二:讓這些變量的值始終保持在內存中,不會再fn1調用后被自動清除,再次執行fn1的時候還能繼續上一次的計算。
注意:fn2創建的時候與fn1是相互獨立的,其中的變量a也互不影響,好比父親給每個孩子都準備了一個新的存錢罐。
場景一:函數作為返回值
function F1(){
var a =100;
// 返回一個函數(函數作為返回值),為了閱讀方便也可以先定義一個函數,然后retrun函數名。
return function(){
console.log(a); //自由變量,去父作用域尋找
}
// 另外放回函數的形式也不只是return,同樣在這里以事件的形式綁定在Dom上也是一樣,再或者調用其他方法傳遞一個函數出去。
}
var f1 = F1();
var a=200;
f1(); // 100
場景二:函數作為參數傳遞
var b=111;
function f1(){
var a =100;
console.log(a,b);
}
function f2(fn){
var a =200;
var b=222;
fn();
}
f2(f1); // 100,111
//并且如果a在F1()沒有定義的話,就會報錯而不是獲取f2中的a,因為它的定義時的父級作用域及之上(即全局作用域)都沒有定義a;
應用舉例一:setTimeout中的參數傳遞。
由于直接setTimeout(function(){},200)這么寫的話,沒辦法傳參,所以可以用閉包的形式來做。
var Fn=function(num){
var a=10;
return function(){
var b=0;
a+=num;
b+=num;
console.log(a,b);
}
}
var fn1=Fn(1);
var fn2=Fn(1);
//閉包特性一:調用函數內部的變量,利用作用域鏈原理,能獲取函數fn1的父級函數的局部變量a進行計算。
setTimeout(fn1,200); //輸出的a=11,b=1;
//閉包特性二:讓變量a的值始終保持在內存中,不會在fn1調用后被自動清除,再次執行fn1的時候還能繼續上一次的計算。
setTimeout(fn1,500); //輸出的a=12,b=1;
//特性二的注意事項:fn2創建的時候與fn1是相互獨立的,對應的父級函數Fn的變量a也互不影響,好比父親在每個孩子出生時都準備了一個新的存錢罐,每個孩子都用自己的。
setTimeout(fn2,800); //輸出的a=11,b=1;
應用舉例二:創建10個<a>標簽,點擊的時候彈出來對應的序號。
<body>
<script type="text/javascript">
for(var i=0;i<10;i++){
(function(i){
var a=document.createElement('a');
a.innerHTML=i+'<br>';
document.body.appendChild(a);
a.addEventListener('click',function(e){
e.preventDefault(); //取消默認事件,指a標簽
alert(i);
});
})(i);
}
</script>
</body>
核心:this要在執行時才能確認值,定義時無法確認。
var a = {
name: 'A',
fn: function (){
console.log(this.name);
}
}
a.fn(); // this === a (即使是b.a.fn(),this也是a)
a.fn.call({name: 'B'}); // this === {name: 'B'}
var fn1 = a.fn;
fn1(); // this === window
this幾種不同的運用場景
1、作為構造函數執行:(例如:new Foo(),this指向這個新對象)
2、作為對象屬性執行:(this指向對象)
3、作為普通函數執行:(this指向window)
4、call()、apply()、bind():(this指向傳入的第一個對象參數,bind只有一個參數)
參考:https://www.w3cschool.cn/jsnote/jsnote-this.html
call,apply、bind都屬于Function.prototype的一個方法,他們的作用改變函數的調用對象,它是JavaScript引擎內在實現的,因為屬于Function.prototype,所以每個Function對象實例(就是每個方法)都有call,apply,bind屬性。既然作為方法的屬性,那它們的使用就當然是針對方法的了,這幾個方法是容易混淆的。
call,apply的用法差不多,只是參數稍微不同;(apply()接收兩個參數,一個是函數運行的作用域(this),另一個是參數數組。call()方法第一個參數與apply()方法相同,但傳遞給函數的參數必須列舉出來。)
// 以最簡單window對象為例
function sum(num1, num2) {
return num1 + num2;
}
console.log(sum.call(window, 10, 10)); //20
console.log(sum.apply(window,[10,10])); //20 這兩都相當于window.sum(10,10);
// 即語法:foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)
而bind的用法有一點差別。(只是傳一個參數對象,然后返回一個函數給接受的變量,再另外調用執行。)
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var OSayColor = sayColor.bind(o);
OSayColor(); //blue
詳情:https://www.w3cschool.cn/xqw2e7/9m2x12y0.html
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。