、
深拷貝
值型別的賦值就是深拷貝:變數賦值時,拷貝的不是記憶體地址,而是將資料完整的在記憶體中復制了一份
const a = 10
const b = a
console.log(b);

淺拷貝
參考型別(null 物件 陣列)的賦值操作都不是深拷貝:拷貝的是記憶體地址,最終兩個變數指向的是同一個地址
const a = {
name: 'jj',
age: 20
}
const b = a
b.age = 18
console.log(a.age); // 18

深拷貝和淺拷貝的區別
- 淺拷貝只復制一層物件的屬性,而深拷貝則遞回復制了所有層級
- 淺拷貝有效性針對的是單一層級物件 [1,2,3]或者{a:1,b:2}
- 深拷貝有效性針對的是單層或者多層級物件 [1,2,3]或者{a:1,b:2}或者[1,[1],{a:1}]或者{a:[1],b:{c:2}}
如何讓參考型別深拷貝
簡單的實作深拷貝
const p1 = {
name: 'jj',
age: 20
}
const deepClone = (obj) => {
let result = {}
for (let i in obj) {
// console.log(obj[i]);
result[i] = obj[i]
}
return result
}
const p2 = deepClone(p1)
p2.age = 12
console.log(p1, p2);

加入遞回
const p1 = {
name: 'ss',
age: 20,
address: {
province: '河北省',
city: '保定市',
area: '蓮池區',
info: '河北軟體職業技術學院'
}
}
const deepClone = (obj) => {
let result = {}
for (let i in obj) {
result[i] = obj[i]
}
return result
}
const p2 = deepClone(p1)
p2.age = 2
p2.address.city = '衡水'
console.log(p1, p2);
輸出結果

-
對于name和age屬性手機深拷貝
-
對于address屬性不是深拷貝
-
深拷貝的核心是我沒找到物件或數值中的值型別(age 和 name屬性的值),然后進行賦值操作,就是深拷貝,而address屬性是一個物件自然采用記憶體地址拷貝
-
解決方案:加入遞回,一致遞回到值型別為止
-
在deepClone函式中的回圈改寫為:
result[i]=deepclone(obj[i])
完整代碼
const deepClone = (obj) => {
// 如果不是物件或者等于null的時候無需進行深拷貝
if (typeof obj !== 'object' || obj == null) {
return obj
}
//宣告變數,用于存盤拷貝的資料
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let i in obj) {
// console.log(deepClone(obj[i]));
result[i] = deepClone(obj[i])
}
return result
}
const p1 = {
name: 'ss',
age: 20,
address: {
province: '河北省',
city: '保定市',
area: '蓮池區',
info: '河北軟體職業技術學院'
}
}
const p2 = deepClone(p1)
p2.age = 2
p2.address.city = '衡水'
console.log(p1, p2);
原型和原型鏈
隱式原型和顯示原型
class People {
constructor(name) {
this.name = name
}
eat() {
console.log('eat');
}
}
class Student extends People {
constructor(name, number) {
super(name)
this.number = number
}
sayHi() {
console.log(`姓名:${this.name},學號:${this.number}`);
}
}
const xialuo = new Student('夏洛特', '110')
console.log(xialuo.__proto__ === Student.prototype);//true
console.log(xialuo.__proto__);//物件的物件原型,也是隱式原型
console.log(Student.prototype);//類的原型物件,也是顯示原型
物件的隱式原型與類的顯式原型是同一個物件,記憶體地址一致

原型關系
- 每個class都有顯示原型prototype
- 每個實體都有隱式宣告__proto__
- 實體的__proto__指向對應的class的prototype
獲取屬性方法時
const xialuo = new Student('夏洛特', '110')
console.log(xialuo.name);//夏洛特
xialuo.sayHi()
xialuo.eat()
- 獲取屬性或方法時,首先在物件的自身屬性中查找如上面name就是物件的自身屬性
- 如果找不到,則自動取__proto__中查找,如上面的sayHi方法,在物件中并沒有,所有取物件的隱式原型上查找
原型鏈
hasownProperty 方法判斷某個屬性或者方法是否在當前隱式原型中
在 JavaScript 中,每個物件都有一個指向它的原型(prototype)物件的內部鏈接(proto),這個原型物件又有自己的原型,直到某個物件的原型為 null 為止(也就是不再有原型指向),這種一級一級的鏈結構就稱為原型鏈(prototype chain), 當查找一個物件的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止;到查找到達原型鏈的頂部(Object.prototype),仍然沒有找到指定的屬性,就會回傳 undefined,

