JS語言精粹

小小知識點(diǎn):

  1. typeof是一個操作符烹看,并不是function澄耍。所以typeof后面不用加括號番官。
    參見MDN


    image.png
  2. 關(guān)于 typeof null // Object
    null 值表示一個空對象指針降传,而這也正是使用 typeof 操作符檢測 null 值時(shí)會返回"object"的原因羞芍,如

  3. 只聲明不賦值堂竟,則默認(rèn)值為undefined

  4. 關(guān)于const

     const num = 1;
     num = 2; //Uncaught TypeError: Assignment to constant variable.
    
     const obj = {
       data: 1
     }
     obj.data = 2;//沒有報(bào)錯
    

    咦魂毁,不是說const定義的常量不能被改變嗎?
    const僅保證指針不發(fā)生改變出嘹,修改對象的屬性不會改變對象的指針席楚,除非我們直接重寫對象也就是說const定義的引用類型只要指針不發(fā)生改變,其他的不論如何改變都是允許的税稼。所以

     const obj = {
         data:1
     }
    
     obj = {
         data:4
     }//Uncaught TypeError: Assignment to constant variable.
    
  5. 關(guān)于arguments烦秩,我們更推薦這么使用
    這時(shí)的args就是真正的數(shù)組了。

     function foo(...args){
         return args;
     }
     foo(1,2,3)//[1,2,3]
    
  6. 函數(shù)的返回值有兩種結(jié)果:
    顯示調(diào)用return 返回 return 后表達(dá)式的求值
    沒有調(diào)用return 返回 undefined

  7. 關(guān)于this
    在普通函數(shù)中
    嚴(yán)格模式下娶聘,this指向undefined
    非嚴(yán)格模式下闻镶,this指向全局對象(Node中的global,瀏覽器中的window)
    在ES6中默認(rèn)使用嚴(yán)格模式丸升,所以在普通函數(shù)中你會發(fā)現(xiàn)this指向undefined铆农。

  8. 語句和表達(dá)式
    JavaScript是一種語句優(yōu)先的語言
    逗號運(yùn)算符的左右兩側(cè)都必須是表達(dá)式!如果是a:1狡耻,那肯定會報(bào)錯墩剖。

逗號表達(dá)式的一般形式是:表達(dá)式1,表達(dá)式2夷狰,表達(dá)式3……表達(dá)式n
逗號表達(dá)式的求解過程是:先計(jì)算表達(dá)式1的值岭皂,再計(jì)算表達(dá)式2的值,……一直計(jì)算到表達(dá)式n的值沼头。最后整個逗號表達(dá)式的值是表達(dá)式n的值爷绘。
看下面幾個例子:
x=8*2,x*4 /整個表達(dá)式的值為64,x的值為16/
(x=8*2,x*4),x2 /整個表達(dá)式的值為128进倍,x的值為16/
x=(z=5,5*2) /
整個表達(dá)式為賦值表達(dá)式土至,它的值為10,z的值為5/
x=z=5,5*2 /
整個表達(dá)式為逗號表達(dá)式猾昆,它的值為10陶因,x和z的值都為5*/
逗號表達(dá)式用的地方不太多,一般情況是在給循環(huán)變量賦初值時(shí)才用得到垂蜗。所以程序中并不是所有的逗號都要看成逗號運(yùn)算符楷扬,尤其是在函數(shù)調(diào)用時(shí),各個參數(shù)是用逗號隔開的贴见,這時(shí)逗號就不是逗號運(yùn)算符烘苹。

