28.jquery:js库
简化版本的js,封装了现成功能的js代码。
jquery就是一些封装好了的现成的方法,供我们直接使用。
jquery能实现的js都能实现。
在使用 记得先引入jquery:在菜鸟教程上直接用jquery的绝对路径引入,jquery他自己已经延迟了先显示html在显示功能。
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
jquery优点:1.市面上仍有些老项目再用;2.jquery适合用于小型的项目,不用像vue react那样搭建框架;3.学习成本低,简单易学时间短。
1.常用的选择器
选择器后面跟方法的话,他会自动对前面选择器所选择到的每一个元素调用后面的方法,自带循环,并不是纯粹的返回一个对象,返回的是会与后面的方法自动循环相匹配的迭代器。在jquery中基本不用写for循环,jQuery不能与原生的js混用(选择器和方法),因为jQuery返回的不是一个元素对象。
$(“*”)选取所有元素
$(“#id”) $(“.class”) $(“p”) $(“h1,div,p”) $(“.intro,.demo”)
$(“选择器:even”)偶数位置的选择器的元素 $(“选择器:odd”)奇数
$(“选择器:first-child”)属于其父元素的第一个选择器 $(“选择器:last-child”) $(“选择器:nth-child(2)”)
$(“ul li:first”) 选取第一个ul元素中的第一个li元素
$(“div>p”)div直接子集的所有p $(“div p”) $(“div+p”)与div同级的相邻的下一个p $(“div~p”)与div同级的div后面的所有p
$(this) 选取当前html元素
$(“选择器:first”)第一个选择器的元素 $(“选择器:last”)
$(“div:eq(3)”)选中第4个div(从0开始) $(“div:gt(3)”) $(“div:lt(3)”)
应用场景:热搜前几条的颜色和后面几条的颜色
2.事件
不能像在js中的$(“#box”).οnclick=function(){}一样使用事件了,因为 jQuery返回的不是对象。
在jQuery中时使用事件时全部去掉on,直接用click、focus、blur、mousemove;不是赋值而是调用这些方法,要做的事情就以函数参数或匿名函数的形式传过去。另外,在jQuery中不能直接使用this了,要用$(this)
$("#box").click(function () {
$(this).hide()
})
$(“.txt”).on(“click”,”选择器(规定只能添加到指定的子元素上的事件处理程序)”,function(){alert(“被点击了”)}) 等同于js中的 事件对象.addEventListener()类
似于事件委托,当时元素本身不存在,之后动态添加后再绑定事件。
并且,在jq中事件委托时的this指向是准确的,不用e.target
ready()该事件在文档就绪后发生,当dom加载完毕且页面完全加载时发生ready事件。
$(window).resize()当调整浏览器窗口大小时触发,常用于响应式布局。
scroll()滚轮事件,前提是必须要有滚动条,里面的内容必须要比父级高。
3.效果:显示/隐藏的方法,有动画
hide(毫秒数/”slow/fast”) show(毫秒数/”slow/fast”) toggle(ms)—自动判断切换隐藏和显示的状态,隐藏变显示,显示变隐藏。实际上用js就是通过定时器来连续修改宽高。
在隐藏/显示的过程所需要的时间(也可以用fast和slow来控制快慢,但工作中常使用毫秒数)。
fadeOut() fadeIn() fadeToggle() 淡出/淡入,实现的过程效果不同,实际上在js中是通过定时器连续修改透明度。
slideUp() slideDown() slideToggle() 滑动,类似卷帘门的效果,实际上在js中式通过定时器来连续修改高度。
delay() 对动画效果有延迟
<body>
<button id="hide">隐藏</button>
<button id="show">显示</button>
<button id="toggle">自动切换</button>
<div class="box"></div>
<script>
$("#hide").click(function () {
$(".box").hide(1000)
})
$("#show").click(function () {
$(".box").show(1000)
})
$("#toggle").click(function () {
$(".box").toggle(1000)//自动切换,显示变隐藏,隐藏变显示
})
</script>
</body>
4. 效果:读取/修改样式,有动画
在引号中的没有歧义该用横线用横线,,在引号外的要用驼峰。
修改单个样式:css(“属性名”,”属性值”)
读取单个样式:css(“属性名”) 内部样式和外部样式的也可以拿到,在之前的js中很麻烦需要document.defaultView.getComputedStyle(对象名,null)才可以。
修改多个样式:css({
属性名:”属性值”;
属性名:”属性值”
})
添加样式一般都用添加/删除类的方法
animate(cssObj,ms) 用法同css方法,唯一的不同是他的样式变化都有动画效果的。有动画有过程的去改样式。但是它不支持修改颜色的样式,也不支持css3中的transform属性。
5. 效果:stop() 停止当前动态效果
显示/隐藏、animate修改样式这些效果会出现多次点击鼠标后再松开时还在继续运行的情况,为了解决这种情况,可以在有动画之前先把之前存在的动画停止再重新开始,这样就只执行了最后一次,类似于防抖。
$("#hide").click(function () {
$(".box").stop().slideToggle()
})
6. 效果:jquery允许链式调用
允许你对连续执⾏多个jq⽅法,会按照绑定的顺序依次执⾏
注意:在使用链式调用时一定要保证操作的主体不能发生变化。
7. 效果:jquery的支持callback(回调函数)
回调函数在当前动画100%完成之后执行,作为显示/隐藏/animate动画的第二/三个参数出现。
$("#hide").click(function () {
$(".box").slideUp(1000, function () {
alert(1)
})
})
8.DOM操作相关的方法
1 获取添加内容和属性
读取:text() 修改:text(“要修改的内容”) 等同于js中的innerText()获取文本内容
html() html(“要修改的内容”)
val() 等同于表单的value
attr("src") 获取或添加属性 等同于setAttribute() getAttribute()
attr()与css()的区别:css()用于修改和设置元素的style样式属性,如color、background-color,而attr()用于修改和设置元素标签本身自带的自身的属性,如src、href等。
2 添加元素
append(“html代码”) 在被选元素的结尾添加内容 –父子级 等同于appendChild(对象)
prepend() 在被选元素的开头添加内容—父子级
after() 在被选元素之后添加内容---同级
before() 在被选元素之前添加内容---同级
3 删除元素
remove() 删除被选元素及其子元素 想删谁就对谁调用remove(),不需要再通过父级去删除了---js:父级对象.removeChild(子元素对象)
empty() 从被选元素中删除子元素,把其内容删掉,本身还存在
4 css添加移出类名
addClass(“类名”) removeClass(“类名”) toggleClass(“类名”)
可用于动态地增加/删除属性。
5 尺寸
width() height() 元素本身的宽高
innerWidth() innerHeight() 包含内边距padding的宽高
outerWidth() outerHeight() 包含内边距padding和边框border的宽高
outerWidth(true) outerHeight(true) 包含内边距padding、边框border和外边距margin的宽高
9.jq的遍历方法
找父级:
parent(空/“选择器”):返回的是所选元素的直接父级;/ 如果加上选择器就要求是该选择器的父级。
parents(空/“选择器”) :返回的是所选元素的所有父级;/ 如果加上选择器就要求是该选择器的父级。
parentsUntil(“选择器”) :返回的是到固定区间的父级;不包含要找到的选择器。
找子级:
children(空/".box") :返回的是所选元素的所有直接子级。。
find("div") :这个的选择器必须要有。返回的是所选元素的所有子级。要是想找所有子级的话传 “*“ 即可。
找同级:不包含自己的
siblings(空/“css选择器”) :返回的是所选元素的所有同级元素(上下都选中)。
next(空/”选择器”) :返回的是所选元素的紧挨着的下一个同级
nextAll() :返回的是所选元素的所有的下面的同级
nextUntil(“选择器”) :直到下面的某个固定区间内得所有同级,不包含要找到的选择器
prev() :返回上一个同级
prevAll() :返回上面的所有同级
prevUntil():直到上面的某个区间内的所有同级
筛选:
first():返回被选元素的首个元素。完全等价于前面的 :first 选择器,不能传参。
last()
eq(数字1) 索引号从0开始
filter(“选择器”):找到符合指定条件的元素
not(“选择器”):找到除了not里面条件的元素
每个:
each(function(){})为每个匹配元素规定要运行的函数。类似于js中内置对象的foreach(),对每一项要进行的操作
10. jquery ajax
1 基本内容
$.ajax({
url:”请求地址”,
type:”请求方式get/post”,
data:{key:value,key2:value2},
dataType:”json”,//预期的后端返回的数据类型
timeOut:5000,//请求超时的时间,可选
success:function(data){//请求成功后的回调函数
//data就是后端返回的数据
},
error:function(err){//请求失败后的回调函数
}
})
$.get(url,data,function(data){},dataType)
$.post()
如: $.get("https://restapi.amap.com/v3/weather/weatherInfo", {key: "e17d487bab9ea0753d34762321979db4",city: $("#city").val(),extensions: "all"}, function (data) {console.log(data)}, "json")
2 使用jq的ajax实现的天气查询案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<input type="text" id="city">
<div id="txt"></div>
<script>
$("#city").blur(function () {
$.ajax({
url: "https://restapi.amap.com/v3/weather/weatherInfo",
type: "get",
data: {
key: "e17d487bab9ea0753d34762321979db4",
city: $("#city").val(),
extensions: "all"
},
dataType: "json",
success: function (data) {
console.log(data)
}
})
})
</script>
</body>
</html>
3 跨域问题:
浏览器的同源策略 同协议 同域名 同端⼝号
解决跨域:jsonp 请求代理
11.其他属性和方法
index():返回指定元素相对于其他指定元素的index位置。
属性:length返回被选元素的数量,常用于判断所选元素是否存在。
hasClass(“类名”):判断该选择器是否存在该类
12.jQuery实战1:手风琴菜单(点开一个下拉展开)
【jQuery插件库】
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #2d2c41;
}
li {
list-style: none;
}
div,
li {
width: 200px;
height: 50px;
background-color: #fff;
text-align: center;
line-height: 50px;
color: #b63b4d;
}
li {
background-color: #444359;
color: #fff;
border: 1px solid #4b4a5e;
}
ul {
display: none;
}
p {
display: inline-block;
}
.arrow {
/* 箭头旋转 */
transform: rotate(90deg);
transition: all 1s;
}
.active {
/* //旋转太生硬,希望有个动画,但是animate不支持css3中的transform属性 */
transform: rotate(270deg);
}
</style>
</head>
<body>
<div>
<p>我是一级</p>
<p class="arrow">></p>
</div>
<ul>
<li>我是1-1</li>
<li>我是1-1</li>
<li>我是1-1</li>
<li>我是1-1</li>
</ul>
<div>
<p>我是二级</p>
<p class="arrow">></p>
</div>
<ul>
<li>我是2-1</li>
<li>我是2-1</li>
<li>我是2-1</li>
<li>我是2-1</li>
</ul>
<div>
<p>我是三级</p>
<p class="arrow">></p>
</div>
<ul>
<li>我是3-1</li>
<li>我是3-1</li>
<li>我是3-1</li>
<li>我是3-1</li>
</ul>
<div>
<p>我是四级</p>
<p class="arrow">></p>
</div>
<ul>
<li>我是4-1</li>
<li>我是4-1</li>
<li>我是4-1</li>
<li>我是4-1</li>
</ul>
<script>
//点击谁就显示谁的二级
$("div").click(function () {
//如果是先所有的ul收起来,再将选中的展开,由于每次都先将所有的收起来了,下次一定是展开。
// $("ul").slideUp()
//正确的应该是将除了当前的ul外其他的收起来
// $(this).next().siblings("ul").stop().slideUp()
// $(this).next().stop().slideToggle()
$(this).next().stop().slideToggle().siblings("ul").slideUp()
//点击让箭头旋转
$(this).children(".arrow").toggleClass("active")
// 让除了自己之外的箭头移除旋转
$(this).siblings("div").children(".arrow").removeClass("active")
/*bug1:再次点击当前的永远是展开的样子。因为先所有的ul收起来,再将选中的展开,由于每次都先将所有的收起来了,下次一定是展开。
bug2:连续多次点击出现抖动。解决方法:再做操作之前先stop()
bug3:观察代码发现都是对$(this).next()做操作,由于jQuery支持链式调用,把他们放在一起写就行。
注意:调用的主体不能发生变化,第一句是$(this).next().siblings("ul")做操作,第二句是对$(this).next()做操作,对如果将下面的放到上面则是错误的
注意:内联元素不支持旋转动画
注意:动画效果的样式animate不支持css3中的transform属性。解决方法:使用css3中的动画效果的transition属性
bug4:箭头变化了但回不来。解决方法:将要修改的属性写成类,自动切换调用该类
bug5:点击自己再点击别人导致全部收起时的箭头有的朝上有的朝下
*/
})
</script>
</body>
</html>
13.jQuery实战2:选项卡
index()找到点击元素所处的位置。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
.tab {
float: left;
width: 80px;
height: 50px;
background-color: gray;
text-align: center;
line-height: 50px;
color: white;
margin-right: 5px;
}
.content {
width: 335px;
height: 300px;
background-color: pink;
display: none;
}
.act {
display: block;
}
.red {
background-color: red;
}
</style>
</head>
<body>
<div class="tab">体育新闻</div>
<div class="tab">科技新闻</div>
<div class="tab">电影新闻</div>
<div class="tab">娱乐新闻</div>
<div style="clear: both;"></div>
<div>
<div class="content act">
<p>我是体育新闻1</p>
<p>我是体育新闻2</p>
<p>我是体育新闻3</p>
</div>
<div class="content">
<p>我是科技新闻1</p>
<p>我是科技新闻2</p>
<p>我是科技新闻3</p>
</div>
<div class="content">
<p>我是电影新闻1</p>
<p>我是电影新闻2</p>
<p>我是电影新闻3</p>
</div>
<div class="content">
<p>我是娱乐新闻1</p>
<p>我是娱乐新闻2</p>
<p>我是娱乐新闻3</p>
</div>
</div>
<script>
$(".tab").click(function () {
// 所点击元素相对于同类型元素的所在位置
var index = $(this).index(".tab")
//把除了当前的所有的content隐藏
// $(".content").eq(index).siblings().hide()
// //找到与点击元素所对应位置的content,设置可见
// $(".content").eq(index).show()
$(".content").eq(index).show().siblings().hide()
//先把除了自己的菜单颜色都恢复原色
// $(this).siblings(".tab").css("background-color", "gray")
// //选中谁把谁的菜单的背景色变为红色
// $(this).css("background-color", "red"
$(this).addClass("red").siblings(".tab").removeClass("red")
})
</script>
</body>
</html>
14.jQuery实战3:淘宝筛选菜单(中等以上难度70%)
同系列的只能选择一个:在添加元素的时候将所选元素的父级的类名也添加进去,添加前先通过length判断下面是否有该系列的,若有将其替换;若没有直接添加即可。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
.clear {
clear: both;
}
.wrap {
width: 800px;
margin: auto;
margin-top: 100px;
}
.type,
.tag,
.tips {
float: left;
}
.tag li {
float: left;
/* 文字块的大小不要给固定大小,要用文字的大小撑开 */
padding: 5px 10px;
margin-right: 15px;
color: #039;
border-radius: 2px;
}
.tag .act {
background-color: #ff6600;
color: #fff;
}
.wrap>div {
margin-bottom: 10px;
}
li:hover {
background-color: #f3edc2;
color: #ff6600;
}
</style>
</head>
<body>
<div class="wrap">
<div class="coat">
<p class="type">上装:</p>
<ul class="tag">
<li class="all act">全部</li>
<li>针织衫</li>
<li>毛呢外套</li>
<li>T恤</li>
<li>羽绒服</li>
<li>棉衣</li>
<li>卫衣</li>
<li>风衣</li>
<div class="clear"></div>
</ul>
<div class="clear"></div>
</div>
<div class="pants">
<p class="type">裤装:</p>
<ul class="tag">
<li class="all act">全部</li>
<li>牛仔裤</li>
<li>小脚/铅笔裤</li>
<li>休闲裤</li>
<li>打底裤</li>
<li>哈伦裤</li>
<div class="clear"></div>
</ul>
<div class="clear"></div>
</div>
<div class="dress">
<p class="type">裙装:</p>
<ul class="tag">
<li class="all act">全部</li>
<li>连衣裙</li>
<li>半身裙</li>
<li>长袖连衣裙</li>
<li>中长款连衣裙</li>
<div class="clear"></div>
</ul>
<div class="clear"></div>
</div>
<div class="selected">
<p class="type">已选条件:</p>
<p class="tips">暂时没有选择过滤条件</p>
<ul class="tag">
<!-- 在这里的li被清空了,当有选择的时候再往里面加 -->
</ul>
<div class="clear"></div>
</div>
</div>
<script>
//点谁谁高亮,li的绑定事件只针对当前已经存在的li,后面再动态添加的li不会被绑定这些事件
$("li").not(".all").click(function () {
//1 实现点击元素高亮
$(this).addClass("act").siblings("li").removeClass("act")
//2 将选中项加入到下面的已选条件中 添加元素
//先获取到当前所选项的内容
var txt = $(this).text()
//获取其父级的类名,以便于知道是哪个系列的,方便之后限制同类别的不能重复添加
var parName = $(this).parent().parent().attr("class")
//如果所选板块所属系列在下面没有,则直接添加;否则就要找到下面对应的元素将其替换
//通过所选元素的length属性判断下面是否有同系列的
if ($(".selected ." + parName).length) {
// 下面已经有了,找到并将其文本替换
$(".selected ." + parName).text(txt)
} else {
//下面没有直接添加即可
$(".selected .tag").append("<li class='act " + parName + "'>" + txt + "</li>")
}
//目前同系列的可以重复添加,要解决这个问题:在添加时通过其父级的父级的类名,先判断下面有没有这个系列,如果有将其替换,如果没有直接添加
//3 当ul类名没有li时才显示提示信息,若有则不显示提示信息
toggleTips()
})
//4 通过全部来做对应项的删除
$(".all").click(function () {
var preName = $(this).parent().parent().attr("class")
$(".selected ." + preName).remove()
//回归高亮状态
$(this).addClass("act").siblings("li").removeClass("act")
toggleTips()
})
/* 5 通过点击下面的已选条件实现删除
事件直接绑定不了,因为这一段代码在一进入页面就执行了,但此时获取不到元素。
解决方法:事件委托,给其父级绑定即可,在jq中的on方法
*/
$(".selected").on("click", "li", function () {
$(this).remove()
//找到所对应系列的全部,回归全部,拿到类名可以通过字符串切割split,也可以substring
var cla = $(this).attr("class").split(" ")[1]
//找到其父级的父级类名是cla的全部,因为分类中有cla,筛选条件中也有cla,要将他们加以区分,只选择wrap的直接子集的cla
//不是找直接子集了,找所有子集用find("选择器")
$(".wrap>." + cla).find(".all").addClass("act").siblings("li").removeClass("act")
toggleTips()
})
//封装:筛选条件的提示部分
function toggleTips() {
if ($(".selected li").length) {
$(".tips").hide()
} else {
$(".tips").show()
}
}
</script>
</body>
</html>
15.jQuery实战4:移动端购物车(高难度90%)
圆圈和勾选使用阿里巴巴矢量图标库。
在全选按钮那里有点小坑,需要分别使用更新前的勾的数量和更新后的勾的数量。
在遇到问题时通过控制台输出变量、审查元素的class名字的更改。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
.wrap {
width: 800px;
margin: 100px auto;
}
.product {
border: 1px solid;
border-left: none;
border-right: none;
padding: 20px;
}
.price {
color: red;
font-size: 20px;
}
.pro_name {
font-weight: bold;
}
.total {
margin-top: 100px;
}
.deal {
width: 80px;
height: 30px;
}
.act {
background-color: #ed535f;
color: #fff;
}
</style>
</head>
<body>
<div class="wrap">
<div class="product">
<img class="sel un" src="images/unselected.png">
<span class="pro_name">iPhone14</span>
<span class="price">¥10000</span>
<button>-</button>
<span class="count">1</span>
<button>+</button>
<img src="images/delete.png" class="delete">
</div>
<div class="product">
<img class="sel un" src="images/unselected.png">
<span class="pro_name">粉底液</span>
<span class="price">¥300</span>
<button>-</button>
<span class="count">1</span>
<button>+</button>
<img src="images/delete.png" class="delete">
</div>
<div class="product">
<img class="sel un" src="images/unselected.png">
<span class="pro_name">ps5</span>
<span class="price">¥5000</span>
<button>-</button>
<span class="count">1</span>
<button>+</button>
<img src="images/delete.png" class="delete">
</div>
<div class="product">
<img class="sel un" src="images/unselected.png">
<span class="pro_name">键盘</span>
<span class="price">¥400</span>
<button>-</button>
<span class="count">1</span>
<button>+</button>
<img src="images/delete.png" class="delete">
</div>
<div class="total">
<img src="images/unselected.png">
<span class="total_select">已选(0)</span>
<span class="total_price">¥0</span>
<button class="deal" disabled="true">结算</button>
</div>
<script>
/*我的第一步:先做圆圈,已选个数(在切换勾圈时加减已选数量)、总金额和结算按钮发生变化
第二步:加减按钮,数量和总金额变化
第三步:已选按钮,总金额变化,勾圈变化
第三步:删除按钮,alert弹窗提示,删除该行,总金额变化
*/
/*注意:勾圈是否被勾选的判断方法:通过类名un al ,在更改勾圈的时候也要同时更改其类名un 和 al*/
//1加减号功能
$(".product button").click(function () {
var sign = $(this).text()
var org = Number($(this).siblings(".count").text())
if (sign == "+") {
$(this).siblings(".count").text(org + 1)
} else if (org > 1) {
$(this).siblings(".count").text(org - 1)
}
totalPrice()
})
//2 点击勾圈事件
$(".product .sel").click(function () {
//2-1 实现勾圈的切换
//通过判断是否存在un的类名来判断是否被勾选。已经被勾选的类名为al,未被勾选的类名是un,默认是没有被选中的
if ($(this).hasClass("un")) {
$(this).attr({
"src": "images/selected.png",
"class": "sel al"
})
} else {
$(this).attr({
"src": "images/unselected.png",
"class": "sel un"
})
}
//2-2 已选数量的变化
//通过勾圈的al类名的数量来判断勾选了几个商品
productCount()
//2-3 计算总价
totalPrice()
})
//3 结算
//封装计算总价的部分,勾圈按钮、全选按钮、数量加减按钮、删除按钮在点击时都需要进行计算总价,因此将其封装起来。
//我原来的想法是每个部分变化后单独算总价,但是总价一定是所有已选商品的单价*数量,不会发生变化的,哪怕是删掉了在这些商品,因此我们计算总价的函数就是 找到所选商品 将其单价*数量 再将所有的相加
function totalPrice() {
//找到每一个选中商品,用他的 数量*单价,再把它们的每个总价求和,
//要对每个选中的商品都要求总价,要用到循环
//先拿到选中的商品
var total = 0;//总价
$(".al").each(function () {
var price = Number($(this).siblings(".price").text().substring(1))
var count = Number($(this).siblings(".count").text())
total += price * count
})
$(".total_price").text("¥" + total)
}
//封装计算已选商品数量的函数,在点击勾圈、删除商品、全选按钮的时候都需要更改已选的数量
function productCount() {
//通过勾圈的al类名的数量来判断勾选了几个商品
var len = $(".al").length
var slen = $(".product").length
//如果全选了既要更改已选数量,也要更改全选按钮
$(".total_select").text(len == slen ? "全选" : "已选(" + len + ")")
$(".total img").attr("src", len == slen ? "images/selected.png" : "images/unselected.png")
//在实际工作中尽量避免使用if else,显得有点低级,像这种两种情况的尽量用三目运算符
// if (len == $(".product").length) {
// $(".total_select").text("全选")
// } else {
// $(".total_select").text("已选(" + len + ")")
// }
}
//4 垃圾桶 删除后移除当前商品、总价、已选要变化
$(".delete").click(function () {
// alert("你确定要删除当前商品?")
$(this).parent(".product").remove()
totalPrice()
productCount()
})
//5全选功能 有小坑坑坑坑坑坑坑
//在点击全选之前,如果全部商品已经被选中,则此时全选按钮被勾选,将其封装在了判断已选商品数量中了
//若在此时再点击全选按钮,则全部取消勾选;若商品未被全部勾选则点击全选之后全选按钮被勾且全部商品被勾,
//另外,已选数量和总价也会发生变化
$(".total img").click(function () {
// 判断全选按钮是否要改变使用的是之前的勾圈数量,根据当前勾圈的数量来决定要将其全部换成勾还是全部换成圈
var len = $(".al").length//更新前的勾的数量
var slen = $(".product").length
$(".sel").attr({
"src": len == slen ? "images/unselected.png" : "images/selected.png",
"class": len == slen ? "sel un" : "sel al"
})
//判断已选数量,我们要使用的是做了全选修改之后的勾圈数量,与前面获取到的不一样了
len = $(".al").length//更新后的数量,
$(".total_select").text(len == slen ? "全选" : "已选(" + len + ")")
$(this).attr("src", len == slen ? "images/selected.png" : "images/unselected.png")
totalPrice()
})
</script>
</body>
</html>
16.jQuery实战5:抽奖大转盘
缩放:background-size:cover;
要是想要让某一个区间内的奖品中将几率大一些,可以将这些度数放在数组中,中奖概率高的就在数组中多出现几次,用二维数组来存储度数。
下次转的角度是要在上次转动角度的基础上再转的,否则下次的角度转动会在上次已经转动的基础上小幅度的转动的,而我们要求每次转动都必须是3-4圈。
前提要求:抽奖转盘的宽和高是一样的,否则在旋转时会出现问题。
<DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
.bg {
width: 600px;
height: 600px;
background: url(images/bg1.png) no-repeat center;
background-size: cover;
/* 将图片按等比例缩放
cover 可能图像的部分内容无法显示在背景中
contain 宽高完全适应背景大小 缩放至图像完全显现出来 */
}
.start {
width: 200px;
position: absolute;
top: 165px;
left: 196px;
}
.wrap {
position: relative;
}
</style>
</head>
<body>
<div class="wrap">
<div class="bg"></div>
<img src="images/start1.png" class="start">
</div>
<script>
// var startNum = 1080
var startNum = 0
$(".start").click(function () {
//1080-1440 转3-4圈
// var d = Math.floor(Math.random() * (1440 - 1080 + 1)) + 1080
// var d = Math.floor(Math.random() * (startNum + 360 - startNum + 1)) + startNum//startnUm=1080版本
var d = Math.floor(Math.random() * (1440 - 1080 + 1)) + 1080 + startNum
console.log(d)
$(".bg").css({
"transform": "rotate(" + d + "deg)",
"transition": "all 2s"
})
startNum = d
//下次转动角度必须在上次角度的基础上再转动3-4圈。
// startNum = d + 1080
var deg = d % 360
setTimeout(function () {
if (deg > 0 && deg < 45) {
alert("恭喜您抽中50积分!!!")
} else if (deg < 90) {
alert("恭喜您抽中手表!!!")
} else if (deg < 135) {
alert("恭喜您抽中6积分!!!")
} else if (deg < 180) {
alert("恭喜您抽中100元红包!!!")
} else if (deg < 225) {
alert("恭喜您抽中50元话费券!!!")
} else if (deg < 270) {
alert("恭喜您抽中红包10元!!!")
} else if (deg < 315) {
alert("恭喜您抽中苹果手机一部!!!")
} else if (deg < 360) {
alert("恭喜您抽中30元现金券!!!")
}
}, 2200)
})
</script>
</body>
</html>
17.jQuery实战5-1:增加概率的抽奖大转盘
<DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<style>
.bg {
width: 600px;
height: 600px;
background: url(images/bg1.png) no-repeat center;
background-size: cover;
}
.start {
width: 200px;
position: absolute;
top: 165px;
left: 196px;
}
.wrap {
position: relative;
}
</style>
</head>
<body>
<div class="wrap">
<div class="bg"></div>
<img src="images/start1.png" class="start">
</div>
<script>
var arr = [[0, 45], [46, 90], [91, 135], [136, 180], [181, 225], [226, 270], [271, 315], [316, 360],
[271, 315], [271, 315], [271, 315], [271, 315]]
//从0-7是随机,其中6是苹果手机,8到arr.length-1是苹果手机
var startNum = 0//旋转度数初始值
var r = 0//随机取数的初始值
$(".start").click(function () {
//从0到arr.length-1取数
var random = Math.floor(Math.random() * (arr.length - 1 - 0 + 1)) + 0
//从arr[random][0]到arr[random][1]之间再取数
var ran = Math.floor(Math.random() * (arr[random][1] - arr[random][0] + 1)) + arr[random][0]//0-360之间取到的数
var d = startNum + 1080 + (ran - r)
//在上次旋转后的基础上先原地转三圈,再在上次最后一圈随机值的基础上转到这次的随机取值
$(".bg").css({
"transform": "rotate(" + d + "deg)",
"transition": "all 2s"
})
startNum = d
r = ran
var deg = d % 360
setTimeout(function () {
if (deg > 0 && deg < 45) {
alert("恭喜您抽中50积分!!!")
} else if (deg < 90) {
alert("恭喜您抽中手表!!!")
} else if (deg < 135) {
alert("恭喜您抽中6积分!!!")
} else if (deg < 180) {
alert("恭喜您抽中100元红包!!!")
} else if (deg < 225) {
alert("恭喜您抽中50元话费券!!!")
} else if (deg < 270) {
alert("恭喜您抽中红包10元!!!")
} else if (deg < 315) {
alert("恭喜您抽中苹果手机一部!!!")
} else if (deg < 360) {
alert("恭喜您抽中30元现金券!!!")
}
}, 2200)
})
</script>
</body>
</html>
20. ES6(ES5.0增加了新特性)
1.let 声明的变量没有提升 不允许重复声明 只在块级作⽤域内有效
暂时性死区:在⼀个块级作⽤域内,如果⽤let声明了某个变量,那么该变量就⾃动绑
定了该作⽤域,该作⽤域就形成了⼀个封闭的作⽤域。
2.const 声明常量
3.模板字符串 `` 允许换⾏ 可以直接写变量,变量要写在${}⾥⾯
4.扩展运算符 ... 将数组或者类数组结构拆分为参数序列
5.函数的扩展 箭头函数 let fn=(m)=>{return m}
如果只有⼀个参数,可以省略⼩括号,如果想返回⼀个值,return连同⼤括号⼀起省略,如
果要返回⼀个对象的话,对象外⾯要套括号避免歧义
箭头函数中的this指向:指向的是函数定义时所在的对象
箭头函数不允许当作构造函数,也就是不能new
6.变量的解构赋值
let [变量1,变量2,变量3]=[值1,值2,值3]
7.对象的扩展
如果对象的属性值是个变量,并且该变量名跟属性名⼀样,那么可以省略为⼀个,
对象中的函数属性,可以省略:function
8.对象的解构赋值
let {name,age}={name:"⼩红",age:18}
- let obj={
- name:"⼩红",
- age:18,
- sex:"girl",
- address:"北京"
- }
- function fn({name,age}){
- console.log(name+age)
- }
- fn(obj)
如果出现多层嵌套的对象需要解构
|
9.Symbol 新的原始数据类型
symbol:类似于字符串的,值永远是独⼀⽆⼆的
10.Set 类似与数组的⼀种数据结构
set数据结构值都是唯⼀的
add()
delete()
has()
size()
keys()遍历属性名
values()遍历属性值
entries()遍历属性名和属性值的
11.Map类似于对象的⼀种数据结构
1 let map=new Map([['name','⼩明'],[12,18],[[1,2,3],567]])
应该叫值值对
map的属性名不局限于字符串
set(“属性名”,"属性值")
get("属性名")取值
has("属性名")
delete("属性名")
clear()
size()
keys()遍历属性名
values()遍历属性值
entries()遍历属性名和属性值的
12. Promise
异步编程:定时器 ajax
解决⽅案: 回调函数
then()⽅法返回的是⼀个全新的promise实例
- function fn() {
- var a=new Promise(function(resolve,reject){
- setTimeout(function(){
- console.log(666);
- resolve("haha")
- },2000)
7
- })
- return a
- }
- function fn2(){
- var b=new Promise(function(resolve,reject){
- setTimeout(function(){
- console.log(777);
- resolve("呵呵")
- },1000)
17
- })
- return b
- }
21
- fn().then((res)=>{
- console.log(123,res);
- return fn2()
- }).then((res)=>{
- console.log(99,res)
- })
13.async await函数:
async 是“异步”的简写,⽽ await 可以认为是 async wait 的简写。所以应该很好理解 async ⽤于申明⼀个 function 是异步的,⽽ await ⽤于等待⼀个异步⽅法执⾏完成。
async函数会返回⼀个promise对象,如果在函数中return⼀个值,那么该值会通过
Promise.resolve()传递出去
⼀般来说,都认为 await 是在等待⼀个 async 函数完成。不过按语法说明,,await 等待的是⼀个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。
因为 async 函数返回⼀个 Promise 对象,所以 await 可以⽤于等待⼀个 async 函数的返回值⸺这也可以说是 await 在等 async 函数,但要清楚,它等的实际是⼀个返回值。注意到
await 不仅仅⽤于等 Promise 对象,它可以等任意表达式的结果,所以,await 后⾯实际是可以接普通函数调⽤或者直接量的。所以下⾯这个示例完全可以正确运⾏
await 等到了它要等的东⻄,⼀个 Promise 对象,或者其它值,然后呢?我不得不先说, await 是个运算符,⽤于组成表达式,await 表达式的运算结果取决于它等的东⻄。
如果它等到的不是⼀个 Promise 对象,那 await 表达式的运算结果就是它等到的东⻄。
如果它等到的是⼀个 Promise 对象,await 就忙起来了,它会阻塞后⾯的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
案例
1 function fn() { 2 | var a=new Promise(function(resolve,reject){ | |
3 | setTimeout(function(){ | |
4 | console.log(666); | |
5 | resolve("haha") | |
6 | },2000) | |
7 8 | }) | |
9 | return a | |
10 | } | |
11 | async function fn2(){ | |
12 | let b=await fn(); | |
13 | console.log("执⾏",b) | |
14 | } | |
15 | fn2() |
14.module
14.module语法
(1)export var a=1 可以导出任何的变量声明语句或者函数声明语句
export {a,b,fn}
export var a=1 ⼀定是完整的变量声明语句
import {a} from "./a.js"
(2)改名字
导出 export {原名 as 新名}
引⼊ import {导出的名字 as 新名}
(3)默认导出:从前⾯的例⼦可以看出,使⽤import命令的时候,⽤户需要知道所要加载的变量名或函数名,否则⽆法加载。但是,⽤户肯定希望快速上⼿,未必愿意阅读⽂档,去了解模块有哪些属性和⽅法。
export default:
⼀个⽂件只能有⼀个默认导出
引⼊的时候不需要⼤括号
(4)整体引⼊
import "./common.css"
import "./../js/main.js"
易混点:
1、变量未声明直接进行了赋值,js会自动帮忙声明为全局变量。
变量声明提前:当遇到变量声明时,会出现将变量声明提前到当前作用域最顶部的情况,只是变量声明提前了,变量赋值并没有提前。
函数提前:函数整体提前到当前作用域的最顶部。
2、关于逻辑运算的返回值问题:返回的不只可以是布尔值,也可以是正常的数值,关键是看输入的内容是什么就行。
3、函数声明方法1:function 函数名(形参){
代码段//想⼲的事
}
函数声明方法2(声明变量的方式声明函数):var 函数名=function(参数){代码段} 要注意声明和调用的顺序,先声明,后调用
这两种函数声明的方法实现的效果是一样的,但是在函数提升上是不一样的。按照函数方式声明时可以将函数整体提前;而按照变量的方式声明的函数只是将函数声明提前了,函数的整体并没有提前。因此在使用以函数方式声明的方法时声明与调用的顺序都可以,而使用以变量方式声明的方法时一定要先声明后调用,否则会出现函数提升的错误。
4、在数组中做判断条件时要将时同一类型的放在else中,不是同一类型的不要放在else中。
另外,在用if判断时不要一开始就做比较判断,先做其他类型的判断,因为不是数字类型的数据也是可以做比较大小的,如number 和string类型的之间可以进行隐式类型转换。
5、原始数据类型数据之间判断是否相等:直接比较大小即可
引用数据类型的数据之间比较是否相等:要判断指针是否相等,(即使里面的数据是一样的),只有复制赋值的是相等的。
var arr = [1, 2, 3, 4]
var arr2 = [1, 2, 3, 4]
console.log(arr == arr2)//输出结果:false
//因为变量中存储的是地址值即指针,是不一样的,尽管堆中存储的数据是一样的
var arr = [1, 2, 3, 4]
var arr2 = arr
console.log(arr2 == arr)//输出结果:true
如:console.log({}=={})的结果为:false。因为{}是空对象,存储的地址是不一样的。
6、js中函数的参数传递是按值传递的,只是将变量中要传递的数据或指针复制一份作为实参传给函数,原来的数据并不会被修改。
复制的时候复制的是栈里面的内容。
按引用传递:实参和形参共用一份数据,其中一个对其更改了,另一个也会更改。
7、函数也是变量,也可以作为实参传递。
var a = function () {
alert("1")
}
function fn(a) {
a()
}
fn(a);
//结果为:弹窗“1”
8、变量代表的是角标还是数据
(1)对象循环访问:变量key代表的是每一个属性名
var obj = { name: "lili", age: 18, gender: "女" }
//变量key代表的是属性名
for (var key in obj) {
console.log(key)//输出结果:name age gender
}
for (var key in obj) {
console.log(obj[key])//输出结果:'lili' 18 '女'
}
(2)内置对象Array对象的map()方法:map(function(item,index){return 新值}) 变量item代表的是每一项的数据,变量index代表的是角标,只有一个参数时代表的是item数据。
var arr = [1, 4, 5, 5]
var narr = arr.map(function (item) { return "星期" + item })
console.log(narr)
//输出结果:['星期1', '星期4', '星期5', '星期5']
9、函数参数是函数的方法(数组Array对象的方法)
(1)map(function(item,index){return 新值}) return后的新值就是新数组的每一项,返回改变后的新数组
(2)filter(function(item,index){return 要筛选的条件}) 过滤,返回一个符合指定条件的新数组
(3)every(function(item,index){return 条件}) 返回布尔值,判断是否全部项都满足条件
(4)some(function(item,index){return 条件}) 返回布尔值,判断是否存在有满足条件的项
(5)forEach(function(item,index){每一项要做的事情}) 循环
(6)sort(function(a,b){return a-b}) 排序
10、常见坑:
数组中删除元素,数组长度会变化的情况,角标也会发生变化。
解决方法:
- 角标回退
- 倒序循环(也会用到角标回退)
11、想要拿到元素对象的样式时:使用 元素对象.属性 只能拿到以行内样式定义的样式,而以内部样式和外部样式的形式定义的样式通过该方法并不能拿到。
而 document.defaultView.getComputedStle(要获取样式的元素对象,null) 的返回值是可以拿到元素对象的所有样式。
12、目前使用到了闭包的场景:DOM实战2的眼睛flag、定时器中防抖和节流函数的定义。
13、整个前端中遇到的是异步的:定时器setTimeout和setIntervalajas、ajax、promise
14、事件:
(1) 鼠标的事件:onclick onmouseover onmouseout onmousemove
(2) 表单的事件:onfocus onblur onchange oninput
(3)键盘事件 onkeydown onkeyup onkeypress 用哪一个都可以
(4)onreadystatechange ajax请求状态发生变化时触发
15、前后端沟通的注意事项:
(1)前端和后端接口的一致性(请求参数的参数名由后端决定的一定要一致)
(2)请求方式是get还是post由后端决定。
16、解决异步的方法:回调函数、Promise、await
常用网站:菜鸟教程、MDN、iconfont阿里巴巴矢量图标库、jquery插件库
面试题
1、笔试题:变量未声明,js自动声明为全局变量+变量声明提前,赋值不提前+(a++)与(++a)的区别,的输出结果。
2、 undefined和null的区别:⭐⭐⭐(面试题)
undefined和null都是原始数据类型;在布尔运算中被认为是false;
undefined:typeof()是undefined;
表示“缺少值”,即此处应有值但还没有定义;
是JS在运行时创建的全局变量,是全局对象的一个属性;
null:typeof()是object,本质上是一个对象,但是没有指向,指向为空。
表示对象的值未设置,指示变量未指向任何对象,看作是尚未创建的对象;
是一个字面量,他不是全局对象的一个属性;
与对象一样,永远不会被JS隐式赋值给变量;
常用作函数的参数;作为对象原型链的重点。
区别:①相等但不全等:undefined值是派生自null值的,undefined和null都代表着无效的值所以二者相等,但由于二者是两种不同的原始数据类型,所以不全等。
null==undefined; //true
null===undefined;//false
②typeof()值不同,typeof(undefined)为undefined,typeof(null)为null。
③在数字运算中被转化为number类型的值不同:null->0;undefined->NaN.
相同点:都是原始数据类型,保存在栈中变量本地。
数组进行相等比较:[]、[null]、[undefined]都隐式转换为’’;对于最里层的[],不管外层嵌套多少个[],最终都可看成只有最里层一个[].
3、 浅拷贝/浅复制⭐⭐⭐VS深拷贝(面试题)
浅拷贝:对于引用数据类型的数据进行复制赋值时,给的都是指针,而不是直接给数据。使得他们都拥有相同的指针,指向相同的数据,这时修改其中一个中的数据,另一个的内容也是会受到影响的。这种情况就是浅拷贝。浅拷贝复制的是指针,不是数据。
深拷贝:复制的是数组里面的数据
JSON的JSON.parse(对象形式的字符串)、JSON.stringify(字符串形式的对象)实现深拷贝:把对象转换为字符串类型的,基本数据类型的复制本身就是深复制,再将复制之后的字符串转换为对象类型,这时再修改该对象,之前的对象的内容是不会发生变化的。
var obj = { hobby: "打篮球", car: "bmw" }//对象
var a = JSON.stringify(obj)//将对象转换为字符出串
var b = a//字符串复制,基本数据类型的复制本身就是深复制
b = JSON.parse(b)//将字符串转换为对象
console.log(b)
b.car = "benz"//更改b的car,obj的car不受影响
console.log(obj, b)
解决深复制的方法:
(1)、JSON.stringfy(字符串类型的对象) JSON.parse(对象类型的字符串)
优点:又简单又好用,且bug少。
弊端:处理不了日期 。(日期对象转换为字符串后想再转回对象实现不了)
但是在实际开发中,基本不会出现属性值为日期的对象,使用该方法足够了。
(2)、ES6的扩展运算符:(该方法只能实现一维的深复制,当是二维数组时就实现不了了)
let arr = [1, 2, 3]
let arr2 = [...arr]
arr[1] = "hrllo"
console.log(arr, arr2)
// arr:[1, 'hrllo', 3] arr2:[1, 2, 3]
(3)、手写递归实现深复制:对对象进行循环复制属性和属性值,如果属性值是引用数据类型的数据,则对该数据再进行循环复制(使用递归实现)。
优点:没有bug,可完全实现深复制。
function deepClone(obj) {
var obj2 = obj instanceof Array ? [] : {}
if (obj && typeof (obj) === "object") {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof (obj[key]) === "object" && obj[key]) {
obj2[key] = deepClone(obj[key])
} else {
obj2[key] = obj[key]
}
}
}
}
return obj2
}
- 面试题:console.log({}=={}) 的结果是false,因为{}为空对象,其中存储的地址指针是不一样的。
- 什么是闭包?闭包有哪些优缺点?
内部函数使⽤了外部函数的局部变量,这种结构叫闭包(函数套函数)
优点:可以保护变量的/避免全局污染(变量放在全局被不小心篡改)
缺点:存在内存泄漏 /内存溢出 。由于闭包中的局部变量始终被全局变量使用着,而全局变量是不能销毁的(不能被垃圾回收机制清除),导致闭包内的局部作用域也无法被垃圾回收机制回收,使得内存无法得到释放,从而导致内存泄露问题。
关键点是函数fn在全局c中被调用了,把返回的函数结果给到了全局变量,而全局变量不能被销毁,导致fn无法被释放.
function fn() {
let n = 0;
return function () {
return n++
}
}
console.log(fn()())//0
console.log(fn()())//0
console.log(fn()())//0
console.log(fn()())//0
//这种情况下没有导致闭包,是因为局部变量fn调用结束之后就被销毁了
function fn() {
let n = 0;
return function () {
return n++
}
}
let c = fn();//这一步是闭包的关键点,函数fn被全局变量c一直使用着,而全局变量是不能被销毁的,导致局部变量fn也不能被销毁,内存无法得到释放
console.log(c())//0
console.log(c())//1
console.log(c())//2
console.log(c())//3
6、面试题原题:var str=”你以为你以为的就是你以为的吗”中一共出现了几次“以为”,每次出现的位置在哪,要求“以为”每次出现的位置用数组存储。
方法1:自己想的,每两个字之间判相等,需要比较n-1次。
方法2:使用内置对象String对象中indexOf(要查找的关键字,从第几位开始找)索引关键字的方法。------更推荐
/* 面试题原题:一共出现了几次“以为”,每次出现的位置在哪
要求: “以为”每次出现的位置用数组存储
*/
//方法一:
/*var str = "你以为你以为的就是你以为的吗";
var num = 0
var arr = []
var j = 0
for (var i = 0; i < str.length - 1; i++) {
var nstr = str[i] + str[i + 1]
if (nstr == "以为") {
arr[j] = i
num++
j++
}
}
console.log(arr, num)
//需要比较n-1次*/
//方法二:indexOf(要查找的关键字,从第几位开始找)方法
//查找关键字indexOf,indexOf只能返回第一次出现的位置,想要返回每次出现的位置,可以每次从发现“以为”后的位置开始找
var str = "你以为你以为的就是你以为的吗以为"
var arr = []//用来存储“以为”出现的位置
//var num = 0;//用来存储“以为”出现的次数
//不用数组的角标了,直接用push()的方法往数组的最后加内容
var j = 0;//用于存储开始查找的位置
for (var i = 0; i < str.length; i++) {
var index = str.indexOf("以为", j)//找到/未找到的角标
if (index != -1) {
//找到了“以为”
arr.push(index)//将找到位置的角标存入数组
j = index + 2;//找到了“以为”之后,下次开始查找的位置为“以为”后面的内容,不能再从头开始查
//num++
} else {
break
}
//当往后找不到“以为”的时候查找就结束了,不用再循环了
}
// console.log(num,arr)
console.log(arr.length, arr)
//输出结果:3 [1,4,10]
7、面试题原题:数组去重:⭐⭐⭐⭐⭐
注意数组长度会变化的情况,角标也会发生变化,在循环的时候要格外注意角标的变化情况。
从第0项开始,每一项都与第0项比较,如果二者相等,则把后者从数组中使用slice()方法删除,这样就保证了第0项在数组中是唯一的;再从第1项开始,后面的每一项都与第1项比较,如果相等,则把后者从数组中删除,这样就保证了第1项在数组中是唯一的;…,依次进行比较。
但此时,还会存在一些bug,在删除重复的数据之后,数组长度发生变化,它后面数据都会向前移动一位,角标会减1,这时如果再次进行比较的时候应该从被删除数据的角标开始比较,否则会出现跳过一些数据的现象发生。
解决方法一:角标回退
每次在删除数据的时候,下次在拿数据的时候拿的是被删除数据所在角标的数据。
解决方法二:倒序循环
拿最后一项与前面的每一项相比较,如果相等,则删除前面的项,这时即使数组的角标发生变化,变化的是后面的,前面要拿的项的角标并不会发生变化。
解决方法三:indexOf()索引方法,循环遍历原数组,将新数组中在原数组中没有的数据放入新数组中,最后输出新数组。
解决方法四:ES6中的Set()自动去重------最简单的方法
//方法一:角标回退
var arr = [1, 2, 3, 2, 5, 6, 7, 6, 6, 6, "a", true, "a", "a", "b", "c", false, true]
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
//如果不j--的话,此时的去重并不彻底,因为在遇到第一个重复项删除之后,j++,下一个要比较的时候拿到的在原来的数组中占的位置不是i++的位置,而是靠后一个的位置,数组的角标不会跳着显示,角标改变会出现有些数据在循环的时候被跳过
j--;
//当有重复项时将该项删除后,下一次开始比较的时候还是从该项的角标开始比较,否则会出现跳过某些项的现象
//在有删除的情况时才进行角标回退
}
}
}
console.log(arr)
//方法二:倒序循环,最后一项
var arr = [1, 2, 3, 2, 5, 6, 7, 6, 6, 6, "a", true, "a", "a", "b", "c", false, true]
for (var i = arr.length - 1; i >= 0; i--) {
for (var j = i - 1; j >= 0; j--) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
i--
//i--是为了取出的标杆是正确的,当删除一个数据之后,数组长度发生变化,标杆的角标也发生了变化,为了之后比较时的标杆不变,将标杆的角标-1,以便于拿到的标杆在一轮循环中始终是同一个
}
}
}
console.log(arr)
//方法三:使用indexOf()索引,循环遍历原数组,如果narr数组中没有该项,则把该项放进新数组narr中,如果narr数组中有了该项,则不放入arr数组中。
var arr = [1, 2, 3, 2, 5, 6, 7, 6, 6, 6, "a", true, "a", "a", "b", "c", false, true]
var narr = []
for (var i = 0; i < arr.length; i++) {
if (narr.indexOf(arr[i]) == -1) {
narr.push(arr[i])
}
}
console.log(narr)
//方法四:ES6中的Set()自动去重
let arr = [1, 2, 3, 2, 5, 6, 7, 6, 6, 6, "a", true, "a", "a", "b", "c", false, true]
let set = new Set(arr)
console.log([...set])//先拆开再合并
//[1, 2, 3, 5, 6, 7, 'a', true, 'b', 'c', false]
8、将字符串的首字母转为大写后输出:字符串切割转数组split+拿到数组的某一项的某一位+转大写toUpperCase()+截取子字符串拼上去+数组拼接为字符串
var wordsArr = str.split(" ")//字符串切割转为数组
//对字符数组的每一项进行操作
for (var i = 0; i < wordsArr.length; i++) {
//将每个单词的转为大写,并将单词剩余的部分拼接上去
wordsArr[i] = wordsArr[i][0].toUpperCase() + wordsArr[i].substring(1)
}
//将数组转换为字符串输出
str = wordsArr.join(" ")
console.log(str)
//输出结果:Where Are You From ? I Come From China
9、手写防抖和节流函数
//防抖函数封装
function debounce(fn) {
var timer = null
return function () {
clearTimeout(timer)
timer = setTimeout(function () {
fn()//要做的事情
}, 1000)
}
}
//节流函数的封装
function throttle(fn) {
var flag = true//开门
return function () {
if (!flag) {
//如果是关门,则不用再继续了
return
}
flag = false//关门
//1s之后再开门
setTimeout(function () {
fn()
flag = true
}, 1000)
}
}
10、原型与原型链
11、事件触发周期及其执行顺序
12、事件委托/代理
13、服务端返回的状态码:404 200(ok) 500 301 304
14、跨域问题及其解决方案
15、改变this指向的方法有有哪些,他们之间的区别有哪些。
16、new关键字的原理,有哪几步?
17、js中的数据类型有几种?再加上symbol
18、
实现深拷贝的方法通常有以下几种:
1. 手动递归复制对象的所有属性和子对象。这是最基本和常见的深拷贝方法,但要求开发者对对象结构和属性进行详细了解,并且需要注意循环引用。
2. 使用JSON序列化和反序列化。你可以将对象转换为JSON格式,再通过解析JSON格式创建新对象。不过这种方法处理起来相对比较缓慢,并且有些对象可能无法被JSON化。
3. lodash库的cloneDeep方法。这是一个JavaScript工具库,它提供了一个优秀的深拷贝函数,能够方便地对对象进行复制。但使用时需要注意lodash库的导入。
4. jQuery库的extend方法。这个方法可以用来合并多个对象,并尝试对对象进行深拷贝。不过需要明确的是,这个方法并不总是能够做到完全的深拷贝。
总之,每种方法都有其适用的场景和局限性,开发者需要根据实际情况选择最合适的实现方法。
1. 手动递归复制对象的所有属性和子对象的方法:
```js
function deepClone(obj) {
let newObj = {};
for (let key in obj) {
if (typeof obj[key] === 'object') {
newObj[key] = deepClone(obj[key]);
} else {
newObj[key] = obj[key];
}
}
return newObj;
}
```
2. 使用 JSON 序列化和反序列化的方法:
```js
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
```
3. 使用第三方库 lodash 的 cloneDeep 方法:
```js
const lodash = require('lodash');
function deepClone(obj) {
return lodash.cloneDeep(obj);
}
```
4. 使用 jQuery 的 extend 方法:
```js
const $ = require('jquery');
function deepClone(obj) {
return $.extend(true, {}, obj);
}
```
以上四种方法都可以实现深拷贝,但用法和适用场景不尽相同,建议根据具体需要选择合适的方法。