2019中高級前端秘籍之JavaScript篇


2019中高級前端秘籍之CSS篇
2019中高級前端秘籍之JavaScript篇
2019中高級前端秘籍之瀏覽器篇
2019中高級前端秘籍之服務端與網(wǎng)絡篇
2019中高級前端秘籍之算法篇


1. 原型 / 構造函數(shù) / 實例

原型(prototype): 一個簡單的對象税灌,用于實現(xiàn)對象的 屬性繼承奏纪÷仍幔可以簡單的理解成對象的爹肢扯。在 Firefox 和 Chrome 中必孤,每個JavaScript對象中都包含一個__proto__ (非標準)的屬性指向它爹(該對象的原型),可obj.__proto__進行訪問牢贸。

構造函數(shù): 可以通過new來 新建一個對象 的函數(shù)句惯。

實例: 通過構造函數(shù)和new創(chuàng)建出來的對象,便是實例输拇。 實例通過proto指向原型摘符,通過constructor指向構造函數(shù)。

說了一大堆策吠,大家可能有點懵逼逛裤,這里來舉個栗子,以Object為例猴抹,我們常用的Object便是一個構造函數(shù)带族,因此我們可以通過它構建實例。

// 實例
const instance = new Object()

則此時蟀给, 實例為instance, 構造函數(shù)為Object蝙砌,我們知道,構造函數(shù)擁有一個prototype的屬性指向原型跋理,因此原型為:

// 原型
const prototype = Object.prototype

這里我們可以來看出三者的關系:

實例.__proto__ === 原型

原型.constructor === 構造函數(shù)

構造函數(shù).prototype === 原型

// 這條線其實是是基于原型進行獲取的择克,可以理解成一條基于原型的映射線
// 例如: 
// const o = new Object()
// o.constructor === Object   --> true
// o.__proto__ = null;
// o.constructor === Object   --> false
實例.constructor === 構造函數(shù)

放大來看,我畫了張圖供大家徹底理解:


實例原型構造函數(shù).png

2.原型鏈:

原型鏈是由原型對象組成前普,每個對象都有 proto 屬性肚邢,指向了創(chuàng)建該對象的構造函數(shù)的原型,proto 將對象連接起來組成了原型鏈汁政。是一個用來實現(xiàn)繼承和共享屬性的有限的對象鏈道偷。

屬性查找機制: 當查找對象的屬性時缀旁,如果實例對象自身不存在該屬性,則沿著原型鏈往上一級查找勺鸦,找到時則輸出并巍,不存在時,則繼續(xù)沿著原型鏈往上一級查找换途,直至最頂級的原型對象Object.prototype如還是沒找到懊渡,則輸出undefined

屬性修改機制: 只會修改實例對象本身的屬性军拟,如果不存在剃执,則進行添加該屬性,如果需要修改原型的屬性時懈息,則可以用: b.prototype.x = 2肾档;但是這樣會造成所有繼承于該對象的實例的屬性發(fā)生改變。

3. 執(zhí)行上下文(EC)

執(zhí)行上下文可以簡單理解為一個對象:

它包含三個部分:

????變量對象(VO)
????作用域鏈(詞法作用域)
????this指向

它的類型:

????全局執(zhí)行上下文
????函數(shù)執(zhí)行上下文
????eval執(zhí)行上下文

代碼執(zhí)行過程:

????創(chuàng)建 全局上下文 (global EC)
????全局執(zhí)行上下文 (caller) 逐行 自上而下 執(zhí)行辫继。遇到函數(shù)時怒见,函數(shù)執(zhí)行上下文 (callee) 被push到執(zhí)行棧頂層
????函數(shù)執(zhí)行上下文被激活,成為 active EC, 開始執(zhí)行函數(shù)中的代碼姑宽,caller 被掛起
????函數(shù)執(zhí)行完后遣耍,callee 被pop移除出執(zhí)行棧,控制權交還全局上下文 (caller)炮车,繼續(xù)執(zhí)行

4.變量對象

變量對象舵变,是執(zhí)行上下文中的一部分,可以抽象為一種 數(shù)據(jù)作用域瘦穆,其實也可以理解為就是一個簡單的對象纪隙,它存儲著該執(zhí)行上下文中的所有 變量和函數(shù)聲明(不包含函數(shù)表達式)

活動對象 (AO): 當變量對象所處的上下文為 active EC 時扛或,稱為活動對象瘫拣。

5. 作用域

執(zhí)行上下文中還包含作用域鏈。理解作用域之前告喊,先介紹下作用域麸拄。作用域其實可理解為該上下文中聲明的 變量和聲明的作用范圍∏可分為 塊級作用域函數(shù)作用域
特性:
????聲明提前: 一個聲明在函數(shù)體內(nèi)都是可見的, 函數(shù)優(yōu)先于變量
????非匿名自執(zhí)行函數(shù)拢切,函數(shù)變量為 只讀 狀態(tài),無法修改

