undeclared 与 undefind 的区别
- undefined : 声明了变量,但是没有赋值
- undeclared : 没有声明变量就直接使用了
let & const & var 的区别
- let :没有变量提升,不能重复声明同一个变量,声明的变量是可以改变的
- const :没有变量提升,不能重复声明同一个变量,声明的基本数据类型不能改变,但是引用类型可以改变其属性,不能只声明不赋值
- var :存在声明提升,可以重复声明同一个变量,声明的变量可以改变
暂时性死区问题
let 和 const 声明的变量不存在变量提上,其作用域都是块级作用域,凡是在声明变量之前使用都会报错,所以,在代码块内,使用 let 或者 const之前,该变量都是不可用的。(简单来讲,没声明就不要用,用就报错)
获取 DOM 元素有哪些方法
|方法|描述|返回类型|
|:----😐:----😐
|document.getElementByid(id)|通过 ID 获取 dom|符合条件的 dom对象|
|document.getElementByTagName(tagName)|通过 标签名 获取 dom|符合条件的所有 dom对象 组成的类数组(假数组)|
|document.getElementByClass(class)|通过 类名 获取 dom|符合条件的所有 dom对象 组成的类数组|
|document.getElementByname(name)|通过 标签属性name 获取 dom|符合条件的所有 dom对象 组成的类数组|
|document.querySelector(选择器)|通过 选择器 获取 dom|符合条件的 第一个dom对象|
|document.querySelectorAll(选择器)|通过 选择器 获取 dom|符合条件的 所有dom对象 组成的类数组|
操作 DOM 元素有哪些方法
标题 | 描述 |
---|---|
createElement | 创建一个标签节点 |
createTexNode | 创建一个文本节点 |
cloneNode(deep) | 克隆一个节点,连同属性与值都一起复制,deep是 true时,连同后代节点一起复制,deep是false时,只复制当前节点 |
appendChild | 追加子元素 |
removeChild | 删除子元素 |
insertBefore | 将元素放到最前面 |
getAttribute | 获取节点属性 |
setAttribute | 设置节点属性 |
removeAttribute | 删除节点属性 |
createAttribute | 创建属性 |
element.attributes | 将属性生成类数组对象 |
DOM 的类型有哪几种
12种
元素节点/属性节点/文本节点/注释节点/CDATA节点/实体引用名称节点/实体名称节点/处理指令节点/文档节点/文档类型节点/文档片段节点/DTD节点
获取子元素节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
获取兄弟元素节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
获取父元素节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
新增节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
删除节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
克隆节点
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
两种定时器
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
删除定时器
- https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448
js 的作用域及作用域链
- 什么是作用域?
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。- 在javaScript中,作用域分为全局作用域和函数作用域
- 全局作用域:代码在程序的任何地方都能访问,window对象的内置属性都属于全局作用域
- 函数作用域:在固定的代码片段才能被访问。函数执行结束之后,函数内部定义的变量会被销毁
- 块级作用域:es6新增的,
- 在javaScript中,作用域分为全局作用域和函数作用域
- 什么是作用域链?
- 如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域
数组的splice和slice的区别
|方法|格式|解释|返回值|是否影响原数组|
|:—😐:—😐
|splice|数组变量.splice(起始,个数,新增元素)|删除/新增元素|被删除的元素形成的数组|影响原数组|
|slice|数组变量.slice(起始,终止)|截取数组(不包含终止位置)|截取后的数组|不影响原数组|
substr 和 substring 的区别
作用:两者都是截取字符串
相同点:如果只传 一个参数,两者都是截取字符串从当前下标开始直到字符串最后的位置
不同点:第二个参数代表的含义不同
- substr(起始,length) : 第二个参数是截取字符串的长度(从起始点截取到某个长度的字符串)
- substring(起始,index) : 第二个参数是截取字符串最终下标(截取两个位置之间的字符串,‘含头不含尾’)
var sisui = '123456789'
// 传一个参数
console.log(substr(2)) // '3456789'
console.log(substring(2)) // '3456789'
// 传2个参数
console.log(subste(2,5)) // '34567'
console.log(substring(2,5)) // '345'
includes比indexOf好在哪里
includes可以检测到NaN indexOf不能检测Nan
说说promise
一种解决异步编程的方案
- 对象的状态不不受外界影响。promise有三种状态:pending(进行中)、fufilled(已成功)、rejected(已失败)。
- 这三种状态只有异步操作可以决定当前属于哪一种状态。一旦状态从pending(进行中)进行到已成功/已失败状态后就不可更改
- 解决回调地狱问题
- 状态只改变一次,一次改变终身不变
什么是 async/await?解决了什么问题
用同步的方式,执行异步操作
async和await怎样解决报错
解决方法之一:try()catch()
js延迟加载的方案有哪些?
- setTimeout定时器延迟代码执行
- : 给 script标签加defer属性,这样的话就会让 加载 和 渲染后续文档元素过程将与 js代码文件的加载与执行并行执行(变成异步执行)
new操作符为什么能创建一个实例对象
1.创建一个空对象
2.继承构造函数的原型
3.this指向obj,并调用构造函数
4.返回对象
function myNew(fn,...args){
// 第一步:创建一个空对象
const sisui = {}
// 第二步:继承构造函数的原型
sisui.__proto__ = fn.protype
// 第三步:this指向obj,并调用构造函数
fn.apply(sisui,args)
// 第四步:返回对象
return sisui
}
什么是文档碎片
- 文档碎片: 一个容器,用于暂存创建的dom元素,使用**document.createDocumentFragment()**创建
- 作用:将需要添加的大量元素 先添加到文档碎片 中,再将文档碎片添加到需要插入的位置,大大减少dom操作,提高性能
// 声明一个 文档碎片容器 sisui
var sisui = document.createDocumentFragment();
for(var i=0; i<10000;i++>){
// 创建 标签节点
var xfq = document.crateElement('div');
// 创建 文本节点
var fjy = document.createTextNode('i');
// 文本节点 追加到 标签节点之中
xfq.appendChild(fjy);
// 把 标签节点 附加到 创建好的文档碎片 中
sisui.appendChild(xfq)
}
// 最后一次添加到 dom 中
document.body.appendChild(sisui)
什么是宏任务和微任务?有哪些是宏任务和微任务?执行顺序是怎样的?
- 微任务执行时机先于宏任务
- 具体实现顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
什么是跨域
由于浏览器的同源安全策略,在页面通过脚本程序访问接口的时候,如果存在域名、协议、端口任意一个不同,则为跨域
跨域的解决方法:
- 后台通过设置 cors 允许忽略同源策略的白名单通过
- 可以通过nginx代理实现跨域 (前台独立部署的时候使用这个)
- 前台开发时候通过设置 webpack的devServer.proxy来通过本地服务代理请求
// 注意:baseUrl要不要配置
// 一般是不用配置的,(使用了代理的话)
// 如果后台接口没有任何规则,可以通过自行设置 baseUrl 添加接口规则,进行拦截 (此时 需要用到 pathRewrite 里面的配置,在打包工具的设置代理那)
// 如果 前台代码独立部署,则需要修改 baseUrl 添加完整的域名
//
proxy:{
// 如果后台接口有规则
// 配置代理 希望拦截当前的host地址 替换为目标地址
// key: {} ; key为拦截的规则,可以是正则表达式
// '/这里写域名后面的那个路劲': {
'/(api|admin|lejuAdmin)': {
target: 'http://leju.bufan.cloud', // 80可以省略不写 所以代理地址和当前项目不是同一个地址!!
changeOrigin: true, // 如果是跨域 需要添加
pathRewrite: {
// 如果你需要,自然就会想到他,一般用不到这个
// '/admin': '/abc' /admin/xx/xx ==> /abc/xx/xx
}
},
// 如果后台接口没有规则,就是 域名 后面的路径一会/aah或者一行/cc或者/sja 之类的,没有 统一的路劲地址
// 如果后台接口没有任何规则,嗯可通过修改request.js中的baseUrl:'/goudan'添加规则进而拦截
// 平时开发一般用不到baseUrl,可以直接注释掉!
'/goudan': {
target: 'http://leju.bufan.cloud', // 80可以省略不写 所以代理地址和当前项目不是同一个地址!!
changeOrigin: true, // 如果是跨域 需要添加
pathRewrite: {
// 如果你需要,自然就会想到他
'/goudan': '/' // /goudan/xx/xx ==> /xx/xx
}
},
}
Object.defineProperty(target,key,options)
含义:数据劫持
/*
以下为Object.defineProperty(obj, key, descriptor)的详细使用说明;
- obj 要在其上定义属性的对象。
- ley 要定义或修改的属性的名称。
- descriptor 将被定义或修改的属性描述符。
使用参数
- value 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
- writable 当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 true。
- 存取描述符
- get 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为 undefined。
- set 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined。
*/
let obj = {
name:"张三"
}
//对obj中的name进行数据劫持
Object.defineProperty(obj,"name",{
configurable: true, //配置是否允许被删除 true(默认值) 可以被删除,false 不能被删除
enumerable: true, //配置是否允许被枚举 true(默认值) 可以被枚举,false 不能被枚举
// ....以下有更多使用方法
//重新给obj中的name赋值时触发
set(newVal){
console.log("这个想要设置的新值",newVal);
},
//获取obj中的name值时触发
get(){
return '给你返回的名字';
}
});
obj.name = 'hello'; //赋值
let a = obj.name; //获取
console.log(a);
什么是防抖?什么是节流
- 防抖:当事件被触发时,设定在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时,(比如:input框输入,搜索联想,用户在不断输入值时,用防抖来节约请求资源等等)但是只触发最后一次,以最后一次为准
- 防抖步骤:
- 第一步: 声明变量存储定时器
- 第二步: 每一次时间触发的时候,先不触发事件,先开启定时器,间隔事件之后在触发
- 第三步: 每一次触发的时候都需要将上一次的定时器清除,执行本次定时器
- 防抖步骤:
// 第一步: 声明变量存储定时器
let timeId = null;
// 第二步: 每一次时间触发的时候,先不触发事件,先开启定时器,间隔事件之后在触发
触发事件.addEventListener('click',function(){
// 第三步: 每一次触发的时候都需要将上一次的定时器清除,执行本次定时器
clearTimeout(timeId)
timeId = setTimeout(()=>{
console.log('防抖成功')
},3000)
});
- 节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。。频繁触发某个事件,但是只能每隔一段事件触发一次
- 节流步骤
- 第一步:声明一个 时间变量 存储时间戳 (开始的值为 null)
- 第二步:获取本次时间戳
- 第三次:判断 本次事时间戳 是否大于 设定的时间
- 第四次:把本次事件戳 存储 到声明的那个 时间变量
- 节流步骤
// 第一步:声明一个变量记录触发时间
let times = null;
let j = 1;
window.onmousemove = function(){
// 获取本次时间戳
let timeDate = Date.now()
// console.log(timeDate)
// 第二步:判断2次触发时间是否大于设定时间
if(timeDate - times >= 5000){
j++;
console.log('移动了'+j+'次')
times = timeDate;
}
}
// 函数封装好的 防抖节流
// 防抖
let btnsss = document.getElementById('numbesfr');
btnsss.addEventListener('click',one(three))
// 防抖
function one(fn){
// 声明一个定时器变量
let two = null;
// 返回一个函数
return function(){
// 判断
if(two !== null){
// 清空定时器
clearTimeout(two)
}
// 启动定时器
two = setTimeout(()=>{
// 执行回调
fn.apply(this,arguments);
// 重置定时器变量
two = null
},500)
}
}
function three(){
console.log('防抖成功')
}
// 节流
<button type="button" id="butt">节流按钮</button>
<script>
// 获取元素
let btn = document.getElementById('butt')
// 绑定点击事件
btn.addEventListener('click',sisui(fiy))
// 定义一个 sisui函数
function sisui(fn){
// 这里为什么要写 let xfq = true???
let xfq = true;
return function(){
if(!xfq){
// 对 xfq 取反,false 的话就不执行,直接跳出,否则就执行下面的代码
return
};
// 设置一个定时器
setTimeout(()=>{
// 执行回调 改变 this 指向后面为什么要跟 arguments
fn.apply(this,arguments)
// 把 xfq 的状态赋值成 true
xfq = true
},2000);
}
}
function fjy(){
console.log('节流成功')
}
</script>
一个函数,,传入参数不确定是几个,此时需要用到 ES6 的剩余参数
语法:function (…形参名){}
function fn(name,xfq,...sisui){
console.log(name)
console.log(xfq)
console.log(sisui)
}
fn('fjy',1,56) // fjy 1 [56]
fn('fjy',1,56,'askjdkj') // fjy 1 ['56','askjdkj']
箭头函数和普通函数的区别
- 区别:
- 箭头函数不可做为构造函数,不能使用new
- 箭头函数没有自己的 this
- 箭头函数没有 arguments对象
- 箭头函数没有原型对象
事件对象
- 事件对象的常见事件属性
- 返回触发的事件名称----触发事件类型 event.type
- 事件触发元素----触发事件的元素 event.target
- 获取鼠标触发点到 页面左上角的距离----e.pageX/e.pageY
document.body.onclick = function(){ console.log(event.type) //click console.log(event.target) // body console.log(event.pageX,event.pageY) // 点击位置距离左上角的距离 }
阻止默认行为 和 阻止事件冒泡
- 阻止默认行为
- event.preventDefault()
- 阻止事件冒泡
- event.stopPropagation()
事件委托
事件委托原理:利用事件的冒泡机制以及事件对象中可以准确获取触发事件元素的属性,将多个使用相同事件进行类似操纵的子元素的事件处理函数委托给父元素绑定的现象
注意点:事件委托*不能用this,因为此时 this交给了父元素,如果想要的话可以使用 e.target获取触发事件的元素
let ules = document.querySelecter('ul')
ules.onclick = function(){
console.log('触发事件的元素:',evevt.target)
if( event.target.tagName == 'Li' ){
event.target.style.color = '#f00'
}
}
深度遍历与广度遍历的区别
时间换空间,空间换时间
- 深度优先不需要记住所有节点,所有占用空间小,而广度遍历需要先记录所有节点占用空间大小
- 深度优先有回溯的操作(没有路走了需要回头)所有相对而言时间会长一些
- 深度优先采用的是 堆栈形式 先进后出
- 广度优先采用的是 队列像是 先进先出
forEach如何跳出循环
forEach是不能通过break或者return来实现跳出循环的。因为forEach的回调函数形成了一个作用域,在里面使用return并不会跳出,只会被当作continue
解决办法: try{}catch{}
function getItem(arr,id){
let item = null;
try{
arr.forEach(function(curitem,i){
if(curitem.id == id){
item = curitem
throw Error()
}
}) catch(e){
// 错误在这打印执行
console.log(e)
}
return item
}
}
js中如何重定向页面
-
- 使用location.href : window.location.href=“路由地址”
-
- 使用location.replace: window.location.replace(‘路由地址’)
原生js解决浏览器兼容的问题
- 样式初始化,尽可能的保证样式的统一性
- 使用不同的浏览器内核前缀
鼠标事件有哪些
事件 | 说明 |
---|---|
click | 鼠标点击 |
dbclick | 双击鼠标左键,右键是无效的 |
mousedown | 鼠标按下 |
mouseup | 鼠标弹起 |
mousemove | 鼠标移动 |
blur | 失去鼠标焦点触发 |
fovus | 获得鼠标焦点触发 |
mouseout | 鼠标离开 |
mouseover | 鼠标经过 |
键盘事件有哪些
事件 | 说明 |
---|---|
keydown | 某个键盘按下 |
keyup | 按下的按键松开触发 |
keypress | 键位按下触发 |
js中鼠标事件的各个坐标
offset系列常用属性 | 作用 |
---|---|
elem.offsetParent | 返回作为该元素带有定位的父级元素,如果父级都没有定位则返回Body |
elem.offsetTop | 返回元素相对带有定位父级元素上方的偏移 |
elem.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 |
elem.offsetWidth | 返回自身包括padding,边框,内容区的宽度,返回值不带单位 |
elem.offsetHeight | 返回自身包括padding,边看,内容区的高度,返回值不带单位 |
<div class="father">
<div class="son"></div>
</div>
<div class="w"></div>
<script>
// offset 系列
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 1.可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop);
console.log(father.offsetLeft);
// 它以带有定位的父亲为准 如果么有父亲或者父亲没有定位 则以 body 为准
console.log(son.offsetLeft);
var w = document.querySelector('.w');
// 2.可以得到元素的大小 宽度和高度 是包含padding + border + width
console.log(w.offsetWidth);
console.log(w.offsetHeight);
// 3. 返回带有定位的父亲 否则返回的是body
console.log(son.offsetParent); // 返回带有定位的父亲 否则返回的是body
console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
</script>
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷上去的上侧距离,返回的数值1不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回不带单位 |
elment.scrollWidth | 返回自身实际的宽度,不包含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际高度,不包含边框。返回数值不带单位 |
注意
元素被卷去的头部是:element.scrollTop。如果是页面被卷去的头部则是:window.pageYoffset
元素内容被卷走的高度 | 页面被卷走的高度 |
---|---|
element.scrollTop | windiw.pageYoffset |
代码演示
<div class="slider-bar">
<!-- 右边侧边栏 -->
<span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体部分</div>
<script>
window.addEventListener('load',function(){
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
var main = document.querySelector('.main')
var goBack = document.querySelector('.goBack');
// 获取 banner 距离顶部的 高度 offsetTop
var bannerTop = banner.offsetTop;
//获取 main 距离顶部的高度
var mainTop = main.offsetTop;
//当侧边栏变成固定定位后的 top 的值
var sliderTop = sliderbar.offsetTop - bannerTop;
console.log(sliderTop);
console.log(bannerTop);
// 页面滚动事件 scroll
document.addEventListener('scroll',function(){
// 滚动到 banner部分
if(window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 滚动到 主体部分
if(window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
})
</script>
client系列属性 | 作用 |
---|---|
element.clientWidth | 返回自身包括padding,内容区宽度,返回数值也是不带单位的。注意:不包含边框 |
element.clientHeight | 返回自身包括padding,内容区高度,返回数值不带单位。注意:不包含边框 |
element.clientTop | 返回元素上边框的大小 |
element.clientLeft | 返回元素左边框的大小 |
offset与client的区别
offset | client |
---|---|
offsetWidth 包含padding+border+width | clientwidth 包含padding+width 不包含 border |
offsetheight 包含padding+border+height | clientheight 包含padding+height 不包含 border |
代码案例
实现原生的 ajax 请求
// get
const sisui = XMLHttpRouters()
sisui.open('get',url,true) // 第三个参数表示是否异步 默认 true
sisui.onreadystatechange = function(){
if(sisui.readyState === 4){
if(sisui.status === 200 && sisui.status === 299){
console.log(sisui.response);
}
}
}
sisui.send() // 参数:null / 空
// psot
const sisui = XMLHttpRouters()
sisui.open('POST',url,true)
// post 的请求需要携带一个 请求头
sisui.setRequestHeader('Content-type','application/x-www-form-urlencoded')
sisui.onreadystatechange = function(){
if(sisui.readyState === 4){
if(sisui.status === 200 && sisui.status === 299){
console.log(sisui.response);
}
}
}
sisui.send(data) // 参数: 需要传入的数据