1、常见操作数组的方法
* 增
* push - 向数组末尾添加元素,返回新增后的数组长度
* unshift - 向数组前方添加元素,返回新数组的长度
* splice - 接收三个参数,分别为起始位置,替换元素的数量,替换后的元素,返回被替换的元素组成的数组
* concat - 创建一个数组的副本,接受新的元素参数,与副本数组组成新的数组,不会改变原数组
* 删
* shift - 删除数组第一位的元素,返回被删除的项
* pop - 删除数组的最后一位元素,返回被删除的项
* splice - 接收两个参数,分别为起始位置,删除元素的数量,返回删除元素组成的数组
* slice - 接收两个参数,起始位置,结束位置(不包含结束位置元素),返回截取元素组成的数组,不会改变原数组
* 改
* splice
* 查
* indexOf - 查找元素位于数组的位置,未查到返回 -1
* includes - 查询元素是否在数组中,返回true或者false
* find - 接收一个查找函数,返回第一个符合函数需求的元素
* 排序方法
* reverse - 数组反转
* sort - 接收一个比较函数,用于判断哪个数值排在前面,比较函数接收两个参数,根据两个参数比较的结果,也就是返回的 -1或者1来判断排序是升序还是降序,前者大于后者,返回-1,这时候是降序;前者大于后者,返回1,这时候是升序
* 转换方法
* join - 接收一个分隔符,使用此分隔符将数组元素连接成为一个字符串
* 迭代方法
* filter - 接收一个删选条件的函数,返回一个符合条件的元素组成的数组
* some - 判断数组内部是否存在符合条件的元素,有则为true,否则为false
* every - 判断数组每一项是不是符合条件,都符合为true,否则为false
* forEach - 对数组的每一项运行传入的函数,没有返回值
* map - 对数组的每一项运行传入的函数,返回每次调用结果构成的一个新数组
2、如何区分数组和对象
* 通过Array.isArray()
* {} instanceof Array
* 通过instanceof
* [] instanceof Array
* 通过constructor
* [].constructor
* {}.constructor
* 通过Object.prototype.toString.call()
* Object.prototype.toString.call([])
* Object.prototype.toString.call({})
3、js异步编程 - 因为是单线程,需要通过异步来提高效率,但是本质还是使用回调处理异步任务
* 回调函数 - 容易形成回调地狱
* promise - 书写繁复
* generator - 星号*加上yield,通过next方法获取,返回由一个value与done组成的对象
* async await - es7提出,await会中断当前执行,等待异步返回
4、promise.catch后面的.then还会执行吗?
* 会执行,catch(onRejected)从内部调用了then(undefined, onRejected)
5、确保构造函数只会被new调用,不会被当做普通函数执行
* 借助 instanceof 和 new 绑定的原理,适用于低版本浏览器
* 借助 new.target 属性,可与 class 配合定义抽象类
* 面向对象编程使用 ES6 class——最佳方案
6、获取实例对象的原型对象
* 从构造函数获取 - Fnc.prototype
* 从对象实例获取
* 实例.__proto__
* Object.getPrototypeOf(实例)
7、防抖与节流
* 防抖 - n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
function debounce(fn, delay){
let timeout;
return function () {
let context = this;
let args = arguments;
clearTimeout(timeout)
timeout = setTimeout(function(){
fn.apply(context, args)
}, delay);
}
}
* 节流 - n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
function throttle(fn, delay){
let timer = null;
return function(...args){
if(!timer){
timer = setTimeout(function(){
fn.apply(this, args);
timer = null
}, delay)
}
}
}
* 常见的应用场景
* 防抖在连续的事件,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染
* 节流在间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部监听
搜索框,搜索联想功能
8、图片懒加载
* 直接在img标签上添加 loading="lazy"
* 监听滚动事件,判断图片是否到达可视区域
* 拿到所有的图片 dom
* 遍历每个图片判断当前图片是否到了可视区范围内
* 如果到了就设置图片的 src 属性,可以将图片的src属性存储在 data-src 属性中
* 绑定 window 的 scroll 事件,对其进行事件监听
* 实现:
function lazyload() {
let viewHeight = document.body.clientHeight
let imgs = document.querySelectorAll('img[data-src]')
imgs.forEach((item, index) => {
if (item.dataset.src === '') return
let rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src
item.removeAttribute('data-src')
}
})
}
function throttle(fn, delay) {
let timer
let prevTime
return function (...args) {
const currTime = Date.now()
const context = this
if (!prevTime) prevTime = currTime
clearTimeout(timer)
if (currTime - prevTime > delay) {
prevTime = currTime
fn.apply(context, args)
clearTimeout(timer)
return
}
timer = setTimeout(function () {
prevTime = Date.now()
timer = null
fn.apply(context, args)
}, delay)
}
}
lazyload();
window.addEventListener('scroll', throttle(lazyload, 200))
* 监听元素显示
const imgs = document.querySelectorAll('img[data-src]')
const config = {
rootMargin: '0px',
threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let img = entry.target
let src = img.dataset.src
if (src) {
img.src = src
img.removeAttribute('data-src')
}
self.unobserve(entry.target)
}
})
}, config)
imgs.forEach((image) => {
observer.observe(image)
})
9、监听页面相关的耗时
* 使用window.performance
* 使用方法以及常用的耗时操作
var e = window.performance;
var t = e.getEntriesByType("navigation")[0];
r=0;
t || (r = (t = e.timing).navigationStart);
t.redirectEnd - t.redirectStart
t.domainLookupStart - t.fetchStart
t.domainLookupEnd - t.domainLookupStart
t.connectEnd - t.connectStart
t.responseStart - t.requestStart
t.responseEnd - t.responseStart
t.responseEnd - t.requestStart
t.domContentLoadedEventEnd - r
t.loadEventEnd - r
10、对象方法
* Object.assign(target, source1, source2, ...)
* 将多个对象的可枚举属性复制到目标元素,只拷贝源对象的自身属性,不拷贝继承的属性
* 进行的是浅拷贝,如果source对象中存在复杂数据结构,那么在拷贝后也只是拷贝的地址
* 只进行值的拷贝
* 如果拷贝数组,那么会把数组当做对象
* Object.create(prototype, [propertiesObject])
* 使用指定的原型对象创建一个新的对象
* 新的属性包含的可选属性:
* configurable - 是否可以被delete删除
* enumerable - 是否可以被for-in循环
* writable - 是否可以写入修改
* value - 属性值
* Object.defineProperties(obj, props)
* 在一个对象上定义新的属性或者修改新的属性,并返回该对象
* Object.defineProperty(obj, prop, descriptor)
* 可同时修改、新增多个属性
* 修改对象上的指定属性、或者新增一个属性
* Object.keys(obj)
* 返回由索引组成的数组
* 如果obj为数组,返回由下标组成的数组;如果是对象,返回由键值组成的数组
* 顺序同for-in循环遍历的相同
* 会忽略Symbol属性
* Object.values(obj)
* 返回由属性值组成的数组
* 会忽略Symbol属性的值
* Object.entries(obj)
* 返回由对象可枚举属性的键值对组成的数组
* 同样会忽略Symbol属性
* obj.hasOwnProperty(name)
* 检测对象自身属性是否包含某个属性
* 只在自身具有某个属性时才会返回true,属性在原型或者继承而来都会返回false
* Object.getOwnPropertyDescriptor(obj, prop)
* 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
* 如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined
* Object.getOwnPropertyDescriptors(obj)
* 获取一个对象的所有自身属性的描述符
* Object.getOwnPropertyNames()
* 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组
* Object.getOwnPropertySymbols()
* 返回一个给定对象自身的所有 Symbol 属性的数组
* Object.getPrototypeOf()
* 返回指定对象的原型