有時(shí)候會遇到一些很奇怪的錯誤,就盡量往語句優(yōu)先這個方向想蝇刀。

  1. 立即執(zhí)行函數(shù)

    function(){}() //Uncaught SyntaxError: Unexpected token (
    這里為什么會報(bào)錯呢螟加?如果不加第一對括號,無論是

     function(){
        /* 代碼 */
     }();
    

    還是

     function(){
         /* 代碼 */
     }
    

    都是會報(bào)錯的吞琐。因?yàn)閖s的引擎會把這里的 function 看成是函數(shù)聲明捆探,而函數(shù)聲明不允許沒有函數(shù)名,因此會對匿名函數(shù)報(bào)錯站粟。

    匿名函數(shù)只允許以表達(dá)式的形式存在黍图,例如:

     setTimeout(function(){
         /* 代碼 */
     }, 1000);
    

    這里的匿名函數(shù)就是作為 setTimeout 的一個參數(shù),是表達(dá)式奴烙,這種寫法是允許的助被。

    或者:

     var foo = function(){
         /* 代碼 */
     };
    

    也就是說我們稍微修改一下,var fn = function(){}()切诀,也不會報(bào)錯揩环。
    這是為什么?
    因?yàn)檫@是把一個匿名函數(shù)賦值給一個變量幅虑,匿名函數(shù)在該語句中充當(dāng)函數(shù)表達(dá)式的角色丰滑。

    如果這里的函數(shù)有名字呢?不會報(bào)錯倒庵,但語義會發(fā)生變化褒墨。例如:

     function foo(x){
         /* 代碼 */
     }(1);
    

    其實(shí)這里的代碼就相當(dāng)于

     function foo(x){
         /* 代碼 */
     };
     (1);
    

    原因是js引擎會認(rèn)為前面的函數(shù)是一個函數(shù)聲明的語句,而后面的(1)是另一個單獨(dú)的語句擎宝,于是執(zhí)行后面的語句郁妈,在控制臺輸出1。

    js的括號有幾種不同的作用绍申,其中一種作用就是:表示在括號內(nèi)的是表達(dá)式而不是語句噩咪。具體到這個例子上,第一對括號就是告訴js引擎极阅,這里面的匿名函數(shù)是一個函數(shù)表達(dá)式胃碾,而不是函數(shù)聲明語句。因此加了這個括號之后涂屁,就不會報(bào)錯了书在。
    (function(){})() //強(qiáng)制其理解為函數(shù),()里面放表達(dá)式拆又,“函數(shù)()”表示執(zhí)行該函數(shù)儒旬,即聲明后立即執(zhí)行了。

  2. 補(bǔ)充原型鏈的一個小知識:
    每個原型對象prototype中都有一個constructor屬性帖族,默認(rèn)指向函數(shù)本身栈源。

  3. 閉包和內(nèi)存泄露的問題
    首先應(yīng)當(dāng)明確的是內(nèi)存泄漏可能是代碼的問題,而不是閉包的問題竖般,如果為了避免內(nèi)存泄漏而不去用閉包的話甚垦,有些問題是很難解決的。

在IE9之前,BOM和DOM對象都是使用C++以COM對象的形式來實(shí)現(xiàn)的艰亮,COM對象的垃圾收集機(jī)制采用的就是計(jì)數(shù)策略闭翩,所以IE中只要涉及到BOM和DOM對象,就很可能會存在循環(huán)引用的問題迄埃。

閉包實(shí)際上很容易造成JavaScript對象和DOM對象的隱蔽循環(huán)引用疗韵。也就是說,只要閉包的作用域鏈中保存著一個HTML元素侄非,那么就意味著這個元素將無法被銷毀蕉汪。例如下面的:

  function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert (element.id);
   }; 
}

注意,閉包的概念是一個函數(shù)的返回值是另外一個函數(shù)逞怨,返回的那個函數(shù)如果調(diào)用了其父函數(shù)內(nèi)部的其他值者疤,這是一個element元素事件處理的一個閉包。這個閉包創(chuàng)建了一個循環(huán)引用叠赦。

  1. JavaScript對象element引用了一個DOM對象(其id為“someElement”)驹马; JS(element) ----> DOM(someElemet)
  2. 該DOM對象的onclick屬性引用了匿名函數(shù)閉包,而閉包可以引用外部函數(shù)assignHandler 的整個活動對象眯搭,包括element 窥翩; DOM(someElement.onclick) ---->JS(element)
    匿名函數(shù)一直保存著對assginHandler()活動對象的引用,它所占的內(nèi)存永遠(yuǎn)不會被回收鳞仙。

