# obj.key 和 Object.defineProperty
给某一个对象
obj
创建属性name
时,可以直接使用:obj.name=XXX
的方式,这样创建的属性就具有默认的配置型:writable:true
可写入,enumerable:true
可枚举,configurable:true
可配置的特性。如果想要添加属性并改变这些默认的配置时,就需要使用Object.defineProperty
进行配置:
Object.defineProperty(obj,'name',{ | |
enumerable: false, | |
value: function(){ | |
// ... | |
} | |
}) |
# category 标签
category 标签定义类别,表示一类的主题内容
<Category title="电影">
<video controls src="媒体文件"></video>
</Category>
# track 标签
用于在媒体标签中处理文字 <track>
# figure 标签
figure 标签(可附标题内容元素,通常内部使用图片和媒体,也有利于 SEO)
<figure> | |
<img | |
src="/zh-CN/docs/Web/HTML/Element/figure/favicon-192x192.png" | |
alt="The beautiful MDN logo." | |
/> | |
<figcaption>MDN Logo</figcaption> | |
</figure> |
# iframe 内联标签
iframe 标签也被称为:嵌入式框架,它可以把一个网页的框架和内容嵌入到现有的网页中,优点是重载页面时不用加载整个页面,提高了网页重载速度。缺点:会产生很多页面,不好管理、会阻塞页面加载、需要调用外部资源,增加了请求次数、浏览器后退按钮无效、SEO 差,搜索引擎会跳过 iframe 的爬取,宽度高度等问题,并且小型设备无法完全显示框架。
# iframe 内联框架标签:
<iframe src="./student.html" name="fm" frameborder="0"></iframe>当点击`<a | |
href=" " | |
target="fm" | |
> | |
</a | |
>` 标签时,只会替换跳转iframe页面,而非整个网页 |
使用 ifram 标签访问页面
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Document</title> | |
<style> | |
iframe { | |
width: 100%; | |
height: 650px; | |
} | |
</style> | |
</head> | |
<body> | |
<iframe src="https://www.csdn.net/" frameborder="0"></iframe> | |
</body> | |
</html> |
# HTML 微格式
HTML 微格式是为了兼容 HTML 文档的人机可读性的目的,在标签中添加语义注解(通过结构化类型名应用到标记中,可以生成人类观众可以清楚理解的内容),一般用于标记人员 (h-me)、联系信息(h-card)、博客(h-entry)、地点等信息。
<a href="./home.html">to home</a> | |
<div> | |
<div>北京</div> | |
<div>China</div> | |
<div>15830294021</div> | |
</div> | |
<!-- 添加微格式之后: --> | |
<a href="./home.html" rel="">to home</a> | |
<div class="vcard"> <!-- 结构化类名 --> | |
<div class="adr">北京</div> | |
<div class="country-name">China</div> | |
<div class="tel">15830294021</div> | |
</div> |
# 替换元素
指的是展现效果不由 css 控制的元素,他们外观的渲染是独立于 css 的
常见的有:
- img 标签
- iframe 标签
- 音视频标签
# <%= 符号 %>
用于 html 中嵌入 js 语句
# css 属性
css 的 100 多个属性
# CSS 基本数据类型
filter 用于定义滤镜
# 选择文件夹
** showDirectoryPicker()
** 方法,用于选择文件夹目录
** showOpenFilePicker()
** 用于显示一个文件选择器
showSaveFilePicker()
用于更改一个文件
# JS 引擎优化
一、小的数字类型 (Sim) , 占据四个字节,正常 num 占据八个字节
二、
# js8 种数据类型
** 基本:**null、undefined、number、boolean、string、symbol、bigInt、
** 引用数据类型 : ** Object
# for in 循环和 for of 循环
(1)for in 通常遍历对象,for of 通常遍历数组
(2) for in
会遍历对象的可枚举属性,这个过程是通过原型链实现的 ; for...of
循环遍历可迭代对象的元素,这个过程是通过迭代器实现的。
(3)实际上,在底层, for...in
循环会遍历对象自身及其原型链上所有可枚举的属性(包括继承自父对象的属性),因此需要使用 hasOwnProperty()
方法来判断属性是否是对象自身的属性。 for...of
循环实际上是基于这个迭代器实现的。循环首先获取可迭代对象的迭代器,然后按照迭代器定义的顺序依次遍历每个元素,并将当前元素的值赋给循环变量。
# 封装的简易随机数
// 随机数 | |
function rannum(min, max) { | |
return Math.round(Math.random() * (max - min)) + min; | |
} |
# 解决获取对象属性时存在的兼容性问题
# 获取对象属性问题
浏览器兼容性:不同浏览器在处理 CSS 属性名称上存在差异。例如,某些浏览器可能将
background-color
属性解析为backgroundColor
,而另一些浏览器则保持原样。这就导致了使用obj.attr
的方式无法跨浏览器获得准确的样式值。获取计算后样式:直接通过
obj.attr
获取的是元素的内联样式(即行内样式),而不是计算后的样式(包括 CSS 样式和继承样式)。如果需要获取元素显示时的实际样式,例如考虑到外部样式表或通过其他选择器影响的样式,需要使用getComputedStyle
或currentStyle
方法。** 例如:如下代码中,dom 获取的属性值和页面呈现的值不一致
<style>
div {
width: 100px !important;
}
</style>
<body>
<div style="width:500px;height:10px;background-color:black"></div>
</body>
<script>
let div = document.querySelector("div");
console.log(div.style.width); // 页面呈现的为 100px, dom 获取到的是行内样式(500px)
</script>
# 解决方案:
封装函数获取计算后的 dom 元素属性值( getComputedStyle
), 同时注意 IE 浏览器没有此方法
function getstyle(obj, attr) { | |
if (window.getComputedStyle) { | |
// 标准 | |
return getComputedStyle(obj)[attr]; | |
} else { | |
//IE | |
return obj.currentStyle[attr]; | |
} | |
} |
// 非 IE 浏览器中,currentStyle 属性是不存在的 | |
function getStyle(obj, attr) { | |
return obj.currentStyle | |
? obj.currentStyle[attr] | |
: window.getComputedStyle(obj, null)[attr]; | |
} |
# 构造函数创建对象方法
静态方法只能通过构造函数调用,实例方法通过对象调用,定义在对象身上,原型方法通过对象调用,定义在对象原型上(节省内存)
原型方法和实例方法是 JavaScript 中定义在对象上的两种不同类型的方法,它们有以下区别和好处:
- 区别:
- 原型方法:定义在对象的原型上,即通过构造函数的
prototype
属性添加的方法。所有通过该构造函数创建的实例对象都共享同一个原型方法。 - 实例方法:定义在对象实例上,通过构造函数创建的每个对象实例都具有自己的实例方法。
- 原型方法:定义在对象的原型上,即通过构造函数的
- 好处:
- 原型方法的好处:
- 节省内存:所有通过构造函数创建的实例对象共享原型方法,避免为每个实例对象重复创建方法,节省了内存开销。
- 动态性:原型方法的修改会立即反映在所有实例对象上。当你在原型上添加、删除或修改方法时,所有实例对象会自动继承这些变化,无需更新每个实例对象。
- 实例方法的好处:
- 封装性:实例方法可以使用构造函数内部的变量和方法,实现更灵活和封装的对象行为。
- 访问实例属性:实例方法可以直接访问实例对象的属性,因为它们是在实例对象中定义的。
- 原型方法的好处:
// 构造函数 | |
function People(color) { | |
this.color = color; | |
this.seeColor = function () { | |
console.log("color is " + this.color); | |
}; | |
} | |
// 静态方法 | |
People.say = function () { | |
console.log("静态方法color is ", this.color); | |
}; | |
// 原型方法 | |
People.prototype.eat = function () { | |
console.log("原型方法color is ", this.color); | |
}; | |
// 实例 | |
var pp = new People(); | |
// 调用静态方法 | |
People.say(); // 静态方法 color is undefined | |
pp.say(); // 报错信息:Uncaught TypeError: pp.say is not a function | |
// 调用原型方法 | |
People.eat(); // 报错信息:Uncaught TypeError: People.eat is not a function | |
pp.eat(); // 原型方法 color is Red | |
// 调用实例方法 | |
People.seeColor(); // Uncaught TypeError: People.seeColor is not a function | |
pp.seeColor(); // color is Red |
# Promise.all()
Promise.all()静态方法接受一个 Promose 可迭代对象作为输入,并返回一个 Promise,当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现,并返回一个包含所有对闲置的数组,如果输入的任何 Promise 都被拒绝,则返回的 Promise 也将被拒绝
# 生成乱文
以下是使用 lorem
快捷键生成乱文的步骤:
- 在编辑器中选中或定位到您想要插入乱文的位置。
- 键入
lorem
并按下 Tab 键。
这将会触发代码片段的展开,生成一段默认长度的乱文。
如果您需要生成特定长度的乱文,可以在 lorem
后面跟随所需的长度,例如 lorem5
会生成 5 个单词的乱文。
# NodeList 和 HTMLCollection
他们都是伪数组 , 用 typeof 进行检测时,返回 function。
NodeList(
.querySelectorAll(selector)
、.childNodes
、.getElementsByName
.querySelectorAll 等获取)NodeList
包含文档中匹配选择器所得到的所有节点,包括元素、文本节点、注释节点等;NodeList
是静态的,即当文档中的节点发生变化时,NodeList
中的内容不会实时发生变化。
HTMLCollection(
.getElementsByClassName()
、.getElementsByTagName( )
等)
而 HTMLCollection
则只包含文档中指定标签名的元素节点;
而 HTMLCollection
是静态的,和 HTML 强相关,即:当文档中的节点发生变化时, HTMLCollction
中的内容会实时发生变化。
两者都可以像数组一样使用索引访问其成员,但 NodeList
对象具有更多的方法(如 forEach
、 item
等),而 HTMLCollection
相对较少。 NodeList
和 HTMLCollection
都可以用于获取文档中的元素集合,并进行相关操作。其中, HTMLCollection
更适用于固定集合的情况,如 document.forms
、 document.links
、 document.images
等;而 NodeList
则更适用于动态查询、筛选和遍历节点的情况,
# HTML 相关性发展
早期使用 getElementsByClassName 等 API, 得到的是 HTMl 强相关的 HTMLCollection
当 DOM 发生变化时,HTMLCollection 也会随之变化,但是由于强相关会导致页面回流重绘好多次
后来出现 querySelectorAll 这个 API, 得到的是 HTML 弱相关的 NodeList
NodeList 对于 DOM 有一层处理,所以是弱相关,操作 NodeList 的元素不会导致浏览器频繁更新渲染
# HTML Element 所有的元素
(HTML 元素)
# HTML 全局属性
# DOM 元素的操作方法 (HTML 强相关):
HTMLCollection 数组
DOM.setAttribute ('style', 'font-size: 1px;');
DOM.getAttribute ('style'); 得到属性值
DOM.getAttributeNS ('namedValue','key','value') 设置属性值 (带命名空间)
DOM.removeAttribute ('style') 移除属性值
DOM.removeAttributeNS ('style') 移除属性值 (带命名空间)
DOM.hasAttribute ('style') 判断是否有属性值
DOM.hasAttributeNS ('style') 判断是否有属性值 (带命名空间)
DOM.toggleAttribute ('style') 反转属性值 (有则删除,无则添加)
# DOM 元素属性 (和 HTML 非强相关)
NodeList 数组
DOM.classList 返回 classList 对象
DOM.style 返回 CSSStyleDeclaration 对象
DOM.dataset 返回自定义属性
DOM.innerHTML 返回元素内部 HTML 内容
DOM.outerHTML 返回元素及其子元素 HTML 内容
DOM.textContent 设置或返回元素及其子元素的文本内容
DOM.value input 等特殊标签才有
# 全局属性列表
data-* 属性
等
# WebSocket
XML 和 WebSocket
XML 是一种用于存储和传输数据的标准格式,常用于 Web 应用程序之间传递数据。XML 的好处是灵活、可扩展、易于阅读,并且可以通过各种编程语言解析和操作。使用 XML,我们可以定义自己的标记来描述数据,达到数据交换的目的。
WebSocket 是 HTML5 提供的一种新的网络通信协议。它可以在浏览器和服务器之间建立一个实时的、双向通信的通道,使得数据可以实时地双向传输。WebSocket 的好处是实时性好、带宽利用率高、开发成本低,能够有效地解决 HTTP 协议中存在的单向请求 - 响应模式不能满足实时性需求的问题。
在实际开发中,XML 和 WebSocket 可以结合使用。例如,可以使用 XML 来编写数据格式,然后使用 WebSocket 将数据传输到客户端或服务器端。这样可以实现双向通信,并且能够按需发送和接收数据,从而提高了系统的实时性和效率。
// WebSocket 构造器创建一个 WebSocket 对象 | |
var socket = new WebSocket("ws://127.0.0.1:9000"); | |
// 当连接上时,给服务端发送数据 | |
socket.addEventListener("open", function (e) { | |
socket.send("Hello Server!"); | |
}); | |
let div = document.querySelector("div"); | |
// 收到数据时,进行处理 | |
socket.addEventListener("message", function (event) { | |
var blob = event.data; | |
console.log(event.data); | |
let a = JSON.parse(event.data); | |
console.log(a.list); | |
for (let i = 0; i < a.list.length; i++) { | |
// 遍历得到每一个数组 | |
let p = document.createElement("p"); | |
console.log(a.list[i].name); | |
p.innerText = a.list[i].name; | |
div.appendChild(p); | |
} | |
}); |
fetch(ES6 原生,是基于 Promise 对象封装的方法)、axios(基于 XMLHttpRequest 的库)和 XMLHttpRequest 都可以用于请求数据
# void 一元运算符
# 在箭头函数中避免泄漏
箭头函数标准中,允许在函数体不使用括号来直接返回值。如果右侧调用了一个原本没有返回值的函数,其返回值改变后,则会导致非预期的副作用。安全起见,当函数返回值不会被使用到的时候,应该使用 void
运算符,来确保 API 改变时,并不会改变箭头函数的行为。
button.onclick = () => void doSomething(); |
运算 js 表达式,返回 undefined
<a href="javascript:void(document.form.submit())">Click here to submit</a> |
# in 关系运算符
使用键,而非值
一、数组:
index in arr (使用索引,不能用值,数组中默认 index 为键,还有 length 键)
二、对象:
"PI" in Math**;**(例子),注意不可枚举的属性会返回 false,如对象中 Prototype
三、字符串
var myString = new String("coral"); | |
"length" in myString; // returns true | |
var mycar = { make: "Honda", model: "Accord", year: 1998 }; | |
"make" in mycar; // returns true | |
"model" in mycar; // returns true |
# draggable 属性
设置元素可拖拽,相关事件:ondragstart 拖拽开始、ondragover 拖拽经过、ondragenter 拖拽进入和 ondrop 停止拖拽。事件中 (event .) 包含 dataTransfer.effectAllowed=“ copy ” 等,表示设置拖拽复制还是移动 move
# css 变量
:root { | |
--big-size: 20px; | |
--color-green: green; | |
} |
# 分号分隔的重要性
// 你不知道的 JS------ 报错 | |
function foo(el) { | |
console.log(el, this.id); | |
} | |
var obj = { | |
id: "awesom", | |
}; | |
// 分号很关键,在特殊用法的时候,不用分号断开会报错 | |
[1, 2, 3].forEach(foo, obj); |
# this
# 普通函数
普通函数的 this 的绑定与函数调用方式有关,与函数定义的位置无关,
# 箭头函数
箭头函数的 this 与函数定义位置有关,与调用方式无关,(但与上层作用域的调用方式有关,上层 this 改变时,会跟着改变),其没有自己的 this,会找沿用上层的 this。
# 立即执行函数
立即执行函数的 this 一定指向 window(因为会立即执行,太早执行了)
# 定时器函数
定时器里的函数的 this 一定指向 window(因为会被赋为函数地址,延迟后普通调用)
# 事件处理函数
事件处理函数普通形式的 this 指向绑定的 DOM 元素( 注意和 e.target 有所区别 ),箭头函数形式指向绑定的作用域
一、this 的默认绑定(独立函数调用)
var obj = { | |
name: "jgas", | |
foo: function () { | |
console.log(this); | |
}, | |
}; | |
var bar = obj.foo; | |
//----------- this 默认绑定 | |
//this 的指向与调用方式有关 | |
obj.foo(); //this 指向 obj | |
bar(); //this 指向 window |
二、this 的隐式绑定(对象函数调用,或数组调用)
// 定义一个对象 | |
const person = { | |
name: "Alice", | |
sayHello: function () { | |
console.log("Hello, " + this.name); | |
}, | |
}; | |
// 调用对象的方法 | |
person.sayHello(); // 输出:Hello, Alice |
三、this 的显示绑定(特殊方法调用)
函数名.call(); //-----call 传参数用,分割 this 写在第一个形参 | |
函数名.apply(); //---apply 传参用 [] 数组,this 写在第一个形参 | |
函数名.bind(); //-------bind , 显示绑定对象中的 this,this 写在第一个形参 | |
// 当使用这些函数改变 this 指向,传入 null 或者 undefined 时,自动将 this 绑定为全局对象 |
四、通过 new 关键字绑定(实例调用)
通过 new 关键字进行绑定时,会创建一个实例对象的时候将 this 绑定到实例上。
function Person(name, age) { | |
this.name = name; | |
this.age = age; | |
} | |
const person1 = new Person("Alice", 25); | |
console.log(person1.name); // 输出:Alice | |
console.log(person1.age); // 输出:25 | |
// JS 中通过 new 关键字进性绑定 this 就是 new 绑定,new 绑定会在生成实例时尽享绑定,指向构造的实例 |
五、this 绑定的优先级:
new 关键字绑定 > bind 方法绑定;new > 隐式绑定;显示绑定 > 隐式绑定;默认绑定优先级最低
六、箭头函数的 this
箭头函数不会创建自己的执行上下文,而是继承了外部作用域的执行上下文。这意味着箭头函数的 this
值是在定义函数时确定的,而不是在函数调用时确定的。箭头函数的 this
始终指向其定义时所处的上下文,无论它在哪里被调用。
function Person(name) { | |
this.name = name; | |
this.obj = { | |
name: "obj", | |
foo1: function () { | |
return function () { | |
console.log(this.name); | |
}; | |
}, | |
foo2: function () { | |
return () => { | |
console.log(this.name); | |
}; | |
}, | |
}; | |
} | |
var person1 = new Person("person1"); | |
var person2 = new Person("person2"); |
① 普通函数,独立调用
person1.obj.foo1()(); // 普通函数独立调用,this 指向 window | |
person1.obj.foo1.call(person2)(); // 普通函数独立调用,this 指向 window | |
person1.obj.foo1().call(person2); // 普通函数,通过 call 进行调用,this 指向 person2 |
② 箭头函数独立调用
person1.obj.foo2()(); // 箭头函数不绑定 this 去上一层找,this 指向 obj | |
person1.obj.foo2.call(person2)(); //call 方法绑定作用域,然后调用,里面的箭头函数没 this,去外层作用域查找,this 指向 person2 | |
person1.obj.foo2().call(person2); //obj 隐式调用函数,后面 call 调用箭头函数没有 this,因此去外层作用于查找,this 指向 obj |
# let 和 const 声明的影响
let 和 const 声明的变量不会存储到 window 对象中,而是存储到 VariableMap 中。
(引入 let 和 const 关键字之后,window 和 GlobalObject 已经不是同一个对象了,let 和 const 声明的变量存在 VariableMap 中、一个 hash map,当使用变量时会在 VariableMap 中查找)
# NaN
NaN===NaN 返回 false,因为 NaN 是一个特殊的 Number 类型的值(不是对象),存储时为双精度浮点型,每次取出都不一样。(NaN 和其他值永不相等!)
相较于 Infinity,Infinity===Infinity 返回的是 true。
# 函数和对象的原型问题
函数可以看成是特殊的对象,函数的原型指向对象,而在 JS 当中 Object 是一个构造函数,每个构造函数都是 Function 的实例,因此,Object 是 Function 的一个实例。
console.log(typeof Function); //function | |
console.log(Object.getOwnPropertyDescriptors(Function)); // 不会报错 | |
console.log(Function instanceof Object); //true | |
console.log(Function instanceof Function); //true | |
console.log(Object instanceof Object); //true | |
console.log(Object instanceof Function); //true | |
console.log(Array instanceof Function); //true | |
console.log(Number instanceof Function); //true | |
console.log(BigInt instanceof Function); //true |
# 双等号和三等号
双等号和三等号的区别在于,是否会进行隐式转换。而在是否判断地址上没有区别(无论是双等号还是三等号 , 判断应用数据类型时都会判断地址是否相等),因此两者的区别仅在于判断基本数据类型时是否隐式转换。
let as = [1, 2]; | |
let bs = [1, 2]; | |
console.log(as == bs); //F | |
console.log(as === bs); //F 判断引用类型值时,无论是双等号,还是三等号,都会判断内存地址 | |
let cs = 100; | |
let ds = "100"; | |
console.log(cs == ds); //T 隐式转换 | |
console.log(cs === ds); //F 判断简单类型值时,都不会判断其地址 |
# Vue
# Vue.use 和 Vue.component
- Vue.use 与 vue.component 的区别
Vue.use
是用于注册插件,这个插件必须是一个对象 。 插件是通过将 Vue 构造函数上添加方法和属性,来扩展 Vue 功能。 - 而
vue.component
是注册组件。差别就像是 Vue.use = 只能穿戴,vue.component = 手机.Vue.use 比 vue.component 更强大,一般是由多个组件组成。组件是 Vue 实例的一部分,用于封装可复用的模块。 - vue.use 如何封装
Vue.use 可以接收一个对象,对象中内置了一个 install 函数,install 函数的参数为 Vue 构造函数(可以用 Vue.use 进行多个组件的批量注册)
在 Vue.use (obj) 时,会自动调用该 install 函数,并传入 Vue 构造器
用 vue.use 进行组件注册时候,会首先判断组件的 installed 属性是否为 true, 为 true 说明此组件已经注册过,如果没有注册的话,在 use 注册的同时,会给组件添加一个属性 installed:true
插件的 install 方法将接收两个参数,第一个是参数是 Vue,第二个参数是配置项 options
// components 文件夹下的 index.js: | |
import PageTools from "@/components/PageTools"; | |
import UploadExcel from "@/components/UploadExcel"; | |
import UploadImg from "@/components/UploadImg"; | |
export default { | |
install(Vue) { | |
Vue.component(PageTools.name, PageTools); | |
Vue.component(UploadExcel.name, UploadExcel); | |
Vue.component(UploadImg.name, UploadImg); | |
}, | |
}; | |
// main.js 文件中: | |
import MyPlugin from "@/components/index"; | |
Vue.use(MyPlugin); |
# Vue 组件
VueComponent是通过Vue.extend()方法创建的组件构造函数的原型。当您使用Vue.extend()创建一个组件时,会返回一个新的组件构造函数,而这个构造函数的原型即为VueComponent | |
VueComponent.prototype.__proto__ === Vue.prototype |
VueComponent
和 Vue.extend
是 Vue.js 中用于创建组件的两个关键概念,它们之间有着紧密的联系。
在 Vue.js 中,每个组件都是一个 VueComponent
实例。 VueComponent
对象代表了一个已经挂载到 DOM 树上的 Vue 组件实例,包括了该组件的状态数据、模板和组件实例方法等信息。通过访问 $options
属性,可以获取该组件的选项对象。
而 Vue.extend
方法则用于创建一个可以动态生成 VueComponent
实例的组件构造函数,每当需要创建一个新的实例时,就可以调用这个构造函数来创建。
具体而言, Vue.extend
会接收一个组件选项对象作为参数,并返回一个组件构造函数,我们可以将这个构造函数传递给 Vue.component
方法或者在某个 Vue 实例中局部注册,使得该组件可以在应用程序的任何地方使用。在 Vue 应用程序中,一个 Vue 组件最终都会被转换成一个 VueComponent
实例,而 Vue.extend
方法则是用于创建这些实例所使用的组件构造函数。
下面是一个简单的示例,展示了如何使用 Vue.extend
方法来创建一个组件构造函数,并使用该构造函数创建多个 VueComponent
实例:
javascriptCopy Code// 创建一个组件构造函数 | |
const MyComponent = Vue.extend({ | |
template: '<div></div>', | |
data() { | |
return { | |
message: 'Hello, Vue!' | |
}; | |
} | |
}); | |
// 创建两个组件实例 | |
new MyComponent().$mount('#app1'); | |
new MyComponent().$mount('#app2'); |
在上述示例中,我们使用 Vue.extend
方法创建了一个名为 MyComponent
的组件构造函数,并在选项对象中定义了模板和状态数据。然后,我们通过创建两个 MyComponent
实例并分别将它们挂载到不同的元素上。
这里需要注意的是,每次调用 MyComponent
构造函数都会生成一个新的 VueComponent
实例。也就是说,虽然这两个实例是基于同一个组件构造函数创建的,但它们之间是相互独立的,彼此不会共享状态数据和实例方法。
# native 修饰符详解
在 Vue.js 中,.native 修饰符用于在自定义组件上监听原生 DOM 事件。通过使用.native 修饰符,可以将原生事件绑定到组件的根元素上,而不是绑定到组件内部的子组件元素上。这使得在组件使用过程中,可以更方便地监听和处理原生事件。如果不使用 native
修饰符,那么在绑定事件时,实际上绑定的是组件的内部事件而不是原生 DOM 的事件。这意味着事件处理函数会被绑定到组件根元素上,而不是直接绑定到原生 DOM 元素上
总结:给组件绑定原生事件后,如果没触发就加 .native
# sync 修饰符详解
sync 修饰符相当于自定义事件的语法糖,用于父子组件数据之间的双向绑定,与 v-model 类似,(sync 修饰符修饰 v-bind 指令)
一、 一般数据进行双向绑定要:①v-bind 绑定数据,② 添加一个自定义事件(父组件上)接受新值改变旧值 ③ 子组件中抛出自定义事件,传入改变后的值(this.$emit)
二、 但是使用 sync 修饰符进行绑定之后,相当于父组件单项绑定数据的同时添加了一个自定义事件(upload : bindName),子组件不用再抛出事件,绑定的数据会随着父组件数据的改变自动改变
三、v-model 和 sync 修饰符的原理相同,都是语法糖,相当于 v-bind 绑定一个 value 属性,然后 v-on 添加一个自定义事件
# this.$set 给对象添加响应式数据
this.$set
是 Vue.js 提供的一个实例方法,用于在 Vue 实例中响应式地添加新属性到已有的对象上。它并不是 JavaScript 或者浏览器原生提供的方法,而是 Vue.js 框架提供的一种特殊功能。
通常情况下,当我们使用 Vue 来创建数据驱动的程序时,如果我们直接给一个对象添加新的属性,这个新属性将不会被 Vue 监听到变化,因为 Vue 无法自动追踪对象属性的添加或删除。这时候就可以使用 this.$set
方法来解决这个问题。
具体用法如下:
javascriptCopy Code// 在 Vue 实例的方法中使用 this.$set | |
this.$set(obj, 'newProp', 123) |
其中, obj
是已有的对象, newProp
是要添加的新属性名, 123
是新属性的初始值。
通过使用 this.$set
方法,Vue 将能够监听到新属性的变化,从而实现响应式更新。
需要注意的是,在 Vue 2.x 版本中,通常情况下是不推荐直接给对象添加新属性的,而是应该在初始化时就声明好所有的属性。但是在某些动态场景下,比如处理服务器返回的数据、动态表单等,可能会用到 this.$set
方法来动态添加属性并保持响应式。
# Vue3 中为 v-for 循环渲染的列表添加 ref
只需要 v-bind ref 为一个函数即可,则函数在每次进行列表渲染的时候都会执行一次,函数的参数为当前列表的 DOM 元素
# Vue 中组件通信
一、父子组件传值和自定义事件
二、全局事件总线
三、依赖注入 Provide / Inject
四、Vuex
五、组件上使用 ref
六、pinia
# Vue3 新增内置组件
一、teleport 传送,通过 to 属性传送对应内容
二、suspense 异步组件 未定(占位)
三、fragment 片段,比 template 更加轻量
# swc-loader
swc-loader 是用于 pollyfill 的打包工具之一,底层采用 rust 进行编写,大大提升了打包构建的速度
# SSE(Server-Send-Event)
SSE 采用服务端主动发送事件的方式(返回响应头 Content-Type:"text/event-stream''),向前端发送信息,前端通过事件的注册(通过 EventSource 构造函数创建实例,通过事件注册的方式)来接受。采用的是发布订阅的模式。
服务端注册 api,res.writeHead 中书写 Content-Type,通过 res.write 方法进行写入
浏览器端
通过
new EventSource(url,options);
创建事件源通过实例的 readState 判断连接状态
通过监听实例的 onmessage 事件来获取服务端响应过来的数据
# 获取网络连接状态
一、事件监听(不断执行)
window.addEventListener("online", () => { | |
console.log("网络连接成功"); | |
}); | |
window.addEventListener("offline", () => { | |
console.log("网络连接失败"); | |
}); |
二、通过 navigator(执行一次)
if (navigator.onLine) { | |
console.log("在线"); | |
} else { | |
console.log("掉线"); | |
} |
获取更多详细连接信息(通过 navigator.connection)
// 需要兼容性处理 | |
if ("connection" in navigator) { | |
const networkInfo = navigator.connection; | |
console.log("当前网络估计下行速度", networkInfo.downlink); | |
console.log("当前网络估计速度类型", networkInfo.effectiveType); | |
console.log("当前网络估计往返时间", networkInfo.rtt); | |
console.log("是否处于数据节省模式", networkInfo.saveData); | |
} else { | |
console.log("当前浏览器不支持"); | |
} |
# ----------------------------JS 函数式编程
在 JavaScript 中,函数式编程(Functional Programming)是一种以函数为基本构建块的编程范式。它强调将计算视为数学函数的执行,并避免了状态和可变数据。函数式编程在 JavaScript 中具有以下特点:
- 函数是一等公民:在函数式编程中,函数被视为一等公民,可以像其他值(如数字、字符串)一样被传递、赋值和返回。这意味着函数可以作为参数传递给其他函数,也可以从函数中返回另一个函数。
- 纯函数(Pure Function):纯函数是指没有副作用的函数,它的输出完全由输入决定,不会改变外部状态或产生其他可见效果。纯函数对于相同的输入始终返回相同的结果,且不会修改原始数据。在函数式编程中,鼓励使用纯函数来处理数据,因为它们易于测试、理解和推理。
- 不可变性(Immutability):函数式编程鼓励使用不可变数据,即数据在创建后不能被修改。当需要对数据进行操作时,函数式编程通常会创建新的数据副本而不是直接修改原始数据。这样可以避免出现意外的副作用和数据竞争,并使代码更加可靠和可维护。
- 高阶函数(Higher-Order Function):高阶函数是指能够接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。JavaScript 中的函数可以作为值进行传递和操作,因此非常适合编写高阶函数。高阶函数可以接收其他函数作为参数,也可以返回新的函数,从而实现函数的组合和抽象。
- 函数组合(Function Composition):函数式编程鼓励使用函数组合来创建复杂功能。函数组合是指将一个函数的输出作为另一个函数的输入,并生成一个新的函数。通过将多个小功能的函数组合在一起,可以形成更复杂的功能,提高代码的可读性和可维护性。
函数式编程的目标是编写简洁、模块化且可复用的代码。通过使用函数式编程范式,可以实现代码的易于测试、可扩展、并行处理以及更好的可维护性。在 JavaScript 中,函数式编程通常与其他编程范式(如面向对象编程)结合使用,以充分发挥 JavaScript 的灵活性和表达力。
# prototype 和、_ _proto _ _
构造函数的 prototype 是实例的原型对象(_ _ proto _ _)
prototype
属性:prototype
是函数对象特有的属性,它指向一个对象,该对象被用作构造函数创建的实例的原型。当使用new
关键字创建一个对象时,该对象的__proto__
属性会指向构造函数的prototype
属性。通过修改构造函数的prototype
属性,可以为通过该构造函数创建的实例添加共享的方法和属性。示例:
javascriptCopy Codefunction Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
const person1 = new Person('Alice');
person1.sayHello(); // 输出:Hello, Alice
__proto__
属性:__proto__
是每个对象(包括函数对象)都具有的属性,它指向该对象的原型。通过__proto__
属性,可以访问和操作对象的原型链。当访问一个对象的属性时,如果对象本身没有该属性,JavaScript 引擎会沿着对象的原型链向上查找,直到找到该属性或到达原型链的顶端(即null
)。示例:
javascriptCopy Codeconst obj = { a: 1 };
console.log(obj.__proto__); // 输出:Object {}
const arr = [1, 2, 3];
console.log(arr.__proto__); // 输出:Array []
function Person(name) {
this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.__proto__); // 输出:Person {}
需要注意的是,
__proto__
属性在现代 JavaScript 中已经被标准化为内部属性[[Prototype]]
,推荐使用Object.getPrototypeOf()
和Object.setPrototypeOf()
方法来访问和设置对象的原型。
总结:
prototype
属性是函数对象特有的属性,用于指定构造函数创建的实例的原型。__proto__
属性是每个对象都具有的属性,用于指向该对象的原型。它可以访问和操作对象的原型链。
# hasOwnProperty 和 in
OBJ . hasOwnProperty 检查对象中是否有属性(只返回在自己身上的,不包括原型)
属性 in OBJ 检查对象中是否有属性,只能检查对象是否可以调用该属性
# ES6 剩余参数
// 形参中使用数组,然后展开 | |
function a(...nums) { | |
// 传参数组 | |
console.log(nums); // 数组 | |
} | |
sum("sd", "s", "a", "b"); |
# 模拟 call 方法定义过程
(用 JS 代码模拟,实际是 C++ 代码)
Function.prototype.hycall = function (thisArg, ...args) { | |
var fn = this; | |
thisArg = thisArg ? Object(thisArg) : window; | |
thisArg.fn = fn; | |
var result = thisArg.fn(...args); | |
delete thisArg.fn; | |
return result; | |
}; | |
function foo() { | |
console.log("foo函数执行", this); | |
} | |
function sum(sum1, sum2) { | |
console.log("sum函数执行", this, sum1, sum2); | |
return num1 + num2; | |
} | |
foo.call(undefined); // 系统调用 call 方法 | |
var result = sum.call({}, 20, 30); // 系统调用 call 方法 | |
foo.hycall(undefined); | |
var result = sum.hycall("abc", 20, 30); | |
console.log("hycall的调用:", result); |
# 模拟 apply 方法定义过程
Function.prototype.hyapply = function (thisArg, argArray) { | |
// 获取到要执行的函数,this 因为是隐式调用,指向当前的函数 | |
var fn = this; | |
// 处理绑定的 thisArg,(边界判断了 0) | |
thisArg = | |
thisArg !== null && thisArg !== undefined ? Object(thisArg) : window; | |
// 执行函数 | |
thisArg.fn = fn; | |
var result; | |
// 没有传参数数组时 | |
// if (!argArray) { | |
// result = thisArg.fn() | |
// } else { | |
// result = thisArg.fn(...argArray) | |
// } 或者 | |
//argArray = argArray ? argArray:[] 或者 | |
argArray = argArray || []; | |
// 形参处理 | |
result = thisArg.fn(...argArray); | |
delete thisArg.fn; | |
return result; | |
}; | |
function sum(num1, num2) { | |
console.log("sum被调用", this, num1, num2); | |
return num1 + num2; | |
} | |
// 自己实现的调用 | |
var result = sum.hyapply("abc", [20, 30]); | |
console.log(result); |
# 关键字 arguments (函数中)
一、类数组对象,默认绑定函数执行时传入的参数
二、可以使用 length 或者索引,但是不能使用数组内置的一些方法(类数组对象)
function ad() { | |
console.log(arguments); | |
console.log(arguments.length); | |
console.log(arguments[0]); | |
} | |
ad(1, 4, 2, 1, 22); | |
ad("sd", "sdj"); | |
ad("d"); |
三、如何让 arguments 类数组对象可以使用数组的内置方法?
// 一、 遍历类数组对象 | |
function foo(num1, num2) { | |
var newArr = []; | |
for (var i = 0; i < arguments.length; i++) { | |
newArr.push[arguments[i] * 10]; | |
} | |
console.log(newArr); | |
} | |
foo(10, 20, 30, 40, 50); | |
// 二、数组原型上使用方法,加到 arguments 上(argument 类数组,可遍历迭代,因此可以使用) | |
var newArr2 = Array.prototype.slice.call(arguments); | |
console.log(newArr2); | |
// 或 三: | |
var newArr3 = [].slice.call(arguments); | |
console.log(newArr3); | |
// 或 四、将类数组对象转成数组类型 | |
var newArr4 = Array.from(arguments); | |
// 或 五、转成数组类型 | |
var newArr5 = [...arguments]; |
四、箭头函数没有 arguments
对象 ,函数执行时会去上层作用域寻找
五、注意浏览器中全局对象中没有 arguments
, 但是 node 环境全局中有 arguments
,里面存放的是模块
# 剩余参数...args
...args 不是关键字!!,只能使用在函数定义时,最后一个形参
...arg
和 arguments
都可以用于处理函数中的参数。但是,它们之间有一些重要的区别和不同:
- 变量类型不同:
...arg
表示一个数组,而arguments
表示一个类似数组的对象。 - 使用限制不同:
...arg
只能用在函数定义时的最后一个参数上,而arguments
可以在任何地方使用。 - 可迭代性不同:由于
...arg
生成的是一个真正的数组,因此可以使用数组的所有方法,如forEach()
、map()
、reduce()
等。而arguments
并非真正的数组,因此不能使用数组方法,需要通过数组转换才能使用。
# 纯函数
一、定义:①、相同的输入,产生相同的输出,输出只和函数内部执行有关。即输出不会因为外部变量或状态改变受影响
②、无副作用,不会改变外部变量的值或状态(副作用是产生 bug 的温床)
二、举例:数组中 slice
方法是纯函数, splice
方法是非纯函数
# 函数的柯里化
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。例如:
const add = (x, y) => x + y; // 柯里化转化为; | |
const add2 = (x) => (y) => x + y; | |
add2(4)(4); | |
// 又或者: | |
function foo(m, n, x, y) { | |
return m + n + x + y; | |
} | |
foo(10, 20, 30, 40); | |
// 柯里化的过程 | |
function bar(m) { | |
return function (n) { | |
return function (x) { | |
return function (y) { | |
m + n + x + y; | |
}; | |
}; | |
}; | |
} | |
bar(10)(20)(30)(40); | |
// 简化柯里化代码 | |
var sum3 = (x) => (y) => (z) => x + y + z; |
柯里化的作用和好处:使得一个函数处理问题尽可能的单一、简单,而不是一大推处理过程交给一个函数来处理,并且可以复用和定制化
柯里化的本质:将某个操作中已完成的结果保留,知道其余部分后续也完成后可以一并提供的机制,通过在一个函数中返回另一个函数实现
var log = (date) => (type) => (message) => { | |
console.log( | |
`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]` | |
); | |
}; | |
var nowLog = log(new Date()); | |
nowLog("DEBUG")("查找到轮播图的bug"); | |
nowLog("FETURE")("新增了添加功能"); | |
var nowAndDebugLog = log(new Date())("DEBUG"); | |
nowAndDebugLog("找到bug"); | |
var nowAndFutureLog = log(new Date())("FETURE"); | |
nowAndFutureLog("添加功能"); |
柯里化的本质:
函数柯里化就是将多参函数转变为单参函数的过程,柯里化将函数转变为高阶的返回新函数的函数,目的是将原先传入的多个参数利用函数作用域储存起来,以供后续直接使用。
# ------------------------- 面向对象编程
# 浏览器事件循环
①setTimeout、Http、DOM、等并不在浏览器内核(如 V8 引擎中)
② 调用函数时,进入函数就将函数放进执行栈,离开函数就将函数弹出执行栈
③ 在栈中表现很慢的东西叫做阻塞(http 请求,多重 for 循环,加载资源等),执行栈中的内容是一个个执行的,因为 js 是一门单线程的语言,那么当执行阻塞时,为了不影响到页面的流畅 ,浏览器会开启其余的线程(交互线程、文件线程、网络线程、计时线程、等)进行辅助处理
# webAPI
Web API 简介 - 学习 Web 开发 | MDN (mozilla.org)
分为:一、浏览器 API 二、第三方 API
# navigator.sendBeacon
// 性能中继器,使用不同指标来衡量和分析应用程序的性能 | |
// 当页面上任何指标值完成计算时,将传递计算出的结果并触发这个函数 | |
// 可以使用它将结果记录到控制台或者发送到特定端点 | |
function sendTo(metric) { | |
const content = JSON.stringify(metric); | |
if (navigator.sendBeacon) { | |
navigator.sendBeacon("http://test", content); //navigator.sendBeacon 的作用是 | |
} else { | |
fetch("http://test.com", { | |
content, | |
method: "POST", | |
keepAlive: true, //fetch 中的 keepAlice 保证了即使页面刷新或者关闭,发出的请求仍然存在并进行,而不是进行到一半的请求突然停止 | |
}) | |
.then(() => { | |
console.log("发送成功"); | |
}) | |
.catch((e) => { | |
console.error(e); | |
}); | |
} | |
} | |
reportWebVitals(sendTo); // 每一次得到计算结果都会执行一次 |
# 对比 Ajax fetch
# 优点
- 不受页面卸载过程的影响,确保数据可靠发送。
- 异步执行,不阻塞页面关闭或跳转。
- 能够发送跨域请求。
# 缺点
- fetch 和 ajax 都可以发送任意请求 而 sendBeacon 只能发送 POST
- fetch 和 ajax 可以传输任意字节数据 而 sendBeacon 只能传送少量数据(64KB 以内)
- fetch 和 ajax 可以定义任意请求头 而 sendBeacon 无法自定义请求头
- sendBeacon 只能传输
ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
、FormData
或URLSearchParams
类型的数据 如果处于危险的网络环境,或者开启了广告屏蔽插件 此请求将无效
# 应用场景
- 发送心跳包:可以使用
navigator.sendBeacon
发送心跳包,以保持与服务器的长连接,避免因为长时间没有网络请求而导致连接被关闭。 - 埋点:可以使用
navigator.sendBeacon
在页面关闭或卸载时记录用户在线时间,pv uv,以及错误日志上报 按钮点击次数。 - 发送用户反馈:可以使用
navigator.sendBeacon
发送用户反馈信息,如用户意见、bug 报告等,以便进行产品优化和改进
# 深拷贝新增
window.structuredClone 方法,用于直接实现深拷贝。
# React.StrictMode
React 的严格模式默认会渲染两次 react 组件(提前进行一个预渲染),第一次渲染用于收集工作(如计算和副作用),第二次渲染实际上显示在屏幕上。这种双重渲染有助于 React 检测组件中可能的副作用。以便于开发者能发现和修复性能问题。例如,如果一个函数组件在渲染时执行了不必要的计算或者副作用,这可能会在两次渲染中表现出来。通过比较两次渲染的结果,React 可以检测出组件中是否有副作用。如果两次渲染的结果不一致,那么可能表明组件在渲染过程中产生了副作用。
# >>> 0 是什么意思
>>>
标识无符号右移,该操作符会将符号位保留,将位数向左移动一位,那么无符号位移 0 有什么作用呢?其实无符号位移 0 相当于:①将非 number 类型的数据转换为 number,②将 number 转换为无符号的 32bit 数据,也就是 Uint32 类型。这些与移位的位数无关,移位 0 位主要就是用了 js 的内部特性做了前两种转换。
# 巧用双等于 null
如果想要同时判断不等于 null 和 undefined,可以巧用双等号,例如:
if(this == null){ return "this is null or undefined"}
,从而同时判断不为 null,undefined。