let foo = function() { console.log(1) };
(function foo() {
    foo = 10  // 由于foo在函數(shù)中只為可讀秆吵,因此賦值無效
    console.log(foo)
}()) 

// 結果打踊匆:  ? foo() { foo = 10 ; console.log(foo) }

6. 作用域鏈

我們知道,我們可以在執(zhí)行上下文中訪問到父級甚至全局的變量,這便是作用域鏈的功勞主穗。作用域鏈可以理解為一組對象列表泻拦,包含 父級和自身的變量對象,因此我們便能通過作用域鏈訪問到父級里聲明的變量或者函數(shù)忽媒。
由兩部分組成:
????[[scope]]屬性: 指向父級變量對象和作用域鏈争拐,也就是包含了父級的[[scope]]AO
????AO: 自身活動對象

如此[[scopr]]包含[[scope]],便自上而下形成一條 鏈式作用域晦雨。

7. 閉包

閉包屬于一種特殊的作用域架曹,稱為 靜態(tài)作用域。它的定義可以理解為: 父函數(shù)被銷毀 的情況下闹瞧,返回出的子函數(shù)的[[scope]]中仍然保留著父級的單變量對象和作用域鏈绑雄,因此可以繼續(xù)訪問到父級的變量對象,這樣的函數(shù)稱為閉包奥邮。

閉包會產(chǎn)生一個很經(jīng)典的問題:

多個子函數(shù)的[[scope]]都是同時指向父級万牺,是完全共享的。因此當父級的變量對象被修改時洽腺,所有子函數(shù)都受到影響杏愤。

解決:

變量可以通過 函數(shù)參數(shù)的形式 傳入,避免使用默認的[[scope]]向上查找
使用setTimeout包裹已脓,通過第三個參數(shù)傳入
使用 塊級作用域,讓變量成為自己上下文的屬性通殃,避免共享

8. script 引入方式

html 靜態(tài)<script>引入
js 動態(tài)插入<script>
<script defer>: 延遲加載度液,元素解析完成后執(zhí)行
<script async>: 異步加載,但執(zhí)行時會阻塞元素渲染

9. 對象的拷貝

淺拷貝: 以賦值的形式拷貝引用對象画舌,仍指向同一個地址堕担,修改時原對象也會受到影響
????Object.assign
????展開運算符(...)

深拷貝: 完全拷貝一個新對象,修改時原對象不再受到任何影響
???? JSON.parse(JSON.stringify(obj)): 性能最快
????具有循環(huán)引用的對象時曲聂,報錯
????當值為函數(shù)霹购、undefined、或symbol時朋腋,無法拷貝
????遞歸進行逐一賦值

10. new運算符的執(zhí)行過程

????新生成一個對象
????鏈接到原型: obj.__proto__ = Con.prototype
????綁定this: apply
????返回新對象(如果構造函數(shù)有自己 retrun 時齐疙,則返回該值)

11. instanceof原理

能在實例的 原型對象鏈 中找到該構造函數(shù)的prototype屬性所指向的 原型對象,就返回true旭咽。即:

// __proto__: 代表原型對象鏈
instance.[__proto__...] === instance.constructor.prototype

// return true

12. 代碼的復用

當你發(fā)現(xiàn)任何代碼開始寫第二遍時贞奋,就要開始考慮如何復用。一般有以下的方式:

????函數(shù)封裝
????繼承
????復制extend
????混入mixin
????借用apply/call

13. 繼承

在 JS 中穷绵,繼承通常指的便是 原型鏈繼承轿塔,也就是通過指定原型,并可以通過原型鏈繼承原型上的屬性或者方法。

????最優(yōu)化: 圣杯模式

var inherit = (function(c,p){
    var F = function(){};
    return function(c,p){
        F.prototype = p.prototype;
        c.prototype = new F();
        c.uber = p.prototype;
        c.prototype.constructor = c;
    }
})();

使用 ES6 的語法糖 class / extends

14.類型轉(zhuǎn)換

大家都知道 JS 中在使用運算符號或者對比符時勾缭,會自帶隱式轉(zhuǎn)換揍障,規(guī)則如下:

-、*俩由、/毒嫡、% :一律轉(zhuǎn)換成數(shù)值后計算
+:
????數(shù)字 + 字符串 = 字符串, 運算順序是從左到右
????數(shù)字 + 對象采驻, 優(yōu)先調(diào)用對象的valueOf -> toString
????數(shù)字 + boolean/null -> 數(shù)字
????數(shù)字 + undefined -> NaN
[1].toString() === '1'
{}.toString() === '[object object]'
NaN !== NaN 审胚、+undefinedNaN