可以對代碼稍稍改進(jìn):

  function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        alert (id);
   }; 
   element = null;
}

可以首先通過引用一個副本變量消除循環(huán)引用寇蚊,但是這個閉包包含外部函數(shù)的全部活動對象,所以就算不直接引用棍好,匿名函數(shù)一直都包含著對element的引用仗岸。所以最后需要手動設(shè)置element變量為null.

閉包的很大作用是可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量的值始終保持在內(nèi)存中借笙。
只要變量被任何一個閉包使用了扒怖,就會被添到詞法環(huán)境中,被該作用域下所有閉包共享业稼。

絕對不能因?yàn)閮?nèi)存泄漏而不使用閉包盗痒。

JS中只要越過了閉包,作用域低散,原型這幾座大山俯邓,對JS的理解就高了一個層次了!
作用域的文章可以查看下面的網(wǎng)址H酆拧;蕖!https://lemontency.github.io/2019/03/20/%E5%8E%9F%E5%9E%8B%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE/

最后來幾道作用域和this的題吧引镊!

if(!(username in window)){
    var username = 'zty';
}
console.log(username) //undefined

怎么理解呢朦蕴。首先在ES5的代碼中并沒有塊級作用域的概念篮条,var username直接變量提升到最頂部,此時(shí)全局作用域中有username并且值為undefined吩抓,所以!(username in window)為false涉茧,跳過賦值部分,username依舊是undefined琴拧。

//差點(diǎn)就錯了
//執(zhí)行順序是
//聲明函數(shù)foo降瞳,注意是整個函數(shù)一起提升上去的
//調(diào)用函數(shù)foo
//聲明變量test
//console.log(test)
//test = "bbb"
//console.log(test)
var test = "aaa";
function foo(){
    console.log(test);
    var test = "bbb";
    console.log(test);
}
foo(); //undefined bbb 
console.log(test)//aaa

也就是說上面的代碼的執(zhí)行順序其實(shí)是這樣的:

var test = aaa;
function foo(){
    var test;
    console.log(test);
    var test = "bbb";
    console.log(test);
}
foo();
console.log(test);

var name = 'global';
function A(name){
    //傳參的優(yōu)先級要高于后面的聲明
    //雖然后面有變量提升嘱支,但是是沒有影響的
    alert(name);//3
    this.name = name;
    var name = '1';
}
A.prototype.name = '2';
var a = new A('3');
alert(a.name);//3
delete a.name;//刪除對象的屬性但是不刪除對象原型鏈上的屬性
alert(a.name);//2

比較好的實(shí)踐就是把要用到的變量聲明都寫到函數(shù)最前面蚓胸。
再來看一道容易做的題:

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){
            return fun(m,n)
        }
    }
}
var a = fun(0);//undefined
a.fun(1);
a.fun(2);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);c.fun(3);

我們看到:return返回的對象的fun屬性對應(yīng)一個新建的函數(shù)對象,這個函數(shù)對象將形成一個閉包作用域除师,使其能夠訪問外層函數(shù)的變量n及外層函數(shù)fun,為了不讓fun屬性和fun對象混淆沛膳,先更改一下代碼:

function _fun_(n,o){
    console.log(o);
    return {
        fun:function(m){
            return _fun_(m,n)
        }
    }
}
var a = _fun_(0);//undefined
a.fun(1);//
a.fun(2);
var b = _fun_(0).fun(1).fun(2).fun(3);
var c = _fun_(0).fun(1);
c.fun(2);c.fun(3);

這道題難度挺大,但是一定要做汛聚。
講師小tips:每次調(diào)用fun就相對應(yīng)的在紙上還原作用域鏈锹安。

