let、var、const函数作用域、全局作用域、块级作用域
函数作用域、全局作用域和块级作用域是 JavaScript 中不同的作用域类型,它们之间有着一些关键的区别:
- 函数作用域(Function Scope):
- 变量在函数内部声明时,它们的作用域范围被限定在该函数内部。这意味着函数内部声明的变量在函数外部是不可访问的,但是函数内部的代码可以访问函数外部声明的变量。
- 使用
var
声明的变量具有函数作用域,它们在整个函数体内都是可见的。 - JavaScript 引擎编译代码时,会将
var
声明的变量提升到作用域的顶部。在函数内声明时,就提升到函数作用域,整个函数都能访问。在全局声明时,是全局作用域,全局能访问
- 全局作用域(Global Scope):
- 全局作用域是指在所有函数外部声明的变量拥有的作用域。这些变量对整个脚本都是可见的。
- 在浏览器中,全局作用域指的是
window
对象;在 Node.js 环境中,指的是global
对象。
- 块级作用域(Block Scope):
- 块级作用域是 ES6 引入的一种作用域,它将变量的作用域限定在一个块(由
{}
定义)内部。 - 使用
let
或const
声明的变量具有块级作用域,它们只在声明它们的块内部可见。 - 块级作用域通常出现在控制流语句(如
if
、for
、while
)的花括号中,以及函数内部。
- 块级作用域是 ES6 引入的一种作用域,它将变量的作用域限定在一个块(由
关于这些作用域的区别:
- 函数作用域仅限于函数内部,而全局作用域适用于整个脚本。
- 块级作用域是 ES6 引入的一种新特性,使得开发者能够更细粒度地控制变量的作用范围,避免了变量泄漏和命名冲突问题。
- 变量提升(hoisting)只会发生在函数作用域和全局作用域中的
var
声明的变量中,而在块级作用域中使用let
或const
声明的变量不会发生变量提升。
代码说明:
function a (){
console.log(b); //这个位置打印 b = undefined 但是没有报错
if(1){var b = 1};
console.log('a',b);
}
// console.log(b); 这个位置打印 b 报错:b is not defined 说明var 提升变量作用域最多在上层
function c (){
if(1){let b = 1;const e = 1};
console.log(b,e); //这个位置报错
}
a()
c()
打印结果:
undefined
a 1
test.js:15 Uncaught ReferenceError: b is not defined
at c (test.js:15:17)
at test.js:18:1
for(var i = 0;i<3 ;i++){
setTimeout(()=>console.log(i),1000)
}
for(let i = 0;i<3 ;i++){
setTimeout(()=>console.log(i),1000)
}
打印结果:
3
3
3
0
1
2
html语义化
结构化标签
标签 | 说明 | 元素类型 |
---|---|---|
<html> | HTML 文档的根元素 | 块元素 |
<head> | 文档的头部,包含元数据信息 | 块元素 |
<title> | 定义文档的标题 | 块元素 |
<body> | 文档的主体内容 | 块元素 |
<header> | 定义页面或区块的页眉 | 块元素 |
<footer> | 定义页面或区块的页脚 | 块元素 |
<nav> | 定义导航链接的容器 | 块元素 |
<article> | 定义独立的文章内容 | 块元素 |
<section> | 定义文档中的节(section),比如章节、页眉、页脚或文档中的其他部分 | 块元素 |
<aside> | 定义页面的侧边栏内容块元素 | 块元素 |
文本内容标签:
标签 | 说明 | 元素类型 |
---|---|---|
<p> | 定义段落 | 块元素 |
<span> | 用于对文本的一部分进行分组或添加样式 | 内联元素 |
<strong> | 定义重要的文本 | 内联元素 |
<em> | 定义强调的文本 | 内联元素 |
<a> | 定义超链接 | 内联元素 |
<br> | 换行 | 内联元素 |
<hr> | 水平分隔线 | 块元素 |
列表标签:
标签 | 说明 | 元素类型 |
---|---|---|
<ul> | 无序列表 | 块元素 |
<ol> | 有序列表 | 块元素 |
<li> | 列表项 | 块元素 |
css盒模型
在网页设计和开发中,CSS盒模型(Box Model)是一个重要的概念,它定义了元素在网页中的空间占用方式。每个元素都被表示为一个矩形盒子,包含内容区域和可选的内边距(padding)、边框(border)和外边距(margin)。盒模型的各个部分如下:
- 内容区域(Content): 元素的实际内容,如文本、图像等。
- 内边距(Padding): 内容与边框之间的空白区域。内边距不会改变元素的背景色。
- 边框(Border): 围绕内容和内边距的边框,可以有不同的宽度、样式和颜色。
- 外边距(Margin): 元素周围的空白区域,将元素与其他元素分开。外边距是透明的。
box-sizing属性
box-sizing有两个属性 默认为content-box 、border-box
.box {
box-sizing: content-box; /* 改变盒模型计算方式 */
width: 200px; /* 总宽度包括内容、内边距和边框 */
height: 100px; /* 总高度包括内容、内边距和边框 */
padding: 20px;
border: 5px solid black;
margin: 15px;
background-color: lightblue;
}
总宽度 = 内容宽度 + 左右内边距 + 左右边框宽度 + 左右外边距
总宽度 = 200px + 20px2 + 5px2 = 250px
.box {
box-sizing: border-box; /* 改变盒模型计算方式 */
width: 200px; /* 总宽度包括内容、内边距和边框 */
height: 100px; /* 总高度包括内容、内边距和边框 */
padding: 20px;
border: 5px solid black;
margin: 15px;
background-color: lightblue;
}
总宽度=width
content-width = width - 左右内边距 - 左右边框宽度-左右外边距
选择器优先级
内联样式(Inline Styles)
- 最高优先级:由元素的
style
属性定义的样式。例如:<div style="color: red;"></div>
。 - 权值:
1000
ID选择器(ID Selectors)
- 用
#
号定义的选择器。例如:#header
- 权值:
0100
类选择器、属性选择器和伪类(Class Selectors, Attribute Selectors, and Pseudo-classes)
- 用
.
号定义的类选择器。例如:.container
- 属性选择器。例如:
[type="text"]
- 伪类。例如:
:hover
,:nth-child
- 权值:
0010
元素选择器和伪元素(Element Selectors and Pseudo-elements)
- 标签名选择器。例如:
div
,h1
- 伪元素。例如:
::before
,::after
- 权值:
0001
css继承
可继承的CSS属性
- 文本属性
color
: 文本颜色font-family
: 字体系列font-size
: 字体大小font-style
: 字体样式(如斜体)font-weight
: 字体粗细font-variant
: 字体变体(如小型大写字母)font-stretch
: 字体拉伸letter-spacing
: 字母间距line-height
: 行高text-align
: 文本对齐方式text-indent
: 首行缩进text-transform
: 文本转换(如大写、小写、首字母大写)text-shadow
: 文本阴影white-space
: 空白处理方式word-spacing
: 单词间距
- 列表属性
list-style
: 列表样式的简写list-style-type
: 列表项标记类型list-style-position
: 列表项标记的位置list-style-image
: 列表项标记的图像
- 表格属性
caption-side
: 表格标题的位置border-collapse
: 表格边框的折叠方式border-spacing
: 表格单元格之间的距离empty-cells
: 显示或隐藏表格中的空单元格
不可继承的CSS属性
- 布局属性
display
: 元素显示类型position
: 元素定位方式top
,right
,bottom
,left
: 定位偏移z-index
: 层叠顺序overflow
: 溢出内容处理方式float
: 浮动clear
: 清除浮动
- 尺寸属性
width
: 宽度height
: 高度max-width
: 最大宽度max-height
: 最大高度min-width
: 最小宽度min-height
: 最小高度
- 盒模型属性
margin
: 外边距padding
: 内边距border
: 边框border-width
: 边框宽度border-style
: 边框样式border-color
: 边框颜色
- 背景属性
background
: 背景的简写background-color
: 背景颜色background-image
: 背景图像background-repeat
: 背景图像重复方式background-position
: 背景图像位置background-size
: 背景图像尺寸
- 其他属性
opacity
: 不透明度visibility
: 可见性box-shadow
: 盒子阴影transition
: 过渡效果的简写transform
: 变换
控制继承的特殊值
CSS还提供了一些特殊值来控制继承行为:
-
inherit: 强制继承父元素的属性值。
.child { color: inherit; }
-
initial: 将属性值设置为默认值。
.child { color: initial; }
-
unset: 如果属性是继承属性,则继承父元素的值;如果不是继承属性,则使用初始值。
.child { color: unset; }
-
revert: 恢复属性的用户代理样式表或用户样式表中的值。
.child { color: revert; }
css3新特性
CSS3 引入了许多新特性,极大地扩展了 CSS 的功能,使网页设计更加灵活和强大。以下是一些最重要的 CSS3 新特性:
1. 选择器
CSS3 增加了许多新的选择器,使选择元素更加方便和灵活:
- 属性选择器:
[attribute^=value]
,[attribute$=value]
,[attribute*=value]
等。 - 结构伪类选择器:
:nth-child(n)
,:nth-of-type(n)
,:first-of-type
,:last-of-type
,:only-of-type
等。 - UI 伪类选择器:
:enabled
,:disabled
,:checked
,:target
等。 - 否定伪类选择器:
:not(selector)
。
2. 边框和背景
-
圆角边框
:
border-radius
.box { border-radius: 10px; }
-
盒阴影
:
box-shadow
.box { box-shadow: 2px 2px 5px rgba(0,0,0,0.3); }
-
背景渐变
:
linear-gradient
,
radial-gradient
.box { background: linear-gradient(to right, red, blue); }
-
-
多背景图像
- 多个
background-image
.box { background-image: url(image1.png), url(image2.png); }
3. 颜色和透明度
-
RGBA 和 HSLA 颜色模型
.box { background-color: rgba(255, 0, 0, 0.5); }
4. 文本效果
-
文本阴影
:
text-shadow
.text { text-shadow: 2px 2px 2px rgba(0,0,0,0.5); }
-
多列布局
:
column-count
,
column-gap
,
column-rule
.text { column-count: 3; column-gap: 20px; column-rule: 1px solid black; }
5. 变换和动画
-
变换
:
transform
(如
rotate
,
scale
,
translate
,
skew
)
.box { transform: rotate(45deg); }
-
过渡
:
transition
(如
transition-property
,
transition-duration
,
transition-timing-function
)
.box { transition: all 0.5s ease; }
-
动画
:
@keyframes
,
animation
@keyframes example { from { background-color: red; } to { background-color: yellow; } } .box { animation: example 5s infinite; }
6. 布局
-
Flexbox
:
display: flex
,简化了复杂的布局需求
.container { display: flex; justify-content: center; align-items: center; }
-
Grid
:
display: grid
,提供强大的二维布局能力
.container { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px; }
7. 其他
-
媒体查询
:
@media
,用于响应式设计
@media (max-width: 600px) { .container { flex-direction: column; } }
-
自定义属性(CSS变量)
:
--variable-name
:root { --main-color: #06c; } .box { background-color: var(--main-color); }
-
-
calc() 函数
- 用于动态计算属性值
.box { width: calc(100% - 50px); }
ES6数组新增方法
ES6(ECMAScript 2015)为数组添加了许多新的方法,使得对数组的操作更加便捷和高效。以下是一些 ES6 中新增的数组方法:
1. Array.from()
将类数组对象或可迭代对象转换为数组。
const str = 'hello';
const chars = Array.from(str); // ['h', 'e', 'l', 'l', 'o']
2. Array.of()
根据传入的参数创建一个新数组。
const nums = Array.of(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
3. Array.prototype.find()
查找数组中符合条件的第一个元素,并返回其值。
const arr = [10, 20, 30, 40, 50];
const result = arr.find(item => item > 30); // 40
4. Array.prototype.findIndex()
查找数组中符合条件的第一个元素,并返回其索引。
const arr = [10, 20, 30, 40, 50];
const index = arr.findIndex(item => item > 30); // 3
5. Array.prototype.includes()
判断数组是否包含某个值,返回布尔值。
const arr = [1, 2, 3, 4, 5];
const hasThree = arr.includes(3); // true
6. Array.prototype.fill()
用指定的值填充数组。
const arr = [1, 2, 3, 4, 5];
arr.fill(0); // [0, 0, 0, 0, 0]
7. Array.prototype.entries()
返回一个包含数组键值对的迭代器对象。
const arr = ['a', 'b', 'c'];
const iterator = arr.entries();
for (const [index, value] of iterator) {
console.log(index, value); // 0 'a',1 'b',2 'c'
}
8. Array.prototype.keys()
和 Array.prototype.values()
分别返回一个包含数组索引和值的迭代器对象。
const arr = ['a', 'b', 'c'];
const keys = arr.keys();
const values = arr.values();
for (const index of keys) {
console.log(index); // 0,1,2
}
for (const value of values) {
console.log(value); // 'a','b','c'
}
9. Array.prototype.flat()
和 Array.prototype.flatMap()
用于处理多维数组,将嵌套的数组“扁平化”为一维数组。
const arr = [1, [2, 3], [4, [5, 6]]];
const flatArr = arr.flat(); // [1, 2, 3, 4, [5, 6]]
flatMap()
除了“扁平化”数组外,还可以对每个元素执行一个映射函数。
const arr = [1, 2, 3];
const mappedArr = arr.flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
10. Array.prototype.sort()
在 ES6 中, sort()
方法不再要求传入比较函数,默认将元素转换为字符串后按 Unicode 顺序进行排序。
const arr = [10, 1, 2, 20];
arr.sort(); // [1, 10, 2, 20]
ES5字符串方法
ES6(ECMAScript 2015)引入了许多新的字符串方法,使得对字符串的操作更加方便和强大。以下是一些 ES6 中新增的字符串方法:
1. String.prototype.startsWith()
判断字符串是否以指定的字符开头。
const str = 'Hello, world!';
const startsWithHello = str.startsWith('Hello'); // true
2. String.prototype.endsWith()
判断字符串是否以指定的字符结尾。
const str = 'Hello, world!';
const endsWithWorld = str.endsWith('world!'); // true
3. String.prototype.includes()
判断字符串是否包含指定的字符。
const str = 'Hello, world!';
const includesHello = str.includes('Hello'); // true
4. String.prototype.repeat()
重复一个字符串指定次数,并返回新的字符串。
const str = 'abc';
const repeatedStr = str.repeat(3); // 'abcabcabc'
5. String.prototype.padStart()
和 String.prototype.padEnd()
在字符串的开始或结尾添加指定的字符,直到达到指定的长度。
const str = '123';
const paddedStart = str.padStart(5, '0'); // '00123'
const paddedEnd = str.padEnd(5, '0'); // '12300'
6. 模板字符串
ES6 引入了模板字符串,使用反引号 (`) 包裹字符串,可以在其中插入变量和表达式,并支持换行。
const name = 'Alice';
const age = 30;
const greeting = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting);
// 输出:
// Hello, my name is Alice and I am 30 years old.
7. String.raw()
返回一个模板字符串的原始字符串形式,忽略转义字符。
const str = String.raw`Hello\nWorld`;
console.log(str); // 'Hello\\nWorld'
8. Unicode 支持
ES6 对 Unicode 字符的处理更加友好,支持处理四字节 UTF-16 编码的字符。
如何判断一个变量是否为数组
在 JavaScript 中,有几种方法可以判断一个变量是否为数组:
1. 使用 Array.isArray()
方法
Array.isArray()
是 JavaScript 中用来判断一个变量是否为数组的最常用方法。
const arr = [1, 2, 3];
const isArr = Array.isArray(arr); // true
2. 使用 instanceof
操作符
instanceof
操作符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
const arr = [1, 2, 3];
const isArr = arr instanceof Array; // true
3. 使用 Object.prototype.toString.call()
这种方法是一种通用的方法,不仅可以判断数组,还可以判断其他类型的变量。
const arr = [1, 2, 3];
const isArr = Object.prototype.toString.call(arr) === '[object Array]'; // true
4.Object.getprototypeof
这个方法可以返回一个对象的原型
const arr = new Array(1,2,3,4)
console.log(Object.getPrototypeOf(arr) == Array.prototype);
防抖/节流
防抖(Debouncing)和节流(Throttling)都是用来优化 JavaScript 函数执行频率的技术,但它们的实现和使用场景略有不同。
1. 防抖(Debouncing)
防抖是指在事件被触发后,延迟一定时间再执行相应的操作。如果在这个延迟时间内又触发了相同的事件,则重新计时。通俗来说,就是触发事件后,在等待一段时间后才执行操作,如果在等待时间内又触发了相同的事件,则重新计时。
实现方式:
function debounce(func, delay) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
使用场景:
- 输入框搜索:当用户输入完成后,再触发搜索操作,避免频繁发送请求。
- 窗口调整:当窗口大小调整完成后,再执行相应的操作,如重新布局页面。
2. 节流(Throttling)
节流是指在一定时间内只执行一次相应的操作。即使在这段时间内触发多次事件,也只会执行一次操作。通俗来说,就是触发事件后,在一定时间内只执行一次操作。
实现方式:
function throttle(func, dela-y) {
let timer;
return function() {
const context = this;
const args = arguments;
if (!timer) {
timer = setTimeout(() => {
func.apply(context, args);
timer = null;
}, delay);
}
};
}
使用场景:
- 页面滚动:滚动事件触发频率很高,可以节流来限制执行次数。
- 鼠标移动:鼠标移动事件也很频繁,节流可以降低事件处理的频率。
总结
- 防抖适合连续触发事件后,在事件停止触发后执行一次操作的场景。
- 节流适合连续触发事件时,控制函数执行频率的场景,使得操作不会过于频繁。
js操作DOM
JavaScript 中有许多用于操作 DOM(Document Object Model)的方法,这些方法允许你添加、删除、修改页面上的元素和属性。以下是一些常见的 DOM 操作方法:
1. 获取元素
-
getElementById(): 通过元素的 ID 获取元素。
const element = document.getElementById('myElement');
-
getElementsByClassName(): 通过元素的类名获取元素集合。
const elements = document.getElementsByClassName('myClass');
-
getElementsByTagName(): 通过元素的标签名获取元素集合。
const elements = document.getElementsByTagName('div');
-
querySelector(): 通过 CSS 选择器获取匹配的第一个元素。
const element = document.querySelector('.myClass');
-
querySelectorAll(): 通过 CSS 选择器获取所有匹配的元素集合。
const elements = document.querySelectorAll('div.myClass');
querySelector 静态的 getelement 动态
当往节点里面添加的 getelement会动态新增而getelement不会
2. 创建元素
-
-
createElement()
- 创建一个新的元素节点。
const newElement = document.createElement('div');
3. 插入、移动和删除元素
-
appendChild(): 将一个新的子节点添加到指定节点的子节点列表的末尾。
parentElement.appendChild(newElement);
-
insertBefore(): 在指定节点之前插入一个新的子节点。
parentElement.insertBefore(newElement, referenceElement);
-
removeChild(): 从父节点中移除指定的子节点。
parentElement.removeChild(childElement);
-
replaceChild(): 替换父节点中的一个子节点。
parentElement.replaceChild(newChild, oldChild);
4. 修改元素属性和内容
-
setAttribute(): 设置指定元素的属性值。
element.setAttribute('id', 'newId');
-
getAttribute(): 获取指定元素的属性值。
const value = element.getAttribute('id');
-
innerHTML: 设置或获取指定元素的 HTML 内容。
element.innerHTML = '<p>Hello, world!</p>';
-
innerText: 设置或获取指定元素的文本内容。
element.innerText = 'Hello, world!';
-
classList: 添加、移除、切换或检查指定元素的类。
element.classList.add('active'); element.classList.remove('inactive'); element.classList.toggle('active'); const hasClass = element.classList.contains('active');
5. 其他操作
-
addEventListener(): 添加事件监听器。
element.addEventListener('click', function(event) { console.log('Clicked!'); });
-
removeEventListener(): 移除事件监听器。
element.removeEventListener('click', eventHandler);
transition与animation区别
transition
和 animation
是 CSS 中用于创建动画效果的两种机制,它们在实现动画时有一些区别。
1. transition 过渡
-
概念:
transition
允许元素在一种状态到另一种状态之间平滑地过渡,比如改变元素的大小、颜色、位置等属性时,可以使过渡更加平滑。 -
注意:只能是数值型才能过度
-
使用方式: 通过设置元素的属性值在变化时进行平滑过渡。
-
特点
:
- 通常用于简单的、一次性的动画效果。
- 只能定义开始状态和结束状态,中间的状态由浏览器自动计算完成。
-
示例
:
.box { width: 100px; height: 100px; background-color: red; transition: width 2s, height 2s, background-color 2s; } .box:hover { width: 200px; height: 200px; background-color: blue; }
2. animation 动画
-
概念:
animation
允许开发者自定义动画序列,可以控制动画的每一帧。 -
使用方式: 通过定义关键帧
@keyframes
和动画属性来定义动画的每一帧和动画效果。 -
特点
:
- 可以定义多个关键帧,精确控制动画过程中的每一帧。
- 可以定义循环播放、播放速度、动画方向等。
-
示例
:
@keyframes myAnimation { 0% { transform: scale(1); } 50% { transform: scale(1.5); } 100% { transform: scale(1); } } .box { animation: myAnimation 2s infinite alternate; }
区别总结
transition
适合简单的、一次性的属性变化,如鼠标悬停时的颜色、大小变化。animation
更适合复杂的动画效果,可以控制每一帧的细节,定义循环播放、播放速度等属性。transition
只能定义开始状态和结束状态,中间的状态由浏览器自动计算完成,而animation
可以定义多个关键帧,精确控制动画过程中的每一帧。transition
通常更容易使用和理解,而animation
更加灵活,可以实现更复杂的动画效果。
js基本数据类型,es6新增了哪些数据类型。
JavaScript 中的基本数据类型包括:字符串(String)、数字(Number)、布尔值(Boolean)、空值(Null)、未定义(Undefined)和符号(Symbol)。
除了这些基本数据类型外,ES6(ECMAScript 2015)引入了一种新的数据类型:BigInt(大整数)。BigInt 类型可以表示任意精度的整数,不受 JavaScript 中 Number 类型的 53 位精度限制。
以下是 BigInt 的一些示例:
const bigInt = BigInt(9007199254740991); // 创建一个大整数
console.log(bigInt); // 9007199254740991n
const bigInt1 = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(2); // 直接通过 Number.MAX_SAFE_INTEGER 创建一个大整数
console.log(bigInt1); // 9007199254740993n
const bigInt2 = 1234567890123456789012345678901234567890n; // 直接写入一个大整数
console.log(bigInt2); // 1234567890123456789012345678901234567890n
大整数类型可以表示比 JavaScript 中普通整数更大范围的整数,但要注意 BigInt 类型和普通 Number 类型之间的差异,例如不能将 BigInt 和 Number 直接相加,需要显式地将 BigInt 转换为 Number 类型后再进行运算。
除了 BigInt 外,ES6 还引入了 Symbol 数据类型,但 Symbol 不是基本数据类型,而是一种特殊的原始数据类型,用于表示独一无二的值,通常用作对象属性名的键。
call、apply、bind的区别
call
、apply
和 bind
都是 JavaScript 中用于改变函数执行上下文(即 this
指向)的方法,它们在使用方式和效果上有一些区别。
1. call()
-
作用: 调用一个函数,并指定函数体内
this
对象的值,以及传递一个或多个参数。 -
语法:
function.call(thisArg, arg1, arg2, ...)
-
特点
:
- 第一个参数指定了函数体内
this
对象的值,后续参数是函数调用时的参数列表。 - 参数数量可变,可以接受任意数量的参数。
- 第一个参数指定了函数体内
-
示例
:
const person = { fullName: function() { return this.firstName + ' ' + this.lastName; } }; const person1 = { firstName: 'John', lastName: 'Doe' }; const fullName = person.fullName.call(person1); // "John Doe"
2. apply()
-
作用: 调用一个函数,并指定函数体内
this
对象的值,以及传递一个参数数组。 -
语法:
function.apply(thisArg, [argsArray])
-
特点
:
- 第一个参数指定了函数体内
this
对象的值,第二个参数是一个数组或类数组对象,表示函数调用时的参数列表。 - 参数数量不固定,传入的参数为一个数组。
- 第一个参数指定了函数体内
-
示例
:
const person = { fullName: function() { return this.firstName + ' ' + this.lastName; } }; const person1 = { firstName: 'John', lastName: 'Doe' }; const fullName = person.fullName.apply(person1); // "John Doe"
3. bind()
-
作用: 创建一个新的函数,指定函数体内
this
对象的值,并部分应用函数参数。 -
语法:
function.bind(thisArg[, arg1[, arg2[, ...]]])
-
特点
:
- 返回一个新函数,不会立即调用原函数,而是将函数体内的
this
对象和一部分参数绑定到新函数上。 - 可以通过传递参数来部分应用原函数的参数,生成一个拥有预设参数的新函数。
- 返回一个新函数,不会立即调用原函数,而是将函数体内的
-
示例
:
const person = { fullName: function(greeting) { return greeting + ' ' + this.firstName + ' ' + this.lastName; } }; const person1 = { firstName: 'John', lastName: 'Doe' }; const fullName = person.fullName.bind(person1, 'Hello'); // 返回一个新函数 const message = fullName(); // "Hello John Doe"
区别总结
call()
和apply()
都是立即调用函数,并改变函数体内this
对象的值,但它们的参数传递方式不同,call()
是逐个传入参数,apply()
是通过数组传入参数。bind()
返回一个新函数,并绑定了函数体内的this
对象和一部分参数,但并不立即调用原函数,而是返回一个新函数,需要在适当的时候调用。
js实现继承有哪些方法
在 JavaScript 中实现继承有几种常见的方法,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承等。下面是每种方法的简要介绍:
1. 原型链继承
通过将子类的原型设置为父类的实例来实现继承。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
function Child() {}
Child.prototype = new Parent();
const child = new Child();
2. 构造函数继承(借用构造函数)
通过在子类构造函数中调用父类构造函数来实现继承。
function Parent() {
this.name = 'Parent';
}
function Child() {
Parent.call(this);
}
const child = new Child();
3. 组合继承
结合原型链继承和构造函数继承的方式,实现父类原型方法的共享和实例属性的继承。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
4. 原型式继承
通过复制一个对象的副本来创建一个新对象,然后对新对象进行修改来实现继承。
const parent = {
name: 'Parent',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
const child = Object.create(parent);
child.name = 'Child';
5. 寄生式继承
在原型式继承的基础上,通过在新对象上添加方法或属性来增强新对象。
const parent = {
name: 'Parent',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
function createChild() {
const child = Object.create(parent);
child.name = 'Child';
return child;
}
const child = createChild();
6. 寄生组合式继承
结合组合继承和寄生式继承的优点,解决了组合继承中两次调用父类构造函数导致的属性重复创建和方法覆盖的问题。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
function Child() {
Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
img 的 srcset 属性的作用
srcset
属性用于为图像元素 <img>
提供不同分辨率或尺寸的图片,以便浏览器根据设备的屏幕尺寸、分辨率或其他条件选择最合适的图片进行显示。这样可以优化图像加载,提升用户体验,特别是在响应式设计中。
srcset
属性的常见用法有两种:
-
提供不同分辨率的图像:
<img src="example-1x.jpg" srcset="example-1x.jpg 1x, example-2x.jpg 2x, example-3x.jpg 3x" alt="Example image">
在这个例子中,浏览器会根据设备的像素密度选择合适的图像。例如,在高像素密度的设备上(如 Retina 屏幕),浏览器可能会选择
example-2x.jpg
或example-3x.jpg
。 -
提供不同尺寸的图像:
<img src="small.jpg" srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 1800w" sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw" alt="Example image">
在这个例子中,
srcset
中的每个图像都指定了一个宽度(以像素为单位),而sizes
属性指定了图像应该占据的视口宽度的百分比或像素值。浏览器会根据视口的大小和sizes
属性的值,选择最合适的图像进行加载。
函数的作用域在定义时确定,而不是在调用时确定。
var value = 1;
function foo() {
console.log(value);
}
function f() {
var value = 2;
foo();
}
f();
- 在全局作用域中,定义了一个全局变量
value
,其值为1
。 - 定义了一个函数
foo
,该函数在调用时会输出变量value
的值。 - 定义了一个函数
f
,该函数内部有一个局部变量value
,其值为2
,并调用foo
函数。 - 当调用
f()
函数时,执行了foo()
函数。
关键点在于:函数的作用域在定义时确定,而不是在调用时确定。也就是说,foo
函数在定义时,其作用域链已经决定,它会引用定义时所在作用域中的变量 value
,而不是调用时所在作用域中的变量 value
。
因此,当 foo
函数调用 console.log(value)
时,它会查找全局作用域中的 value
,而不是函数 f
内部的局部变量 value
。因为全局作用域中的 value
是 1
,所以输出 1
。
css浮动
float:left|none|right|inline-start|inline-end|
清除浮动
//1.直接清除
clear:both;
//父元素
overflow:hidden
//标签法 在兄弟标签上加上 clear:both
//伪元素法
:after 后面 clear:both
js数据类型
js的基本数据类型有6种
引用数据类型
null和undefined的区别?(基本的大家都知道,但怎么回答的让面试官耳目一新呢?)
(1)设计历史:
之前看过相关文献,作者在设计js的时候,先有null后有undefined,因为借鉴了java的语言,null的数据类型时object,会被隐式转换成0,不容易发现错误;所以就设计了undefined,它是一个基本数据类型,转成数值为NaN;
(2)数据:
null已经声明赋值为空;引用数据类型;数值转换为0;
undefined已经声明未赋值;基本数据类型;数值转换为NaN(数值类型,不表示数字);
symbol数据类型的特点?
symbol 是ES6引入了一种新的基本数据类型(原始数据类型),表示独一无二的值。
(1)Symbol的值是唯一的,用来解决命名冲突的问题;
(2)Symbol值不能与其他数据类型进行运算;
(3)Symbol定义得的对象的属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名;
两种数据类型的存放机制?
基本数据类型体积小,存放在栈里面;
引用数据类型体积大,存放在堆里面;
引用数据类型会有个指针放在栈里面,定位堆里面存放的引用数据;
如何判断这两种数据类型?
(1)typeof:查找基本数据类型,引用数据类型除了function,其他都为object;
(2)instanceof:查找引用数据类型;
原理:instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype, 返回true,不是返回false;
(3)Object.prototype.toString().call():所有数据类型;
原理:原型链和原型有关,首先toString()这个方法本来应该返回string的字符串形式,但是大多数情况会返回[object,xxxx]形式;因为js重写了某些数据类型的toString()方法;所以这时候我们找到原型上最原始的toSting()方法;call的作用就是转变this的指向;
数据类型如何转换?
(1)转为字符串:toString() / String() 不能用于null和undefined
(2)转为数值:Number() / parseInt() 转为整数,parseFloat 转为浮点数
(3)隐式转换:js是一门弱语言,运行期间,变量会根据运行环境自动类型转换;
举例:字符串数据+0/*1可以转换成数值;字符串+数值=字符串
(4)转为布尔值:false(0,null,undefined,‘’,NaN)
2,== 与 === 的区别?
==比较值
string == number || boolean || number都会隐式转换,通过valueOf()方法通常由js在后台自动转换
===比较值和数据类型,除了比较值,还比较类型
3,JS的宏任务和微任务?
(1)首先得解释一下JS的单线程:
js是单线程的语言,用途(浏览器的脚本语言,主要负责交互,动画,效果)决定了它必须是单线程的语言。单线程是指同一时间只能做一件事。如果能做多件事情的话,假如用户同时有两个操作,一个是删除节点,一个是新增节点,那到底应该选择是新增还是删除呢?所以js在设计的时候就必须是单线程的。
(2)其次解释一下JS代码的执行流程:
同步执行完==》事件循环;同步的任务都执行完了,才会执行事件循环的内容,进入事件循环有哪些:请求、定时器、事件。
(3)然后解释一下事件循环:
进入事件循环也得分哪个先哪个后呀?所以事件循环里面又分微任务和宏任务。
微任务:promise.then();
宏任务:setTimeOut;
执行顺序为:先执行微任务,在执行宏任务,宏任务里面还有微任务,先执行微任务,以此循环下去;记住关键,要执行宏任务的前提是清空了所有的微任务。
(4)最后总结一下代码的执行流程:
同步=》事件循环【微任务,宏任务】=》微任务=》宏任务=》微任务…;
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.youkuaiyun.com/outsider76557/article/details/126985213
闭包
function test (){
let num=0;
return function(){
num+=1
}
}
const a = test()
a()
闭包的原理是创建函数会有一个作用域 执行函数也会有一个作用域,当执行函数的作用域和创建函数的作用域重合时作用域链就不会断,以至于还可以通过函数访问到这个变量。
test 函数执行时候的作用域
test 执行完毕
优点:实现了变量私有化
缺点:容易导致内存泄露
应用
setTimeout传参
function test(a){
return function (){
console.log(a)
}
}
setTimeout(test(1),500)
//1
diff算法
箭头函数的特点
-
箭头函数不能用作构造函数。使用
new
调用它们会引发TypeError
。它们也无法访问new.target
关键字。 -
箭头函数不能在其主体中使用
yield
,也不能作为生成器函数创建。 -
箭头函数的参数和箭头之间不能换行。
由于
=>
的优先级低于大多数运算符,因此需要使用括号来避免callback || ()
被解析为箭头函数的参数列表。const func = (a, b, c)=>1; // SyntaxError: Unexpected token '=>'
文档流
float浮动布局脱离文档流,不脱离文本流
absolute脱离文档流和文本流
grid布局
划分行列
横向为行、纵向为列,横纵交汇之间为单元格。
display:grid;
grid-template-columns:1fr
gap:30px
grid-template-rows:repeat(4,100px)
grid-template-columns::minmax(100px,200px)
grid-gap:行 列
grid-auto-flow:row / column //默认横向显示
place-item:align-item justify-item
justify-content
align-content
place-content
grid-column-start:2
grid-column-end:3
grid-row-start:1,
grid-row-end:3
vue2 和vue3的区别
① 在性能提升上:
Vue3 在内部实现上进行了大量的优化,使得渲速度更快,性能更好,内存占用更少。
例如对响应式的处理以及重写 diff 算法 ;
响应式系统升级:Vue3 使用了 Proxy 替代 Object.defineProperty 实现响应式,并且使用了静态提升技术来提高渲染性能。
Object.defineProperty 只能监听某个对象的某个属性,新加入属性需要手动添加
Diff 算法重写:新增了最长递归子序列的算法,来计算出最小的修改偏移量。还增加了静态标记
② 在编码方式上的区别:
Vue3 引入了 Composition 组合式APl,允许开发者更好地组织和复用逻辑代码,提高代码的可维护性。
③ 对 TypeScript 的支持更加友好
提供了完整的类型定义
④ 在设计理念的区别
Vue3 更注重模块上的拆分,还对所有的 API 进行重写。采用函数式编程,使得 Vue3 支持更好的 Tree Shaking,精确地实现了按需引入,减少了打包体积。
⑤ 支持更多的特性
如片段、Teleport 、suspence等
总的来说,Vue3 在性能、可维护性和特性上都有所提升。但是需要注意的是,由于 API 发生了较大的变化,因此 Vue3 与 Vue2 之间并不完全兼容,需要进行相应的迁移工作。
vue3生命周期
vue2
创建前 deforeCreate
创建后 Created
挂载前 beforeMount
挂载后 Mounted
更新前 beforeUpdate
更新后 Updated
销毁前 beforeUnmount
销毁后 unmounted
vue3
setup 创建
onBeforeMount onMounted 挂载
onBeforeUpdate onUpdated 更新
onBeforeUnmount onUnmounted 销毁
v-if 和 v-for优先级
当它们同时存在于一个节点上时,v-if
比 v-for
的优先级更高。这意味着 v-if
的条件将无法访问到 v-for
作用域内定义的变量别名:
<!--
这会抛出一个错误,因为属性 todo 此时
没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
uniapp 页面生命周期
页面初始化 onlnit
页面加载 onLoad
页面显示 onShow
页面初次加载完 onReady
页面隐藏 onHide
页面关闭 onUnload
Webstore
-
localtionstore
永久有效,需要手动清除 5M
-
sessionstore
仅在当前会话下有效,关闭窗口失效 5M
-
cookie
到期时间失效 4kb
get与post的区别
-
GET在浏览器回退时是无害的,而POST会再次提交请求。
-
GET产生的URL地址可以被Bookmark,而POST不可以。
-
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
-
GET请求只能进行url编码,而POST支持多种编码方式。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的,而POST没有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
GET参数通过URL传递,POST放在Request body中。
事件传播的三个阶段
一、事件传播
1、概述
(1)当事件发生在DOM元素上时,该事件并不完全发生在那个元素
(2)在冒泡阶段中,事件冒泡或向上传播至父级、祖父级、祖父的父级,直到 window 为止
(3)在捕获阶段中,事件从 window 开始,向下触发元素、事件或 event.target
2、事件传播的三个阶段
(1)捕获阶段——事件从 window 开始,然后向下到每个元素,直到到达目标元素
(2)目标阶段——事件已达到目标元素
(3)冒泡阶段——事件从目标元素冒泡,然后上升到每个元素,直到到达 window
event.stopPropagetion()
event.preventDefault()
Array
const arr = new Array(5) //创建5个空间的数组
arr.fill(5) //在这个数组中填充5
const arr = new Array(1,2,3) //创建一个[1,2,3]数组
const arr = new Array([1,2,3]) //创建一个[[1,2,3]]数组
数组方法
-
Array.splice //改变原数组 返回的是[2,3]里面包含被移除的值 当第一参数超过范围返回一个空数组,第二参数超出范围时,返回最大范围内的[2,3]
-
Array.slice //返回提取的数组
-
Array.concat //不改变原数组,返回连接后的数组
-
Array.push //改变原数组 返回新的数组长度
-
Array.shift //改变原数组 返回弹出的值
-
Arr.ay.unshift() //改变原数组 返回新数组的长度
-
Array.pop //改变原数组 返回弹出的数值,如果为空数值返回 undefind
-
Array.reverse //改变原数组 返回排序后的数组
-
Array.includes //查找一个基本类型 存在返回true 否则返回false
对象类型查找是他的地址如果地址相同,返回true -
Array.indexOf //返回查找元素的第一个index 失败返回-1
-
Array.sort() //改变原数组
-
Array.join() //把数组通过传入的字符连接起来 不改变原数组
-
Array.forEach()
在里面用return是不会返还外部函数
该方法是一个异步方法
-
Array.map()
-
Array.filter()
-
Array.reduce()
-
Array.every()
-
Array.some()
-
Array.find()
const array1 = [5, 12, 8, 130, 44]; const found = array1.find((element) => element > 10); console.log(found); // Expected output: 12
-
Array.findIndex
const array1 = [5, 12, 8, 130, 44]; const isLargeNumber = (element) => element > 13; console.log(array1.findIndex(isLargeNumber)); // Expected output: 3
-
Array.findLast()
-
Array.findLastIndex()
-
Array.flat()
const arr1 = [0, 1, 2, [3, 4]]; console.log(arr1.flat()); // expected output: Array [0, 1, 2, 3, 4] const arr2 = [0, 1, [2, [3, [4, 5]]]]; console.log(arr2.flat()); // expected output: Array [0, 1, 2, Array [3, Array [4, 5]]] console.log(arr2.flat(2)); // expected output: Array [0, 1, 2, 3, Array [4, 5]] console.log(arr2.flat(Infinity)); // expected output: Array [0, 1, 2, 3, 4, 5]
-
Array.with(index,value) //创建一个新数组,返回一个创建后的新数组,不改变原数组
下表列出了会修改原始数组的方法,以及相应的非修改方法:
字符串方法
字符串中没有改变原字符串的方法
String.split()
String.toLowerCase() //转化为小写
String.toUpperCase() //转化为大写
String.slice() //提出某一段字符串
String.replace() //把原字符串中的某个串替换为传入串
Srting.repeat() //重复该字符串
String.startsWith() //字符串是否以 传入字符串开头
String.endsWith()
String.trimStart() //移除字符串开头的字符 返回一个新的字符串
String.trimEnd()
String.includes()
const sentence = 'The quick brown fox jumps over the lazy dog.';
const word = 'fox';
console.log(
`The word "${word}" ${
sentence.includes(word) ? 'is' : 'is not'
} in the sentence`,
);
// Expected output: "The word "fox" is in the sentence"
String.concat()
String.indexOf() //返回符合条件的第一个下标
hello world hello'.indexOf('o', -5)
返回4
——因为它使该方法的行为类似于第二个参数为0
,并且o
在大于或等于0
位置的第一次出现是在4
位置。
String.lastindexOf()
Match常用方法
Math.abs()
Math.random()
Math.ceil() //向上取整
Math.round() //四舍五入
Math.floor() //向下取整
Math.max() //输入参数最大值
Math.min() //输入参数最小值
Math.pow() //指数函数
input相关方法
-
onfocus事件
当文本框聚焦时触发
-
onblur事件
当文本框失去焦点时触发
-
onchange事件
onchange事件是当内容发生变化并且元素失去焦点时触发。
-
oninput事件
当输入框的值发生变化时,触发该事件。
-
input.focus()聚焦 input.select() 选择功能
ES6新特性
-
箭头函数
-
let 和const
const 不能被更改 必须赋值不能使用null占位
-
symbol
-
对象数组解构
-
Class类
-
模块化
-
proxy和promise
-
Map、Set
-
对象扩展符
-
数组新方法 from includes map filter some every
CSS3新特性
-
边框
border-radius、box-shadow、border-image
-
颜色
RGBA、liner-gradient 、radial-gradient、conic-gradient
-
文字
text-overflow、word-warp、text-shadow
-
背景
background-origin、background-clip、background-size
-
变形
rotate、scale
-
多列布局
column-width、column-count、columns、column-gap、column-rule
-
盒子模型
box-sizing
-
布局
flexbox
-
动画
animation、transition、transform
-
单位
rem、vw、vh
-
媒体查询 media
-
透明度 opacity
BFC块级格式化上下文
BFC是一块对立的渲染区域,内部元素不会干扰外部元素的渲染
解决问题:
- margin重叠 、塌陷问题 //border padding 解决
- float父级元素塌陷问题
- 避开其他元素的浮动覆盖
开启BFC的6种方式
- overflow:hidden
- display:flex
- display:inline-flex
- display:inline-block
- position:absolute
- position:fixed
当行内元素,正常情况下无法设置宽高,但是加上flex就可以设置了。
固定布局(fixed layout)
固定布局,例如豆瓣网,无论如何change浏览器的宽度,页面主体区域的宽度是固定不变了,里面的内容布局也是打死不动的;
利
任意浏览器下的显示宽度都一样,对于图像、表单、视频等宽度固定的内容,潜在的冲突更少(设计师看到什么,用户看到的就是什么)。
不用设定min-width(最小宽度)和max-width(最大宽度),这两个属性并不被所有浏览器支持。
即使网页被设计成兼容最小屏幕分辨率800×600的大小,在大分辨率显示器下,内容仍然足够易读。
弊
固定宽度的布局可能会给高分辨率屏幕用户带来巨大的页面空白,从而破坏“神圣比例”、“三分割法则”、整体平衡,乃至其他设计原则。
小屏幕上可能会出现水平滚动条,影响用户体验。
无缝材质拼图,纹样和其他连续图像需要针对大分辨率做出优化。
总体来说,固定宽度的可用性更低。
使用技巧:
1024×768以及更高分辨率的屏幕选择960px作为网页内容主容器的像素宽度,还能有一点留白。而800×600分辨率下的主容器最佳显示宽度是760px,设置成这一宽度能够照顾到那10%,而较大屏幕上看起来也还不赖。
固定宽度设计,务必保证主容器居中对齐,以保持平衡(一般利用margin: 0 auto;就可以了)。
流体布局(fluid layout)
流体布局,也称为自适应布局布局,宽度使用了百分比宽度来适应不同屏幕分辨率,高度大都是用px来固定住。例如google analytics,当你change浏览器的宽度,显示区域也会如同水流般自适应于显示器的宽度显示(浏览器宽度变一点,web页面宽度也跟着变一点),主体结构布局不变。开发中一般通过调整内容区块的宽度来适应,或调整空白区域大小来适应不同分辨率。
利
流动网页布局拥有更强的亲和力,因为它能根据客户端的情况自适应。
在不同浏览器和屏幕分辨率下的额外空白量都差不多,更符合视觉吸引力要求。
如果设计得当,流动布局能避免在小屏幕上的水平滚动条。
弊
设计师对客户端的显示效果更难以控制,由于他们使用特定大小的屏幕调试,也更不容易发现潜在问题。
图片、视频以及其他拥有固定宽度的内容不得不被设置上不同宽度,以配合不同的屏幕分辨率。
对于特别大的显示屏,内容不够多的话就会造成过量空白,破坏美感。
因为宽度使用%百分比定义,但是高度和文字大小等大都是用px来固定,所以在大屏幕的手机下显示效果会变成有些页面元素宽度被拉的很长,但是高度、文字大小还是和原来一样(即,这些东西无法变得“流式”),显示非常不协调。
使用技巧:
采用简单设计:应用到流动网页布局的图案和复杂技巧越少,其建立和维护也就越容易。同时也能更方便地适应不同屏幕分辨率。
min-width 和 max-width,可以用来为过大或过小屏幕的用户指定一个固定宽度。屏幕过小的时候,内容区块固定成指定宽度,屏幕下方也出现一个水平滚动条;屏幕过大的时候,内容也固定到最大宽度,以免延展得太开,影响文字的可读性。
弹性布局(elastic layout)
其要点就在于使用单位em或rem来定义元素宽度。em/rem则是相对于字体大小的单位宽度。它随着字体大小的变化而变化,反应用户对字体大小的设定。
利
如果合理运用,这种布局设计能带来非常友好的用户界面。目标效果是所有东西都能根据用户的偏好增大或缩小尺寸。
可以用于字体缩进text-indent;使用rem单位的弹性布局在移动端也很受欢迎;
弊
尽管第一条“利”说得没错,弹性布局还是为可用性埋下了很多地雷。得需要十分的聪明才智和不断测试才能让布局适合所有用户。
这种布局比前述两种要难实现得多,可能那一小点的可用性并不值得你花这么多功夫。
由于这种布局的特殊性,有些弹性布局设计可能需要额外的样式表,并针对IE6做些特别的调整 ;
REM支持的浏览器:Mozilla Firefox 3.6+、Apple Safari 5+、Google Chrome、IE9+和Opera11+。IE6-8无法支持。
使用技巧:
包裹文字的各元素的尺寸采用em/rem做单位,而页面的主要划分区域的尺寸仍使用百分数或px做单位(同「流式布局」或「静态/固定布局」)。
浏览器的默认字体高度一般为16px,即1em=1rem=16px,但是 1:16 的比例不方便计算,为了使单位em/rem更直观,CSS编写者常常将页面根节点字体设为62.5%,比如选择用rem控制字体时,先需要设置根节点html的字体大小,因为浏览器默认字体大小16px*62.5%=10px。这样1rem便是10px,方便了计算。。
注意:实际项目设置成 font-size: 62.5%可能会出现问题,如下
html {font-size: 62.5%;/10 ÷ 16 × 100% = 62.5%/}
body {font-size: 1.4rem;/*1.4 × 10px = 14px */}
h1 { font-size: 2.4rem;/2.4 × 10px = 24px/}
因为有些浏览器默认的不是16px,或者用户修改了浏览器默认的字体大小(因浏览器分辨率大小,视力,习惯等因素)。如果我们将其设置为10px,一定会影响在这些浏览器上的效果,所以最好用绝大多数用户默认的16作为基数 * 62.5% 得到我们需要的10px。
因为chrome不支持小于12px的字体,计算小于12px的时候,会默认取12px去计算,导致chrome的em/rem计算不准确。针对这个现象,可以尝试设置html字体为100px,body 修正为16px,这样 0.1rem 就是 10px,而body的字体仍然是默认大小,不影响未设置大小的元素的默认字体的大小。
html {font-size: 625%;/*字体换算为1rem=100px*/}
body {font-size:0.1rem;/*全文基础字体0.1rem = 10px */}
h2 { font-size: 0.24rem;/*0.24 × 100px = 24px*/}
响应布局(responsive layout)
随着宽屏的不断普及,CSS3出现了@media媒体查询技术,又出现了响应式设计的概念。响应式设计的目标是确保一个页面在所有终端上(各种尺寸的PC、手机、手表、冰箱的Web浏览器等等)都能显示出令人满意的效果,对CSS编写者而言,在实现上不拘泥于具体手法,但通常是糅合了百分比流式布局+rem弹性布局,再搭配媒体查询技术使用。响应式几乎已经成为优秀页面布局的标准。
响应布局是浏览器的宽度满足某一个条件的时候,web页面的布局发生变化。这种变化往往不仅仅是web页面宽度的变化,还有主体结构布局的改变。例如导航栏在大屏幕下是横排,在小屏幕下是竖排,在超小屏幕下隐藏为菜单,也就是说如果有足够的耐心,在每一种屏幕下都应该有合理的布局,完美的效果。如京东商城的首页。
@media (min-width: 768px){ //>=768的设备 }
@media (min-width: 992px){ //>=992的设备 }
利
适应pc和移动端,如果足够耐心,效果完美
弊
媒体查询是有限的,也就是可以枚举出来的,只能适应主流的宽高。
要匹配足够多的屏幕大小,工作量不小,设计也需要多个版本。
布局特点:屏幕分辨率变化时,页面里面元素的位置会变化而大小不会变化。流式布局是用于解决类似的设备不同分辨率之间的兼容(一般分辨率差异较少);响应式是用于解决不同设备之间不同分辨率之间的兼容问题(一般是指PC,平板,手机等设备之间较大的分辨率差异)。
使用技巧:通常是糅合了百分比流式布局+rem弹性布局,再搭配媒体查询技术使用。——使用 @media 媒体查询给不同尺寸和介质的设备切换不同的样式,分别为不同的屏幕分辨率定义布局。同时,在每个布局中,主区域划分应用流式布局的理念,即页面元素宽度随着窗口调整而自动适配。
响应式布局具体实践看上一篇文章:https://blog.youkuaiyun.com/zhouzuoluo/article/details/95756442
选择何种布局方式
选择何种布局应该由网站的性质决定。权衡上述利弊,根据你的网站需求找到合适的解决方案,如以下选择:
1、如果只实现pc端简单设计,那么静态固定布局(定宽度)是最好的选择。
2、如果做移动端,且设计对高度和元素间距要求不高,那么弹性布局(rem+js)是最好的选择,一份css+一份js调节font-size搞定;
3、一些特定的界面可以使用主区域流动+侧边栏固定+一些弹性应用 来很好的实现。
4、如果pc,移动要兼容,而且要求很高那么响应式布局还是最好的选择,前提是设计根据不同的高宽做不同的设计,响应式根据媒体查询做不同的布局。
watch 和computed
-
watch
watch主要是监听数据的变量,当数据到达某些变化时候,执行某些结果
监视四种数据:
ref 、reactive、函数返回的一个值,包含以上内容的数组import {watch} from 'vue' watch(sum,(new,old)=>{ })
监听ref对象类型
let person = ref({ name:'张三', age:1 }) watch(person,(new.lod)=>{ //只关注整个对象的变化 //只关注地址是否变化 }) watch(person,(new.lod)=>{ //只关注整个对象的变化 //只关注地址是否变化 },{deep:true}) //new和old一样值
reactive定义的数据默认开启深度检测,关不掉
监视对象中的某个属性 getter函数
let person = ref({ name:'张三', age:1 }) watch(person,(new.lod)=>{ //只关注整个对象的变化 //只关注地址是否变化 }) watch(()=>person.name,(new,lod)=>{ //只关注整个对象的变化 //只关注地址是否变化 },{deep:true}) //new和old一样值
-
watchEffect
watchEffect{()=>{ if(tem>0); if(b > 10); }} //开始默认执行一次
-
computed
let full = computed(()=>{return })
1.依赖的值变化了,只计算一次,适合某几个对象综合起来计算出某一个值
css实现垂直居中
-
absolute + transfrom
-
flex+align-item
-
margin
auto 只能实现水平方向的居中,不能实现垂直方向的居中。除非父元素为弹性布局,也有高度,也能实现自动居中,增加的位置是margin内边距
事件循环机制
主线程
延迟队列
交互队列
微队列
线程和进程
进程和线程是操作系统中的两个基本概念。
进程是程序执行的基本单位,每个进程都有自己独立的内存空间和系统资源,它拥有自己的虚拟地址空间、代码段、数据段、堆栈段等。一个程序可以对应多个进程,每个进程之间是独立运行的,互相之间不会影响。
线程是在进程内部执行的一个单一顺序流,它是比进程更小的执行单位。一个进程可以创建多个线程,在同一时间内运行多个线程可以提高执行效率。线程共享所属进程的代码段、数据段和系统资源,但每个线程却有自己独立的运行栈和栈指针寄存器。
所以说,线程必须依赖于进程而存在,并且不能脱离进程而独立存在。在这种关系下,一个进程可以包含多个线程,这些线程共享同一份内存资源,并协同完成任务。同时多个线程之间也需要进行协作与同步,以避免不必要的竞争和冲突。
总而言之,一个进程可以包含多个共享同一资源的并发执行路径即线程。通过合理地利用多线技术可以有效地提高程序性能和响应速度。
setTimeout和线程之间的关系
loader和plugin的区别
Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果
HTTP常见的状态码
keep-alive原理
保活组件 不销毁
手写bind
手写Promise.all
http缓存
前言
HTTP缓存机制是优化web性能的重要手段,也是优化用户体验的重要一环。了解和熟悉HTTP缓存机制也成为了前端工作者必不可少的技能。
HTTP缓存是用于临时存储网页资源(如HTML页面、图像等),以减少服务器延迟的一种技术。HTTP缓存系统会保存下通过这套系统的文档的副本;如果满足某些条件,则可以由缓存满足后续请求。HTTP缓存系统既可以指设备,也可以指计算机程序。
一、HTTP缓存的类别
HTTP缓存可分为强制缓存和协商缓存。
**强制缓存:**直接使用客户端缓存,不从服务器拉取新资源,也不验证缓存资源是否过期。返回的状态码为200(OK)。
**协商缓存:**通过服务器验证资源有效性,资源有效则返回304(Not Modified),资源失效则返回最新的资源文件。
HTTP主流的有三个版本:HTTP/1.0、HTTP/1.1、HTTP/2.0。其中HTTP/1.0和HTTP/1.1的应用最为广泛。HTTP/2.0因对缓存机制的改动有别于HTTP/1.0和HTTP/1.1,因此HTTP/2.0相关内容会在文末总结部分进行介绍。HTTP/1.0与HTTP/1.1可根据缓存类别区分如下:
HTTP版本 强制缓存 协商缓存
HTTP/1.0 Expires Last-Modified
HTTP/1.1 Cache-Control ETag
二、主流的HTTP缓存参数
2.1 强制缓存
2.1.1 HTTP/1.0 - Expires
Expires的值为服务端返回的到期时间,是一个GMT(格林尼治标准时间)绝对时间,如:Tue, 17 Jan 2023 03:48:45 GMT。下一次请求时,客户端判断当前系统GMT时间是否小于缓存携带的GMT时间。若小于,直接使用缓存数据,否则从服务器请求新的文件。
不过Expires存在的问题也显而易见。
首先,使用客户端获取的GMT时间与服务器GMT时间作比较,如果客户端主动修改了系统时间,就会出现缓存命中的误差。
其次,GMT时间是基于格林尼治天文台测算时间后,每隔一小时想全世界发放调时信息。观测本身存在的误差以及非实时的同步机制,都可能会导致出现缓存命中的误差。
所以在HTTP/1.1版本中,使用Cache-Control中的max-age替代。
2.1.2 HTTP/1.1 - Cache-Control
Cache-Control 是HTTP/1.1中重要的缓存规则。它可以在HTTP请求头和响应头中使用,提供了多样化的配置参数。同时也可以适用于更广泛的复杂场景。
指令格式具有以下有效规则:
不区分大小写,但建议使用小写。
多个指令以逗号分隔。
具有可选参数,可以用令牌或者带引号的字符串语法。
常用的指令如下:
no-store:不使用任何形式的缓存。具有HTTP缓存的最高优先级。
no-cache:不使用强制缓存。每次进行响应前都向服务器进行缓存有效性验证。
public:公共缓存。任何从源服务器到客户端中的每个节点都可以对资源进行缓存。
private:私有缓存。仅客户端可以对资源进行缓存。
max-age:客户端缓存存储的最长时间,单位秒。判断的优先级高于Expires,客户端会判断资源已缓存的时长是否小于设置的max-age时长。是则直接使用缓存数据,否则会进行Expires的判断流程。
s-maxage:代理缓存服务器最长的缓存时间,单位秒。优先级高于max-age和Expires,仅适用于缓存服务器。
2.2 协商缓存
客户端缓存失效后会向服务器进行进行缓存有效性验证,这个缓存有效性验证的过程就是协商缓存。若资源有效,则返回304(Not Modified)。客户端拿到304状态码后会再从本地缓存中获取资源。整个请求响应过程是与无缓存流程一样的。相对于无缓存流程的优势在于仅响应状态码后,客户端直接从本地缓存获取文件,而无需进行文件下载。减少了网络响应的文件大小,进而加快了网络响应速度。
协商缓存的请求和响应是需要相互配合的,可组合使用。如下表:
版本\阶段 请求 响应
HTTP/1.0 If-Modified-Since/If-Unmodified-Since Last-Modified
HTTP/1.1 If-None-match/If-Match E/Tag
协商缓存会先判断请求头中是否携带no-store。如果携带,则直接返回最新的服务器文件。
2.2.1 HTTP/1.0 - Last-Modified
客户端第一次向服务器请求资源时,服务器会返回资源。同时会在响应头中添加Last-Modified字段来表明资源的最后修改时间。当客户端强制缓存失效后,会重新向服务器进行缓存有效性验证。在验证的请求头中,会添加If-Modified-Since字段。服务器会对请求头中的If-Modified-Since和其存储的资源Last-Modified进行比较。若If-Modified-Since的时间不小于Last-Modified,则资源有效,返回304(Not Modified)。否则返回资源本身,并且重新记录文件的Last-Modified。
Last-Modified:响应头携带的资源最后修改时间。格式为last-modified:GMT。
如:last-modified: Sat, 14 Jan 2023 08:40:00 GMT
1
If-Modified-Since:请求头携带的资源是否在某个时间后有修改。服务器会使用此值和其本身存储的时间进行比较。格式为:If-Modified-Since:GMT。只可以用在 GET 或 HEAD请求中。
If-Unmodified-Since:请求头携带的资源是否在某个时间后没有修改。格式为:if-unmodified-since:GMT 。有别于If-Modified-Since,If-Unmodified-Since被用于POST或其他非简单请求。如果在If-Unmodified-Since指定的时间内有过修改,则返回412(Precondition Failed)。
Last-Modified也是存在严重问题的。
首先,Last-Modified只关注文件的最后修改时间,和文件内容无关。所以文件内容在修改后又重新恢复,也会导致文件的最后修改时间改变。此时客户端的请求则无法使用缓存。
其次,Last-Modified只能监听到秒级别的文件修改,如果文件在1秒内进行了多次修改,那么响应头返回的Last-Modified的时间是不变的。 此时客户端因接收到响应304,会导致资源无法及时更新,使用缓存的资源文件。
因此HTTP/1.1使用了ETag来进行缓存协商。
2.2.1 HTTP/1.1 - ETag
为了解决上述Last-Modified可能存在的不准确的问题,HTTP/1.1推出了新的响应字段ETag来进行协商缓存。ETag的优先级比Last-Modified高。
服务器接收到浏览器请求后,会先进行If-None-Match与ETag值的比较。若相等,则资源有效,返回304(Not Modified)。否则返回资源本身,并且重新记录文件的ETag。
ETag:响应头携带的资源标识符。格式为ETag:ETag-value可由服务器自行设置算法生成,通常是使用内容的散列或简单的使用版本号。
如:etag: “I82YRPyDtSi45r0Ps/eo8GbnDfg=”
1
If-None-Match:请求头携带的是否无匹配文件字段。优先级高于Last-Modified。当服务器没有任何资源的ETag与请求头携带的ETag值完全一样时,返回最新的资源,否则服务器会返回304。
如: if-none-match:“I82YRPyDtSi45r0Ps/eo8GbnDfg=”
1
If-Match:请求头携带的是否存在匹配文件字段。对于简单请求需要搭配 Range首部使用。对于非简单请求,如PUT,可用于上传ETag。
如: if-match:“I82YRPyDtSi45r0Ps/eo8GbnDfg=”
1
三、总结
通过前文,我们了解到 HTTP 缓存主要分:强制缓存、协商缓存。
强制缓存由Exipres(HTTP/1.0)、 Cache-Control(HTTP/1.1)控制。客户端直接读本地缓存,不会再跟服务器端交互,状态码 200。
协商缓存由 Last-Modified / If-Modified-Since(HTTP/1.0), Etag /If-None-Match(HTTP/1.1)进行有效性验证,每次请求需要让服务器判断一下资源是否更新过,从而决定客户端是否使用缓存,如果是,则返回 304,否则返回最新文件。
HTTP/2.0中设计了新的缓存方式,服务器推送(Push Server)。有别于强制缓存和协商缓存,属于推送缓存。这种新的缓存方式主要是为了解决客户端缓存时效性的问题,即还没有收到客户端的请求,服务器就把各种资源推送给客户端。比如,客户端只请求了a.html,但是服务器把a.html、a.css、a.png全部发送给客户端。这样的话,只需要一次请求,客户端就更新了所有文件的缓存,提高了缓存的时效性。
判断一下一个对象是否为空
使用Object.keys()
经典面试题,研发时也经常遇见的一个问题:如何判断一个是空对象
方法一:将对象转换成字符串,再判断是否等于"{}"
let obj = {};
console.log(JSON.stringify(obj) === “{}”);//返回true
方法二:for in 循环
let result = function (obj) {
for (let key in obj) {
return false;//如果不为空,可遍历,返回 false
}
return true;
}
console.log(result(obj));//返回true
方法三:Object.keys()方法,返回对象的属性名组成的一个数组,若长度为0,则返回空对象(ES6写法)
console.log(Object.keys(obj).length == 0);//返回true
方法四:Object.getOwnPropertyNames()方法获取对象的属性名,存到数组中,如果长度为0,则为空对象
console.log(Object.getOwnPropertyNames(obj).length == 0);//返回true
Object常见的方法
Object.getOwnPropertyNames()方法获取对象的属性名,存到数组中
const object1 = {
a: 1,
b: 2,
c: 3,
};
console.log(Object.getOwnPropertyNames(object1));
// Expected output: Array ["a", "b", "c"]
Object.assign()
Object.assign() 静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// Expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target);
// Expected output: true
Object.assign()
方法只会拷贝源对象可枚举的的自有属性到目标对象。该方法在源对象上使用 [[Get]]
,在目标对象上使用 [[Set]]
,因此它会调用 getter 和 setter。故它对属性进行赋值,而不仅仅是复制或定义新的属性。如果合并源对象包含 getter 的新属性到原型中,则可能不适合使用此方法。
如果要将属性定义(包括它们的可枚举性)复制到原型中,则应改用 Object.getOwnPropertyDescriptor()
和 Object.defineProperty()
方法。
针对深拷贝,需要使用其他办法,因为 Object.assign()
只复制属性值。
假如源对象是一个对象的引用,它仅仅会复制其引用值。
jsCopy to Clipboard
const obj1 = { a: 0, b: { c: 0 } };
const obj2 = Object.assign({}, obj1);
console.log(obj2); // { a: 0, b: { c: 0 } }
obj1.a = 1;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 0, b: { c: 0 } }
obj2.a = 2;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 2, b: { c: 0 } }
obj2.b.c = 3;
console.log(obj1); // { a: 1, b: { c: 3 } }
console.log(obj2); // { a: 2, b: { c: 3 } }
// 深拷贝
const obj3 = { a: 0, b: { c: 0 } };
const obj4 = JSON.parse(JSON.stringify(obj3));
obj3.a = 4;
obj3.b.c = 4;
console.log(obj4); // { a: 0, b: { c: 0 } }
Object.values()
const object1 = {
a: 'somestring',
b: 42,
c: false,
};
console.log(Object.values(object1));
// Expected output: Array ["somestring", 42, false]
Object.defineProperty()
Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false,
});
object1.property1 = 77;
// Throws an error in strict mode
console.log(object1.property1);
// Expected output: 42
Object.defineProperty()
允许精确地添加或修改对象上的属性。通过赋值添加的普通属性会在枚举属性时(例如 for...in
、Object.keys()
等)出现,它们的值可以被更改,也可以被删除。此方法允许更改这些额外细节,以使其不同于默认值。默认情况下,使用 Object.defineProperty()
添加的属性是不可写、不可枚举和不可配置的。此外,Object.defineProperty()
使用 [[DefineOwnProperty\]]
内部方法,而不是 [[Set\]]
,因此即使属性已经存在,它也不会调用 setter。
对象中存在的属性描述符有两种主要类型:数据描述符和访问器描述符。数据描述符是一个具有可写或不可写值的属性。访问器描述符是由 getter/setter 函数对描述的属性。描述符只能是这两种类型之一,不能同时为两者。
数据描述符和访问器描述符都是对象。它们共享以下可选键(请注意:在使用 Object.defineProperty()
定义属性的情况下,下述所有键都是默认值):
-
当设置为
false
时,该属性的类型不能在数据属性和访问器属性之间更改,且该属性不可被删除,且其描述符的其他属性也不能被更改(但是,如果它是一个可写的数据描述符,则value
可以被更改,writable
可以更改为false
)。默认值为 false。 -
当且仅当该属性在对应对象的属性枚举中出现时,值为
true
。默认值为 false。
数据描述符还具有以下可选键值:
-
与属性相关联的值。可以是任何有效的 JavaScript 值(数字、对象、函数等)。默认值为 undefined。
-
如果与属性相关联的值可以使用赋值运算符更改,则为
true
。默认值为 false。
访问器描述符还具有以下可选键值:
-
用作属性 getter 的函数,如果没有 getter 则为
undefined
。当访问该属性时,将不带参地调用此函数,并将this
设置为通过该属性访问的对象(因为可能存在继承关系,这可能不是定义该属性的对象)。返回值将被用作该属性的值。默认值为 undefined。 -
用作属性 setter 的函数,如果没有 setter 则为
undefined
。当该属性被赋值时,将调用此函数,并带有一个参数(要赋给该属性的值),并将this
设置为通过该属性分配的对象。默认值为 undefined。
如果描述符没有 value
、writable
、get
和 set
键中的任何一个,它将被视为数据描述符。如果描述符同时具有 [value
或 writable
] 和 [get
或 set
] 键,则会抛出异常。
这些属性不一定是描述符本身的属性。继承的属性也会被考虑在内。为了确保这些默认值得到保留,你可以预先冻结描述符对象原型链中的现有对象,明确指定所有选项,或使用 Object.create(null)
指向 null
。
const obj = {};
// 1. 使用 null 原型:没有继承的属性
const descriptor = Object.create(null);
descriptor.value = "static";
// 默认情况下,它们不可枚举、不可配置、不可写
Object.defineProperty(obj, "key", descriptor);
// 2. 使用一个包含所有属性的临时对象字面量来明确其属性
Object.defineProperty(obj, "key2", {
enumerable: false,
configurable: false,
writable: false,
value: "static",
});
// 3. 重复利用同一对象
function withValue(value) {
const d =
withValue.d ||
(withValue.d = {
enumerable: false,
writable: false,
configurable: false,
value,
});
// 避免重复赋值
if (d.value !== value) d.value = value;
return d;
}
// 然后
Object.defineProperty(obj, "key", withValue("static"));
// 如果 freeze 可用,防止添加或删除对象原型属性
// (value、get、set、enumerable、writable、configurable)
(Object.freeze || Object)(Object.prototype);
当属性已经存在时,Object.defineProperty()
会尝试根据描述符和属性的当前配置修改属性。
如果旧描述符的 configurable
特性被设置为 false
,则该属性被称为不可配置的。无法更改不可配置的访问器属性的任何特性,也不能将其在数据类型和访问器类型之间切换。对于具有 writable: true
的数据属性,可以修改其值并将 writable
特性从 true
改为 false
。当试图更改不可配置的属性(除非允许更改 value
和 writable
)时,会抛出 TypeError
,除非在数据属性上定义一个与原始值相同的值。
当当前属性是可配置的时,将特性设置为 undefined
可以有效地删除它。例如,如果 o.k
是一个访问器属性,Object.defineProperty(o, "k", { set: undefined })
将删除 setter,使 k
只有 getter 并变成只读的。如果新描述符中缺少一个特性,则会保留旧描述符该特性的值(不会被隐式重新设置为 undefined
)。通过提供不同类型的描述符,可以在数据属性和访问器属性之间切换。例如,如果新描述符是数据描述符(带有 value
或 writable
),则原始描述符的 get
和 set
属性都将被删除。
示例
创建属性
当对象中不存在指定的属性时,Object.defineProperty()
将根据描述符创建一个新的属性。描述符中的字段可以省略,省略的字段将使用默认值。
jsCopy to Clipboard
const o = {}; // 创建一个新对象
// 通过 defineProperty 使用数据描述符添加对象属性的示例
Object.defineProperty(o, "a", {
value: 37,
writable: true,
enumerable: true,
configurable: true,
});
// 'a' 属性存在于对象 o 中,其值为 37
// 通过 defineProperty 使用访问器属性描述符添加对象属性的示例
let bValue = 38;
Object.defineProperty(o, "b", {
get() {
return bValue;
},
set(newValue) {
bValue = newValue;
},
enumerable: true,
configurable: true,
});
o.b; // 38
// 'b' 属性存在于对象 o 中,其值为 38。
// o.b 的值现在始终与 bValue 相同,除非重新定义了 o.b。
// 数据描述符和访问器描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() {
return 0xdeadbeef;
},
});
// 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
修改属性
当修改已存在的属性时,操作的结果取决于当前属性的配置,可能会成功、不执行任何操作,或抛出一个 TypeError
异常。
Writable 特性
当 writable
特性设置为 false
时,该属性被称为“不可写的”。它不能被重新赋值。尝试对一个不可写的属性进行写入不会改变它,在严格模式下还会导致错误。
jsCopy to Clipboard
const o = {}; // 创建一个新对象
Object.defineProperty(o, "a", {
value: 37,
writable: false,
});
console.log(o.a); // 37
o.a = 25; // 不会抛出错误
// (在严格模式下,即使值相同,它也会抛出异常)
console.log(o.a); // 37;赋值不会成功
// 严格模式
(() => {
"use strict";
const o = {};
Object.defineProperty(o, "b", {
value: 2,
writable: false,
});
o.b = 3; // 抛出 TypeError: "b" is read-only
return o.b; // 如果没有上一行的话,会返回 2
})();
Enumerable 特性
enumerable
特性定义了属性是否可以被 Object.assign()
或展开运算符所考虑。对于非 Symbol
属性,它还定义了属性是否会在 for...in
循环和 Object.keys()
中显示。更多信息,请参见属性的枚举性和所有权。
jsCopy to Clipboard
const o = {};
Object.defineProperty(o, "a", {
value: 1,
enumerable: true,
});
Object.defineProperty(o, "b", {
value: 2,
enumerable: false,
});
Object.defineProperty(o, "c", {
value: 3,
}); // enumerable 默认为 false
o.d = 4; // 通过赋值创建属性时 enumerable 默认为 true
Object.defineProperty(o, Symbol.for("e"), {
value: 5,
enumerable: true,
});
Object.defineProperty(o, Symbol.for("f"), {
value: 6,
enumerable: false,
});
for (const i in o) {
console.log(i);
}
// 打印 'a' 和 'd'(总是按这个顺序)
Object.keys(o); // ['a', 'd']
o.propertyIsEnumerable("a"); // true
o.propertyIsEnumerable("b"); // false
o.propertyIsEnumerable("c"); // false
o.propertyIsEnumerable("d"); // true
o.propertyIsEnumerable(Symbol.for("e")); // true
o.propertyIsEnumerable(Symbol.for("f")); // false
const p = { ...o };
p.a; // 1
p.b; // undefined
p.c; // undefined
p.d; // 4
p[Symbol.for("e")]; // 5
p[Symbol.for("f")]; // undefined
Configurable 特性
configurable
特性控制属性是否可以从对象中删除以及其特性(除了 value
和 writable
)是否可以更改。
以下示例演示了一个不可配置的访问器属性。
jsCopy to Clipboard
const o = {};
Object.defineProperty(o, "a", {
get() {
return 1;
},
configurable: false,
});
Object.defineProperty(o, "a", {
configurable: true,
}); // 抛出 TypeError
Object.defineProperty(o, "a", {
enumerable: true,
}); // 抛出 TypeError
Object.defineProperty(o, "a", {
set() {},
}); // 抛出 TypeError(set 在之前未定义)
Object.defineProperty(o, "a", {
get() {
return 1;
},
}); // 抛出 TypeError
// (即使新的 get 做的事情完全相同)
Object.defineProperty(o, "a", {
value: 12,
}); // 抛出 TypeError
// ‘value’只有在 writable 为 true 时才可以被修改
console.log(o.a); // 1
delete o.a; // 无法删除;严格模式下会抛出错误
console.log(o.a); // 1
如果 o.a
的 configurable
特性为 true
,则不会抛出任何错误,并且该属性会在最后被删除。
以下示例说明了一个不可配置但可写的数据属性。该属性的 value
仍然可以被更改,writable
也仍然可以从 true
切换到 false
。
jsCopy to Clipboard
const o = {};
Object.defineProperty(o, "b", {
writable: true,
configurable: false,
});
console.log(o.b); // undefined
Object.defineProperty(o, "b", {
value: 1,
}); // 即使 configurable 为 false,因为对象是可写的,我们仍然可以替换属性的值。
console.log(o.b); // 1
o.b = 2; // 我们也可以使用赋值运算符来更改属性的值
console.log(o.b); // 2
// 切换属性的可写性
Object.defineProperty(o, "b", {
writable: false,
});
Object.defineProperty(o, "b", {
value: 1,
}); // TypeError: because the property is neither writable nor configurable, it cannot be modified
// 此时,无法再次修改属性 'b' 或者恢复它的可写性。
这个示例演示了一个可配置但不可写的数据属性。该属性的 value
仍然可以使用 defineProperty
进行替换(但不能使用赋值运算符),并且 writable
特性仍然可以切换。
jsCopy to Clipboard
const o = {};
Object.defineProperty(o, "b", {
writable: false,
configurable: true,
});
Object.defineProperty(o, "b", {
value: 1,
}); // 我们可以使用 defineProperty 方法替换属性的值
console.log(o.b); // 1
o.b = 2; // 在严格模式下抛出 TypeError:cannot change a non-writable property's value with assignment
这个示例演示了一个不可配置且不可写的数据属性。无法更新该属性的任何特性,包括它的 value
值。
jsCopy to Clipboard
const o = {};
Object.defineProperty(o, "b", {
writable: false,
configurable: false,
});
Object.defineProperty(o, "b", {
value: 1,
}); // TypeError: the property cannot be modified because it is neither writable nor configurable.
添加多个属性和默认值
考虑属性默认值应用的方式是非常重要的。通常,在使用属性访问器分配一个值和使用 Object.defineProperty()
之间存在差异,如下面的示例所示。
jsCopy to Clipboard
const o = {};
o.a = 1;
// 等价于:
Object.defineProperty(o, "a", {
value: 1,
writable: true,
configurable: true,
enumerable: true,
});
// 另一种情况
Object.defineProperty(o, "a", { value: 1 });
// 等价于:
Object.defineProperty(o, "a", {
value: 1,
writable: false,
configurable: false,
enumerable: false,
});
自定义 setter 和 getter
下面的例子展示了如何实现一个自存档对象。当设置 temperature
属性时,archive
数组会收到日志条目。
jsCopy to Clipboard
function Archiver() {
let temperature = null;
const archive = [];
Object.defineProperty(this, "temperature", {
get() {
console.log("get!");
return temperature;
},
set(value) {
temperature = value;
archive.push({ val: temperature });
},
});
this.getArchive = () => archive;
}
const arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
下面这个例子中,getter 总是会返回一个相同的值。
jsCopy to Clipboard
const pattern = {
get() {
return "我总是返回这个字符串,无论你的赋值是什么";
},
set() {
this.myname = "这是我名称的字符串";
},
};
function TestDefineSetAndGet() {
Object.defineProperty(this, "myproperty", pattern);
}
const instance = new TestDefineSetAndGet();
instance.myproperty = "test";
console.log(instance.myproperty);
// 我总是返回这个字符串,无论你的赋值是什么
console.log(instance.myname); // 这是我名称的字符串
继承属性
如果访问器属性被继承,它的 get
和 set
方法会在派生对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
jsCopy to Clipboard
function MyClass() {}
let value;
Object.defineProperty(MyClass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
},
});
const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // 1
这可以通过将值存储在另一个属性中解决。在 get
和 set
方法中,this
指向某个被访问和修改属性的对象。
jsCopy to Clipboard
function MyClass() {}
Object.defineProperty(MyClass.prototype, "x", {
get() {
return this.storedX;
},
set(x) {
this.storedX = x;
},
});
const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // undefined
与访问器属性不同,数据属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
jsCopy to Clipboard
function MyClass() {}
MyClass.prototype.x = 1;
Object.defineProperty(MyClass.prototype, "y", {
writable: false,
value: 1,
});
const a = new MyClass();
a.x = 2;
console.log(a.x); // 2
console.log(MyClass.prototype.x); // 1
a.y = 2; // 没有作用;严格模式下会报错
console.log(a.y); // 1
console.log(MyClass.prototype.y); // 1
Object.defineProperties()
Object.defineProperties() 静态方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
const object1 = {};
Object.defineProperties(object1, {
property1: {
value: 42,
writable: true,
},
property2: {},
});
console.log(object1.property1);
// Expected output: 42
Object.getPrototypeOf()
Object.getPrototypeOf() 静态方法返回指定对象的原型(即内部 [[Prototype]]
属性的值)
const prototype1 = {};
const object1 = Object.create(prototype1);
console.log(Object.getPrototypeOf(object1) === prototype1);
// Expected output: true
Object.entries()
Object.entries() 静态方法返回一个数组,包含给定对象自有的可枚举字符串键属性的键值对。
const object1 = {
a: 'somestring',
b: 42,
};
for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}
// Expected output:
// "a: somestring"
// "b: 42"
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor() 静态方法返回一个对象,该对象描述给定对象上特定属性(即直接存在于对象上而不在对象的原型链中的属性)的配置。返回的对象是可变的,但对其进行更改不会影响原始属性的配置。获取一个对象的属性描述符信息
const object1 = {
property1: 42,
};
const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');
console.log(descriptor1.configurable);
// Expected output: true
console.log(descriptor1.value);
// Expected output: 42
let o, d;
o = {
get foo() {
return 17;
},
};
d = Object.getOwnPropertyDescriptor(o, "foo");
console.log(d);
// {
// configurable: true,
// enumerable: true,
// get: [Function: get foo],
// set: undefined
// }
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors() 静态方法返回给定对象的所有自有属性描述符。
const object1 = {
property1: 42,
};
const descriptors1 = Object.getOwnPropertyDescriptors(object1);
console.log(descriptors1.property1.writable);
// Expected output: true
console.log(descriptors1.property1.value);
// Expected output: 42
Object.is()
Object.is() 静态方法确定两个值是否为相同值。
console.log(Object.is('1', 1));
// Expected output: false
console.log(Object.is(NaN, NaN));
// Expected output: true
console.log(Object.is(-0, 0));
// Expected output: false
const obj = {};
console.log(Object.is(obj, {}));
// Expected output: false
Object.is()
确定两个值是否为相同值。如果以下其中一项成立,则两个值相同:
- 都是
undefined
- 都是
null
- 都是
true
或者都是false
- 都是长度相同、字符相同、顺序相同的字符串
- 都是相同的对象(意味着两个值都引用了内存中的同一对象)
- 都是 BigInt 且具有相同的数值
- 都是 symbol 且引用相同的 symbol 值
- 都是数字且
Object.is()
与 ==
运算符并不等价。==
运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false
的结果是 true
,但是 Object.is()
不会对其操作数进行类型转换。
Object.is()
也不等价于 ===
运算符。Object.is()
和 ===
之间的唯一区别在于它们处理带符号的 0 和 NaN
值的时候。===
运算符(和 ==
运算符)将数值 -0
和 +0
视为相等,但是会将 NaN
视为彼此不相等。
// 案例 1:评估结果和使用 === 相同
Object.is(25, 25); // true
Object.is("foo", "foo"); // true
Object.is("foo", "bar"); // false
Object.is(null, null); // true
Object.is(undefined, undefined); // true
Object.is(window, window); // true
Object.is([], []); // false
const foo = { a: 1 };
const bar = { a: 1 };
const sameFoo = foo;
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(foo, sameFoo); // true
// 案例 2: 带符号的 0
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(-0, -0); // true
// 案例 3: NaN
Object.is(NaN, 0 / 0); // true
Object.is(NaN, Number.NaN); // true
Object.prototype.valueOf()
function MyNumberType(n) {
this.number = n;
}
MyNumberType.prototype.valueOf = function () {
return this.number;
};
const object1 = new MyNumberType(4);
console.log(object1 + 3);
// Expected output: 7
in
in一个属性是否在这个对象里面
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car);
// Expected output: true
delete car.make;
if ('make' in car === false) {
car.make = 'Suzuki';
}
console.log(car.make);
// Expected output: "Suzuki"
for in | for of
-
for of 每一个item代表集合中的一个元素
const iterable = new Map([ ["a", 1], ["b", 2], ["c", 3], ]); for (const entry of iterable) { console.log(entry); } // ['a', 1] // ['b', 2] // ['c', 3] for (const [key, value] of iterable) { console.log(value); } // 1 // 2 // 3
-
for in 每一个item代表对象中的某个属性
const object = { a: 1, b: 2, c: 3 }; for (const property in object) { console.log(`${property}: ${object[property]}`); } // Expected output: // "a: 1" // "b: 2" // "c: 3"
懒加载
延迟加载(懒加载)是一种将资源标识为非阻塞(非关键)资源并仅在需要时加载它们的策略。这是一种缩短关键渲染路径长度的方法,可以缩短页面加载时间。
延迟加载可以在应用程序的不同时刻发生,但通常会在某些用户交互(例如滚动和导航)上发生。
概览
随着网络的发展,发送给用户的资源数量和规模都在急剧增加。从 2011 年到 2019 年,桌面端的资源中位数从 ~100KB 增至 ~400KB,移动端的资源中位数从 ~50KB 增至 ~350KB。桌面端上图片资源的大小已经从 ~250KB 增至 ~900KB,而移动设备上的图片 ~100KB 增至 ~850KB。
解决这个问题的方法之一是延迟加载对第一次渲染并不重要的资源来缩短关键渲染路径的长度。一个实际例子是,当用户打开一个电商网站的主页时,该页面有一个指向购物车页面/区域的链接,并且只有在用户导航到购物车页面/区域时才会下载其所有资源(如 JavaScript、CSS 和图片)。
策略
延迟加载可以通过多种策略应用于多个资源。
常规
代码拆分
JavaScript、CSS 和 HTML 可以被分割成较小的代码块。这样就可以在前期发送所需的最小代码,改善页面加载时间。其余的可以按需加载。
- 入口点分离:通过应用的入口点分离代码
- 动态分离:通过动态 import() 语句分离代码
JavaScript
脚本类型模块
任何类型为 type="module"
的脚本标签都被视为一个 JavaScript 模块,并且默认情况下会被延迟。
CSS
默认情况下,CSS 被视为渲染阻塞资源,因此,在 CSSOM 构造完成之前,浏览器不会渲染任何已处理的内容。CSS 必须尽量小,才能尽快送达,建议使用媒体类型和查询实现非阻塞渲染。
htmlCopy to Clipboard
<link href="style.css" rel="stylesheet" media="all" />
<link href="portrait.css" rel="stylesheet" media="(orientation:portrait)" />
<link href="print.css" rel="stylesheet" media="print" />
可以执行一些 CSS 优化来实现这一目标。
字体
默认情况下,字体请求会延迟到构造渲染树之前,这可能会导致文本渲染延迟。
可以使用 <link rel="preload">
、CSS font-display 属性和字体加载 API 来覆盖默认行为并预加载网络字体资源。
参见 Link 元素。
图片和 iframe
很多时候,网页包含许多图片,这些图片会影响数据的使用和网页的加载速度。这些图片大部分都在屏幕之外(非关键),需要用户互动,如滚动,才能看到它们。
Loading 属性
元素上的 loading 属性(或 上的 loading 属性)可用于指示浏览器推迟加载屏幕外的图像/iframe,直到用户滚动到其附近。
htmlCopy to Clipboard
<img src="image.jpg" alt="..." loading="lazy" />
<iframe src="video-player.html" title="..." loading="lazy"></iframe>
当早期加载的内容全部加
载完毕时,load
事件就会触发;这时,完全有可能(甚至有可能)在可视视口范围内有一些延迟加载的图片还没有加载。
你可以通过检查其布尔属性 complete
的值来确定某个图片是否已经完成加载。
Polyfill
包含这个 polyfill,以提供对旧的和目前不兼容的浏览器的支持:loading-attribute-polyfill。
Intersection Observer API
Intersection Observer 允许用户得知被观察的元素何时进入或退出浏览器的视口。
事件处理器
当浏览器的兼容性至关重要时,有以下几种选择:
- polyfill intersection observer
- 回退到滚动、调整大小或方向变化事件处理器,以确定特定元素是否在视口中
Map
-
使用forEach()迭代Map
myMap.forEach((value, key) => { console.log(`${key} = ${value}`); }); // 0 = zero // 1 = one
-
Map与数组对象的关系
const kvArray = [ ["key1", "value1"], ["key2", "value2"], ]; // 使用常规的 Map 构造函数可以将一个二维的键值对数组转换成一个 Map 对象 const myMap = new Map(kvArray); console.log(myMap.get("key1")); // "value1" // 使用 Array.from 函数可以将一个 Map 对象转换成一个二维的键值对数组 console.log(Array.from(myMap)); // 输出和 kvArray 相同的数组 // 更简洁的方法来做如上同样的事情,使用展开运算符 console.log([...myMap]); // 或者在键或者值的迭代器上使用 Array.from,进而得到只含有键或者值的数组 console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]
-
使用Majp
const myMap = new Map(); const keyString = "a string"; const keyObj = {}; const keyFunc = function () {}; // 添加键 myMap.set(keyString, "和键'a string'关联的值"); myMap.set(keyObj, "和键 keyObj 关联的值"); myMap.set(keyFunc, "和键 keyFunc 关联的值"); console.log(myMap.size); // 3 // 读取值 console.log(myMap.get(keyString)); // "和键'a string'关联的值" console.log(myMap.get(keyObj)); // "和键 keyObj 关联的值" console.log(myMap.get(keyFunc)); // "和键 keyFunc 关联的值" console.log(myMap.get("a string")); // "和键'a string'关联的值",因为 keyString === 'a string' console.log(myMap.get({})); // undefined,因为 keyObj !== {} console.log(myMap.get(function () {})); // undefined,因为 keyFunc !== function () {}
typyof
类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "object" (原因) |
Boolean | "boolean" |
Number | "number" |
BigInt | "bigint" |
String | "string" |
Symbol | "symbol" |
Function(在 ECMA-262 中实现 [[Call]];classes也是函数) | "function" |
其他任何对象 | "object" |
// 数值
typeof 37 === "number";
typeof 3.14 === "number";
typeof 42 === "number";
typeof Math.LN2 === "number";
typeof Infinity === "number";
typeof NaN === "number"; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === "number"; // Number 会尝试把参数解析成数值
typeof Number("shoe") === "number"; // 包括不能将类型强制转换为数字的值
typeof 42n === "bigint";
// 字符串
typeof "" === "string";
typeof "bla" === "string";
typeof `template literal` === "string";
typeof "1" === "string"; // 注意内容为数字的字符串仍是字符串
typeof typeof 1 === "string"; // typeof 总是返回一个字符串
typeof String(1) === "string"; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === "boolean";
typeof false === "boolean";
typeof Boolean(1) === "boolean"; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!1 === "boolean"; // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
// Undefined
typeof undefined === "undefined";
typeof declaredButUndefinedVariable === "undefined";
typeof undeclaredVariable === "undefined";
// 对象
typeof { a: 1 } === "object";
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === "object";
typeof new Date() === "object";
typeof /regex/ === "object";
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === "object";
typeof new Number(1) === "object";
typeof new String("abc") === "object";
// 函数
typeof function () {} === "function";
typeof class C {} === "function";
typeof Math.sin === "function";
link:外部资源链接元素
HTML 元素规定了当前文档与某个外部资源的关系。该元素最常用于链接样式表,此外也可以被用来创建站点图标(比如 PC 端的“favicon”图标和移动设备上用以显示在主屏幕的图标)
<link href="main.css" rel="stylesheet" />
在这个简单的示例中,使用了 href
属性设置外部资源的路径,并设置 rel
属性的值为 stylesheet
。rel
表示“关系”,它可能是 <link>
元素其中一个关键的特性——属性值表示 <link>
项的链接方式与包含它的文档之间的关系。
URL
URL 接口用于解析,构造,规范化和编码 URL。它通过提供允许你轻松阅读和修改 URL 组件的属性来工作。通常,通过在调用 URL 的构造函数时将 URL 指定为字符串或提供相对 URL 和基本 URL 来创建新的 URL 对象。然后,你可以轻松读取 URL 的已解析组成部分或对 URL 进行更改。
如果浏览器尚不支持URL()
构造函数,则可以使用Window
中的Window.URL
属性。确保检查你的任何目标浏览器是否要求对此添加前缀。
备注: 此特性在 Web Worker 中可用。
构造器
-
创建并返回一个
URL
对象,该 URL 对象引用使用绝对 URL 字符串,相对 URL 字符串和基本 URL 字符串指定的 URL。
属性
-
包含
'#'
的USVString
,后跟 URL 的片段标识符。 -
一个
USVString
,其中包含域(即主机名),后跟(如果指定了端口)“:”和 URL 的端口。 -
包含 URL 域名的
USVString
。 -
包含完整 URL 的
USVString
。 -
origin
只读返回一个包含协议名、域名和端口号的
USVString
。 -
包含在域名前面指定的密码的
USVString
。 -
以 ‘/’ 起头紧跟着 URL 文件路径的
DOMString
。 -
包含 URL 端口号的
USVString
。 -
包含 URL 协议名的
USVString
,末尾带':'
。 -
一个
USVString
,指示 URL 的参数字符串;如果提供了任何参数,则此字符串包括所有参数,并以开头的“?”开头 字符。 -
searchParams
只读URLSearchParams
对象,可用于访问search
中找到的各个查询参数。 -
包含在域名前面指定的用户名的
USVString
。
方法
-
返回包含整个 URL 的
USVString
。它返回与href
属性相同的字符串。
静态方法
-
返回一个
DOMString
,包含一个唯一的 blob 链接(该链接协议为以 blob:,后跟唯一标识浏览器中的对象的掩码)。 -
销毁之前使用
URL.createObjectURL()
方法创建的 URL 实例。
使用说明
如果url
参数是相对 URL,则构造函数将使用url
参数和可选的base
参数作为基础。
const url = new URL('../cats', 'http://www.example.com/dogs');
console.log(url.hostname); // "www.example.com"
console.log(url.pathname); // "/cats"
可以设置 URL 属性以构造 URL:
url.hash = 'tabby';
console.log(url.href); // "http://www.example.com/cats#tabby"
URL 根据 RFC 3986中的规则进行编码。例如:
url.pathname = 'démonstration.html';
console.log(url.href); // "http://www.example.com/d%C3%A9monstration.html"
URLSearchParams
接口可用于构建和处理 URL 查询字符串。
要从当前窗口的 URL 获取搜索参数,可以执行以下操作:
// https://some.site/?id=123
const parsedUrl = new URL(window.location.href);
console.log(parsedUrl.searchParams.get("id")); // "123"
URL 的toString()
方法仅返回href
属性的值,因此构造函数可以 用于直接对 URL 进行规范化和编码。
const response = await fetch(new URL('http://www.example.com/démonstration.html'));
ref与reactive区别
对比之前先看一下如何使用,它们的使用方法都很简单,也很类似:
<template>
<div>{{user.first_name}} {{user.last_name}}</div>
<div>{{ age }}</div>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
const age = ref(18)
const user = reactive({
first_name: "Karl",
last_name: "Max",
})
return { user , age}
}
}
</script>
复制代码
接下来我们就来分析一下它们的不同点:
可接受的原始数据类型不同
ref()
和reactive()
都是接收一个普通的原始数据,再将其转换为响应式对象,例如上面代码中的user
和age
。却别在于:ref可以同时处理基本数据类型和对象,而reactive只能处理处理对象而支持基本数据类型。
这是因为二者响应式数据实现的方式不同:
ref
是通过一个中间对象RefImpl
持有数据,并通过重写它的set和get方法实现数据劫持的,本质上依旧是通过Object.defineProperty 对RefImpl
的value
属性进行劫持。reactive
则是通过Proxy进行劫持的。Proxy无法对基本数据类型进行操作,进而导致reactive
在面对基本数据类型时的束手无策。
ref对应的源码如下:
删减整合后的reactive代码如下:
Object.defineProperty、Proxy和数据劫持的相关内容详情可见:Vue3数据劫持优化。
总结:ref可以存储基本数据类型而reactive则不能
返回值类型不同
运行如下代码:
输出结果为:
ref()
返回的是一个持有原始数据的RefImpl
实例。而reactive()
返回的类型则是原始数据的代理Proxy
实例
因此,在定义数据类型时,有些许差别:
另外如果reactive
中有响应式对象,它会被自动展开,所以下面代码是正确的:
const countReactiveRef: Count = reactive({num:ref(2)})
结论:ref(value: T)返回的Ref 类型,而reactive(object: T)返回的T 类型的代理
访问数据的方式不同
返回值的类型不同,就会导致数据的访问方式不同。通过上文的可知:
ref()
返回的是RefImpl
的一个实例对象,该对象通过_value私有变量持有原始数据,并重写了value
的get方法。因此,当想要访问原始对象的时候,需要通过xxx.value
的方式触发get函数获取数据。同样的,在修改数据时,也要通过xxx.value = yyy
的方式触发set函数。reactive()
返回的是原始对象的代理,代理对象具有和原始对象相同的属性,因此我们可以直接通过.xxx
的方式访问数据
反应在代码中如下:
const objectRef = ref({ count: 0 });
const refCount = objectRef.value.count;
const objectReactive = reactive({ count: 0});
const reactiveCount = objectReactive.count;
总结:ref需要通过value属性间接的访问数据(在templates中vue做了自动展开,可以省略.value),而reactive可以直接访问。
原始对象的可变性不同
Event对象
Element 是最通用的基类,Document
中的所有元素对象(即表示元素的对象)都继承自它。它只具有各种元素共有的方法和属性。更具体的类则继承自 Element
。
- scrollTo 跳转到指定坐标,可产生过度效果
Set对象
set对象只能存入唯一的基本数据类型,对于对象类型数据是通过索引地址查找和对比的。
Set.has() 查看某个元素是否存在
Set.add()
Set.forEach()
Set.delete()
// 在 Set 和 Array 之间转换
const mySet2 = new Set([1, 2, 3, 4]);
console.log(mySet2.size); // 4
console.log([...mySet2]); // [1, 2, 3, 4]
// 可以通过如下代码模拟求交集
const intersection = new Set([...mySet1].filter((x) => mySet2.has(x)));
// 可以通过如下代码模拟求差集
const difference = new Set([...mySet1].filter((x) => !mySet2.has(x)));
把一个字符转化为整型
-
隐式转换
+item
-
parseInt(item)
-
parseFloat(item)
获取视口的宽度和高度
JS获取元素的宽高及位置,视口的宽高的几种方法
第一次使用优快云,记录下遇到的几种JS获取DOM元素的宽高的方法。
1.不同视口的获取方法
1.1获取包括滚动条的视口的大小
var width = window.innerWidth
var height = window.innerHeight
1
2
1.2获取视口大小(内容区域大小,包括侧边栏,窗口镶边,和调整窗口大小的边距)
var width = window.outerWidth
var height = window.outerHeight
1
2
1.3获取屏幕理想视口大小,固定值,屏幕分辨率大小
var width = window.screen.width
var height = window.screen.height
1
2
1.4获取浏览器可用窗口的大小(包含内边距,但不包含垂直滚动条,边框和外边距)
var width = window.screen.availWidth
var height = window.screen.availHeight
1
2
如图演示
1.5包括内边距、滚动条、边框和外边距
var width = document.documentElement.offsetWidth,
var height = document.documentElement.offsetHeight;
1
2
1.6在不使用滚动条的情况下适合视口中的所有内容所需的最小宽度和高度
var width = document.documentElement.scrollWidth,
var height = document.documentElement.scrollHeight;
1
2
1.6包含元素的内边距,但不包括边框、外边距或者垂直滚动条
var width = document.documentElement.clientWidth,
var height = document.documentElement.clientHeight;
1
2
2.获取DOM元素的宽高
2.1内高度、内宽度: 内边距 + 内容框
var width = element.clientWidth
var height = element.clientHeight
1
2
2.2外高度,外宽度: 边框 + 内边距 + 内容框
var width = element.offsetWidth
var height = element.offsetHeight
1
2
2.3上边框、左边框
element.clientTop
element.clientLeft
1
2
2.4元素的大小及其相对于视口的位置
element.getBoundingClientRect()
x\y //元素的左上角和父元素左上角的距离
width/height //边框 + 内边距 + 内容框
top //元素的上边界和父元素上边界的距离
left //元素的左边界和父元素左边界的距离
right //元素的右边界和父元素的左边界的距离
bottom //元素的下边界和父元素上边界的距离
1
2
3
4
5
6
7
2.5上边偏移量,左边的偏移量
element.offsetTop
element.offsetLest
http常见状态码
状态码 | 说明 |
---|---|
200 | 请求成功 |
204 | 请求成功但没有返回结果 |
301 | 重定向 |
302 | 临时重定向 |
303 | 由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源 |
304 | 表示在客户端采用带条件的访问某资源时,服务端找到了资源,但是这个请求的条件不符合。跟重定向无关 |
403 | 请求被服务器拒绝了 |
404 | 服务器上无法找到请求的资源 |
500 | 服务端执行请求时发生了错误 |
503 | 服务器正在超负载或者停机维护,无法处理请求 |
把数字转化为字符
let num = 1;
let letter = String.fromCharCode(64 + num);
console.log(letter); // 'A'
windows
screen屏幕相关
history历史访问记录
navigator剪切板 内核 浏览器信息
location 操作浏览器 url
如何实现页面跳转?
1.location.href
使用 location.href 属性可以设置或获取当前页面的 URL。通过将其值设置为新的 URL,可以实现页面跳转。
// 跳转到指定的 URL 地址
location.href = “https://www.example.com”;
2.location.assign()
使用 location.assign() 方法同样可以实现页面跳转。它接受一个 URL 参数作为要跳转的目标地址。
// 跳转到指定的 URL 地址
location.assign(“https://www.example.com”);
3.window.open()
window.open() 方法:它可以在一个新窗口或选项卡中打开一个指定的 URL。
// 在新窗口中打开指定的 URL 地址
window.open(“https://www.example.com”);
4.replace()
使用replace()方法:location.replace() 方法可以实现页面跳转,但与前两种方式不同的是,它会替换当前页面的历史记录,导致用户无法返回到前一个页面
// 跳转到指定的 URL 地址,并替换当前页面的历史记录
location.replace(“https://www.example.com”);
5.history.forward()
使用 history.forward() 方法可以让浏览器前进到历史记录中的下一个页面。
history.forward();
6.history.back()
使用 history.back() 方法可以让浏览器后退到历史记录中的上一个页面。
history.back();
7.history.go()
使用 history.go() 方法可以跳转到指定的历史记录索引。负数表示后退,正数表示前进。
// 后退一步
history.go(-1);
// 前进一步
history.go(1);
获取一个对象可枚举对象
Object.entries()
Object.values()
Object.keys()
引用数据类型 在对象class中的传播
class changeArray{
constructor(arr){
this.arr = arr
this.arr[0] = 1
}
}
const arr = new Array(5).fill(0)
const a1 = new changeArray(arr)
console.log(a1.arr);
console.log(arr);
[1,0,0,0,0]
[1,0,0,0,0]
传递的是地址 改变值 外部值同样改变
typeof能识别那些类型
// 数值
typeof 37 === "number";
typeof 3.14 === "number";
typeof 42 === "number";
typeof Math.LN2 === "number";
typeof Infinity === "number";
typeof NaN === "number"; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === "number"; // Number 会尝试把参数解析成数值
typeof Number("shoe") === "number"; // 包括不能将类型强制转换为数字的值
typeof 42n === "bigint";
// 字符串
typeof "" === "string";
typeof "bla" === "string";
typeof `template literal` === "string";
typeof "1" === "string"; // 注意内容为数字的字符串仍是字符串
typeof typeof 1 === "string"; // typeof 总是返回一个字符串
typeof String(1) === "string"; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === "boolean";
typeof false === "boolean";
typeof Boolean(1) === "boolean"; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!1 === "boolean"; // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
// Undefined
typeof undefined === "undefined";
typeof declaredButUndefinedVariable === "undefined";
typeof undeclaredVariable === "undefined";
// 对象
typeof { a: 1 } === "object";
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === "object";
typeof new Date() === "object";
typeof /regex/ === "object";
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === "object";
typeof new Number(1) === "object";
typeof new String("abc") === "object";
// 函数
typeof function () {} === "function";
typeof class C {} === "function";
typeof Math.sin === "function";
阻止事件传递
event.returnValue = false
复制粘贴
-
vue3
npm install vue-clipboard3
使用
import clipBoard from 'vue-clipboard3' let {toClipboard} = clipBoard() const CopyEvent = async()=>{ await toClipboard('value')}
参考文献
2024前端面试(JS专题)_js面试题2024-优快云博客
Vue2 和 Vue3 的区别(设计理念、性能提升、编码方式以及特性)_vue2和vue3区别-优快云博客
判断一个对象为空对象的五种方法_判断对象是否为空-优快云博客
Vue中ref()和 reactive() 的区别_vue ref和reactive-优快云博客