15. 類型判斷

判斷 Target 的類型,單單用 typeof 并無法完全滿足礼旅,這其實并不是 bug膳叨,本質(zhì)原因是 JS 的萬物皆對象的理論。因此要真正完美判斷時痘系,我們需要區(qū)分對待:

????基本類型(null): 使用 String(null)
????基本類型(string / number / boolean / undefined) + function: 直接使用 typeof即可
????其余引用類型(Array / Date / RegExp Error): 調(diào)用toString后根據(jù)[object XXX]進行判斷

很穩(wěn)的判斷封裝:

let class2type = {}
'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase()) 

function type(obj) {
    if (obj == null) return String(obj)
    return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}

16. 模塊化

模塊化開發(fā)在現(xiàn)代開發(fā)中已是必不可少的一部分菲嘴,它大大提高了項目的可維護、可拓展和可協(xié)作性汰翠。通常龄坪,我們 在瀏覽器中使用 ES6 的模塊化支持,在 Node 中使用 commonjs 的模塊化支持复唤。

分類:

????es6: import / export
????commonjs: require / module.exports / exports
????amd: require / defined

require與import的區(qū)別

????require支持 動態(tài)導入健田,import不支持,正在提案 (babel 下可支持)
????require是 同步 導入佛纫,import屬于 異步 導入
????require是 值拷貝妓局,導出值變化不會影響導入值;import指向 內(nèi)存地址呈宇,導入值會隨導出值而變化

17. 防抖與節(jié)流

防抖與節(jié)流函數(shù)是一種最常用的 高頻觸發(fā)優(yōu)化方式好爬,能對性能有較大的幫助。

防抖 (debounce): 將多次高頻操作優(yōu)化為只在最后一次執(zhí)行甥啄,通常使用的場景是:用戶輸入存炮,只需再輸入完成后做一次輸入校驗即可。

function debounce(fn, wait, immediate) {
    let timer = null

    return function() {
        let args = arguments
        let context = this

        if (immediate && !timer) {
            fn.apply(context, args)
        }

        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, wait)
    }
}

節(jié)流(throttle): 每隔一段時間后執(zhí)行一次蜈漓,也就是降低頻率穆桂,將高頻操作優(yōu)化成低頻操作,通常使用場景: 滾動條事件 或者 resize 事件融虽,通常每隔 100~500 ms執(zhí)行一次即可充尉。

function throttle(fn, wait, immediate) {
    let timer = null
    let callNow = immediate
    
    return function() {
        let context = this,
            args = arguments

        if (callNow) {
            fn.apply(context, args)
            callNow = false
        }

        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(context, args)
                timer = null
            }, wait)
        }
    }
}

18. 函數(shù)執(zhí)行改變this

由于 JS 的設計原理: 在函數(shù)中,可以引用運行環(huán)境中的變量衣形。因此就需要一個機制來讓我們可以在函數(shù)體內(nèi)部獲取當前的運行環(huán)境驼侠,這便是this姿鸿。

因此要明白 this 指向,其實就是要搞清楚 函數(shù)的運行環(huán)境倒源,說人話就是苛预,誰調(diào)用了函數(shù)。例如:

????obj.fn()笋熬,便是 obj調(diào)用了函數(shù)热某,既函數(shù)中的 this === obj
????fn(),這里可以看成 window.fn()胳螟,因此 this === window

但這種機制并不完全能滿足我們的業(yè)務需求昔馋,因此提供了三種方式可以手動修改 this 的指向:

????call: fn.call(target, 1, 2)
????apply: fn.apply(target, [1, 2])
????bind: fn.bind(target)(1,2)

19.ES6/ES7

由于 Babel 的強大和普及,現(xiàn)在 ES6/ES7 基本上已經(jīng)是現(xiàn)代化開發(fā)的必備了糖耸。通過新的語法糖秘遏,能讓代碼整體更為簡潔和易讀。

聲明

let / const: 塊級作用域嘉竟、不存在變量提升邦危、暫時性死區(qū)、不允許重復聲明
const: 聲明常量舍扰,無法修改

解構賦值

class / extend: 類聲明與繼承

Set / Map: 新的數(shù)據(jù)結構

異步解決方案

Promise的使用與實現(xiàn)
generator:
????yield: 暫停代碼
????next(): 繼續(xù)執(zhí)行代碼

function* helloWorld() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
const generator = helloWorld();
generator.next()  // { value: 'hello', done: false }
generator.next()  // { value: 'world', done: false }
generator.next()  // { value: 'ending', done: true }
generator.next()  // { value: undefined, done: true }

