JS知识点-0
BFC
https://juejin.cn/post/6950082193632788493
官方定义:BFC(Block Formatting Context)块格式化上下文, 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
说人话:BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
-
BFC
就是一个块级元素,块级元素会在垂直方向一个接一个的排列 -
BFC
就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签 -
垂直方向的距离由margin决定, 属于同一个
BFC
的两个相邻的标签外边距会发生重叠- 计算
BFC
的高度时,浮动元素也参与计算
- 计算
如何创建一个BFC
这里简单列举几个触发BFC
使用的CSS
属性
- overflow: hidden
- display: inline-block
- position: absolute
- position: fixed
- display: table-cell
- display: flex
BFC有什么特点
在BFC中,块级元素从顶端开始垂直地一个接一个的排列。(当然了,即便不在BFC里块级元素也会垂直排列)
如果两个块级元素属于同一个BFC,它们的上下margin会重叠(或者说折叠),以较大的为准。但是如果两个块级元素分别在不同的BFC中,它们的上下边距就不会重叠了,而是两者之和。
BFC的区域不会与浮动的元素区域重叠,也就是说不会与浮动盒子产生交集,而是紧贴浮动边缘。
计算BFC的高度时,浮动元素也参与计算。BFC可以包含浮动元素。(利用这个特性可以清除浮动)
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
BFC有什么用
1、解决外边距折叠问题、外边距塌陷
外边距折叠(Margin collapsing)也只会发生在属于同一BFC的块级元素之间。
对第一个div的margin-bottom设置为10px,第二个div的margin-top设置为20px,我们可以看到两个盒子最终的边距是20px,是两者之中较大的一个。这就是外边距重叠的问题。
为了解决这个问题,我们可以让这两个div分属于不同的BFC,或者只要把其中一个div放到BFC中就可以。原因是:BFC就是页面上的一个隔离的独立容器,容器里面的元素不会对外边产生影响。
2、制作两栏布局
BFC****的区域不会与浮动的元素区域重叠。
我们可以利用这个特性来创建CSS中常用的两栏布局(左边宽度固定,右边宽度自适应)。
HTML:
3、清除元素内部的浮动
这里清除浮动的意思并不是清除你设置的元素的浮动属性,而是清除设置了浮动属性之后给别的元素带来的影响。例如我们给子元素设置浮动,那么父元素的高度就撑不开了。
BFC有一个特性:计算BFC的高度时,浮动元素也参与计算,利用这个特性可以清除浮动。
https://blog.csdn.net/weixin_43974265/article/details/115416184
三栏布局 :grid
...
Canvas api
重绘与重排
当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。
重绘(repaint或redraw):
重绘是指一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)
重排(重构/回流/reflow):
当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。
触发重排的条件:任何页面布局和几何属性的改变都会触发重排:
页面渲染初始化(无法避免)
添加或删除可见的DOM元素
元素位置的改变,或者使用动画
元素尺寸的改变——大小,外边距,边框
浏览器窗口尺寸的变化
填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变
重排必定会引发重绘,但重绘不一定会引发重排。
如何避免触发回流和重绘:
避免频繁使用 style,而是采用修改class的方式。
将动画效果应用到position属性为absolute或fixed的元素上。
也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
使用createDocumentFragment进行批量的 DOM 操作。
对于 resize、scroll 等进行防抖/节流处理。
避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
利用 CSS3 的transform、opacity、filter这些属性可以实现合成的效果,也就是GPU加速。
杂项
Flex容器默认属性:align-items:stretch列等高
需要更改: align-items:flex-start
Vw?vh?:::
前面聊两个相对的单位em和rem,不过随着时代的发展,em和rem对于有些移动端的内容显示虽然可以达到效果,所以又衍生了两个单位vw和vh。
两个也是相对单位不过其相对的是视口(非body标签定义的宽度)的,
vw
: viewport width 意思是视口的宽度
vh
: viewport height 意思是视口的高度。
现在说一下具体的单位大小都是各自的百分之一。
1vw = 1/100 * 视口宽度。
1vh =1/100 * 视口高度
如果这样看有人想到这个不就是百分比吗?前面在定义宽度和高度的时候也是使用了。
两者自然有不同,其和em和rem的区别有点相似,就是百分比相对于父类,而vm是相对于视口。
虽然两个单位表示了两个方向作为参考依据,但是一般的时候使用的vw而不是使用vw和vh一起使用,因为在推动窗口的时候希望按比例缩放的
拖拽功能
<div class="box"> <div class="left" ondragover="dragoverx(event)" ondrop="dropx(event)"></div> <div class="middle" ondragover="dragoverx(event)" ondrop="dropx(event)"> <div id="tars" class="target" draggable="true" ondragstart="dragstartx(event)">拖拽</div> </div> <div class="right" ondragover="dragoverx(event)" ondrop="dropx(event)"></div> </div> <script> // 开始拖拽,对应拖拽元素 // dataTransfer对象用于保存拖拽元素的信息,setData就是保存信息函数,函数包含两个参数 // 第一个参数为保存信息的MIME类型,第二个参数为保存信息。 // 例子里的drag函数就是当元素被拖拽时把元素的id记录在dataTransfer对象中(当元素在被拖入元素容器内放置时会使用该信息)。 function dragstartx(e) { var data = e.dataTransfer.setData("Text",e.target.id); } // 拖拽元素在框内移动时,对应框 // preventDefault函数则是不要执行与事件关联的默认动作。 // 因为元素是默认禁止其他元素在范围内拖动的,需要通过调用preventDefault函数来解禁。 function dragoverx(e) { e.preventDefault(); } // 放下拖拽,对应框 // 在ondrop函数内同样需要调用preventDefault函数,原因同上。 // 第二句则是读取从之前的drag函数中的保存信息(即拖入元素的id),第三句则是把拖入元素拼为元素容器的最后一个子元素。 function dropx(e) { e.preventDefault(); var el = e.dataTransfer.getData("Text"); console.log(this); e.target.appendChild(document.getElementById(el)); } </script>
Grid--x
1.简单grid
.grid{ display: grid; /* 下面句的意思就是这个容器里面的子元素分成三列,每列都是200px宽 */ grid-template-columns: 200px 200px 200px; /* 下面这句的意思就是这个容器里面的子元素分成俩行,每行都是200px的高 */ grid-template-rows: 200px 200px; /* 处理完之后发现 每一个子元素的宽高都变成了192px * 192px */ /* 由于我们给子元素加了2px的边框,最后展现的192px * 192px也就清楚了 ,grid布局还将容器下的所有子元素变成了box-sizing: border-box;怪异盒模型。 */ }
现在上面的这种方法只是给子元素写固定的宽度高度,这并不是我们想要的,它并不会随着浏览器宽度高度的变化而进行变化,我们要的是能够自适应的
2.自适应
CSS 栅格布局带来了一个全新的值:fraction单位,fraction单位通常简写为fr,它允许你根据需要将容器拆分为多个块。
.grid{ display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 200px 200px; }
结果是栅格布局将会把整个宽度分成三个 fraction,每列占据一个 fraction 单位。
如果我们将grid-template-columns的值更改为1fr 2fr 1fr,第二列的宽度将会是其它两列的两倍。总宽现在是四个 fraction 单位,第二列占据两个 fraction 单位,其它列各占一个 fraction。
3.高级响应
然而,上面列子并没有给出我们想要的响应性,因为网格总是三列宽。我们希望网格能根据容器的宽度改变列的数量。
repeat()
首先我们学习repeat()函数。这是一个强大的指定列和行的方法。让我们使用repeat()函数来更改网格: 容器CSS更改为:
.grid{ display: grid; grid-template-columns: repeat(3,200px); grid-template-rows: repeat(2,200px); }
在上面代码中,repeat(3, 100px)等于100px 100px 100px。第一个参数指定行与列的数量,第二个参数指定它们的宽度,因此它将为我们提供与开始时完全相同的布局。
auto-fit
然后是auto-fit。让我们跳过固定数量的列,将3替换为自适应数量:
现在,栅格将会根据容器的宽度调整其数量。它会尝试在容器中容纳尽可能多的 100px 宽的列。但如果我们将所有列硬写为 100px,我们将永远没法获得所需的弹性,因为它们很难填充整个宽度。
minmax()
为了解决上述问题,我们需要minmax()。我们将 100px 替换为 minmax(100px, 1fr),代码如下:
.grid{ display: grid; grid-template-columns: repeat(auto-fit,minmax(160px,1fr)); grid-template-rows: repeat(2,200px); }
现在的效果堪称完美。minmax()函数定义的范围大于或等于 min, 小于或等于 max。
因此,现在每列将至少为 100px。但如果有更多的可用空间,栅格布局将简单地将其均分给每列,因为这些列变成了 fraction 单位,而不是 100px。
#文件上传和下载 == 大文件分片上传和下载
一、a标签下载
• function download() { • var url = "http://127.0.0.1:5500/test.html"; • fetch(url).then((res)=>{ • res.blob().then((blob)=>{ • const blobUrl = window.URL.createObjectURL(blob); • const fileName = 'user.html'; • const a = document.createElement("a"); • a.href = blobUrl; • a.download = fileName; • a.click(); • document.removeChild(a); • }) • }) • }
优点:
可以下载txt、png、pdf等类型文件
download的属性是HTML5新增的属性 href属性的地址必须是非跨域的地址,如果引用的是第三方的网站或者说是前后端分离的项目(调用后台的接口),这时download就会不起作用。 此时,如果是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器会自动下载,但是如果使用浏览器可以解析的文件,比如.txt,.png,.pdf....浏览器就会采取预览模式;所以,对于.txt,.png,.pdf等的预览功能我们就可以直接不设置download属性(前提是后端响应头的Content-Type: application/octet-stream,如果为application/pdf浏览器则会判断文件为 pdf ,自动执行预览的策略)
缺点:
· a标签只能做get请求,所有url有长度限制
· 无法获取下载进度
· 无法在header中携带token做鉴权操作
· 跨域限制
· 无法判断接口是否返回成功
· IE兼容问题
二、ajax下载--切片下载/切片上传
Hard
项目亮点
JS选取DOM元素的方法,js5种不同的选择器
1.document.querySelector("选择的元素") (最常用的) document.querySelectorAll("所有选择的元素") (返回伪数组)
2.getElementById('idBox') 通过ID选取元素
3.getElementsByClassName('classBox') 通过类选择元素(返回伪数组)
4.getElementsByTagName('div') 通过标签名选取元素(返回伪数组)
5.getElementsByName ('nameBox') 通过名称name选取元素(返回伪数组)
JS 深浅拷贝?
通俗的讲:
浅拷贝是拷贝了对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是另外申请了一块内存,内容和原对象一样,更改原对象,拷贝对象不会发生变化;
深层次:
浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;
其实总结来看,浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。
实现浅拷贝的第一种方法
var originObj = { a:1, b:{ c:2, d:4, }, c:[1,2,3,4], d:function(){ console.log("hhh") } } var newObj = simpleCopy(originObj); // originObj.b = {y:1,h:0}; // 为什么newObj不会改变?===>因为此时b已经更改地址了,不再指向原来的地址 // console.log(newObj); originObj.b.c = 1000; // 为什么newObj改变了===>因为此时b没有更改地址,而且因为是浅拷贝,所以拷贝了原对象的b对象的引用 console.log(newObj) // 实现浅拷贝 function simpleCopy(obj) { // 先新建一个空对象,因为浅拷贝是拷贝一层,深层次的对象级别,就只拷贝引用了 var newObj = {}; for (const key in obj) { if (Object.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } return newObj; }
实现浅拷贝的第二种方法:js自带的API :Object.assign
ES6中的Object.assign方法,Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
参数:
target:目标对象。 sources:任意多个源对象。 返回值:目标对象会被返回。
深拷贝的实现方式
对象只有一层的话可以使用上面的:Object.assign()函数
这时候也不用分 深浅拷贝了(笔者注)
递归拷贝
var newObj = {}; var oldObj = { ob1:{ str:"hello", arr:[1,2,3,4], x:function(){ alert('111'); } }, num:1, } var n = deepClone({},oldObj); oldObj.ob1.str = "sss"; console.log(n); // 深拷贝 function deepClone(newObj,oldObj) { var news = newObj; // 对旧对象进行属性遍历 for (const key in oldObj) { if (Object.hasOwnProperty.call(oldObj, key)) { // 如果该属性是object-进行深拷贝 if(typeof oldObj[key] === "object"){ // 判断是数组还是对象 news[key] = (oldObj[key].constructor === Array) ? []:{}; // 这一段表示 自递归,相当于 deepClone(news,oldObj[key]),一般用在匿名函数里面 arguments.callee(news[key],oldObj[key]); } // 如果不是object else{ news[key] = oldObj[key]; } } } return news; }
使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
注意:但是此时其实是将oldObj拷贝到了newObj的原型对象上面,如果想要取得深拷贝的结果,需要 newObj._ _ proto _ _ 才可以得到。
For in for of 区别
for in
for in 可以用来遍历数组和对象,但是值得注意的是for in遍历的是其索引或者属性,for in 更适合用来遍历对象
//for in 遍历数组,操作的是其索引值 var arr=['tom','jack','john']; for(var i in arr){ console.log(i);//0,1,2 } //for in 遍历对象,操作的是属性 var obj={name:'tom',age:'18'}; for(var key in obj){ console.log(key);//name age }
for of
for of 可以用来遍历数组,字符串,Maps和Sets,遍历的是其内容,不可循环普通对象,除非一个数据结构只有部署了 Symbol.iterator 属性, 才具有 iterator接口可以使用 for of循环。 对象不具有iterator属性,因此for of 不可用于对象。
//for of 遍历数组,遍历的是其值 var arr=['tom','jack','john']; for(var i of arr){ console.log(i);//tom jack john } //for of 遍历字符串 var str='asv'; for(var i of str){ console.log(i);// a s v }
哪些数据结构部署了 Symbol.iteratoer属性了呢?
- 数组 Array
- Map
- Set
- String
- arguments对象 伪数组
- Nodelist对象, 就是获取的dom列表集合 伪数组
- 伪数组
如果想让对象可以使用 for of循环怎么办?使用 Object.keys() 获取对象的 key值集合后,再使用 for of 或者使用内置的Object.values()方法获取对象的value值集合再使用for of
js柯里化函数
https://zhuanlan.zhihu.com/p/120735088
”函数柯里化”是指将多变量函数拆解为单变量的多个函数的依次调用, 可以从高元函数动态地生成批量的低元的函数。简单讲:就是利用函数执行,可以形成一个不销毁的私有作用域,把预先处理的内容都存在这个不销毁的作用域里面,并且返回一个函数,以后要执行的就是这个函数。
柯里化后的函数,每次都是传入单个参数,返回的函数都会保留之前传入的所有参数,并在最后一个函数传入后进行最终的计算。这就等于说,函数一直保留着之前的所有状态,等到所有条件都满足后,执行最终的操作。
Object.bind() 就是curry函数的典型代表。
// 普通的add函数 function add(x, y) { return x + y } // Currying后 function curryingAdd(x) { return function (y) { return x + y } } add(1, 2) // 3 curryingAdd(1)(2) // 3
实际上就是把add函数的x,y两个参数变成了先用一个函数接收x然后返回一个函数去处理y参数。现在思路应该就比较清晰了,就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
currying返回的是一个函数
//实现一个curry函数,它可以f(1)(2)(3)(4)....(99)并得到这些数字的和。 function curryFn(){ let sum = 0; let arg = Array.prototype.slice.call(arguments); arg.forEach((value)=>{ sum+=value; }) return function xnums() { if (arguments.length===0) { return sum; } let arg = Array.prototype.slice.call(arguments); arg.forEach((value)=>{ sum+=value; }) return xnums; } } console.log(curryFn(1,100)(2)(4)(5)(6)()); //柯里化函数实际运用 let getPrice = function(priceOff){ return function(Price){ return Price * (1 - priceOff); } } let takeOff = getPrice(0.1); console.log("折扣后的价钱为:",takeOff(5000)); //将普通函数改造成柯里化函数 function addNum(a,b,c){ return a + b + c; } let curryFn = function(fn){ let L = []; return function nest(){ L.push(arguments[0]); if (L.length===fn.length) { return fn(...L); } else return nest; } } let currf = curryFn(addNum); console.log(currf(1)(2)(3));
来列一列Currying有哪些好处呢?
1. 参数复用
// 正常正则验证字符串 reg.test(txt) // 函数封装后 function check(reg, txt) { return reg.test(txt) } check(/\d+/g, 'test') //false check(/[a-z]+/g, 'test') //true // Currying后 function curryingCheck(reg) { return function(txt) { return reg.test(txt) } } var hasNumber = curryingCheck(/\d+/g) var hasLetter = curryingCheck(/[a-z]+/g) hasNumber('test1') // true hasNumber('testtest') // false hasLetter('21212') // false
上面的示例是一个正则的校验,正常来说直接调用check函数就可以了,但是如果我有很多地方都要校验是否有数字,其实就是需要将第一个参数reg进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用,调用起来也更方便。
var currying=function(fn){ var args=[]; return function cb(){ if(arguments.length===0){ return fn.apply(this,args) } console.log(arguments) Array.prototype.push.apply(args,[].slice.call(arguments)) //args.push([].slice.call(arguments)) console.log(args) return cb } } function add(a,b,c){ console.log(a+b+c); } var s=currying(add); s(1)(2)(3)();
2. 提前确认
var on = function(element, event, handler) { if (document.addEventListener) { if (element && event && handler) { element.addEventListener(event, handler, false); } } else { if (element && event && handler) { element.attachEvent('on' + event, handler); } } } var on = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } })(); //换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了 var on = function(isSupport, element, event, handler) { isSupport = isSupport || document.addEventListener; if (isSupport) { return element.addEventListener(event, handler, false); } else { return element.attachEvent('on' + event, handler); } }
3. 延迟运行
参数复用那个例子就有延迟执行的意思 像我们js中经常使用的bind,实现的机制就是Currying.
Function.prototype.bind = function (context) { var _this = this var args = Array.prototype.slice.call(arguments, 1) return function() { return _this.apply(context, args) } }
Currying 封装
// 支持多参数传递 function progressCurrying(fn, args) { var _this = this var len = fn.length; var args = args || []; return function() { var _args = Array.prototype.slice.call(arguments); Array.prototype.push.apply(args, _args); // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数 if (_args.length < len) { return progressCurrying.call(_this, fn, _args); } // 参数收集完毕,则执行fn return fn.apply(this, _args); } }
我的封装
// 函数柯里化用到了闭包的概念 var currying = function(fn){ // 用来存放所调用函数的所有参数 var allArgs = []; return function cu(){ // 如果不再传入的参数,则表明currying进程已完毕 if(arguments.length===0){ // 用apply进行调用(因为参数支持 数组) return fn.apply(this,allArgs); } // 如果还有长度,则递归调用,将所有的参数都放到allArgs里面 else{ allArgs.push(...Array.from(arguments)); return cu; } } } function add(a,b,c,d) { return a+b+c+d; } var result = currying(add); console.log(result(1)(2)(3)(4)());
js .toString方法
- toString()方法可以根据所传递的参数把数值转换为对应进制的数字字符串。参数范围为 2~36 之间的任意整数。
var a = 32; console.log(a.toString(2)); //返回字符串100000 console.log(a.toString(4)); //返回字符串200 console.log(a.toString(16)); //返回字符串20
- 数值直接量不能直接调用 toString() 方法,必须先使用小括号或其他方法转化数字。
- 对象Object类型及自定义对象类型加括号返回[object Object]
- 类型识别
- 常常使用Object.prototype.toString()来进行类型识别,返回代表该对象的[object 数据类型]字符串表示,Object.prototype.toString()可以识别标准类型及内置对象类型,但不能识别自定义类型
console.log(Object.prototype.toString.call("jerry"));//[object String] console.log(Object.prototype.toString.call(12));//[object Number] console.log(Object.prototype.toString.call(true));//[object Boolean] console.log(Object.prototype.toString.call(undefined));//[object Undefined] console.log(Object.prototype.toString.call(null));//[object Null] console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object] console.log(Object.prototype.toString.call(function(){}));//[object Function] console.log(Object.prototype.toString.call([]));//[object Array] console.log(Object.prototype.toString.call(new Date));//[object Date] console.log(Object.prototype.toString.call(/\d/));//[object RegExp] function Person(){}; console.log(Object.prototype.toString.call(new Person));//[object Object] //除了类型识别之外,还可以进行其他识别,如识别arguments或DOM元素 (function(){ console.log(Object.prototype.toString.call(arguments));//[object Arguments] })() console.log(Object.prototype.toString.call(document));//[object HTMLDocument]
- 当我们对一个自定义函数调用toString()方法时,可以得到该函数的源代码;如果对内置函数使用toString()方法时,会得到一个’[native code]'字符串。因此,可以使用toString()方法来区分自定义函数和内置函数
JS中的Generator函数
https://juejin.cn/post/6986875247811100680
Generator 函数的定义
语法上,Generator 函数是一个状态机,封装了多个内部状态。 形式上,Generator是一个函数。不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。 整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器,异步操作需要暂停的地方,都用yield语句。
- function 关键字和函数之间有一个星号(*),且内部使用yield表达式,定义不同的内部状态。
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
要创建一个 generator,我们需要一个特殊的语法结构:function*
,即所谓的 “generator function”。
function* generateSequence() { yield 1; yield 2; return 3; } // "generator function" 创建了一个 "generator object" let generator = generateSequence(); alert(generator); // [object Generator]
到目前为止,上面这段代码中的 函数体 代码还没有开始执行:
一个 generator 的主要方法就是 next()。当被调用时(译注:指 next() 方法),它会恢复上图所示的运行,执行直到最近的 yield
value: 产出的(yielded)的值。 done: 如果 generator 函数已执行完成则为 true,否则为 false。 例如,我们可以创建一个 generator 并获取其第一个产出的(yielded)值:
function* generateSequence() { yield 1; yield 2; return 3; } let generator = generateSequence(); let one = generator.next(); alert(JSON.stringify(one)); // {value: 1, done: false}
Generator 是可迭代的
当你看到 next()
方法,或许你已经猜到了 generator 是可迭代(iterable)的。(译注:next()
是 iterator 的必要方法)
我们可以使用 for..of
循环遍历它所有的值:
这是因为当 done: true
时,for..of
循环会忽略最后一个 value
。因此,如果我们想要通过 for..of
循环显示所有的结果,我们必须使用 yield
返回它们:不能用return
运用场景
代替递归::斐波那契数列的实现
function * fibonacci(seed1, seed2) { while (true) { yield (() => { seed2 = seed2 + seed1; seed1 = seed2 - seed1; return seed2; })(); } } const fib = fibonacci(0, 1); fib.next(); // {value: 1, done: false} fib.next(); // {value: 2, done: false} fib.next(); // {value: 3, done: false} fib.next(); // {value: 5, done: false} fib.next(); // {value: 8, done: false}
试一试? 用 Generator做一下其他递归?
伪数组转数组的方法
伪数组的介绍: 伪数组我们可以理解为类似数组的一个集合,我们常见的有:一个是arguments还有一个是DOM的children属性,字符串,获取回来的子节点集合。他们与数组一样,具有索引(下标)和length属性。可以通过for循环写循环语句去循环遍历。但是不具有数组的方法,比如pop、push等。
伪数组转为数组的方法
1、使用Array.prototype.slice.call()或者Array.prototype.slice.apply();
2、使用[].slice.call()或者[].slice.apply();这种方法和上面的方法是一样的,但是上面的方式效率相对较高;
3、使用Array.from();ES6
4、使用Array.of();
5、使用new Array();
6、[...(obox.children)]
JS杂项
arr.slice(8,-1) 表示的是 arr数组第8个到第倒数第1个范围截取;
parseInt(a,b):第二个参数表示进制 : 2 8 10 16 其他为NaN
冒泡 捕获执行顺序问题:
Js事件执行满足先绑定,先执行的机制,事件捕获和冒泡的顺序是:先捕获后冒泡。