JS難點(diǎn)同步異步和作用域與閉包及原型和原型鏈詳解
JS三座大山
同步異步
前端中只有兩個(gè)操作是異步的:
- 定時(shí)器異步執(zhí)行;
- ajax異步請(qǐng)求
編譯器解析+執(zhí)行代碼原理:
1.編譯器從上往下逐一解析代碼
2.判斷代碼是同步還是異步
- 同步:立即執(zhí)行
- 異步:不執(zhí)行。放入事件隊(duì)列池
3.等所有同步執(zhí)行完畢開始執(zhí)行異步
同步異步區(qū)別
api : 異步有回調(diào),同步?jīng)]有回調(diào)
性能 : 異步性能好(不阻塞線程) 同步會(huì)阻塞線程
順序 : 同步有序執(zhí)行,異步無序執(zhí)行
另:回調(diào)函數(shù):如果一個(gè)函數(shù)的參數(shù)是一個(gè)函數(shù),那么這個(gè)參數(shù)函數(shù)叫做回調(diào)函數(shù)
作用域、閉包
函數(shù)作用域鏈
- 只有
var聲明的變量認(rèn)的是函數(shù)作用域 - 只有函數(shù)才能開辟新的函數(shù)作用域
- 默認(rèn)全局區(qū)域,我們稱之為0級(jí)作用域,在0級(jí)上聲明的函數(shù),會(huì)開一個(gè)新的作用域,我們稱之為1級(jí)作用域
- 在1級(jí)里聲明的函數(shù),開辟的函數(shù)開辟作用域,我們就2級(jí)作用域,以此類推像這樣的作用域
- 像一個(gè)鏈條一樣稱之為作用域鏈

塊作用域
1.一個(gè) {} 就是一個(gè)塊級(jí)作用域,let 認(rèn)塊級(jí)作用域
2.對(duì)于let而言,默認(rèn)的全局也稱之為0級(jí)塊作用域,然后除了對(duì)象以外,大括號(hào)開辟新的塊作用域,0級(jí)上開辟的就是1級(jí),1級(jí)里開辟的就是2級(jí)以此類推
3.跟我們之前學(xué)的函數(shù)作用域鏈?zhǔn)且粯拥?,只不過函數(shù)作用域只有函數(shù)開辟函數(shù)作用域,塊作用是大括號(hào)開辟塊作用域,var認(rèn)函數(shù)作用域,let認(rèn)塊級(jí)作用域
閉包
閉包就是一個(gè)函數(shù), 能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
代碼示例:
function foo () {
// foo的內(nèi)部變量
let num = 10
// 這個(gè)inner就叫閉包
function inner() {
console.log(num)
}
}
閉包有什么用呢?
- 閉包相當(dāng)于是溝通外界和函數(shù)內(nèi)部的橋梁
- 也就是使用閉包就可以讓外界訪問到函數(shù)內(nèi)部的變量了
閉包和直接返回值有差別
直接返回值,其實(shí)本質(zhì)上只是返回這個(gè)值的數(shù)據(jù),后面的更改跟函數(shù)內(nèi)部的變量沒有任何關(guān)系
所以用閉包,就能讓外界間接的訪問到函數(shù)內(nèi)部的變量
閉包的幾種寫法
簡(jiǎn)單來說:只要符合閉包特征的都算閉包
閉包:是一個(gè)函數(shù),一個(gè)能讓外界訪問函數(shù)內(nèi)部變量的函數(shù)
閉包作用1:延長(zhǎng)變量生命周期
生命周期: 指從聲明到銷毀的周期
全局變量生命周期 從網(wǎng)頁打開到網(wǎng)頁結(jié)束
局部變量生命周期 從它所在的作用域開始到結(jié)束
正是因?yàn)殚]包能延長(zhǎng)變量的生命周期,所以外界依然能一直訪問局部變量
所以大量使用閉包,可能會(huì)導(dǎo)致內(nèi)存泄漏
內(nèi)存泄漏:一個(gè)數(shù)據(jù)后面已經(jīng)不需要用了,但是它還占著內(nèi)存空間,沒有被回收
解決辦法:
- 把閉包賦值為null即可
- 賦值為null,就代表這個(gè)數(shù)據(jù)沒有“人”引用了,沒人引用了那么就會(huì)被標(biāo)記為“垃圾”,然后會(huì)被GC在合適的時(shí)候回收
- GC:垃圾回收機(jī)制
- chrome瀏覽器用了這個(gè)機(jī)制
閉包作用2: 限制訪問
把變量變?yōu)榫植孔兞客饨缇蜔o法直接訪問
它只能通過我們寫的閉包(橋梁)來間接訪問,這樣做我們就可以對(duì)閉包里面做一些限制,這樣就起到了限制訪問的目的
閉包調(diào)用注意事項(xiàng)
注意:
產(chǎn)生閉包的外層函數(shù)調(diào)用多少次,就會(huì)產(chǎn)生多少個(gè)閉包,那么到時(shí)候操作的是不同的數(shù)據(jù),
如果產(chǎn)生閉包的外層函數(shù)調(diào)用1次,那么就只產(chǎn)生1個(gè)閉包,到時(shí)候操作的數(shù)據(jù)還是同一個(gè)
閉包解決用var導(dǎo)致下標(biāo)錯(cuò)誤的問題
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
width: 30px;
height: 30px;
background-color: yellow;
line-height: 30px;
text-align: center;
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
// 以前有l(wèi)et嗎?沒有,所以我們看用var如何解決下標(biāo)問題
// 找到所有的li
var lis = document.getElementsByTagName('li')
for (var i = 0; i < lis.length; i++) {
// 現(xiàn)在這里的i,只有1個(gè)變量,所以最終點(diǎn)擊的時(shí)候,大家訪問的都是一個(gè)i
// 而i最終值是5,所以無論點(diǎn)誰都是5
// 解決辦法:我有5個(gè)li就要有5個(gè)變量,變量的值分別是0,1,2,3,4
// 如何才能產(chǎn)生5個(gè)不一樣的變量?就要開辟函數(shù)作用域
(function () {
// 因?yàn)檫@個(gè)循環(huán)會(huì)產(chǎn)生5個(gè)自執(zhí)行函數(shù)
// 就意味著有5個(gè)不同的index變量
// 然后它的值需要分別是0,1,2,3,4,而i的值剛好是0,1,2,3,4
// 所以把i賦值給index即可
var index = i
// 給每個(gè)li加點(diǎn)擊事件
lis[i].onclick = function () {
alert(index)
}
})()
}
</script>
</body>
</html>
投票機(jī)
// 要做一個(gè)能投票,能得到當(dāng)前票數(shù)的功能
function votor() {
// 有一個(gè)變量,用來存票數(shù)
let ticket = 0
return {
getTicket: function () {
console.log('當(dāng)前票數(shù)為:' + ticket)
},
// 投票
add: function () {
ticket++
}
}
}
let tp = votor()
tp.add()
tp.add()
tp.add()
tp.add()
tp.getTicket()
閉包兩個(gè)面試題
// 思考題 1:
window.name = "The Window";
let object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
console.log(object.getNameFunc()());
// 思考題 2:
window.name = "The Window";
let object = {
name: "My Object",
getNameFunc: function () {
let that = this;
return function () {
return that.name;
};
}
};
console.log(object.getNameFunc() ());
原型、原型鏈
原型對(duì)象
作用:
構(gòu)造函數(shù)中共有的屬性和方法添加到原型對(duì)象中,可以節(jié)省內(nèi)存
原型鏈
無論任何一個(gè)對(duì)象開始出發(fā), 按照 proto 開始向上查找, 最終都能找到 Object.prototype, 這個(gè)使用 __proto__串聯(lián)起來的對(duì)象鏈狀結(jié)構(gòu), 叫做原型鏈
完整原型鏈圖
示意圖前置知識(shí):
每一個(gè)對(duì)象都有__proto__屬性, 指向所屬構(gòu)造函數(shù)的原型對(duì)象;
每一個(gè)對(duì)象都有prototype屬性, 是一個(gè)對(duì)象;
函數(shù)也是對(duì)象;
當(dāng)一個(gè)對(duì)象, 沒有準(zhǔn)確的構(gòu)造函數(shù)來實(shí)例化的時(shí)候, 我們都看作是內(nèi)置構(gòu)造函數(shù)
Object 的實(shí)例;Object的protype是頂級(jí)原型對(duì)象, 他的__protype__指向是null;
Function是頂級(jí)構(gòu)造函數(shù), 它自己是自己的構(gòu)造函數(shù), 同時(shí)自己是自己的實(shí)例對(duì)象(通俗來說, 就是它自己創(chuàng)建了自己)
完整原型示意圖:

以上就是JS難點(diǎn)同步異步的作用域與閉包原型及原型鏈詳解的詳細(xì)內(nèi)容,更多關(guān)于JS同步異步作用域與閉包原型及原型鏈的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Javascript基礎(chǔ):運(yùn)算符與流程控制詳解
這篇文章主要給大家介紹了關(guān)于Javascript中運(yùn)算符及流程控制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-10-10
JavaScript中將數(shù)組進(jìn)行合并的基本方法講解
這篇文章主要介紹了JavaScript中將數(shù)組進(jìn)行合并的基本方法講解,包括快速合并多個(gè)數(shù)組的方法,需要的朋友可以參考下2016-03-03
javascript驗(yàn)證form表單數(shù)據(jù)的案例詳解
這篇文章主要介紹了javascript驗(yàn)證form表單數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Javascript中的數(shù)學(xué)函數(shù)
Javascript中的數(shù)學(xué)函數(shù)...2007-04-04
JavaScrip關(guān)于創(chuàng)建常量的知識(shí)點(diǎn)
這篇文章主要介紹了JavaScrip創(chuàng)建常量的相關(guān)知識(shí)點(diǎn),幫助大家對(duì)JS更加深入的學(xué)習(xí),參考下吧。2017-12-12
Typescript中interface與type的相同點(diǎn)與不同點(diǎn)的詳細(xì)說明
這篇文章主要介紹了Typescript中interface與type的相同點(diǎn)與不同點(diǎn),并配有實(shí)例說明,需要的朋友可以參考下2022-11-11
關(guān)于鍵盤事件中keyCode、which和charCode 的兼容性測(cè)試
關(guān)于鍵盤事件中keyCode、which和charCode 的兼容性測(cè)試...2006-12-12

