前言
本人最近在看Js红宝书,由于书籍太厚担心有些知识点看完不复习会忘记,所以打算写个博客记录下。
=========================================================================
正片开始
Q:大家都知道将js代码写在<body>中页面内容后面,不会阻塞页面渲染。那当js代码写在head中时,要也想达到不会阻塞页面渲染的效果要如何操作呢?
A:使用 script 中的async或者defer属性,这两个属性会告诉浏览器立即开始下载,二者都只适用于外部脚本。
区别是 defer 会把脚本推迟到文档渲染完毕后再根据出现顺序执行,async 则是异步加载,不需要等待其他脚本同时也不阻塞文档渲染,自然也不能保证脚本的执行顺序。
Q:关于<noscript>如何使用,何时使用?
A: <noscript> 包含除<script>外任何可以出现在<body>中的代码,当浏览器不支持脚本或者浏览器对脚本的支持被关闭,二者任一条件被满足时,就会渲染 <noscript> 中的代码。
Q:声明变量的三个关键字 var let const的区别
A:
- var会进行变量提升,var可以重复声明,var声明的范围是函数作用域,var 在全局作用域声明的变量会成为window 对象的属性
- let 和 const 不会进行变量提升,不可以进行重复声明且声明范围为块级作用域,在全局作用域声明的变量不会成为window 对象的属性
- const声明变量时必须同时初始化变量,且之后不允许修改。(当然,如果 const 变量引用的是一个对象,那么修改对象的内部属性是可以的)
小tips: 在let 声明前的执行瞬间为 “ 暂时性死区” ,在此阶段引用任何后面才声明的变量都会报错
Q: 基本类型和引用类型 如何确定?
A:基本类型有:Undefined、Null、String、Boolean、Number、Symbol
注:Symbol类型创建出来的属性是私有类型
可以使用 typeof 确定基本类型,不过有个例外 Null 会返回 “object”
基本类型外的数据也可以使用typeof, function会返回function,object会返回object。
引用类型可使用 instanceof,constructor,object.prototype.toString()等方法来确定。
eg:
let date = new Date();
console.log(date instanceof Date); // 输出 true
console.log(date.constructor === Date); // 输出 true
console.log(Object.prototype.toString.call(date)); // 输出 "[object Date]"
let arr = [];
console.log(arr instanceof Array); // 输出 true
console.log(arr.constructor === Array); // 输出 true
console.log(Object.prototype.toString.call(arr)); // 输出 "[object Array]"
基本类型都储存在栈中,引用类型的引用储存在栈中,value储存在堆中。
Q:object实例有哪些属性和方法
A:
- constructor:用于创建当前对象的函数
- hasOwnProperty( propertyName:string ):判断当前对象实例上是否存在给定的属性(就是看这个属性是不是对象本身的,不包括原型链继承的)
- isPrototypeOf( object ): 判断当前对象是否是另一个对象的原型(代替原_proto_属性)
- propertyIsEnumerable( propertyName:string ):判断给定的属性是否可以使用for-in语句枚举(就是看这个属性是不是对象本身的可枚举的,不包括原型链继承的。如果一个属性是对象,那它就是不可枚举的,就会返回false)
- toString():返回对象的字符串形式
- toLocaleString():返回对象的字符串形式,该字符串反映对象所在的本地化执行环境
- valueOf():返回对应的字符串、数值或布尔值表示。(通常与toString()返回值相同)
Q:垃圾回收
A:Js是使用垃圾回收的编程语言。垃圾回收是周期性的,每隔一段时间垃圾回收程序就会自动运行。离开作用域的值会被自动标记为可回收,然后再垃圾回收期间被删除。
垃圾回收算法有标记清理和引用计数
标记清理:先给不适用的值加上标记,再回收它们的内存。(主流使用)
引用计数:记录每个值被引用的次数,引用一次就加一,当一个值引用的变量被其他值覆盖的时候就减一,数量为0时就回收。(在循环引用中会出现问题后被放弃使用)
如果内存中分配了很多变量,则可能造成性能损失。因此垃圾回收的时间调度很重要,重点是:无论什么时候开始收集垃圾,都要让它尽快结束。
(解除变量的引用不仅可以消除循环引用还对垃圾回收有帮助。)
Q:字符串操作方法
A:concat():拼接字符串,同 +
slice() 、substr() 和 substring():三者都是提取子字符串的方法,都接受1~2个参数。第一个参数表示子字符串起始位置。区别是
slice():第二个参数表示子字符串结束位置(不包含结束位)。当有参数是负数的时候,slice()会将所有负值参数都当做字符串长度+负参数值。
substr():第二个参数表示返回的子字符串长度。如有参数是负数的时候,substr() 会把第一个参数的负值转换为字符串长度+负参数值,第二个参数的负值转换为0。
subString():第二个参数表示子字符串结束位置(不包含结束位)。如有参数是负数的时候,subString() 会把所有负值转换为0,且这个方法会将较小的参数作为起点,较大的参数作为终点。 即:substring(3,0) 等价于 substring(0,3)
trim():去除字符串前后空格,原字符串不受影响。衍生出来的方法有trimLeft()和trimRight(),分别去除字符串开始和结尾的空格。
repeat():复制字符串,参数传的是数字,代表复制多少次.
padStart()和padEnd(): 为字符串填充值,该方法接收两个参数。第一个参数为number类型,第二个参数为string类型(可为空)。
let k = "fooof"
console.log(k.padStart(4)) //输出 “fooof”
console.log(k.padStart(-1)) //输出 “fooof”
console.log(k.padStart(7)) //输出 “ fooof”
console.log(k.padStart(8,".")) //输出 “fooof...”
console.log(k.padEnd(7)) //输出 “fooof ”
console.log(k.padEnd(8,".")) //输出 “fooof...”
Q: Math对象的属性和方法
A:
Math.PI:π
Math.ceil(): 向上取整。
Math.floor(): 向下取整。
Math.round(): 四舍五入。
Math.fround(): 返回数值最接近的 单精度(32)位浮点数。 Math.fround(0.4) =》0.4000000059604645
Math.random():返回0~1的随机数。
- 如果想返回1~10的随机数则使用:num=Math.floor(Math.random()*10+1)。
- 如果想返回2~10的随机数则使用:num=Math.floor(Math.random()*9+2)。
- 可以推算出公式 num = Math.floor(Math.random()* totalNum + firstNum), totalNum代表范围内一共有多少整数,firstNum代表第一个整数。
Math.abs(x): 返回x的绝对值。
Math.sqrt(x): 返回x的平方根。
Math.pow(x,power): 返回x的power次幂。(忽然想起 a**2,代表a的平方)
Q:数组和伪数组的区别
A:
数组和伪数组的区别可以归结为以下几个方面:
- 类型与结构:
- 数组:数组是一种特殊的数据结构,用于在单一变量中存储多个值。这些值按照特定的顺序排列,可以通过索引来访问。数组的类型是Array。
- 伪数组:伪数组在结构上类似于数组,具有索引和length属性,但它们的类型并不是Array,而是Object。这意味着它们没有数组的方法,如push、shift、map等。常见的伪数组有函数的arguments对象和DOM元素集合(如通过
document.getElementsByTagName
获取的对象)。
- 属性与方法:
- 数组:具有内置的length属性和各种数组方法,如push(向数组末尾添加一个或多个元素,并返回新的长度)、shift(删除并返回数组的第一个元素)、map(创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果)等。
- 伪数组:同样具有length属性,但这个属性是自定义的,并且在某些情况下可能不是number类型。伪数组不具有数组的方法,但可以使用Object的方法。
- _proto_不同,数组的prototype有很多方法,伪数组的prototype只有几个方法
- 索引:
- 数组:索引是按顺序排列的整数,如0、1、2、3等。
- 伪数组:索引也可以是非负整数,但它们在伪数组中作为对象的属性存在,因此会被当做字符串来处理。不过在实际应用中,我们可以将其视为非负整数串来理解。
- 动态性:
- 数组:其length属性是内置的,可以自动更新以反映数组中的元素数量。
- 伪数组:length属性是自定义的,可能需要根据实际情况手动更新。
- 转换: 伪数组没有数组的方法,但结构类似,因此有时需要将其转换为真正的数组以便使用数组的方法。转换方法有多种,如使用
Array.from(arrayLike)
、Array.prototype.slice.call(arrayLike)
、Array.prototype.splice.call(arrayLike, 0)
或Array.prototype.concat.apply([], arrayLike)
等。
Q:生成器和迭代器
A:迭代器是一个按需创建的一次性对象。它定义了一个对象,在其元素上进行迭代。支持连续获取对象产出的每一个值。任何实现Iterable接口的对象都有一个 Symbol.iterator 属性,这个属性引用默认迭代器。
迭代器必须通过连续调用next()方法才可以连续获取值,这个方法返回一个InterableObject。这个对象包含done和value两个属性,分别代表是否还有更多值可以访问和迭代器当前的返回值。
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1, limit = this.limit;
return {
next () {
if(count <= limit) {
return { done: false, value: count++ };
} else {
return { done: true, value: undefined };
}
}
}
}
}
Q: map 和 object的区别
A:
- 键的类型:
- Map的键可以是任意的数据类型,包括基本数据类型(如字符串、数字、布尔值)、对象以及函数。
- Object的键则仅限于使用字符串(或Symbol,但在日常开发中字符串更为常见)作为键名。
- 有序性:
- Map中的键是有序的,会按照插入的顺序进行迭代。
- Object的键则是无序的,虽然ES2015规范规定了键的插入顺序应与枚举顺序一致,但在实际开发中,并不能确保对象属性的顺序。
- 获取长度:
- Map具有一个
size
属性,可以直接获取Map中键值对的数量。 - Object则需要通过
Object.keys(obj).length
来计算对象的属性数量,这种方式在性能上可能不如Map直接获取size
属性。
- Map具有一个
- 迭代方式:
- Map是内置的可迭代对象,可以直接使用
for...of
循环进行迭代。 - Object虽然也可以通过
for...in
循环进行迭代,但这种方式会遍历对象自身以及原型链上的所有可枚举属性,因此可能需要使用hasOwnProperty()
方法来过滤掉原型链上的属性。另外,Object.keys()、Object.values()和Object.entries()方法也可以用来遍历对象的属性。
- Map是内置的可迭代对象,可以直接使用
- 性能:
- 在频繁增删键值对的场景下,Map的性能通常优于Object。因为Map是专门为动态键值对设计的,而Object则是为了模拟类似于真实世界对象的结构而设计的。
可参考 前端JavaScript篇之map和Object的区别、map和weakMap的区别_前端map-CSDN博客
Q:https证书存放在什么位置?
A:浏览器端放在浏览器证书库中
HTTPS是在HTTP和TCP之间通过加入SSL协议来实现安全的通信的
Q:js代码中,比如函数,它们都是异步的,是异步的就会创建一个回调,这个回调是谁来调用的?
A:在JavaScript中,当异步操作完成时,是JavaScript运行时环境(runtime environment)或特定的异步API(如浏览器或Node.js)负责调用回调函数。
具体来说:
-
浏览器环境:当你在浏览器中执行异步操作(如使用
fetch
API进行网络请求)时,浏览器会处理这个请求。一旦请求完成(无论是成功还是失败),浏览器会检查是否有回调函数与这个异步操作相关联。如果有,浏览器会调用这个回调函数,并将操作的结果(或错误信息)作为参数传递给回调函数。 -
Node.js环境:在Node.js中,情况类似。Node.js的底层(基于libuv)和事件循环(event loop)负责处理异步操作。当异步操作完成时,Node.js会将其结果放入事件队列中。事件循环会监视这个队列,并在适当的时候将事件(包括完成的异步操作)和相关的回调函数取出执行。
因此,无论是浏览器还是Node.js,都是由JavaScript的运行时环境或特定的异步API来负责调用这些回调函数。这种机制允许JavaScript代码在等待异步操作完成时继续执行其他任务,从而提高应用程序的响应性和性能。
Q:js如何判断一个元素在视图区域内
A:
可以使用getBoundingClientRect()
方法与窗口的尺寸进行比较,或者使用Intersection Observer API
。下面分别介绍这两种方法:
1) 使用getBoundingClientRect()
getBoundingClientRect()
方法返回一个DOMRect
对象,该对象包含了元素的大小及其相对于视口的位置。通过比较这个对象的top
、right
、bottom
和left
属性与视口的大小(window.innerHeight
和window.innerWidth
),你可以判断元素是否在视口内。
function isElementInView(element) {
const rect = element.getBoundingClientRect();
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
// 检查元素是否在视口内
// 注意:这里假设“在视口内”意味着元素至少有一部分在视口内
return !(rect.bottom < 0 || rect.top > viewHeight || rect.right < 0 || rect.left > viewWidth);
}
// 使用
const element = document.querySelector('#myElement');
if (isElementInView(element)) {
console.log('元素在视图区域内');
} else {
console.log('元素不在视图区域内');
}
2) 使用Intersection Observer API
Intersection Observer API
提供了一种异步检测目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。这是处理这类需求的现代且推荐的方式,因为它不仅更简洁,而且性能更好。
function observeElementInView(element, callback) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// isIntersecting 属性表明目标元素是否与视口交叉
if (entry.isIntersecting) {
callback(true);
} else {
callback(false);
}
});
}, {
root: null, // null 值表示视口
rootMargin: '0px', // 边界区域
threshold: [0] // 当元素与视口的交集达到或超过指定的百分比时,会触发回调函数
});
observer.observe(element);
// 记得在组件卸载或不再需要观察时取消观察
// observer.unobserve(element);
// 或者
// observer.disconnect();
}
// 使用
observeElementInView(document.querySelector('#myElement'), isInView => {
if (isInView) {
console.log('元素现在在视图区域内');
} else {
console.log('元素现在不在视图区域内');
}
});
在这个例子中,Intersection Observer API
会在元素与视口交叉时(即元素进入或离开视口时)自动调用回调函数。通过设置threshold
选项,你可以控制交叉多少百分比时才触发回调函数,但在这个例子中,我们使用了[0]
,意味着即使元素只有一点点与视口交叉,也会触发回调函数。如果你想要更精确地控制何时触发(比如元素完全在视口内时),你可能需要设置一个更合适的threshold
值,或者使用多个threshold
值。
Q:不同屏幕设置不同css
A:使用@media
/* 默认样式,适用于所有屏幕 */
body {
background-color: lightblue;
}
/* 屏幕宽度小于600px时应用的样式 */
@media screen and (max-width: 599px) {
body {
background-color: lightcoral;
}
.nav-menu {
display: none; /* 例如,隐藏导航菜单 */
}
}
/* 屏幕宽度大于或等于600px时应用的样式 */
@media screen and (min-width: 600px) {
body {
background-color: lightgreen;
}
.nav-menu {
display: block; /* 显示导航菜单 */
}
}