HTTP
客戶端/伺服器模型
- 客戶端主動傳送要求。
- 被動地等待來自使用者端的要求,處理請求並傳回響應
- 客戶端等待,然後處理響應。
效能優化
雪碧圖 http快取 虛擬dom webpack
三次握手
為什麼要三次握手? 三次握手: A:我能連你了嗎? B: 可以連我,你連吧 A:那我連你了 開始傳送資料
原因:因為要保證A/B 都可以收發資訊 ,資料才能在AB之間傳輸
A:我能連你了嗎? B: 可以 說明A可以發資訊,B可以接受資訊
B: 可以連我,你連吧 A:那我連你了 說明B可以傳送資訊,A可以接受資訊
HTML5
html5中新增加的標籤,技術(api)(包括promise,localStorage,canvas等),統稱為html5.
CSS
CSS選擇器
-
.
類選擇器,#
id選擇器,input[type=text] [data-x] [target=_blank]
屬性選擇器 -
直接子代選擇器
>
後代選擇器空格,
代表兩個都選 -
偽類選擇器
:
input:focus
選擇獲得焦點的 input 元素。a:hover
p:first-child
p:last-child
p:nth-child(1)
p:nth-child(even)
p:nth-child(odd)
p:before
p:after
input:enabled
選擇每個啟用的 元素。
input:disabled
選擇每個禁用的 元素
input:checked
選擇每個被選中的 元素。
CSS2:高度與寬度
- inline的高度是行高,而不是font-size
- 所有inline和inline-block元素之間,不管有多少回車和空格或者tab,都只顯示一個空格,解決方法:1float:left,然後clearfix清除浮動,2display:flex
- 超出一行文字省略:
div{ border:1px solid red; /*以下三行*/ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } 複製程式碼
- 阻止父子上下margin合併的方法:1.父親padding0.1或者兒子border0.1擋住,可以防止合併。2父親overflow:hidden;
- 文件流:1.內聯元素從左到右依次排列,如果空間不夠,就換一行從左到右依次排列2.塊級元素另起一行,從上到下佔滿整行。
- 脫離文件流的情況
父元素div高度變小
float
position:absolute
position:fixed
注意:position:relative
沒有脫離文件流,只是相對於之前的位置定位,原來的地方還佔據位置。 - 寬高固定居中套路:
position:absolute;top:0;bottom:0;left:0;right:0;margin:auto;
- 內聯元素的padding 和margin只會影響寬度,但是不會影響高度,內聯元素的高度只受line-height影響。
CSS4 icon:SVG
SVG:SVG其實是一段程式碼,瀏覽器可以解析這段程式碼,將它變成圖形 優點:可以漸變,不會失真,可以動
flex佈局(css6)
我理解flex從兩個方面去理解:
- 元素佈局
主軸和交叉軸,彈性元素永遠沿主軸排列
/*容器上:*/ flex-direction/* 彈性元素按主軸排列的方向*/ flex-wrap/*nowrap | wrap | wrap-reverse,主軸排列不下,是否換行 。預設是nowrap不折行。不會溢位。元素的彈性伸縮*/ flex-flow/*上面兩個的縮寫*/ justify-content/* 主軸對齊方式,五個屬性*/ align-items/* 單行交叉軸的元素對齊方式。預設stretch(元素未在交叉軸上設定尺寸,將把交叉軸自動填滿,有flex-start,end,center,baseline幾個屬性)*/ align-content/*多行交叉軸的對齊方式。flex-wrap: wrap的時候。stretch(這幾行在垂直方向上佔滿) | flex-start(這幾行擠在上面) | flex-end(擠在下面) | center(擠在中間) | space-between | space-around */ /*元素上:*/ order/* 元素排列次序*/ align-self/* 單個元素的align-items*/ 複製程式碼
- 元素伸縮
當容器為
flex-wrap: nowrap;
時,元素會伸縮。 在元素上寫:
flex-shrink
預設為1,按比例縮小,佔滿一行flex-grow:
放大比例。預設為0,不放大。如果兩個子元素flex-grow:3,flex-grow:2,那麼意思就是剩下的空間按3:2分配。flex-basis
預設為為auto
。如果有width
,就由width
決定,如果沒有width
,就由內容的寬度決定。flex-basis
有數值。忽略width
,由flex-basis
決定flex-basis:0
由內容寬度決定。因為他的優先順序比width
高
flex
是上面三個的縮寫。flex: 1 = flex: 1 1 0%
既以內容的大小(不管width是多少) 按比例放大或縮小,佔滿一行 -flex: auto = flex: 1 1 auto;
既考慮width, 按比例放大或縮小,佔滿一行flex: none = flex: 0 0 auto;
不放大也不縮小,width是多少,就是多少常用於固定尺寸 不伸縮
calc()
width: calc(50% - 10px);
寬度100%會把父盒子撐破, 解決方法:
box-sizing:border-box;
width: calc(100% - 10px);
佈局CSS7
聖盃佈局和雙飛翼佈局 是老的佈局方式,2012年以前流行,現在已不流行,但是面試可能會考察到. why it?
- 是三列布局,兩邊固定寬度,中間自適應
- 中間內容元素在 dom 元素次序中優先位置
原理:實際上都用了浮動和負邊距的特性。 雙飛翼實現方法: 1先全部fl 2main width100% 3 左邊的一塊 margin-left: -100%; /4/ 右邊的一塊 margin-left: -150px; /5/ 4.main.wrap{ margin-left: 100px; /6/ margin-right: 150px; /6/ background: blue; /7/ height: 350px; /7/ } 雙飛翼:js.jirengu.com/fevifumike/…
CSS8:到底什麼是BFC?
內部元素完全包起來(float等),外部元素界限分明,不重疊。
面試官問的時候就說:
- 首先一個BFC可以包住浮動元素,不讓他超過邊界(但這不是清除浮動,清除浮動用clearfix),即使是子元素的margin也可以包住,不衝出父元素。比如舉一個程式碼的例子,兒子是浮動元素,那麼只要在爸爸上寫overflow:hidden;或者overflow:auto;或者diaplay:flow-root;那麼爸爸就會包住兒子,且margin也會在爸爸裡面)
- 讓兩個相鄰的BFC界限分明。 舉個例子: 兩個div是是兄弟關係,如果哥哥浮動了,那麼哥哥會疊在弟弟上面,會遮蓋弟弟。這時候如果把弟弟也變成BFC,那麼哥哥和弟弟之間就會界限分明,誰都不會遮擋誰。
移動端開發
響應式兩點:(CSS5)
- 媒體查詢:
2.加上@media (max-width: 768px){/*0-768*/ body{ background-color: blue; } } @media (max-width: 425px){/*0-425*/ body{ background-color: red; } } 複製程式碼
meta viewport
歷史原因:最開始手機瀏覽器(蘋果三)會在自己的三四百畫素的手機上模擬980畫素的顯示效果,然後讓使用者自己去縮放,檢視網頁. 那麼就告訴瀏覽器不要縮放網頁,手機螢幕是多少畫素,就顯示多少畫素的網頁.使用下面的程式碼<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 複製程式碼
手機端什麼CSS屬性都可以用,不用考慮相容的問題
CSS9動態 REM
動態 REM是手機專用,是如何適配所有手機的方案。
em
:一個m的寬度.如果面試官問,就說是一個漢字的寬度.這個單位是相對於font-size的
rem
:root em,根元素的font-size
.即 <html>
的font-size
.rem是相對於html的font-size的.
vh
:視口高度,總共100vh
vw
:視口寬度,總共100cw
pc端只需要選一個佈局(flex或者float),然後定寬就可以了。但是手機端不行,因為螢幕太多了 解決方法:
- 使用百分比佈局。(缺點:高度無法使用百分比)
- 使用vw,vh。(缺點是相容性不好)
- 使用動態 REM整體縮放
動態rem:
因為rem
這個單位跟<html>
標籤的font-size
掛鉤。那麼用js
將<html>
標籤的font-size
與頁面寬度掛鉤(pagewidth),那麼rem
這個單位就間接地與頁面寬度掛鉤了。
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>動態REM</title>
<script></script>
</head>
複製程式碼
然後佈局等大單位就用rem,但是border,font-size等小單位,還用px即可,因為這些單位就算統一,在任何手機上的表現都沒有多少影響。
這樣寫之後,10rem就是頁面寬度。這樣就可以整體按比例縮放了。
JS
&&和||
&&
,他的返回值就是遇到的第一個falsy值,後面的不看了
1&&2&&3&&0&&2&&3
返回0,後面的不執行。
||
,他返回的就是第一個遇到的true值,後面的不看
0||0||0||false||1||2||0
返回1,後面的不執行。
七個資料型別
七種資料型別分為六個基本型別和物件。
六個基本型別分別是null
,undefined
,string
,number
,boolean
,symbol
物件又分為object
,function
,array
null和undefined的區別
相同點:if裡都是都是false值
不同點:
-
null
轉換為數字的時候是0
,undefined
表示未定義轉換為數字的時候是NaN
-
呼叫函式時,某個引數未設定任何值,這時就可以傳入
null
,表示該引數為空。而傳入undefined
就代表undefined
typeof
yuchengkai.cn/docs/zh/fro… typeof只需要記住兩點。
- typeof 對於基本型別,除了
null
顯示為object
,其餘都可以顯示正確的型別 - typeof 對於物件,除了函式為
function
都會顯示object
- typeof運算子只能顯示陣列的型別是Object,而
Array.isArray(arr)
方法可以識別陣列。
六個false值
除了下面六個值被轉為false
,其他值都視為true
undefined
null
false
0
NaN
""或''(空字串)
複製程式碼
NaN
NaN === NaN; // false
typeof NaN//number
複製程式碼
for in 和forEach
for in 遍歷物件
for in迴圈注意點
for(var key in person){
console.log(person.key)
}
複製程式碼
注意:person.key
等於 person['key']
要用
這裡遍歷要用person[key]
for... in可能會隨機遍歷,不按照宣告順序
forEach遍歷陣列
var arr=[1,2,3]
arr.forEach(function(e){
console.log(e)
})
//1 2 3
複製程式碼
其他型別轉換為String
null
和undefined
沒有tostring()
方法。只能null+''
和undefined+''
- 其他的用
.toString()
方法。或直接+''
。或者window.String()
方法
其他型別轉換為Number(主要是String)
五個方法
- 簡便方法
'1234'-0
(常用) - .
parseInt()
//預設十進位制 - 簡便方法二
+'1'
取正
其他型別轉換為Boolean(五個false值)
記住5個false值
null undefined NaN 0 ''
記住下面易錯點:
[]//ture
{}//ture
' '//ture 裡面有空格
複製程式碼
轉換方法兩個:
window.Boolean(xxx)
- 前面加上
!!xxx
記憶體
- 資料區(存的變數的資料)分為 Stack(棧記憶體) 和 Heap(堆記憶體)
- 簡單型別的資料直接存在 Stack 裡
- 複雜型別的資料是把 Heap 地址存在 Stack 裡(物件包括:狹義物件(object),函式(function),陣列(array))
關於記憶體的面試題
引用: 一個廣義物件(object,array,function) 例如 var a= {xxx:'xxx'} a存的是{xxx:'xxx'}這個廣義物件的地址,假如是10000。那麼a(或a存的地址10000)就是這個物件({xxx:'xxx'})的引用。
var a = 1 var b = a b = 2 請問 a 顯示是幾? 2 1 (深拷貝)
var a = {name: 'a'}
var b = a
b = {name: 'b'}
複製程式碼
請問現在 a.name 是多少? 'a'
var a = {name: 'a'}
var b = a
b.name = 'b'
複製程式碼
請問現在 a.name 是多少? 'b'
var a = {name: 'a'}
var b = a
b = null
複製程式碼
請問現在 a 是什麼? {name: 'a'}
GC垃圾回收
如果一個物件沒有被引用,他就是垃圾,就會被回收(沒有引用找不到他,所以要回收)
{name:'a'}
就是垃圾,就要被回收,釋放記憶體
不會回收,因為document.body.onclick
這個引用在棧記憶體中存了fu這個函式的地址
記憶體洩漏:在ie6的時候,如果關閉頁面,一些垃圾是沒有被清除,記憶體被永久的佔用了
深拷貝
- 首先:值的儲存方法:兩種。基礎型別六個存入stack****棧記憶體,複合型別物件存入heap****堆記憶體
所以:
- 對於六個基本資料型別來說,直接賦值就是深拷貝,因為他們直接存在棧記憶體裡。
- 但是如果吧舊的物件賦值給新宣告的物件,那麼實際上只是把棧記憶體裡的地址賦值給了新物件,實際上堆記憶體裡仍然是原來的物件,如果修改新物件裡面的屬性,那麼原來的物件也會改變,因為用的是同一塊堆記憶體。直接給新物件賦值,無法實現拷貝。
如何實現物件的淺與深拷貝?
- 物件淺拷貝
- 淺拷貝
首先可以通過
Object.assign
來解決這個問題。
Object.assign()只拷貝第一層,如果屬性存的是一個引用,那麼他也只拷貝到引用。所以是淺拷貝let a = { age: 1 } let b = Object.assign({}, a) a.age = 2 console.log(b.age) // 1 複製程式碼
- 深拷貝
深拷貝
這個問題通常可以通過
JSON.parse(JSON.stringify(object))
來解決。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製程式碼
但是該方法也是有侷限性的:
- 會忽略
undefined
- 不能序列化函式
- 不能解決迴圈引用的物件 但是可以解決大部分問題。
作用域
JavaScript有函式級作用域,僅僅函式能建立新作用域。
變數的作用域表示這個變數存在的上下文。
setTimeout中的函式所處在於全域性作用域中,所以函式中使用this
關鍵字時,這個this
關鍵字指向的是全域性物件(window
)
變數提升
兩個題目
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo);
}
bar();
複製程式碼
答案是10
第二個例子
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
複製程式碼
alert輸出了1
變數提升後的順序 提升時程式碼表現上的排序為:
1 var a;
2 function f (){}
3 形參
4 this和arguments
複製程式碼
總結:
var
宣告變數,只會提升var a=undefined
(自動賦值為undefined
)。
function
宣告函式,function f (){}
,所有的都會提升。
let與const暫時死區
{ console.log(x) // Uncaught ReferenceError: x is not defined let x = 1 } let x = 1之前到花括號{,就是x的暫時死區,不用使用變數x,const一樣
instanceof
instanceof
運算子用來驗證,一個物件是否為指定的建構函式的例項。obj instanceof Object
返回true
,就表示obj
物件是Object
的例項。
標準庫
api太多
new
-
使用new命令時,它後面的函式依次執行下面的步驟。
- 建立一個空物件,作為將要返回的物件例項。
- 將這個空物件的原型,指向建構函式的prototype屬性。
- 將這個空物件賦值給函式內部的this關鍵字。
- 開始執行建構函式內部的程式碼。
既:
- 新生成了一個物件(新生成一個臨時物件)
- 連結到原型(新生成的物件
新物件.__proto__=建構函式.prototype
) - 繫結 this (
this = 新物件
) - 返回新物件 new應用舉例:
第一步寫私有屬性,第二步寫共有屬性.
可以看到這個物件的
1自有屬性 2__proto__指向的原型物件含有共有屬性. 3 共共有屬性(原型的屬性)constructor指向的建構函式ES5的Object.create()
以現有的物件作為模板,生成新的例項物件,這時就可以使用Object.create()
方法。
var person1 = {
name: '張三',
age: 38,
greeting: function() {
console.log('Hi! I\'m ' + this.name + '.');
}
};
var person2 = Object.create(person1);
person2.name // 張三
person2.greeting() // Hi! I'm 張三.
複製程式碼
this
this的實質:
- 引擎會將函式單獨儲存在記憶體中,然後再將函式的地址賦值給變數。
- 由於函式是一個單獨的值,所以它可以在不同的**環境(上下文)**執行。
- JavaScript 允許在函式體內部,引用當前環境的其他變數。
- 現在問題就來了,由於函式可以在不同的執行環境執行,所以需要有一種機制,能夠在函式體內部獲得當前的執行環境(context)。所以,
this
就出現了,它的設計目的就是在函式體內部,指代函式當前的執行環境。
總結一句話:this就是指函式所在的當前環境(既所在的物件)
this:
- 在建構函式裡,
this
代表新生成的例項物件 - 其他情況下,
this
就是屬性或方法“當前”所在的物件。它總是返回一個物件。
面試回答:;
fn()
裡面的this
就是window
(fn在最頂層)fn()
是strict mode
,this
就是undefined
(fn在最頂層,嚴格模式下是undefined)a.b.c.fn()
裡面的this
就是a.b.c
(屬性或方法當前所在的物件。)new F()
裡面的this
就是新生成的例項() => console.log(this)
裡面this
跟外面的this
的值一模一樣(ES6新增語法)(箭頭函式裡,this
就上一層的this
的值) 面試答上面的幾個↑
面試回答call() apply() bind():
1
call()方法,可以指定函式內部this
的指向(即函式執行時所在的作用域),然後在所指定的作用域中,呼叫該函式。
(這個所指定的作用域意思就是所指定的物件)
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
複製程式碼
call
後面的引數則是函式呼叫時所需的引數
2
apply()
與call()
唯一的區別就是,它接收一個陣列作為函式執行時的引數。
x.call(obj,1,2,3)
x.apply(obj,[1,2,3])
複製程式碼
3
bind方法用於將函式體內的this繫結到某個物件,然後返回一個新函式。
call
和apply
都是繫結新this
後直接執行函式,而bind
沒有執行,而是返回新函式,這個在一函式作為引數的一些方法裡就用bind
this面試題 帶call的面試題遵循以下兩個規則
-
如果call傳了引數,this就是傳的引數。
-
如果call沒傳引數或者為undefined,那麼this是window(飛嚴格模式),或者undefined(嚴格模式)
帶call的面試題仍然遵循規則 3.
4.記住了,當call()
的第一個引數是undefined
的時候, this
是 window
.
this面試題 this題目
答案: 呼叫B處的console.log().結果是options window(console.log()中console是全域性window物件裡的一個方法)
第二題:
答案:D Object
第三題:
答案:Object
JS 原型是什麼?
舉例
var a = [1,2,3]
- 只有0、1、2、length 4 個key
- 為什麼可以
a.push(4)
,push
是哪來的? a.__proto__ === Array.prototype
(a是例項陣列物件,Array是建構函式)push
函式 就是沿著a.__proto__
找到的,也就是Array.prototype.push
Array.prototype
還有很多方法,如join
、pop
、slice
、splice
、concat
Array.prototype
就是 a 的原型(proto)
聚完例子後用new物件舉例,說給面試官聽: 比若說
-
我們新建立一個建構函式
function Person() {} 複製程式碼
-
然後根據建構函式構造一個新物件
var person1 = new Person(); ``` 複製程式碼
-
每個函式都有一個
prototype
屬性,這個建構函式的 prototype 屬性指向了一個物件,這個物件正是呼叫該建構函式而建立的例項的原型。 -
當我們給
Person
的prototype
的name
屬性賦值為'Kevin'
Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin 複製程式碼
每一個新的例項物件物件都會從原型"繼承"屬性,例項物件擁有該原型的所有屬性。
-
說白了,原型就是 建構函式 用來 構造 新例項 的 模板物件。
-
這就是原型。 開始解釋原型鏈 那麼我們該怎麼表示例項與例項原型,也就是 person1 和 Person.prototype 之間的關係呢,這時候我們就要講到第二個屬性
__proto__
。
什麼是原型鏈?
先回答什麼是原型。在上面,然後繼續從__proto__
開始往下說。
說:
-
JavaScript物件除了 null 都具有的一個屬性,叫
__proto__
,這個屬性會指向該物件的原型物件。 -
當讀取例項的屬性時,如果找不到,就會通過
__proto__
查詢原型中的屬性,如果還查不到,就去找原型的原型。 -
例如
Person.prototype
這個原型的原型就是Object
這個建構函式的prototype
,既Object.prototype
這個原型物件。然後,Person.prototype.__proto__
就指向Object.prototype
這個原型。然後Object.prototype
原型是null
。 -
這些原型物件通過
__proto__
像鏈子一樣連起來,就叫做原型鏈。 然後給面試官畫:
鏈子上都畫上__proto__
person1----->Person.prototype----->Object.prototype----->null
Array.prototype----->Object.prototype----->null
例項的.__proto__
指向建構函式. prototype
(原型物件)
面試題:
Number.prototype.__proto__ === Object.prototype
//true
String.prototype.__proto__ === Object.prototype
//true
Boolean.prototype.__proto__ === Object.prototype
//true
Array.prototype.__proto__ === Object.prototype
複製程式碼
//同理
Function.prototype.__proto__=== Object.prototype//true
複製程式碼
關於 Function只需記住下面: 建構函式的原型:
String.__proto__===Function.prototype//true
Number.__proto__===Function.prototype//true
Boolean.__proto__===Function.prototype//true
Object.__proto__ === Function.prototype//true
複製程式碼
特殊的:
Function.__proto__===Function.Prototype//true
複製程式碼
constructor
每一個建構函式的prototype屬性都指向原型物件。 每一個原型物件的constructor屬性都指向建構函式。 原型物件
console.log(Person === Person.prototype.constructor); // true
複製程式碼
Array注意點、偽陣列
-
大BUG:
var a = Array(3)
一個引數,且引數為數字,那麼久宣告陣列長度為3的空陣列var a = Array(3,3)
兩個引數以上的時候,裡面的引數都是陣列內部的值。 所以宣告陣列不要用new Array()的方法。 -
陣列本質上是一個物件。
-
陣列可以用for i和for in迴圈
-
偽陣列:
- 偽陣列實際是一個物件,他的
__proto__
指向Object.prototype
,沒有pop
push
等方法 - 它裡面的屬性都是數字(內容是函式所傳進來的引數)和一個
length
- JS裡面只有一個偽陣列
arguments
,代表函式裡面所傳入的所有的引數。
- 偽陣列實際是一個物件,他的
-
forEach
[1,2,3].forEach(function(value,index){ console.log(value) console.log(index) }) 複製程式碼
-
sort&join&concat&map&filter&reduce segmentfault.com/a/119000001…
DOM
-
什麼是DOM?
- DOM 是 JavaScript 操作網頁的介面,全稱為“文件物件模型”。
- 它的作用是將網頁轉為一個 JavaScript 物件,從而可以用JS進行各種操作(比如增刪內容)。
-
什麼是DOM樹?
- 瀏覽器會根據 DOM 模型,將文件解析成節點。此後HTML中的每個標籤元素,屬性,文字都能看做是一個DOM的節點
- 再由這些節點組成一個樹狀結構(DOM Tree)。
- 最後提供操作這個樹的各種方法。
-
Javascript操作DOM常用API總結http://luopq.com/2015/11/30/javascript-dom/
6Document.querySelectorAll()
函式題目
函式宣告的五種方式
注意其中一種方式:
- 函式的name
1具名函式
function f(x,y){
return x+y
}
f.name // 'f'
2匿名函式
var f
f = function(x,y){
return x+y
}
f.name // 'f'
3具名函式賦值
var f
f = function f2(x,y){ return x+y }
f.name // 'f2'
console.log(f2) // undefined
4window.Function
var f = new Function('x','y','return x+y')
f.name // "anonymous"
5箭頭函式
var f = (x,y) => {
return x+y
}
f.name//"f"
複製程式碼
只要記住3、4兩種特殊的情況就好。
- this
- 特例:當call()的第一個引數是undefined的時候, this 是 window.
- 當啟用嚴格模式的時候,call 裡的第一個引數是什麼,this 就是什麼
- 函式的呼叫棧:執行的時候函式進入棧,return的時候函式彈出棧
- Stack Overflow堆疊溢位,超出call stack 函式呼叫棧。
立即執行函式
作用:建立一個獨立的作用域,避免變數汙染
(function(){alert('我是匿名函式')} ()) // 用括號把整個表示式包起來
(function(){alert('我是匿名函式')}) () //用括號把函式包起來
//下面的都是執行這個表示式,而不管返回值是什麼
!function(){alert('我是匿名函式')}()
複製程式碼
另一種建立獨立作用域的方法是使用let
作用域鏈
題目:
var a = 1
function f1(){
var a = 2
console.log(a)
f4()
}
function f4(){
console.log(a)
}
f1()
複製程式碼
答案,2,1 這個f4裡面的a只能是他自己本身的作用域和他的父作用域,跟f1裡面的a沒有關係 先看面試題 題目1
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() //2
複製程式碼
題目2
var a = 1
function fn1(){
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() //1
複製程式碼
題目3
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() //undefined
複製程式碼
解密
- 函式在執行的過程中,先從自己內部找變數
- 如果找不到,再從建立當前函式所在的作用域去找, 以此往上
- 注意找的是變數的當前的狀態
非同步函式的經典題
下面程式碼列印出的是6 因為setTimeout是非同步函式,主執行緒的同步任務執行完畢,非同步任務才從任務佇列裡拿出來執行。 即使時間是0,他也是非同步的。也要等到所有程式碼解析完。等程式碼解析完畢,i已經是6了。 上面這個同理,當我點選按鈕的時候,點選事件才觸發(非同步),相當於是js早就解析完了程式碼,i已經變成6了,才執行事件繫結,事件繫結與觸發都是非同步的閉包經典面試題
經典面試題,迴圈中使用閉包解決 var 定義函式的問題
for ( var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
首先因為 setTimeout 是個非同步函式,所有會先把迴圈全部執行完畢,這時候 i 就是 6 了,所以會輸出一堆 6。
解決辦法兩種,第一種使用閉包(建立的這個立即執行函式就是一個新塊級作用域,並使用了外部的變數i,解析完之後,也不隨著i的最終改變而改變)+立即執行函式
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
第三種就是使用 let 定義 i 了
for ( let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
因為對於 let 來說,他會建立一個塊級作用域,相當於剛才的閉包建立的塊級作用域。
複製程式碼
===
用===
而不用==
。只需要注意兩點
NaN ===NaN//false
- 所有即使是內容一樣的物件
===
也都是false
。因為地址不一樣。
輪播
-
高階輪播 // 第一步,迴圈給按鈕新增點選事件 //第二步:新增定時器,定時出發,迴圈輪播,並且進入時暫停輪播 jsbin.com/funumujoqe/…
-
無縫輪播 說思路就行了,然後說我在工作的時候都不手寫錄播,主要是為了理解他的原理,然後用別人寫好的輪播庫swiper,這樣工作中的BUG會少一點。
回撥函式
callback 是一種特殊的函式,這個函式被作為引數傳給另一個函式去呼叫。這樣的函式就是回撥函式。 舉一個Callback(回撥函式)的例子 ,例如:
$button.on('click', function(){})
div.addEventListener('click', function(){})
複製程式碼
click 後面的 function 是一個回撥,因為「我」沒有呼叫過這個函式,是瀏覽器在使用者點選 button 時呼叫的。
再舉一個使用回撥函式的例子
function setClock(callBack){
console.log('1定一個鬧鐘,三秒鐘之後響');
setTimeout(()=>{
console.log('2三秒到了,鬧鐘響了!');
callBack();
},3000)
}
function getUp(){
console.log('3鬧鐘已經響了,該起床了')
}
setClock(getUp);
複製程式碼
回撥函式一般只和**非同步操作(setTimeOut)**在一起才使用!!!
什麼是非同步
1單執行緒模式
JavaScript 只在一個執行緒上執行,JavaScript 同時只能執行一個任務,其他任務都必須在後面排隊等待。
2同步任務(synchronous)和非同步任務(asynchronous)
同步任務主執行緒上排隊執行的任務。只有前一個任務執行完畢,才能執行後一個任務。
非同步任務不進入主執行緒、而進入任務佇列的任務。只有引擎認為某個非同步任務可以執行了(比如 Ajax 操作從伺服器得到了結果,或者setTimeOut到時間了(事件迴圈)),該任務(採用回撥函式的形式)才會進入主執行緒執行
3任務佇列和事件迴圈
JavaScript 執行時,除了一個正在執行的主執行緒,引擎還提供一個任務佇列(task queue),裡面是各種需要當前程式處理的非同步任務。
首先,主執行緒會去執行所有的同步任務。等到同步任務全部執行完,就會去看任務佇列裡面的非同步任務。如果滿足條件,那麼非同步任務就重新進入主執行緒開始執行,這時它就變成同步任務了。等到執行完,下一個非同步任務再進入主執行緒開始執行。一旦任務佇列清空,程式就結束執行。
非同步任務的寫法通常是回撥函式。一旦非同步任務重新進入主執行緒,就會執行對應的回撥函式。如果一個非同步任務沒有回撥函式,就不會進入任務佇列,也就是說,不會重新進入主執行緒,因為沒有用回撥函式指定下一步的操作。
JavaScript 引擎怎麼知道非同步任務有沒有結果,能不能進入主執行緒呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的非同步任務,是不是可以進入主執行緒了。這種迴圈檢查的機制,就叫做事件迴圈(Event Loop)。
問關於非同步、主執行緒、事件迴圈(Event Loop)的時候:按照下面回答: 面試官問問題:js是單執行緒模式的,那麼他是怎麼實現非同步操作的? 答:
- js裡面的任務分為同步任務和非同步任務
- 同步任務進入主執行緒一個一個得執行。非同步任務進入任務佇列,等同步任務執行完了之後,只有觸發了某個條件,才把任務佇列裡面的任務放到主執行緒執行(比如ajax得到返回的資料,就開始執行回撥函式,setTimeOut的時間到了,就執行回撥函式)
- 非同步任務的寫法通常是回撥函式。一旦非同步任務重新進入主執行緒,就會執行對應的回撥函式。
- 同步任務執行完之後,引擎就一遍一遍得檢查。JavaScript 引擎怎麼知道非同步任務有沒有結果,能不能進入主執行緒呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的非同步任務,是不是可以進入主執行緒了。這種迴圈檢查的機制,就叫做事件迴圈(Event Loop)。事件迴圈機制一遍一遍得檢查,符合條件,就把非同步任務放到主執行緒裡面去執行(比如ajax返回的資料到了,setTimeOut裡面的時間到了)
非同步操作的方法: 1.回撥函式 2.事件監聽(觸發條件,執行回撥函式) 3.ES6:Promise
定時器
它們向任務佇列新增定時任務。時間到就新增,然後事件迴圈就會掃到,掃到了就執行裡面的回撥函式。
Promise
什麼是Promise? 舉例: $.ajax().then(成功函式,失敗函式)
作用:避免回撥巢狀,使回撥變的可控
ES6劃入標準標準。Promise現在是js的內建物件。Promise 物件是 JavaScript 的非同步操作的解決方案。Promise 可以讓非同步操作寫起來,就像在寫同步操作的流程,(鏈式使用,then之後可以繼續then)而不必一層層地巢狀回撥函式
寫一個Promise非同步操作解決方案
function dosomething(){
// Promise 接受函式作為引數兩個引數,
// resolve: 非同步事件成功時呼叫
// reject: 非同步事件失敗時呼叫
return new Promise((resolve, reject) => {
let result = 非同步操作()
// 下面給出承諾,面對不同的結果,我會 執行 不同的解決方案
if (result === 'success')
resolve('成功資料')
else
reject('失敗資料')
})
}
// 非同步操作,模擬概率事件
function 非同步操作() {
return Math.random() > 0.5 ? 'success' : 'fail'
}
// 你在dosomething
dosomething()
// 非同步操作成功了,那麼我們列印成功的資料
.then(res => console.log(res))
// 非同步操作失敗了,那麼我們列印失敗的資料
.catch(res => console.log(res))
複製程式碼
Sync:同步的意思 Async:非同步的意思
MVC與模組化
傳統模組化方法:
- 使用window
雖然不同變數,但是是同樣的地址//module1.js !function(){ var person = window.person = { name:"frank", } //區域性變數person和全域性變數person用的是同一個地址 }.call() 複製程式碼
//module2.js !function(){ var person = person;//即var person = window.person; console.log(person); }.call(); 複製程式碼
- 使用閉包
//module1.js
!function(){
var person = {
name:"mataotao",
age:18,
};
window.mataotaoGrowUp = function(){
person.age+=1;
return person.age;
};
}.call();
複製程式碼
//module2.js
!function(){
var newAge = window.mataotaoGrowUp();
console.log(newAge);//19
}.call();
複製程式碼
用閉包的好處:
- 用來 隱藏資料細節 (不告訴你多少歲但是你可以讓他漲一歲,隱藏了age 的細節和name)
- 可以用來 做訪問控制 (只能訪問到age,無法訪問到name)