所有 class 都是從 Object 繼承的,也就是說 Object 是其他類的原型
作用域
script標簽的開始和結束標記之間,叫全域作用域
函式內部的是函式作用域
花括號之間的是塊級作用域
自由變數:在當前作用域中沒有,在上級找到的變數就是自由變數如果全域沒有找到就回報 is not defiend
自由變數的查找就是在函式定義的地方,向上級作用域查找
閉包
在函式呼叫完之后作用域和變數都被清除
閉包在函式呼叫之后不會清除作用域和變數,只有呼叫內部函式的時候才會清除
閉包是一種現象 閉包可以訪問其他作用域的變數 有兩種情況:1.函式作為回傳值 2.函式作為引數
this指向
this 是在函式執行時(呼叫時)確定的,而不是定義時,
在全域作用域中this是指 window
普通函式this指向
作為普通函式呼叫this值是window
apply、bind 和 call 方法可以改變普通函式中 this 的指向,傳遞的第一個引數是什么,則函式中的 this 就是什么
function fn () {
console.log(this);
}
fn.call({ x: 100 }) // {x:100}
const fn_res = fn.bind({ x: 100 })
fn_res() // {x:100}
定義在物件里面的函式(this指向)
定義在物件里面的函式this指向的是這個物件
const person = {
name: 'onlifes',
sayHi () {
console.log(this);
},
wait () {
setTimeout(function () {
console.log(this);
}, 0)
}
}
person.sayHi()
person.wait()
結果
{name: "onlifes", sayHi: ?, wait: ?}
Window
原因:因為 wait 方法中使用了 setTimeOut 函式,其中的函式永遠被當作一個普通函式,所以 this=window
箭頭函式(this指向)
箭頭函式沒有自己的this指向,箭頭函式的this指向是父級作用域的this指向決定的
class類中方法的this(this指向)
class類中方法的this指向的是宣告的物件實體
同步和異步
單執行緒和異步
JS 是單執行緒語言,只能同時做一件事,這時候就需要異步解決單執行緒的問題 異步是解決JS語言單執行緒瓶頸的一個解決方案
異步和同步的區別
異步是基于 JS 單執行緒特點而產生的
1.異步不會阻塞代碼執行
同步會阻塞代碼執行
2.同步就相當于是 當客戶端發送請求給服務端,在等待服務端回應的請求時,客戶端不做其他的事情,當服務端做完了才回傳到客戶端,這樣的話客戶端需要一直等待,用戶使用起來會有不友好,
異步就是,當客戶端發送給服務端請求時,在等待服務端回應的時候,客戶端可以做其他的事情,這樣節約了時間,提高了效率,
存在就有其道理 異步雖然好 但是有些問題是要用同步用來解決,比如有些東西我們需要的是拿到回傳的資料在進行操作的,這些是異步所無法解決的,
解決異步問題
使用Promise方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="info">
</div>
</body>
</html>
<script>
const url1 =
'https://th.bing.com/th/id/R33674725d9ae34f86e3835ae30b20afe?rik=Pb3C9e5%2b%2b3a9Vw&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fscenery%2f20180626%2f4c8157d07c14a30fd76f9bc110b1314e.jpg&ehk=9tpmnrrRNi0eBGq3CnhwvuU8PPmKuy1Yma0zL%2ba14T0%3d&risl=&pid=ImgRaw'
const url2 = 'https://tse2-mm.cn.bing.net/th/id/OIP.T1-KOJxH7Dg0YqlcZiJ6vAHaHa?pid=ImgDet&rs=1'
const url3 =
'https://th.bing.com/th/id/Rc6c03edea530e9caa677c9d17f193a4d?rik=MBgpsjumbTD5eQ&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fscenery%2f20170209%2fca186d97701674b996264b2d352894a7.jpg&ehk=HunG%2fPF7pUbpcS34cWpNvlS%2faoDPbcaTYL6LFFPQIIM%3d&risl=&pid=ImgRaw'
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.src = url
img.onload = function () {
resolve(img)
}
})
}
loadImg(url1)
.then(img => {
loadInfo(img)
return loadImg(url2)
}).then(img => {
loadInfo(img)
return loadImg(url3)
}).then(img => {
loadInfo(img)
})
function loadInfo(ele) {
const sp = document.createElement('span')
sp.innerHTML = ele.width
document.querySelector('.info').appendChild(sp)
document.body.appendChild(ele)
}
</script>
用 await方法改造
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="info">
</div>
</body>
</html>
<script>
const url1 =
'https://th.bing.com/th/id/R33674725d9ae34f86e3835ae30b20afe?rik=Pb3C9e5%2b%2b3a9Vw&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fscenery%2f20180626%2f4c8157d07c14a30fd76f9bc110b1314e.jpg&ehk=9tpmnrrRNi0eBGq3CnhwvuU8PPmKuy1Yma0zL%2ba14T0%3d&risl=&pid=ImgRaw'
const url2 = 'https://tse2-mm.cn.bing.net/th/id/OIP.T1-KOJxH7Dg0YqlcZiJ6vAHaHa?pid=ImgDet&rs=1'
const url3 =
'https://th.bing.com/th/id/Rc6c03edea530e9caa677c9d17f193a4d?rik=MBgpsjumbTD5eQ&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fscenery%2f20170209%2fca186d97701674b996264b2d352894a7.jpg&ehk=HunG%2fPF7pUbpcS34cWpNvlS%2faoDPbcaTYL6LFFPQIIM%3d&risl=&pid=ImgRaw'
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.src = url
img.onload = function () {
resolve(img)
}
})
}
async function fn() {
loadInfo(await loadImg(url1))
loadInfo(await loadImg(url2))
loadInfo(await loadImg(url3))
}
fn()
function loadInfo(ele) {
const sp = document.createElement('span')
sp.innerHTML = ele.width
document.querySelector('.info').appendChild(sp)
document.body.appendChild(ele)
}
</script>
web api
- ECMA 和 W3C 是兩個機構,但這兩個機構的會員有很大重合
- ECMA 規定 ECMAScript 的標準,W3C 規定了 Web API 的標準,也就是說 ECMA 負責 ECMAScript 腳本語言的研究和發布,W3C 定義了 ECMAScript 能做什么
- ECMA 262 是基礎,與 Web API 結合才能發揮作用
DOM操作
dom的本質是什么
我們都知道,DOM 是一棵樹,梨樹還是桃樹?
首先要明白,通過 VS Code 編輯的 HTML 源代碼,最侄訓被瀏覽器拿到 瀏覽器拿到源代碼經過決議,構建成DOM樹,瀏覽器可以識別并通過JS可以操作的這個DOM樹 所以,要將源代碼與 DOM 區別開來
- 源代碼是開發者撰寫的,由瀏覽器進行決議并渲染成頁面,但渲染完成后,無法通過 JS 進行修改
- DOM 存在于瀏覽器的記憶體中(我自己的理解),通過 JS 可以修改,修改后,瀏覽器會根據修改后 的 DOM 樹重新渲染頁面
DOM節點操作
- property :修改物件屬性,不會體現到 HTML 結構中
- attribute:修改 html 結構,會改變 HTML 結構
- 兩者都可能引起 DOM 結構重新渲染
// property 方式:
element.style.color='red'
element.className='blue'
// atribute 方式
element.setAttribute('class','blue')
element.getAttribute('href')
結論:盡量使用 property 的方式,萬不得已,再使用 attribute 方式
DOM 結構操作
- 新增,插入節點
- 獲取子元素串列,獲取父元素
- 洗掉元素
新增節點
document.createElement('p')
插入節點
父節點.appendChild('p')
獲取子元素串列
父元素.childNodes
獲取父元素串列
子元素.parentNode
洗掉子元素
父元素.removeChild(子元素)
DOM性能優化
- 對DOM查詢做快取
- 將頻繁的DOM操作作為一次性操作
用代碼片段進行一次性的操作
body
<ul id="list"></ul>
//js
const listNode = document.getElementById("list")
// 創建一個檔案片段,此時還沒有插入到DOM樹中
const frag = document.createDocumentFragment()
// 執行插入
for (let x = 0; x < 10; x++) {
const li = document.createElement('li')
li.innerHTML = "list item " + x
frag.appendChild(li)
}
// 完成之后,再插入到DOM樹中
listNode.appendChild(frag)
BOM操作
BOM:瀏覽器物件模型
1.如何識別瀏覽器型別
用Navigator物件的
| 屬性 | 說明 |
|---|---|
| appCodeName | 回傳瀏覽器的代碼名 |
| appName | 回傳瀏覽器的名稱 |
| appVersion | 回傳瀏覽器的平臺和版本資訊 |
| cookieEnabled | 回傳指明瀏覽器中是否啟用 cookie 的布林值 |
| platform | 回傳運行瀏覽器的作業系統平臺 |
| userAgent | 回傳由客戶機發送服務器的user-agent 頭部的值 |

