# 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;
}

# 解决获取对象属性时存在的兼容性问题

# 获取对象属性问题
  1. 浏览器兼容性:不同浏览器在处理 CSS 属性名称上存在差异。例如,某些浏览器可能将 background-color 属性解析为 backgroundColor ,而另一些浏览器则保持原样。这就导致了使用 obj.attr 的方式无法跨浏览器获得准确的样式值。

  2. 获取计算后样式:直接通过 obj.attr 获取的是元素的内联样式(即行内样式),而不是计算后的样式(包括 CSS 样式和继承样式)。如果需要获取元素显示时的实际样式,例如考虑到外部样式表或通过其他选择器影响的样式,需要使用 getComputedStylecurrentStyle 方法。

    ** 例如:如下代码中,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 中定义在对象上的两种不同类型的方法,它们有以下区别和好处:

  1. 区别:
    • 原型方法:定义在对象的原型上,即通过构造函数的 prototype 属性添加的方法。所有通过该构造函数创建的实例对象都共享同一个原型方法。
    • 实例方法:定义在对象实例上,通过构造函数创建的每个对象实例都具有自己的实例方法。
  2. 好处:
    • 原型方法的好处:
      • 节省内存:所有通过构造函数创建的实例对象共享原型方法,避免为每个实例对象重复创建方法,节省了内存开销。
      • 动态性:原型方法的修改会立即反映在所有实例对象上。当你在原型上添加、删除或修改方法时,所有实例对象会自动继承这些变化,无需更新每个实例对象。
    • 实例方法的好处:
      • 封装性:实例方法可以使用构造函数内部的变量和方法,实现更灵活和封装的对象行为。
      • 访问实例属性:实例方法可以直接访问实例对象的属性,因为它们是在实例对象中定义的。
// 构造函数
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 快捷键生成乱文的步骤:

  1. 在编辑器中选中或定位到您想要插入乱文的位置。
  2. 键入 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 对象具有更多的方法(如 forEachitem 等),而 HTMLCollection 相对较少。 NodeListHTMLCollection 都可以用于获取文档中的元素集合,并进行相关操作。其中, HTMLCollection 更适用于固定集合的情况,如 document.formsdocument.linksdocument.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-* 属性

autofocus (en-US)

autocapitalize

# 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

  1. Vue.use 与 vue.component 的区别
    Vue.use 是用于注册插件,这个插件必须是一个对象 。 插件是通过将 Vue 构造函数上添加方法和属性,来扩展 Vue 功能。
  2. vue.component注册组件。差别就像是 Vue.use = 只能穿戴,vue.component = 手机.Vue.use 比 vue.component 更强大,一般是由多个组件组成。组件是 Vue 实例的一部分,用于封装可复用的模块。
  3. 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

VueComponentVue.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 中具有以下特点:

  1. 函数是一等公民:在函数式编程中,函数被视为一等公民,可以像其他值(如数字、字符串)一样被传递、赋值和返回。这意味着函数可以作为参数传递给其他函数,也可以从函数中返回另一个函数。
  2. 纯函数(Pure Function):纯函数是指没有副作用的函数,它的输出完全由输入决定,不会改变外部状态或产生其他可见效果。纯函数对于相同的输入始终返回相同的结果,且不会修改原始数据。在函数式编程中,鼓励使用纯函数来处理数据,因为它们易于测试、理解和推理。
  3. 不可变性(Immutability):函数式编程鼓励使用不可变数据,即数据在创建后不能被修改。当需要对数据进行操作时,函数式编程通常会创建新的数据副本而不是直接修改原始数据。这样可以避免出现意外的副作用和数据竞争,并使代码更加可靠和可维护。
  4. 高阶函数(Higher-Order Function):高阶函数是指能够接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。JavaScript 中的函数可以作为值进行传递和操作,因此非常适合编写高阶函数。高阶函数可以接收其他函数作为参数,也可以返回新的函数,从而实现函数的组合和抽象。
  5. 函数组合(Function Composition):函数式编程鼓励使用函数组合来创建复杂功能。函数组合是指将一个函数的输出作为另一个函数的输入,并生成一个新的函数。通过将多个小功能的函数组合在一起,可以形成更复杂的功能,提高代码的可读性和可维护性。

函数式编程的目标是编写简洁、模块化且可复用的代码。通过使用函数式编程范式,可以实现代码的易于测试、可扩展、并行处理以及更好的可维护性。在 JavaScript 中,函数式编程通常与其他编程范式(如面向对象编程)结合使用,以充分发挥 JavaScript 的灵活性和表达力。

# prototype 和、_ _proto _ _

构造函数的 prototype 是实例的原型对象(_ _ proto _ _)

  1. 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
  2. __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 不是关键字!!,只能使用在函数定义时,最后一个形参

...argarguments 都可以用于处理函数中的参数。但是,它们之间有一些重要的区别和不同:

  1. 变量类型不同...arg 表示一个数组,而 arguments 表示一个类似数组的对象。
  2. 使用限制不同...arg 只能用在函数定义时的最后一个参数上,而 arguments 可以在任何地方使用。
  3. 可迭代性不同:由于 ...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

# 优点
  1. 不受页面卸载过程的影响,确保数据可靠发送。
  2. 异步执行,不阻塞页面关闭或跳转。
  3. 能够发送跨域请求。
# 缺点
  1. fetch 和 ajax 都可以发送任意请求 而 sendBeacon 只能发送 POST
  2. fetch 和 ajax 可以传输任意字节数据 而 sendBeacon 只能传送少量数据(64KB 以内)
  3. fetch 和 ajax 可以定义任意请求头 而 sendBeacon 无法自定义请求头
  4. sendBeacon 只能传输 ArrayBuffer ArrayBufferView Blob DOMString FormData URLSearchParams 类型的数据
  5. 如果处于危险的网络环境,或者开启了广告屏蔽插件 此请求将无效

# 应用场景

  1. 发送心跳包:可以使用 navigator.sendBeacon 发送心跳包,以保持与服务器的长连接,避免因为长时间没有网络请求而导致连接被关闭。
  2. 埋点:可以使用 navigator.sendBeacon 在页面关闭或卸载时记录用户在线时间,pv uv,以及错误日志上报 按钮点击次数。
  3. 发送用户反馈:可以使用 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。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

dmq 微信支付

微信支付

dmq 支付宝

支付宝