一、作用域:變量可以被訪問的范圍
1.局部作用域
1.1函數作用域
在函數內部聲明的變量,在函數內部被訪問的,外部無法直接訪問。
總結:1、函數內部聲明的變量,在函數外部無法直接訪問
2、函數的參數也是函數內部的局部變量
3、不同函數內部聲明的變量無法互相訪問
4、函數執行完畢后,函數內部的變量事件被清空
1.2塊作用域
有“{}”的包裹的代碼稱為代碼塊,內部聲明的變量外部將【有可能】無法被訪問
###let /const聲明的變量產生塊作用域,var聲明的不會產生塊級作用域
2.全局作用域
寫在<script>標簽的或者寫在js文件的,盡可能少聲明全局作用域,防止全局變量被污染
3.作用域鏈
本質是底層的變量查找機制:(1)函數執行時,優先查找當前函數作用域中查找
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(2)查不到則依次逐級查找父級作用域直到全局作用域
###子作用域可以訪問父作用域,但是父作用域無法訪問子作用域
###相同作用域鏈按從小到大規則查找變量
4.js垃圾回收機制(GC)
4.1內存生命周期
(1)分配內存:聲明變量、函數、對象時,系統自動分配內存
(2)內存使用:讀寫內存,使用函數,變量
(3)內存回收:使用完畢,垃圾回收機制自動回收不在使用的內存
###全局變量一般不會回收(關閉頁面回收),
(4)內存泄漏:程序中分配的內存由于某種原因,程序無法釋放或者未釋放。
??算法說明:
1.棧(操作系統):由操作系統自動分配釋放函數的參數值,局部變量等,基本數據類型放到棧里面
2.堆(操作系統):一般由程序員分配釋放,不釋放,則垃圾回收機制回收。復雜數據類型放到堆里面
4.2垃圾回收算法
(1)引用計數法
跟蹤記錄被引用的次數,引用一次則記錄一次,多次引用會累加++,減少一次引用就--
次數為0,回收
缺點:嵌套使用。兩個對象相互引用,盡管它們不再使用,GC不會進行回收,內存泄漏
(2)標記清除法
核心:1.將“不再使用的對象”定義為“無法達到的對象”。
? ? ? ? ? ?2.從根部出發定時掃描內存中的對象。凡是從根部到達的對象,都是還需要使用的。
? ? ? ? ? ?3.無法觸及的對象被標記為不再使用,稍后進行回收。
5.閉包
一個函數對周圍狀態的引用捆綁在一起,內層函數中訪問到其外層函數作用域,會產生內存泄漏
閉包=內層函數+外層函數的變量
<script>
function outer(){let a = 1function fn(){console.log(a)}return fn
}
const fun = outer()
fun()
</script>
//外層函數可以訪問內層函數的變量
//const fun = outer() = fn = function fn(){}
//調用fun()
作用:閉包的應用:實現數據的私有,封閉數據。做個統計函數調用次數,調用一次就++
外部可以訪問函數內部的變量
問題:內存泄漏
<script>function count(){let i = 0function fn(){i++console.log(`函數被調用了${i}次`) }return fn
}
const fun = count()</script>
6.變量提升
把var聲明的變量提升到 當前作用域的最前面。
只提升聲明,不提升賦值
總結:
-
變量在未聲明即被訪問時會報語法錯誤
-
變量在聲明之前即被訪問,變量的值為
undefined
-
let
聲明的變量不存在變量提升,推薦使用let
-
變量提升出現在相同作用域當中
-
實際開發中推薦先聲明再訪問變量
<script>
function(){console.log(num)var num = 10
}
fun()等價于
function(){var numconsole.log(num)num = 10
}
fun()
</script>
二、函數進階
1.函數提升(只提升聲明,不提升調用)
聲明之前調用
函數表達式 必須先聲明和賦值,后調用,否則報錯。
總結:1、函數提升能夠使函數的聲明調用更靈活
2、函數表達式不存在提升的現象
3、函數提升出現在相同作用域當中
2.函數參數
###默認值
總結:1、聲明函數時為形參賦值即為參數的默認值
2、如果參數未自定義默認值時,參數的默認值為undefined
3、調用函數時沒有差引入對應實參時,參數的默認值被當做實參傳入
2.1動態參數
arguments是一個偽數組?只存在于 函數里面
作用是動態的獲取函數的實參
可以通過for循環依次得到傳遞過來的參數
2.2剩余參數
(1)...是語法符號,置于最末函數形參之前,用于獲取多余的實參
(2)借助...獲取的剩余參數,是個真數組
<script>function getSum(a,b,...arr){console.log(arr);}getSum(2,3,4,5,6,7)getSum(1,2,3,4,5,6,7,8,9)</script>
##使用場景:用于獲取多余的實參
##和動態參數的區別是什么?
? ? ? ? 動態參數是偽數組;
? ? ? ? 剩余參數是真數組
??展開運算符【數組中使用】:將數組的數字全展開
const arr = [1,2,3,4,5]console.log(...arr)console.log(Math.max(...arr));//求最大值console.log(Math.min(...arr));//求最小值
合并數組:
const arr1 = [1,2,3]const arr2 = [4,5,6]const arr3 = [...arr1,...arr2]console.log(arr3);// [1,2,3,4,5,6]
3.箭頭函數
(1)基本語法
const fn=()=>{
}
##只有一個形參時,可以省略小括號
const fn = x=>{
}
##只有一行代碼時,可以省略大括號和return
const fn = (x,y) =>x+y
console.log(fn(1,2))
##箭頭函數可以直接返回同一個對象
const fn1 = uname=>({uname:name})
console.log('劉德華')
總結:
1、箭頭函數屬于表達式函數,因此不存在函數提升
2、箭頭函數只有一個參數時可以省略圓括號()
3、箭頭函數函數體只有一行時代碼可以省略花括號{},并自動作為返回值被返回
(2)箭頭函數參數
只有剩余參數,沒有動態參數arguments
(3)箭頭函數this
箭頭函數不會創建自己的this,從自己的作用域鏈的上一層沿用this
三、解構賦值
1.數組解構 :
將數組的單元值快速批量賦值給一系列變量的簡介語法
<script>// 以下代碼是否會正常執行,如果不會,如何改正const [min, avg, max] = [100,200,300];//必須加分號(function() {console.log(min);})();</script>
<script>const arr = [60,100,80]const [max,min avg] = arr
//將按照順序賦值給變量console.log(max)
</script>
交換變量
<script>
let a = 1
let b = 2;
const [b,a]=[a,b]
console.log(a,b)
</script>
<script>1. 變量多, 單元值少 , undefinedconst [a, b, c, d] = [1, 2, 3]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // undefined2. 變量少, 單元值多const [a, b] = [1, 2, 3]console.log(a) // 1console.log(b) // 23. 剩余參數 變量少, 單元值多const [a, b, ...c] = [1, 2, 3, 4]console.log(a) // 1console.log(b) // 2console.log(c) // [3, 4] 真數組4. 防止 undefined 傳遞const [a = 0, b = 0] = [1, 2]const [a = 0, b = 0] = []console.log(a) // 1console.log(b) // 25. 按需導入賦值const [a, b, , d] = [1, 2, 3, 4]console.log(a) // 1console.log(b) // 2console.log(d) // 4const arr = [1, 2, [3, 4]]console.log(arr[0]) // 1console.log(arr[1]) // 2console.log(arr[2]) // [3,4]console.log(arr[2][0]) // 3// 多維數組解構const [a, b, [c, d]] = [1, 2, [3, 4]]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // 4</script>
2.對象解構
<script>// 對象解構const obj = {uname: 'pink老師',age: 18}obj.unameobj.age //const uname = 'red老師'解構的語法const { uname, age } = {age: 18, uname: 'pink老師' }// 等價于 const uname = obj.uname//要求屬性名和變量名必須一致才可以
</script>
3.多級對象解構
<script>const pig = {name: '佩奇',family: {mother: '豬媽媽',father: '豬爸爸',sister: '喬治'},age: 6}// // 多級對象解構const { name, family: { mother, father, sister } } = pig
</script>
4.多級數組對象解構
<script>//多級對象數組解構const person = [{name: '佩奇',family: {mother: '豬媽媽',father: '豬爸爸',sister: '喬治'},age: 6}]const [{ name, family: { mother, father, sister } }] = personconsole.log(name)console.log(mother)console.log(father)console.log(sister)</script>
5.多級對象解構案例
<script>// 1. 這是后臺傳遞過來的數據const msg = {"code": 200,"msg": "獲取新聞列表成功","data": [{"id": 1,"title": "5G商用自己,三大運用商收入下降","count": 58},{"id": 2,"title": "國際媒體頭條速覽","count": 56},{"id": 3,"title": "烏克蘭和俄羅斯持續沖突","count": 1669},]} // 需求1: 請將以上msg對象 采用對象解構的方式 只選出 data 方面后面使用渲染頁面const {data} = msgconsole.log(data) // 需求2: 上面msg是后臺傳遞過來的數據,我們需要把data選出當做參數傳遞給 函數function render({data}){console.log(data)}render(msg)// 需求3, 為了防止msg里面的data名字混淆,要求渲染函數里面的數據名改為 myDatafunction render({data:myData}){console.log(myData)}
</script>
###forEach()方法
用于調用數組的每個元素,并將元素傳遞給回調函數
注意:
1、forEach主要是遍歷數組
2、參數當前數組元素使必須要寫的,索引號可選。
<script>// forEach 就是遍歷 加強版的for循環 適合于遍歷數組對象const arr = ['red', 'green', 'pink']const result = arr.forEach(function (item, index) {console.log(item) // 數組元素 red green pinkconsole.log(index) // 索引號})// console.log(result)</script>
###篩選數組filter方法
filter()方法創建的一個新數組,新數組中的元素是通過檢查指定數組中符合條件的所有元素
使用場景:篩選數組符合條件的元素,并返回篩選之后元素的新數組
語法:
<script>const arr = [10, 20, 30]// const newArr = arr.filter(function (item, index) {// // console.log(item)// // console.log(index)// return item >= 20// })// 返回的符合條件的新數組const newArr = arr.filter(item => item >= 20)console.log(newArr)</script>
###渲染商品案例
<script>// 2. 初始化數據const goodsList = [{id: '4001172',name: '稱心如意手搖咖啡磨豆機咖啡豆研磨機',price: '289.00',picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',},{id: '4001594',name: '日式黑陶功夫茶組雙側把茶具禮盒裝',price: '288.00',picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',},{id: '4001009',name: '竹制干泡茶盤正方形瀝水茶臺品茶盤',price: '109.00',picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',},{id: '4001874',name: '古法溫酒汝瓷酒具套裝白酒杯蓮花溫酒器',price: '488.00',picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',},{id: '4001649',name: '大師監制龍泉青瓷茶葉罐',price: '139.00',picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',},{id: '3997185',name: '與眾不同的口感汝瓷白酒杯套組1壺4杯',price: '108.00',picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',},{id: '3997403',name: '手工吹制更厚實白酒杯壺套裝6壺6杯',price: '99.00',picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',},{id: '3998274',name: '德國百年工藝高端水晶玻璃紅酒杯2支裝',price: '139.00',picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',},]function render(arr) {let str = ''arr.forEach(item => {const { name, price, picture } = itemstr += `<div class="item"><img src="${picture}" alt=""><p class="name">${name}</p><p class="price">${price}</p></div>`});document.querySelector('.list').innerHTML = str}render(goodsList)document.querySelector('.filter').addEventListener('click', e => {const { tagName, dataset } = e.targetif (tagName == 'A') {//console.log(11);let arr = goodsListif (dataset.index === '1') {arr = goodsList.filter(item => item.price > 0 && item.price < 100)} else if (dataset.index === '2') {arr = goodsList.filter(item => item.price >= 100 && item.price < 300)} else if (dataset.index === '3') {arr = goodsList.filter(item => item.price >= 300)}render(arr)}})</script>
四、構造函數與數據常用函數
1.深入對象
1.1創建對象三種方式
(1)利用對象字面量創建對象
const 0 = {
? ? ? ? name :?'abc'
}
(2)利用new Object創建對象
const 0 = new Object({name:'abc'})
(3)利用構造函數創建對象
1.2構造函數
一種特殊的函數,主要用來初始化對象。
##使用new關鍵字的調用函數行為被稱為實例化
##實例化構造函數時沒有參數可以省略
##構造函數內部無需寫return,返回值即新創建的對象
##構造函數內部的return返回值無效,所以不要寫return
##new Object()new Date()也是實例化構造函數
兩個約定:
<1>命名大寫字母開頭
<2>它們只能由“new”操作符來執行
<script>function Pig(uname,age){this.uname = unamethis.age = age
}const p = new Pig('peiqi',6)const q = new Pig('qiaozhi',3)console.log(p,q)
</script>
??實例化執行過程
(1)創建新對象
(2)構造函數this指向新對象
(3)執行函數構造代碼,修改this,添加新屬性
(4)返回新的對象
1.3實例成員和靜態成員
1.3.1實例成員
通過構造函數創建的對象稱為實例對象,實例對象中的屬性和方法稱為實例成員(實例屬性和實例方法)
1.3.2靜態成員
構造函數的屬性和方法被稱為靜態成員
<script>function Pig(name){this.name = name
}
Pig.eyes = 2//靜態屬性
Pig.sayHi = function(){//靜態方法console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes)//2
</script>
說明:
1.靜態成員只能構造函數來訪問
2.靜態方法中的this指向構造函數
2.內置構造函數
1.Object
.keys(obj):返回所有屬性名
.values(obj):獲得所有的屬性值【返回的是數組】
const o = { uname: 'pink', age: 18 }// 1.獲得所有的屬性名console.log(Object.keys(o)) //返回數組['uname', 'age']// 2. 獲得所有的屬性值console.log(Object.values(o)) // ?['pink', 18]
.assign(new,old):對象拷貝
// 3. 對象的拷貝const o = { uname: 'pink', age: 18 }// const oo = {}// Object.assign(oo, o)// console.log(oo)//對象中添加屬性Object.assign(o, { gender: '女' })console.log(o)
2.Array
方法 | 作用 | 說明 |
filter | 過濾數組 | 返回新數組,返回的是篩選滿足條件的數組與數組元素 |
forEach | 遍歷數組 | 不返回數組,經常用于查找遍歷數組元素 |
map | 迭代數組 | 返回新數組,返回的是處理后的數組元素,想要使用返回的新數組 |
reduce | 累計器 | 返回累計處理的結果,經常用于求和等 |
join | 拼接 | 數組元素拼接成字符串,返回字符串 |
find | 查找元素 | 返回符合測試條件的第一個數組,如果沒有符合條件的則返回undefined |
every | 檢測數組所有元素是否都符合指定條件 | 如果都符合,返回true,否則返回false |
sort | 排序 | 對原數組單元值進行排序 |
from | 偽數組轉換為真數組 | 返回真數組 |
<script>const arr = [1,3,5,7,9]const result = arr.reduce((a,b)=>(a+b),10)console.log(result)//35
</script>
(1)reduce(function(prev,curret){return prev+curret},start)
prev:代表初始值
curret:代表累加的值
start:初始值具體數字
###
1.如果沒有初始值,則上一次的值以數組的第一個數組元素的值。
2.每一次循環,把返回值給作為 下一次循環的上一次值。
3.如果有初始值,則起始值作為上一次值。
<script>const arr = [{name: '張三',salary: 10000}, {name: '李四',salary: 10000}, {name: '王五',salary: 20000},]const money = arr.reduce((prev, item) => prev + item.salary * 1.3, 0)console.log(money)</script>
包裝類型:
字符串、數值、布爾類型數據是js底層使用Object構造函數“包裝”來的,即為包裝類型
3.String
.split【‘分隔符’】:把字符串轉換成數組(和join相反)
.substring【第一個索引,最后一個索引】截取字符串
.startsWith(檢測字符串,【檢測位置的索引號】),檢測是否以某字符開頭
.includes(搜索的字符串,檢測的字符串位置索號),判斷一的字符串是否包含在另一個字符串中,根據情況返回true或false
.replace(代替字符串)
4.Number
.toFixed:設置保留小數位的長度,四舍五入。
3.綜合案例
五、深入面向對象
1.編程思想
(1)面向過程
分析解決問題的步驟,然后用函數一步一步地實現,使用的時候調用
優點:性能高
缺點:沒有面向易維護,復用,擴展
(2)面向對象(oop)
優點:易維護、復用、擴展,使系統更加易于維護,更加靈活
缺點:性能低
分解為一個個對象,然后對象之間分工和合作。
###特性:
【1】封裝性:封裝代碼,js面向對象可以通過構造函數實現的封裝,同時將變量和函數組合到一起并能通過this實現數據的共享,所不同的是借助構造函數創建出來的實例對象之間互相不影響。
總結:
1、構造函數體現了面向對象的封裝特性
2、構造函數實例創建的對象彼此獨立,互不影響。
【2】繼承性:繼承接口
【3】多態性
2.構造函數
存在浪費內存的問題
3.原型對象
構造函數通過原型分配的函數是所有對象所共享的
###js規定,每一個構造函數都有一個prototype屬性,指向另一個對象,所以我們也稱為原型對象
###這個對象可以掛載函數,對象實例化不會多次創建原型上函數,節約內存
###我們可以把那些不變的方法,直接定義為prototype對象上,這樣所有對象的實例就可以共享這些方法
###構造函數和原型對象中的this都指向實例化的對象
原型作用:
(1)共享方法
(2)可以把那些不變的方法,直接定義在prototype對象上
構造函數和原型里面的this指向誰?
指向實例化對象
constructor 屬性
在哪里? 每個原型對象里面都有個constructor 屬性(constructor 構造函數)
作用:該屬性指向該原型對象的構造函數, 簡單理解,就是指向我的爸爸,我是有爸爸的孩子
使用場景:
如果有多個對象的方法,我們可以給原型對象采取對象形式賦值.
但是這樣就會覆蓋構造函數原型對象原來的內容,這樣修改后的原型對象 constructor 就不再指向當前構造函數了
此時,我們可以在修改后的原型對象中,添加一個 constructor 指向原來的構造函數。
對象原型
構造函數創建實例對象和含有原型對象(prototype),實例對象的__proto__指向原型對象,實例對象的__protype__和原型對象的constructor指向構造函數。
對象都會有一個屬性 __proto__ 指向構造函數的 prototype 原型對象,之所以我們對象可以使用構造函數 prototype
原型對象的屬性和方法,就是因為對象有__proto__ 原型的存在。
注意:
-
__proto__是JS非標準屬性
-
[[prototype]]和__proto__意義相同
-
用來表明當前實例對象指向哪個原型對象prototype
-
__proto__對象原型里面也有一個 constructor屬性,指向創建該實例對象的構造函數
原型繼承
繼承是面向對象編程的另一個特征,通過繼承進一步提升代碼封裝的程度,JavaScript 中大多是借助原型對象實現繼承
的特性。
龍生龍、鳳生鳳、老鼠的兒子會打洞描述的正是繼承的含義。
<body><script>// 繼續抽取 公共的部分放到原型上// const Person1 = {// eyes: 2,// head: 1// }// const Person2 = {// eyes: 2,// head: 1// }// 構造函數 new 出來的對象 結構一樣,但是對象不一樣function Person() {this.eyes = 2this.head = 1}// console.log(new Person)// 女人 構造函數 繼承 想要 繼承 Personfunction Woman() {}// Woman 通過原型來繼承 Person// 父構造函數(父類) 子構造函數(子類)// 子類的原型 = new 父類 Woman.prototype = new Person() // {eyes: 2, head: 1} // 指回原來的構造函數Woman.prototype.constructor = Woman// 給女人添加一個方法 生孩子Woman.prototype.baby = function () {console.log('寶貝')}const red = new Woman()console.log(red)// console.log(Woman.prototype)// 男人 構造函數 繼承 想要 繼承 Personfunction Man() {}// 通過 原型繼承 PersonMan.prototype = new Person()Man.prototype.constructor = Manconst pink = new Man()console.log(pink)</script>
</body>
原型鏈(查找規則)
基于原型對象的繼承使得不同構造函數的原型對象關聯在一起,并且這種關聯的關系是一種鏈狀的結構,我們將原型對象的鏈狀結構關系稱為原型鏈
<body><script>// function Objetc() {}console.log(Object.prototype)console.log(Object.prototype.__proto__)function Person() {}const ldh = new Person()// console.log(ldh.__proto__ === Person.prototype)// console.log(Person.prototype.__proto__ === Object.prototype)console.log(ldh instanceof Person)console.log(ldh instanceof Object)console.log(ldh instanceof Array)console.log([1, 2, 3] instanceof Array)console.log(Array instanceof Object)</script>
</body>
① 當訪問一個對象的屬性(包括方法)時,首先查找這個對象自身有沒有該屬性。
② 如果沒有就查找它的原型(也就是__?proto__指向的 prototype 原型對象)
③ 如果還沒有就查找原型對象的原型(Object的原型對象)
④ 依此類推一直找到 Object 為止(null)
⑤ __proto__對象原型的意義就在于為對象成員查找機制提供一個方向,或者說一條路線
⑥ 可以使用 instanceof 運算符用于檢測構造函數的 prototype 屬性是否出現在某個實例對象的原型鏈上
六、深淺拷貝
只針對引用數據類型
1.淺拷貝
拷貝的是地址
常見方法:
(1)拷貝對象:Object.assgin()/展開運算符{…obj}拷貝對象
(2)拷貝數組:Array/ptotype.concat()或者{…arr}
2.深拷貝
拷貝的是對象,不是地址
2.1遞歸實現深拷貝
1.深拷貝實現拷貝出來的新對象不會影響舊對象?,通過函數遞歸實現
2.普通拷貝的話直接賦值就可以,遇到數組再次調用這個遞歸函數
3.如果遇到對象,就調用遞歸函數解決對象,【先array,后對象】??
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = {}// 拷貝函數function deepCopy(newObj, oldObj) {debuggerfor (let k in oldObj) {// 處理數組的問題 一定先寫數組 在寫 對象 不能顛倒if (oldObj[k] instanceof Array) {newObj[k] = []// newObj[k] 接收 [] hobby// oldObj[k] ['乒乓球', '足球']deepCopy(newObj[k], oldObj[k])} else if (oldObj[k] instanceof Object) {newObj[k] = {}deepCopy(newObj[k], oldObj[k])}else {// k 屬性名 uname age oldObj[k] 屬性值 18// newObj[k] === o.uname 給新對象添加屬性newObj[k] = oldObj[k]}}}deepCopy(o, obj) // 函數調用 兩個參數 o 新對象 obj 舊對象console.log(o)o.age = 20o.hobby[0] = '籃球'o.family.baby = '老pink'console.log(obj)console.log([1, 23] instanceof Object)// 復習// const obj = {// uname: 'pink',// age: 18,// hobby: ['乒乓球', '足球']// }// function deepCopy({ }, oldObj) {// // k 屬性名 oldObj[k] 屬性值// for (let k in oldObj) {// // 處理數組的問題 k 變量// newObj[k] = oldObj[k]// // o.uname = 'pink'// // newObj.k = 'pink'// }// }</script>
</body>
2.2js庫lodash里面cloneDeep
<body><!-- 先引用 --><script src="./lodash.min.js"></script><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = _.cloneDeep(obj)console.log(o)o.family.baby = '老pink'console.log(obj)</script>
</body>
2.3JSON序列化
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}// 把對象轉換為 JSON 字符串// console.log(JSON.stringify(obj))const o = JSON.parse(JSON.stringify(obj))console.log(o)o.family.baby = '123'console.log(obj)</script>
</body>
七、異常處理
異常處理指預估代碼執行過程中可能發生的錯誤,然后最大程度的避免錯誤的發生導致整個程序無法繼續運行
throw拋異常
1、throw拋出異常信息后,程序也會終止執行
2、throw后面跟的是錯誤提示信息
3、Error對象配合throw使用,能夠設置更詳細的錯誤信息
<script>function counter(x, y) {if(!x || !y) {// throw '參數不能為空!';throw new Error('參數不能為空!')}return x + y}counter()
</script>
try/catch捕獲錯誤信息
-
try...catch
用于捕獲錯誤信息 -
將預估可能發生錯誤的代碼寫在
try
代碼段中 -
如果
try
代碼段中出現錯誤后,會執行catch
代碼段,并截獲到錯誤信息
<script>function foo() {try {// 查找 DOM 節點const p = document.querySelector('.p')p.style.color = 'red'} catch (error) {// try 代碼段中執行有錯誤時,會執行 catch 代碼段// 查看錯誤信息console.log(error.message)// 終止代碼繼續執行return}finally {//不管代碼報不報錯誤,仍然執行該行代碼alert('執行')}console.log('如果出現錯誤,我的語句不會執行')}foo()
</script>
debugger
八、處理this
1.普通函數this指向
誰調用就指向誰
嚴格模式下指向undefined
2.箭頭函數this指向
箭頭函數中的 this
與普通函數完全不同,也不受調用方式的影響,事實上箭頭函數中并不存在 this
!箭頭函數中訪問的 this
不過是箭頭函數所在作用域的 this
變量。
總結:1.函數內部不存在this,沿用上一級的this
2.不適用:構造函數、原型函數、dom事件函數等
3.適用:需要使用上層的this的地方
4.如果正確的話,它會在很多地方帶來方便。
3.改變this指向
(1)call
<script>// 普通函數function sayHi() {console.log(this);}let user = {name: '小明',age: 18}let student = {name: '小紅',age: 16}// 調用函數并指定 this 的值sayHi.call(user); // this 值為 usersayHi.call(student); // this 值為 student// 求和函數function counter(x, y) {return x + y;}// 調用 counter 函數,并傳入參數let result = counter.call(null, 5, 10);console.log(result);
</script>
總結:
-
call方法能夠在調用函數的同時指定
this
的值 -
使用
call
方法調用函數時,第1個參數為this
指定的值 -
call
方法的其余參數會依次自動傳入函數做為函數的參數
(2)apply
總結:
-
apply
方法能夠在調用函數的同時指定this
的值 -
使用
apply
方法調用函數時,第1個參數為this
指定的值 -
apply
方法第2個參數為數組,數組的單元值依次自動傳入函數做為函數的參數
call和apply的區別是?
都是調用函數,都能夠改變this指向。但它們的參數不一樣,apply傳遞的必須是數組
(3)bind
不會調用函數,但是能改變this的指向
返回值是個函數,但是這個函數里面的this是更改過的obj
主要應用場景:
call調用函數并且可以傳遞參數
apply經常跟數組有關系,比如借助數學對象實現求最大值和最小值
bind不調用函數,但是還想改變this指向,比如改變定時器內部的this指向
九、防抖節流
防抖(debounce):
單位時間內,頻繁觸發事件,只執行最后一次
所謂防抖,就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間
使用場景:輸入手機號,郵箱驗證輸入檢測
節流(throttle):
單位時間內,頻繁出發事件,只執行一次
所謂節流,就是指連續觸發事件但是在 n 秒中只執行一次函數
使用場景:鼠標移動mousemove,頁面尺寸縮放resize、滾動條滾動scroll