摘要:這篇文章總結下之前看的文章和自己在工作中遇到的坑,純手寫的,有什么寫的不對的請多多提出修正哈變量提升何為變量提升里面的變量在聲明之前就可以使用,因為該聲明已經被提升至該作用域函數或全局的頂部直接上代碼和都會變量提升優先級上面可理解為函數這時
這篇文章總結下之前看的文章和自己在工作中遇到的坑,純手寫的,有什么寫的不對的請多多提出修正哈
變量提升何為變量提升?js里面的變量在聲明之前就可以使用,因為該聲明已經被提升至該作用域(函數或全局)的頂部
直接上代碼
function fn1(){ console.log(a) var a=10; function a(){}; console.log(a) } fn1(); //var和function都會變量提升,優先級function>var,上面可理解為 function fn1(){ var a; function a(){}; console.log(a);//a函數 a=10; console.log(a);//10 }
這時我們加上一個參數來比較
function fn1(a){ console.log(a); var a=20; function a(){}; console.log(a); } fn2(10);//打印出函數a //變量提升有個優先級:函數a>參數>變量,上面可理解為 function fn2(a){//a=10 var a; //a被參數的a賦值為10或者理解為后聲明的覆蓋不了前面已賦值的 function a(){};//變量a變為函數a console.log(a);//函數a a=20;//a重新賦值為20 console.log(a);//20 }作用域
作用域有全局作用域和函數作用域,我的理解其實就是變量(標識符)作用域,當執行一段代碼時會在所在作用域解析變量和函數,該作用域變量會覆蓋外圍的作用域,該作用域找不到的標識符會沿著作用域鏈向上延伸查找,找不到就報錯;
var a = 10; !function(){ console.log(a); var a= 20; console.log(a); }(); console.log(a); //上面理解為 var a = 10;//全局作用域 !function(){ var a;//函數作用域 console.log(a);//undefined,a向上查看-》函數作用域-》全局作用域 a= 20; console.log(a);//20 }(); console.log(a);//10,查找到全局作用域,函數作用域不可見
再來看一個例子
console.log(b) if("b" in window){ var b=10; console.log(b); }; console.log(b); //ES3,ES5中if..else,with,while,for,switch等等是沒有作用域的, //只有全局作用域和函數作用域,如下 var b; console.log(b);//undefined if("b" in window){ b =10; console.log(b);//10 }; console.log(b);//10閉包
每個人都有不同理解,我的理解是閉包就是讓函數閉不了包,外部變量的值被緩存,內部變量可訪問外部變量,也可以說是外部變量可訪問內部變量,用法不同說法就不同,閉包在一些簡單例子上可以代替new實例化的開銷,用自執行函數先把該執行的執行完
var Fn =(function(){ var obj = {}; obj.id=10; return { obj:obj }; })();
來看一個最常用的淘寶tab欄切換例子
第一種
for(var a=0;a第二種
for(var a=0;a第三種
for(let a=0;athis this指向執行時所在的作用域,一般為window和函數,node環境為global, 來看個例子就明白了
var id=10; var obj = { id:100, show:function(){ console.log(this.id); console.log(this.name); } } var b = obj.show; b(); obj.show(); //obj.show先保存起來后在調用時,這時是直接調用一個函數b,函數的this //指向window,注意window.name是window默認就有的為空,結果就是10 "" //obj.show()方式是直接調用,this對象指向obj,name為undefined, //結果就是100 undefinedthis中的bind,call,applybind,call,apply可以改變當前函數的作用域,bind不一定要立即執行函數,call,apply必須立即執行函數 來看一個構造函數繼承的例子
var Father = function(){ this.name="張三"; this.age=50; //以下三種都可以 var son = Son.bind(this);//指向的對象 son("葫蘆娃",10);//間接調用,也可以直接調用 //Son.apply(this,["葫蘆娃",10]);//第一個參數是指向的對象,第二個參數是數組 //Son.call(this,"葫蘆娃",10);//第一個參數是指向的對象,后面分開寫 } Father.prototype.show=function(){ console.log("爸爸叫"+this.name); } var Son = function(name1,age1){ this.name1=name1; this.age1=age1; } var father = new Father;//通過改變Son構造函數的this指向為Father console.log(father);//{name: "張三", age: 50, name1: "葫蘆娃", age1: 10}來看個bind的例子
var obj = {}; obj.show = function () { function _show() { console.log(this) }; return _show.bind(obj); }(); obj.show(); // 打印obj對象,由于先聲明賦值了,自執行函數后,函數_show的this //指向被bind方法改為obj var obj = { show: function () { function _show() { console.log(this) }; return _show.bind(obj); }() }; obj.show(); //打印window對象,沒事先聲明賦值,這里obj變量提升,自執行函數后, //bind里面的obj為undefined,this為undefined的默認指向window箭頭函數:默認指向所在的宿主對象,也就是上一級對象,而不是執行時的對象,基于這個this指向上一級的特殊性,我們在某些情況下就不需要緩存this的值,直接使用;
var obj = { id:100, show:function(){ (()=>{ console.log(this) })(); setTimeout(()=>{ console.log(this) },1); }, show1:()=>{ console.log(this); } }; var obj1 = obj.show;. obj1();//window,箭頭函數上一層是個普通函數,普通函數this指向window obj.show();//obj,箭頭函數上一層作用域的this指向obj obj.show1();//window,this指向上一級即window箭頭函數與普通函數的混合嵌套
var obj = { show:function(){ setTimeout(fn); function fn(){ console.log(this); setTimeout(()=>{ console.log(this); setTimeout(()=>{ console.log(this); }) }) }; }, show1:function(){ setTimeout(fn.bind(obj)); function fn(){ console.log(this); setTimeout(()=>{ console.log(this); setTimeout(()=>{ console.log(this); }) }) }; } }; obj.show();//都是window,最外面的定時器是普通函數,普通函數this指向window,每個箭頭函數this指向上一層 obj.show1();//都是obj,最外面的定時器this指向被改變為obj,每個箭頭函數this指向上一層綜合題第一道
var a = 1; !function(){ var a = 10; function fn(){ this.a += this.a; a+=a; console.log(this.a); console.log(a); }; var obj = { a:5, show:fn }; obj.show(); var obj1 = obj.show; obj1(); }(); //這里最主要是考查this指向,去年面試時候做到的筆試題,自己加以改進 //"obj.show()"直接調用,此時函數fn的this指向obj, //this.a就是5,this.a累加后就是10,根據就近原則,變量a會 //沿著作用域鏈向上查找,找到上一層的10就停止了, //a累加后就是20 //"var obj1 = obj.show;"這步保存起普通函數,普通函數被調用時 //this指向就是window//,this.a===window.a就是1,累加后就是2,變量a依然 //會沿著作用域鏈向上查找,找到上一層的是20,因為上面已經被累加了一次, //這是一個坑,很容易忘記,上下兩次調用是會互相影響的,a=20在累加就是40 //答案為: //obj.show();//10 20 //var obj1 = obj.show; //obj1();//2 40第二道(閉包的經典題目,原題奉上)
function fun(n,o) { console.log(o); return { fun:function(m){ return fun(m,n); } }; }; //寫出a,b,c的運行結果 //var a = fun(0); a.fun(1); a.fun(2); a.fun(3); //var b = fun(0).fun(1).fun(2).fun(3); //var c = fun(0).fun(1); c.fun(2); c.fun(3); //這里就是考查閉包的層層嵌套與多次回調,之前面試遇到的, //當時做的時候有點緊張,不過后面自己運行后發現做的還是正確的 //var a = fun(0); a.fun(1); a.fun(2); a.fun(3); //第一個a保存fun(0)的運行結果即return里的對象,由于o沒傳參數, //打印undefined,參數n=0被保存在當前作用域中,不會被銷毀, //供當前作用域鏈及以下使用,這是閉包的精髓,后面回調函數傳參也是如此, //這個a.fun(1)就是調用fun(0)的結果,傳參m=1,執行返回fun(m=1,n=0) //在執行回調fun(n=1,o=0),console.log(o)就是0,后面的a.fun(2)和 //a.fun(3)也是如此,打印都是0; //var b = fun(0).fun(1).fun(2).fun(3); //fun(0).fun(1)這步其實就是前面說的, //fun(0).fun(1)的返回就是return的對象,.fun(2)在調用改對象并且 //傳參m=2,返回fum(m=2,n=1)在執行回調fun(n=2,o=1) //打印console.log(o)就是1,并且返回return對象, //在.fun(3)在調用改對象并且傳參m=3,返回fum(m=3,n=2) //在執行回調fun(n=3,o=2)打印console.log(o)就是2 //var c = fun(0).fun(1); c.fun(2); c.fun(3); //前面兩個理解了,這個也不會有問題,就不多做解釋了 //var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined 0 0 0 //var b = fun(0).fun(1).fun(2).fun(3);//undefined 0 1 2 //var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined 0 1 1寫到這里終于結束啦,js里面還有很多奇淫技巧,每次看一篇好文或者一本書都會被新的視角沖擊到,前方高能,還需繼續踩坑,有什么需要交流指正的請留言呀!
也可以加微信討論哦!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107027.html
摘要:引言滿滿的干貨,面試必系列,參考大量資料,并集合自己的理解以及相關的面試題,對核心知識點中的作用域閉包上下文進行了梳理。如果在小區這個作用域找到了張老師,我就會在張老師的輔導下學鋼琴我張老師房間鋼琴構成了學琴的上下文環境。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 滿滿的干貨,面試必bei系列,參考大...
摘要:作用域執行上下文變量提前函數聲明提前確定值范圍一段或者一個函數都會生成一個執行上下文全局一段變量定義函數聲明函數變量定義函數聲明參數集合變量提前代碼解析執行過程變量定義提前賦值函數聲明提前代碼解析函數聲明函數表達式執行過程執行過程執行時才能 1.作用域 執行上下文 (變量提前、函數聲明提前、確定this值、arguments) 范圍:一段或者一個函數(都會生成一個執行上下文) ...
摘要:刪除對匿名函數的引用,以便釋放內存在匿名函數從中被返回后,它的作用域鏈被初始化為包含函數的活動對象和全局變量對象。閉包與變量我們要注意到,閉包只能取到任意變量的最后值,也就是我們保存的是活動對象,而不是確定值。 工作中會遇到很多 this對象 指向不明的問題,你可能不止一次用過 _self = this 的寫法來傳遞this對象,它每每會讓我們覺得困惑和抓狂,我們很可能會好奇其中到底發...
摘要:變量的作用域無非就是兩種全局變量和局部變量。其中內部函數中可以訪問外部函數的變量,是因為內部函數的作用域鏈中包含了外部函數的作用域也可以理解為內部函數的作用范圍輻射到了外部函數的作用范圍另一方面,在函數外部自然無法讀取函數內的局部變量。 以前學習的時候,了解過變量提升和閉包,但是沒有深入了解,網上查了資料,這里記錄下,只供參考。部分內容引用: https://www.cnblogs.c...
摘要:至此作用域鏈創建完畢。好了,通過深入理解作用域鏈,我們能跟好的理解的運行機制和閉包的原理。 前言 理解javascript中的作用域和作用域鏈對我們理解js這們語言。這次想深入的聊下關于js執行的內部機制,主要討論下,作用域,作用域鏈,閉包的概念。為了更好的理解這些東西,我模擬了當一個函數執行時,js引擎做了哪些事情--那些我們看不見的動作。 關鍵詞: 執行環境 作用域 作用域鏈 變...
閱讀 992·2023-04-25 14:20
閱讀 1868·2021-11-24 10:20
閱讀 3764·2021-11-11 16:55
閱讀 2905·2021-10-14 09:42
閱讀 3466·2019-08-30 15:56
閱讀 1142·2019-08-30 15:55
閱讀 1063·2019-08-30 15:44
閱讀 771·2019-08-29 11:28