_fun_函數(shù)執(zhí)行,因?yàn)榈?個參數(shù)未定義,輸出undefined倚舀。然后返回一個對象叹哭,帶有fun屬性,指向一個函數(shù)對象痕貌,帶有閉包,能夠訪問到_fun_和變量n风罩。
a.fun(1)執(zhí)行返回的對象的fun方法,傳入m的值1舵稠,調(diào)用返回_fun_(1,0)超升,打印出0;
a.fun(1)執(zhí)行返回的對象的fun方法哺徊,傳入m的值1室琢,調(diào)用返回_fun_(2,0),打印出0落追;

接下來的var b = _fun_(0).fun(1).fun(2).fun(3);可以等價(jià)為

var b = _fun_(0);
var b1 = b.fun(1);
var b2 = b1.fun(2);
var b3 = b2.fun(3);
前兩句的和我們剛剛分析的情況是一樣的盈滴,var b = _fun_(0);首先返回一個對象,帶有fun屬性轿钠,指向一個函數(shù)對象巢钓,帶有閉包,能夠訪問_fun_和變量n谣膳。
b.fun(1)傳入m的值為1竿报,調(diào)用返回_fun_(1,0),打印出0继谚;
b1.fun(2)傳入m的值為2烈菌,調(diào)用返回_fun_(2,1),打印出1;
b2.fun(3)傳入m的值為3芽世,調(diào)用返回_fun_(3,2)挚赊,打印出2;

接下來var c = _fun_(0).fun(1);情況和前面的是一樣的济瓢,先打印出undefined荠割,再打印出0;
c.fun(2)傳入m的值為2,調(diào)用返回_fun_(2,1)旺矾,打印出1蔑鹦;
c.fun(3)傳入m的值為,調(diào)用返回_fun_(3,1)箕宙,打印出1嚎朽;

還要注意,這里傳遞的是一個簡單的數(shù)據(jù)類型柬帕,而不是引用的數(shù)據(jù)類型哟忍。
看看這個?
https://lemontency.github.io/2019/03/20/JS%E4%B8%AD%E7%9A%84%E6%8C%89%E5%80%BC%E4%BC%A0%E9%80%92%E5%92%8C%E6%8C%89%E5%BC%95%E7%94%A8%E9%97%AE%E9%A2%98-1/

參考:https://www.zhihu.com/question/48238548
https://segmentfault.com/a/1190000004187681

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陷寝,一起剝皮案震驚了整個濱河市锅很,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凤跑,老刑警劉巖爆安,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異饶火,居然都是意外死亡鹏控,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門肤寝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來当辐,“玉大人,你說我怎么就攤上這事鲤看≡稻荆” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵义桂,是天一觀的道長找筝。 經(jīng)常有香客問我,道長慷吊,這世上最難降的妖魔是什么袖裕? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮溉瓶,結(jié)果婚禮上急鳄,老公的妹妹穿的比我還像新娘谤民。我一直安慰自己,他們只是感情好疾宏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布张足。 她就那樣靜靜地躺著,像睡著了一般坎藐。 火紅的嫁衣襯著肌膚如雪为牍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天岩馍,我揣著相機(jī)與錄音碉咆,去河邊找鬼。 笑死兼雄,一個胖子當(dāng)著我的面吹牛吟逝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赦肋,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼励稳!你這毒婦竟也來了佃乘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驹尼,失蹤者是張志新(化名)和其女友劉穎趣避,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體新翎,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡程帕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了地啰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愁拭。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亏吝,靈堂內(nèi)的尸體忽然破棺而出岭埠,到底是詐尸還是另有隱情,我是刑警寧澤蔚鸥,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布惜论,位于F島的核電站,受9級特大地震影響止喷,放射性物質(zhì)發(fā)生泄漏馆类。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一弹谁、第九天 我趴在偏房一處隱蔽的房頂上張望乾巧。 院中可真熱鬧技羔,春花似錦、人聲如沸卧抗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽社裆。三九已至拙绊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泳秀,已是汗流浹背标沪。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗜傅,地道東北人金句。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像吕嘀,于是被迫代替她去往敵國和親违寞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容