await / async: 是generator的語法糖倦蚪, babel中是基于promise實現(xiàn)。

async function getUserByAsync(){
   let user = await fetchUser();
   return user;
}
const user = await getUserByAsync()
console.log(user)

20. AST

抽象語法樹 (Abstract Syntax Tree)边苹,是將代碼逐字母解析成 樹狀對象 的形式陵且。這是語言之間的轉(zhuǎn)換、代碼語法檢查个束,代碼風格檢查慕购,代碼格式化,代碼高亮播急,代碼錯誤提示,代碼自動補全等等的基礎售睹。例如:

function square(n){
    return n * n
}

通過解析轉(zhuǎn)化成的AST如下圖:


AST.png

21. babel編譯原理

babylonES6/ES7 代碼解析成 AST
babel-traverseAST 進行遍歷轉(zhuǎn)譯桩警,得到新的 AST
AST 通過 babel-generator 轉(zhuǎn)換成 ES5

22. 函數(shù)柯里化

在一個函數(shù)中,首先填充幾個參數(shù)昌妹,然后再返回一個新的函數(shù)的技術捶枢,稱為函數(shù)的柯里化。通撤裳拢可用于在不侵入函數(shù)的前提下烂叔,為函數(shù) 預置通用參數(shù),供多次重復調(diào)用固歪。

const add = function add(x) {
    return function (y) {
        return x + y
    }
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21

23. 數(shù)組(array)

map: 遍歷數(shù)組蒜鸡,返回回調(diào)返回值組成的新數(shù)組

forEach: 無法break胯努,可以用try/catchthrow new Error來停止

filter: 過濾

some: 有一項返回true,則整體為true

every: 有一項返回false逢防,則整體為false

join: 通過指定連接符生成字符串

push / pop: 末尾推入和彈出叶沛,改變原數(shù)組, 返回推入/彈出項

unshift / shift: 頭部推入和彈出忘朝,改變原數(shù)組灰署,返回操作項

sort(fn) / reverse: 排序與反轉(zhuǎn),改變原數(shù)組

concat: 連接數(shù)組局嘁,不影響原數(shù)組溉箕, 淺拷貝

slice(start, end): 返回截斷后的新數(shù)組,不改變原數(shù)組

splice(start, number, value...): 返回刪除元素組成的數(shù)組悦昵,value 為插入項肴茄,改變原數(shù)組

indexOf / lastIndexOf(value, fromIndex): 查找數(shù)組項,返回對應的下標

reduce / reduceRight(fn(prev, cur)旱捧, defaultPrev): 兩兩執(zhí)行独郎,prev 為上次化簡函數(shù)的return值,cur 為當前值(從第二項開始)

數(shù)組亂序:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});

數(shù)組拆解: flat: [1,[2,3]] --> [1, 2, 3]

Array.prototype.flat = function() {
    return this.toString().split(',').map(item => +item )
}

作者:郭東東
鏈接:https://juejin.im/post/5c64d15d6fb9a049d37f9c20
來源:掘金
著作權歸作者所有枚赡。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權氓癌,非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贫橙,一起剝皮案震驚了整個濱河市贪婉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卢肃,老刑警劉巖疲迂,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莫湘,居然都是意外死亡尤蒿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來澜公,“玉大人弦讽,你說我怎么就攤上這事∈竟” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵呵萨,是天一觀的道長奏属。 經(jīng)常有香客問我,道長潮峦,這世上最難降的妖魔是什么囱皿? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任勇婴,我火速辦了婚禮,結果婚禮上铆帽,老公的妹妹穿的比我還像新娘咆耿。我一直安慰自己,他們只是感情好爹橱,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布萨螺。 她就那樣靜靜地躺著,像睡著了一般愧驱。 火紅的嫁衣襯著肌膚如雪慰技。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天组砚,我揣著相機與錄音吻商,去河邊找鬼。 笑死糟红,一個胖子當著我的面吹牛艾帐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盆偿,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼柒爸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了事扭?” 一聲冷哼從身側響起捎稚,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎求橄,沒想到半個月后今野,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡罐农,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年条霜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涵亏。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡宰睡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溯乒,到底是詐尸還是另有隱情夹厌,我是刑警寧澤豹爹,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布裆悄,位于F島的核電站,受9級特大地震影響臂聋,放射性物質(zhì)發(fā)生泄漏光稼。R本人自食惡果不足惜或南,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望艾君。 院中可真熱鬧采够,春花似錦、人聲如沸冰垄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虹茶。三九已至逝薪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝴罪,已是汗流浹背董济。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留要门,地道東北人虏肾。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像欢搜,于是被迫代替她去往敵國和親封豪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345