第一章:JS 基礎(chǔ)上篇(js 類型)
- 值類型
var a = 100
var b = a
a = 200
console.log(b) // 100
- 引用類型
var a = {age:100}
var b = a
a.age = 200
console.log(a.age) // 200
問(wèn)題:為什么對(duì)值類型和引用類型進(jìn)行修改會(huì)出現(xiàn)不同的結(jié)果呢队贱?
答:值類型:存的是具體的數(shù)值欣除,比如 a=200住拭,那么 a 變量?jī)?nèi)存中地址存的就是 200。所以改變 a=100 ,僅僅只是影響到了 a滔岳。
引用類型:內(nèi)存中存的是對(duì)象的地址杠娱,所以 a 和 b 此刻指向了同一個(gè)對(duì)象的地址。改變 a.age 就會(huì)同時(shí)影響到 b.age谱煤。
- 引用類型包括
對(duì)象摊求、數(shù)組、函數(shù)(為什么需要用到引用呢刘离?因?yàn)檫@些類型可能非常大室叉,每次賦值都新建一個(gè)新對(duì)象,就會(huì)比較浪費(fèi)空間) - typeof 運(yùn)算符
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object (它也是引用類型)
typeof console.log // function
結(jié)論: typeof 能區(qū)分值類型硫惕;對(duì)于引用類型只能區(qū)分 function 其他無(wú)法區(qū)分茧痕。
-
問(wèn):JS 中有哪些內(nèi)置函數(shù) -- 數(shù)據(jù)封裝類對(duì)象
答:
Object
Array
Boolean
Number
String
Function
Date
RegExp
Error
第二章:JS 基礎(chǔ)上篇(原型和原型鏈)
- var a = {} 其實(shí)是 var a = new Object() 的語(yǔ)法糖
- var a = [] 其實(shí)是 var a = new Array() 的語(yǔ)法糖
- function Foo(){...} 其實(shí)是 var Foo = new Function(){...}
- 使用 instanceof 判斷一個(gè)函數(shù)是否是一個(gè)變量的構(gòu)造函數(shù).
總結(jié):引用類型都有一個(gè)構(gòu)造函數(shù)
- 下寫(xiě)一個(gè)原型鏈繼承的例子
function Animal() {
this.eat = function () {
console.log('animal eat')
}
}
function Dog() {
this.bark = function () {
console.log('dog bark')
}
}
Dog.prototype = new Animal();
let hashiqi = new Dog()
hashiqi.bark()
hashiqi.eat()
- 描述 new 一個(gè)對(duì)象的過(guò)程
- 創(chuàng)建一個(gè)新對(duì)象
- this 指向這個(gè)新對(duì)象
- 執(zhí)行代碼,即對(duì) this 賦值
- 返回 this
第三章:JS 基礎(chǔ)中篇(作用域和閉包)
- 表達(dá)式和聲明的區(qū)別
fn() // 輸出: fn
// fn1() // 輸出: fn1 is not a function
console.log(a); // 輸出: undefined
// 這個(gè)叫做"聲明"
function fn() {
console.log('fn');
}
// 這個(gè)叫做"表達(dá)式"
var fn1 = function () {
console.log('fn1');
}
// 這個(gè)叫做"表達(dá)式"
var a = 100;
- JavaScript 解釋器中存在一種變量聲明被提升的機(jī)制恼除,也就是說(shuō)函數(shù)聲明會(huì)被提升到作用域的最前面踪旷,即使寫(xiě)代碼的時(shí)候是寫(xiě)在最后面,也還是會(huì)被提升至最前面豁辉。
- 而函數(shù)表達(dá)式創(chuàng)建的函數(shù)是在運(yùn)行時(shí)進(jìn)行賦值令野,且要等到表達(dá)式賦值完成后才能調(diào)用。
前面的函數(shù)徽级,相當(dāng)于如下寫(xiě)法:
// 這個(gè)叫做"函數(shù)聲明"
function fn() {
console.log('fn');
}
var a = undefined;
var fn1 = undefined;
fn() // 輸出: fn
// fn1() // error: fn1 is not a function
console.log(fn1); // 輸出: undefined
console.log(a); // 輸出: undefined
// 這個(gè)叫做"表達(dá)式"
var fn1 = function () {
console.log('fn1');
}
// 這個(gè)叫做"表達(dá)式"
var a = 100;
2.函數(shù)內(nèi)部會(huì)默認(rèn)有 this 和 arguments 自帶變量
fn('zhansan')
function fn(name) {
console.log(this); //輸出:復(fù)雜對(duì)象
console.log(arguments); //輸出:{ '0': 'zhansan' }
age = 20
console.log(name, age); //輸出:zhansan 20
var age
}
- this 要在執(zhí)行時(shí)才能確認(rèn)值气破,定義時(shí)無(wú)法確認(rèn)
let a = {
name: 'A',
fn: function () {
console.log(this.name)
// console.log(this)
}
}
a.fn() // this === a
a.fn.call({ name: 'B' }) // this === {name:'B'}
this.name = 'here'
this.fn1 = a.fn
this.fn1() // this === window
- JavaScript 無(wú)塊級(jí)作用域
if (true) {
var name = 'zhansan'
}
console.log(name); // 輸出: name === 'zhansan'
- JavaScript 有 函數(shù)作用域和全局作用域
var a = 'global'
function fn() {
var a = 'function'
console.log(a);
}
fn() // 輸出 'fn'
console.log(a); // 輸出 'global'
6.作用域鏈:就是函數(shù)定義的時(shí)候,內(nèi)部使用的變量沒(méi)有進(jìn)行定義灰追,會(huì)向父級(jí)作用域?qū)ふ以撟兞俊?/p>
函數(shù)作用域中使用沒(méi)有在本函數(shù)作用域中的變量叫做自由變量:自由變量的定義一定是在定義時(shí)候的父級(jí)作用域去尋找堵幽。
var a = 100
function F1(){
var b = 200
function F2(){
var c = 300
console.log(a);
console.log(b);
console.log(c);
}
F2()
}
F1()
- 閉包-閉包的定義,自己從下面 代碼一作為返回值弹澎、代碼二作為參數(shù) 中體會(huì)朴下。
// 代碼一:閉包作為返回值
function F1() {
var a = 100
// 返回一個(gè)函數(shù)(函數(shù)作為返回值)
return function(){
console.log(a); // 自由變量,父級(jí)作用域?qū)ふ?
}
}
// f1 得到一個(gè)函數(shù)
var f1 = F1()
var a = 200
f1() // 輸出: 100
// 代碼二:閉包作為參數(shù)值
function F1() {
var a = 100
// 返回一個(gè)函數(shù)(函數(shù)作為返回值)
return function(){
console.log(a); // 自由變量苦蒿,父級(jí)作用域?qū)ふ? }
}
// f1 得到一個(gè)函數(shù)
var f1 = F1()
function F2(fn) {
var a = 200
fn()
}
F2(f1) // 依然輸出: 100
閉包使用的時(shí)候經(jīng)常會(huì)搞不清楚自由變量是什么殴胧?請(qǐng)返回閱讀上一條自由變量相關(guān)內(nèi)容。
- 閉包實(shí)際應(yīng)用中主要用于封裝變量佩迟,收斂權(quán)限团滥。
function isFirstLoad() {
var _list = []
return function (id) {
if (_list.indexOf(id) >= 0) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10)); // true
console.log(firstLoad(10)); // false
console.log(firstLoad(20)); // true
上面的代碼,使用閉包报强,就會(huì)將 _list 很好的隱藏起來(lái)灸姊,保護(hù)它的其他操作權(quán)限。
第四章:JS 基礎(chǔ)下篇(異步和單線程秉溉、其他知識(shí)點(diǎn))
- 什么是異步力惯?什么是同步碗誉?setTimeout() 就是異步(不阻塞代碼),alert()就是同步(阻塞代碼)父晶。
- 什么是單線程哮缺?就是一次只能做一件事情,類似一次執(zhí)行一行代碼甲喝。
- 日期
Date.now() // 獲取當(dāng)前時(shí)間毫秒數(shù)
var dt = new Date()
dt.getTime() // 獲取毫秒數(shù)
dt.getFullYear() // 年
dt.getMonth() // 月(0-1)注意:這里是比較特殊
dt.getDate() // 日 (1-31)
dt.gatHours() // 小時(shí)(0-23)
dt.getMinutes() // 分鐘 (0-59)
dt.getSeconds() // 秒 (0-59)
- math 主要應(yīng)用:math.random() 獲取一個(gè) 0-1 之間的隨機(jī)數(shù)
第五章:JS-Web-API 上篇(DOM 尝苇、 BOM)
- 常說(shuō)的 JS(瀏覽器執(zhí)行的 JS)包含兩部分:
① JS 基礎(chǔ)知識(shí)( ECMA262 標(biāo)準(zhǔn) )
② JS-Web-API( W3C 標(biāo)準(zhǔn) )
- DOM 的本質(zhì)是什么?
瀏覽器把拿到的 HTML 代碼埠胖,結(jié)構(gòu)化成一個(gè)瀏覽器能識(shí)別并且 JS 可操作的一個(gè)模型而已糠溜。
- 獲取 DOM 節(jié)點(diǎn)(最常用的幾種)
var div1 = document.getElementById('div1') //元素
var divList = document.getElementsByTagName('div') //集合
var containerList = document.getElementsByClassName('container') //集合
console.log(divList.length)
console.log(divList[0])
console.log(containerList.length)
var pList = document.querySelectorAll('p') //集合
var p = pList[0]
console.log(p.style.width)
p.style.width = '200px' // 改變寬度
console.log(p.style.width)
console.log(p.className)
p.className = 'p2class'
// 獲取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
- Property 和 Attribute 中文翻譯都是‘屬性’,有什么不同呢直撤?
var pList = document.querySelectorAll('p') //集合
var p = pList[0]
p.style.width // 這里就是 property 改的是 JS 對(duì)象的屬性
p.setAttribute('data-name', 'helloworld') // 改的是 dom 文檔的屬性
p.setAttribute('style', 'font-size: 26px;')
console.log(p.getAttribute('data-name'))
console.log(p.getAttribute('style'))
- navigator & screen (BOM 內(nèi)置變量)
// navigator 判斷瀏覽器
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(!!isChrome)
// screen 判斷窗體大小
console.log(screen.width)
console.log(screen.height)
- location & history
// location
console.log(location.href) // 'https://www.baidu.com/s?wd=aaa'
console.log(location.protocol) // 'https:'
console.log(location.host) // www.baidu.com
console.log(location.pathname) // '/s'
console.log(location.search) // ?號(hào)后面的內(nèi)容 wd=aaa
// history
history.back()
history.forward()
第六章:JS-Web-API 下篇(事件诵冒、跨域、存儲(chǔ))
- 通用事件綁定(寫(xiě)一個(gè)通用事件綁定方法谊惭,可以減少方法名長(zhǎng)度)
event.preventDefault() // 阻止控件的默認(rèn)事件
var btn = document.getElementById('btn1')
btn.addEventListener('click',(event)=>{
console.log('click')
})
function bindEvent(element, type, fn) {
element.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', (event)=> {
event.preventDefault() // 阻止 a 標(biāo)簽?zāi)J(rèn)行為
alert('clicked')
})
- 事件冒泡
event.stopPropagation() // 阻止事件冒泡
問(wèn):如何讓所有<p>綁定點(diǎn)擊事件,使得點(diǎn)擊激活侮东;執(zhí)行激活操作圈盔,點(diǎn)擊取消,執(zhí)行取消操作悄雅。
<body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div1">
<p id="p5">取消</p>
<p id="p6">取消</p>
<p id="p7">取消</p>
<p id="p8">取消</p>
</div>
</body>
答:根據(jù)事件冒泡原理驱敲,我們可以先綁定 body 的事件。
<script>
var body = document.body
body.addEventListener('click',(event)=>{
alert('取消')
})
var activeDiv = document.getElementById('p1')
activeDiv.addEventListener('click',(event)=>{
event.stopPropagation(); //阻止事件繼續(xù)向上冒泡
alert('激活');
})
</script>
- 問(wèn)題如下:如何讓程序?qū)崿F(xiàn)點(diǎn)擊 a 標(biāo)簽宽闲,彈出 a 標(biāo)簽內(nèi)的內(nèi)容众眨?
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
<!-- 會(huì)隨時(shí)新增更多 a 標(biāo)簽 -->
</div>
答:通過(guò) event.target 可以獲取點(diǎn)擊事件觸發(fā)的最頂層控件
<script>
let div1 = document.getElementById('div1')
div1.addEventListener('click',(event)=>{
let target = event.target
if (target.nodeName === 'A') {
alert(target.innerHTML)
}
})
</script>
- 什么是跨域?
- 瀏覽器有同源策略容诬,不允許 ajax 訪問(wèn)其他域接口娩梨。
- 跨域條件:協(xié)議、域名览徒、端口狈定,有一個(gè)不同就算跨域。
- 有哪三個(gè)標(biāo)簽運(yùn)行跨域訪問(wèn)习蓬?
- <img src=xxx>
- <link src=xxx>
- <script src=xxx>
- 三個(gè)標(biāo)簽的特殊應(yīng)用場(chǎng)景纽什?
- <img> 用于打點(diǎn)統(tǒng)計(jì),統(tǒng)計(jì)網(wǎng)站可能是其他域
- <link><script> 可以使用 CDN躲叼,CDN 也是其他域
- <script> 可以用 JSONP芦缰。??下面代碼展示什么是 JSONP
<script>
window.callback = function (data) {
// 這是我們跨域得到的信息
console.log(data)
}
</script>
<script src="http://xxx.com/api.js"></script>
<!--以上將返回 callback({x:100, y:200}) -->
- 本地存儲(chǔ)之 cookie
- 本身用于客戶端和服務(wù)器端通信
- 但是它有本地存儲(chǔ)的功能,于是就被“借用”
- 使用 document.cookie = ... 獲取和修改即可
- cookie 用于存儲(chǔ)的缺點(diǎn)
- 存儲(chǔ)量太小枫慷,只有 4KB
- 所有 http 請(qǐng)求都帶著让蕾,會(huì)影響獲取資源的效率
- API 簡(jiǎn)單浪规,需要封裝才能用 document.cookie = ...
- locationStorage 和 sessionStorage
- HTML5 專門(mén)為存儲(chǔ)而設(shè)計(jì),最大容量 5M
- API 簡(jiǎn)單易用
- localStorage.setItem(key, value); localStorage.getItem(key);
- locationStorage 和 sessionStorage
- iOS safari 隱藏模式下
- localStorage.getItem 會(huì)報(bào)錯(cuò)
- 建議統(tǒng)一使用 try-catch 封裝
第七章:模塊化
- 先看不用模塊化的代碼會(huì)出現(xiàn)的問(wèn)題涕俗。有如下說(shuō)明代碼:
// util.js
function getFormatDate(date, type){
// type === 1 返回 2019-04-30
// type === 2 返回 2019 年4 月 15 日
// ...
}
// a-util.js
function aGetFormatDate(date) {
// 要求返回 返回 2019 年4 月 15 日 格式
return getFormatDate(date, 2)
}
// a.js
let dt = new Date()
console.log(aGetFormatDate(dt))
對(duì)于上述代碼的調(diào)用:
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
- 這些代碼中的函數(shù)必須是全局變量罗丰,才能暴露給使用方。容易造成全局變量污染再姑。
- a.js 知道藥引用 a-util.js 萌抵,但是他知道還需要依賴于 util.js 嗎?
- 使用模塊化元镀。有如下說(shuō)明代碼:
// util.js
export {
getFormatDate: function (date, type){
// type === 1 返回 2019-04-30
// type === 2 返回 2019 年4 月 15 日
// ...
}
}
// a-util.js
var getFormatDate = require('util.js')
export {
aGetFormatDate: function (date){
return getFormatDate(date, 2)
}
}
// a.js
var aGetFormatDate = require('a-util.js')
let dt = new Date()
console.log(aGetFormatDate(dt))
模塊化的代碼有如下好處:
- 直接
<script src="a.js"></script>
绍填,其他的根據(jù)依賴關(guān)系自動(dòng)引用。- 那兩個(gè)函數(shù)栖疑,沒(méi)必要做成全局變量讨永,不會(huì)來(lái)帶污染和覆蓋。
第八章:運(yùn)行環(huán)境
- 瀏覽器加載一個(gè)資源的過(guò)程
- 瀏覽器根據(jù) DNS 服務(wù)器得到域名的 IP 地址
- 向這個(gè) IP 的機(jī)器發(fā)送 http 請(qǐng)求
- 服務(wù)器收到遇革、處理并返回 http 請(qǐng)求
- 瀏覽器得到返回內(nèi)容
- 瀏覽器渲染頁(yè)面的過(guò)程
- 根據(jù) HTML 結(jié)構(gòu)生成 DOM Tree
- 根據(jù) CSS 生成 CSSOM
- 將 DOM 和 CSSOM 整合形成 RenderTree
- 根據(jù) RenderTree 開(kāi)始渲染和展示
- 遇到 <script> 時(shí)卿闹,會(huì)執(zhí)行并阻塞渲染
- window.onload 和 DOMContentLoaded
window.addEventListener('load', function(){
// 頁(yè)面全部資源加載完成才會(huì)執(zhí)行,包括圖片萝快、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完成即可執(zhí)行锻霎,此時(shí)圖片、視頻還可能沒(méi)有加載完
})
- 性能優(yōu)化
原則
- 多使用內(nèi)存揪漩、緩存
- 減少 CPU 計(jì)算旋恼、減少網(wǎng)絡(luò)請(qǐng)求 、 減少 DOM 操作
- 加載資源優(yōu)化
- 靜態(tài)資源的壓縮合并
- 靜態(tài)資源緩存
- 使用 CDN 讓資源加載更快
- 使用 SSR 后端渲染奄容,數(shù)據(jù)直接輸出到 HTML 中
- 渲染優(yōu)化
- CSS 放前面冰更,JS 放后面
- 懶加載(圖片懶加載、下拉加載更多)
- 減少 DOM 查詢昂勒,對(duì) DOM 查詢做緩存
- 減少 DOM 操作蜀细,多個(gè)操作盡量合并在一起執(zhí)行
- 事件節(jié)流
- 盡早執(zhí)行操作(如 DOMContentLoaded)
- 安全性之 XSS (全稱跨站腳本攻擊)
- 在新浪博客寫(xiě)一篇文章,同時(shí)偷偷插入一段<script>
- 攻擊代碼中戈盈,獲取 cookie 审葬,發(fā)送到自己的服務(wù)器
- 發(fā)布博客,有人查看博客內(nèi)容奕谭,會(huì)執(zhí)行代碼涣觉。
- 解決方案:前端或者后端替換關(guān)鍵字,例如替換 < 為 < ; > 為 > ;
- 安全性之 XSRF (全稱跨站請(qǐng)求偽造)
- 你已登錄一個(gè)購(gòu)物網(wǎng)站血柳,正在瀏覽商品
- 該網(wǎng)站的付費(fèi)接口是 xxx.com/pay?id=100 但是沒(méi)有任何驗(yàn)證
- 然后你收到一封郵件官册,隱藏著<img src=xxx.com/pay?id=100>
- 當(dāng)你查看郵件的時(shí)候,就已經(jīng)悄悄的付費(fèi)購(gòu)買(mǎi)了
- 解決方案:增加驗(yàn)證流程难捌,如輸入指紋膝宁、密碼鸦难、短信驗(yàn)證碼≡币或者服務(wù)端更多信息的校驗(yàn)合蔽。