- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
深拷贝和浅拷贝的区别
- 浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级
- 浅拷贝有效性针对的是单一层级对象 [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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
加入递归
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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
输出结果
-
对于name和age属性手机深拷贝
-
对于address属性不是深拷贝
-
深拷贝的核心是我没找到对象或数值中的值类型(age 和 name属性的值),然后进行赋值操作,就是深拷贝,而address属性是一个对象自然采用内存地址拷贝
-
解决方案:加入递归,一致递归到值类型为止
-
在deepClone函数中的循环改写为:
result[i]=deepclone(obj[i])
- 1
完整代码
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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
原型和原型链
隐式原型和显示原型
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);//类的原型对象,也是显示原型
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
对象的隐式原型与类的显式原型是同一个对象,内存地址一致
原型关系
- 每个class都有显示原型prototype
- 每个实例都有隐式声明__proto__
- 实例的__proto__指向对应的class的prototype
获取属性方法时
const xialuo = new Student('夏洛特', '110')
console.log(xialuo.name);//夏洛特
xialuo.sayHi()
xialuo.eat()
- 1
- 2
- 3
- 4
- 获取属性或方法时,首先在对象的自身属性中查找如上面name就是对象的自身属性
- 如果找不到,则自动取__proto__中查找,如上面的sayHi方法,在对象中并没有,所有取对象的隐式原型上查找
原型链
hasownProperty 方法判断某个属性或者方法是否在当前隐式原型中
- 1
在 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}
- 1
- 2
- 3
- 4
- 5
- 6
定义在对象里面的函数(this指向)
定义在对象里面的函数this指向的是这个对象
const person = {
name: 'onlifes',
sayHi () {
console.log(this);
},
wait () {
setTimeout(function () {
console.log(this);
}, 0)
}
}
person.sayHi()
person.wait()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
结果
{name: "onlifes", sayHi: ƒ, wait: ƒ}
Window
- 1
- 2
原因:因为 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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
用 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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
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')
- 1
- 2
- 3
- 4
- 5
- 6
结论:尽量使用 property 的方式,万不得已,再使用 attribute 方式
DOM 结构操作
- 新增,插入节点
- 获取子元素列表,获取父元素
- 删除元素
新增节点
document.createElement('p')
- 1
插入节点
父节点.appendChild('p')
- 1
获取子元素列表
父元素.childNodes
- 1
获取父元素列表
子元素.parentNode
- 1
删除子元素
父元素.removeChild(子元素)
- 1
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
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 对象方法
事件
事件绑定
1.嵌入式
<button οnclick="open()">按钮</button>
<script>
function open(){
alert(1)
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.事件监听
<button id="btn">按钮</button>
<script>
document.getElementById('btn').addEventListener('click',function(){
alert(1)
})
</script>
- 1
- 2
- 3
- 4
- 5
- 6
3.直接绑定
<button id="btn">按钮</button>
<script>
document.getElementById('btn').onclick = function(){
alert(1)
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
addEventListener和on的区别
on:如果事件相同就会覆盖前一个但是有时我们又需要执行多个相同的事件 on就完成不了我们想要的效果
addEventListener:可以多次绑定同一个事件并且不会覆盖上一个事件。
事件代理
**事件代理:**将事件代理到父容器上,这样修改子元素,父容器都会监听到,从而执行对应的操作,若是将事件绑定到子元素上,假设新增了子元素,又得重新给子元素绑定事件,是非常繁琐的。
事件冒泡,事件捕获
<div>
<p>元素</p>
</div>
- 1
- 2
- 3
事件捕获
当你使用事件捕获时,父级元素先触发,子级元素后触发,即div先触发,p后触发。
事件冒泡
当你使用事件冒泡时,子级元素先触发,父级元素后触发,即p先触发,div后触发。
组织冒泡的方法
第一种:event.stopPropagation();
第二种:return false;
第三种:event.preventDefault();