分析拆解 URI 各個部分

Screen 物件
Screen 物件包含有關客戶端顯示螢屏的資訊,
| 屬性 | 描述 |
|---|---|
| availHeight | 回傳顯示螢屏的高度 (除 Windows 任務欄之外), |
| availWidth | 回傳顯示螢屏的寬度 (除 Windows 任務欄之外), |
| bufferDepth | 設定或回傳呼色板的位元深度, |
| colorDepth | 回傳目標設備或緩沖器上的調色板的位元深度, |
| deviceXDPI | 回傳顯示螢屏的每英寸水平點數, |
| deviceYDPI | 回傳顯示螢屏的每英寸垂直點數, |
| fontSmoothingEnabled | 回傳用戶是否在顯示控制面板中啟用了字體平滑, |
| height | 回傳顯示螢屏的高度, |
| logicalXDPI | 回傳顯示螢屏每英寸的水平方向的常規點數, |
| logicalYDPI | 回傳顯示螢屏每英寸的垂直方向的常規點數, |
| pixelDepth | 回傳顯示螢屏的顏色解析度(位元每像素), |
| updateInterval | 設定或回傳螢屏的重繪率, |
| width | 回傳顯示幕螢屏的寬度, |
History 物件
History 物件包含用戶(在瀏覽器視窗中)訪問過的 URL
History 物件屬性
| 屬性 | 描述 |
|---|---|
| length | 回傳瀏覽器歷史串列中的 URL 數量, |
History 物件方法
| 方法 | 描述 |
|---|---|
| back() | 加載 history 串列中的前一個 URL, |
| forward() | 加載 history 串列中的下一個 URL, |
| go() | 加載 history 串列中的某個具體頁面, |
事件
事件系結
1.嵌入式
<button onclick="open()">按鈕</button>
<script>
function open(){
alert(1)
}
</script>
2.事件監聽
<button id="btn">按鈕</button>
<script>
document.getElementById('btn').addEventListener('click',function(){
alert(1)
})
</script>
3.直接系結
<button id="btn">按鈕</button>
<script>
document.getElementById('btn').onclick = function(){
alert(1)
}
</script>
addEventListener和on的區別
on:如果事件相同就會覆寫前一個但是有時我們又需要執行多個相同的事件 on就完成不了我們想要的效果
addEventListener:可以多次系結同一個事件并且不會覆寫上一個事件,
事件代理
**事件代理:**將事件代理到父容器上,這樣修改子元素,父容器都會監聽到,從而執行對應的操作,若是將事件系結到子元素上,假設新增了子元素,又得重新給子元素系結事件,是非常繁瑣的,
事件冒泡,事件捕獲
<div>
<p>元素</p>
</div>
事件捕獲
當你使用事件捕獲時,父級元素先觸發,子級元素后觸發,即div先觸發,p后觸發,
事件冒泡
當你使用事件冒泡時,子級元素先觸發,父級元素后觸發,即p先觸發,div后觸發,
組織冒泡的方法
第一種:event.stopPropagation();
第二種:return false;
第三種:event.preventDefault();
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/288011.html
標籤:其他
上一篇:Web前端-JS基礎
