intersectionObsever 可视范围控制
// substr,substring,slice区别
substr(1,5)截取的是从字符串下标为1开始和截取后面4个字符,一共5个字符返回
substring(1,5)截取的是从字符串下标为1开始到下标为5为止,不包括5,一共5-1个字符返回
slice(1,5)和substring一致,slice第二参数为负数时,是从尾部倒数来计算或者说是与字符串或数组的长度相加得出的结果来计算
var a = function (a, b, c) {} a.length == 3
// 触发模拟点击 dom.dispatchEvent(new MouseEvent('click')) === a.click()
Symbol 类型是不可枚举的,可以使用 Object.getOwnPropertySymbols()方法获取到
Object.seal():防止新的属性被添加,防止存在到属性被删除,但是,你还是可以对存在对属性进行修改
document.elementFromPoint(x, y) //传入坐标值,返回当前页面上包含该坐标点的顶层元素。技巧:比如拖拽一个元素,当前元素四个顶点所接触到的元素会变色,该元素可以结合 pointer-event:none;来屏蔽掉 elementFromPoint 的阻碍,从而获取到该元素下的元素
var range = document.createRange() var fragment = range.createContextualFragment(str) //根据字符串直接生成元素
// js 运算时候,只要存在+一个是字符串的,结果都为字符串
;[1, 2, 3, 4].reduce((x, y) => console.log(x, y)) //reduce 的最后一个参数 initialValue 不提供时候,为数组第一个元素
//info.js export const name = 'a' export default 'aaa' //index.js import * as info from './info' console.log(info) //{default:'aaa',name:'a'}
Intl.NumberFormat()可以设置任意区域数字的格式 console.log(new Intl.NumberFormat().format(355500000))//355,500,000 var number = 123456.789; console.log(new Intl.NumberFormat('de-DE').format(number)); // → 123.456,789 console.log(new Intl.NumberFormat('ar-EG').format(number)); // → ١٢٣٤٥٦٫٧٨٩ console.log(new Intl.NumberFormat('en-IN').format(number)); // → 1,23,456.789 console.log(new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec').format(number)); // → 一二三,四五六.七八九 console.log(new Intl.NumberFormat(['ban', 'id']).format(number)); // → 123.456,789
function getPersonInfo(a, b, c) { console.log('a,b,c: ', a, b, c) } var person = 'aaa' var age = 22 getPersonInfo`${person} is ${age} years old` // → ['','is','years old'],aaa,22 // 字符串模板第一个参数始终是字符串值的数组,其余参数为字符串模板中的表达式
// setInterval方法返回的是一个唯一的id,此id用于clearInterval函数清除
Object.hasOwnProperty() 方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。 function F() { //自定义数据类型 this.name = "自有属性"; } F.prototype.name = "继承属性"; var f = new F(); //实例化对象 console.log(f.hasOwnProperty("name")); //返回true,说明当前调用的 name是自有属性 console.log(f.name); //返回字符串“自有属性” var d = Date; console.log(d.hasOwnProperty("toString")); //返回false,说明toString()是Date的自有属性 var d = Date.prototype; console.log(d.hasOwnProperty("toString")); //返回true,说明toString()是Date.prototype属性
// delete 操作符返回一个boolean值,true删除成功,否则返回false
String.raw 函数是用来获取一个模板字符串的原始字符串,他返回一个字符串,其中忽略了转义符( , ,v 等)。 const path=`C:DocumentsProjects able.html` String.raw`${path}`//"C:DocumentsProjects able.html" String.raw`C:DocumentsProjects able.html`//"C:DocumentsProjects able.html"
const name='111' console.log(!typeof name==='object') 以上例子先执行!typeof name返回 false ,然后执行 false === 'object',所以返回 false
const person = { name:'aa', address:{ street:'bb' } } Object.freeze(person) Object.freeze 函数冻结一个对象,但是为浅冻结,只要该对象的某个属性为对象,仍然可以进行给该对象的该属性进行修改
箭头函数的`prototype`为`undefined`,非箭头函数的prototype为 `{coustructor}`
通过defineProperty方法,我们可以给对象设置一个新的属性,或者修改已存在的属性。新增的属性,默认不可枚举,默认不可变。
webpack需要掌握的核心: Entry:webpack开始构建的入口模块。 Output:如何命名输出的文件,以及输出的目录,比如常见的dist目录。 loaders:作用在于解析文件,将无法处理的非js文件处理成webpack能够处理的模块。要使用 loaders,需要在 webpack.config.js 文件中声明它们,而要声明它们,你需要添加一个 module.rules 的属性。 Plugins:更多的是优化,提取精华(公共模块去重),压缩处理(css/js/html)等,对webpack功能的扩展。 Chunk:个人觉得这个是webpack 4的Code Splitting的产物,抛弃了webpack 3的CommonsChunkPlugin,它最大的特点就是配置简单,当你设置mode是production,那么webpack4就会自动开启Code Splitting,可以完成将某些公共模块去重,打包成一个单独的chunk。 webpack优化: 1.设置不参与构建的库 externals 走cdn 2.设置短路径 config.resolve.alias 3.删除懒加载模块的 prefetch preload,降低带宽压力 4.进行分包操作 config.optimization.splitChunks 5.dll缓存式打包 比如dllPlugin HardSourceWebpackPlugin 6.开启gzip压缩
webpack打包过程 run:开始编译 make:从entry开始递归分析依赖并对依赖进行build build-moodule:使用loader加载文件并build模块 normal-module-loader:对loader加载的文件用acorn编译,生成抽象语法树AST program:开始对AST进行遍历,当遇到require时触发call require:事件 seal:所有依赖build完成,开始对chunk进行优化(抽取公共模块、加hash等) optimize-chunk-assets:压缩代码 emit:把各个chunk输出到结果文件
while/for执行内运行return可以停止循环, 而且会停止while/for方法后面的所有运行
Math.log10函数求到的是10为基底的对数
求带有log的算法复杂度字眼的一般要用到二分算法
Number.isNaN(any)函数可以检测你传递的数字值是否等价于NaN。
isNaN(any)函数可以检测你传的参数是否为一个number,是number返回false,否则返回true
'abc'.padStart(5, 'xx')和'abc'.padEnd(5, 'xx')方法接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。 'xxx'.padStart(2, 'ab') // 'xxx' 'xxx'.padEnd(2, 'ab') // 'xxx' 如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。 'abc'.padStart(10, '0123456789')// '0123456abc' 如果省略第二个参数,默认使用空格补全长度。 'x'.padStart(4) // ' x' 'x'.padEnd(4) // 'x ' padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。 '1'.padStart(10, '0') // "0000000001" '12'.padStart(10, '0') // "0000000012" '123456'.padStart(10, '0') // "0000123456" 另一个用途是提示字符串格式。 '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12" '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
JSON.stringify()的第二个参数是替代者,替代者可以是一个函数或数组,用于控制哪些属性的值转换为字符串。 如果替代者是一个数组,那么包含在数组中的属性将会被转换为字符串,不在数组内的属性,则不会转换,例如: JSON.stringify({1:1,2:2,3:3},['2','3'])//'{"2":2,"3":3}' 如果替代者是函数将被这个对象的每个属性都调用一遍,函数返回这个属性的值,最终会显示到转换后的JSON字符串中,如果返回的是undefined,则这个属性不会显示到结果中
对象默认是不可迭代的,如果迭代规则被定义,则这个对象是可迭代的。我们可以通过迭代器Symbol的`[Symbol.iterator]`来定义迭代规则,器返回一个generator对象,比如说构建一个generator函数`*[Symbol.iterator](){}` const person={ name:'aa', age:21 } [...person]//SyntaxError 以上例子为例,如果我们想返回['aa',21],则需要自定义迭代器 const person={ name:'aa', age:21, *[Symbol.iterator](){ yield* Object.values(this) } } [...person]//[ 'aa', 21 ] 注意,yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
const a = [1, 2, 3] ;({ item: a[3] } = { item: 4 }) //a:[1,2,3,4]
function* generatorOne() { yield ['a', 'b', 'c'] } function* generatorTwo() { yield* ['a', 'b', 'c'] } const one = generatorOne() const two = generatorTwo() console.log(one.next().value) //['a', 'b', 'c'] console.log(two.next().value) //['a']
import 命令是编译阶段执行的,在代码运行之前。因此意味着被导入的模块会先运行,而在哪个模块导入的,当前的这个模块的文件会后执行。
generator函数在遇到yield关键字时会暂停其执行行。
yield表达式本身没有返回值,或者说总会返回undefined。
next方法可以带一个参数,该参数会被当作上一个yield表达式的返回值。
var let const function声明的区别 var: 1. 预解析:在环境最顶端,创建变量,并初始化为undefined—— 变量提升; 2. 为变量赋值; 3. 对变量进行操作,可以在后续操作中对变量值进行修改; 试例: function test() { /* 预解析 虽然if判断没有执行,但var的变量提升已经发生,此时执行环境中,已经存在变量a,值为undefined; */ console.log(a) // undefined,变量提升 if (false) { var a = 1; console.log(a) //不执行 } console.log(a); //undefined,变量未赋值 a = 1; //注意,此时因为a的变量提升,未加声明符号的赋值,并没有提升到全局环境中 console.log(a); // 1 } test(); console.log(a); // 报错,因为if中a的变量提升,a = 1的赋值并没有存在与全局中。如果注释掉if判断中的内容,a = 1因为没加变量声明符号,相当于在与全局中声明,那么最后的console.log(a)将打印1 let: 1、不存在隐藏步骤;隐藏步骤指的是在环境最顶端,创建变量,并初始化为undefined—— 变量提升; 2、创建变量、初始化并赋值; 3、对变量进行操作,可以在后续操作中对变量值进行修改; 4、同一个变量名,不能在let中重复使用 const: 1、不存在隐藏步骤;隐藏步骤指的是在环境最顶端,创建变量,并初始化为undefined—— 变量提升; 2、创建变量、初始化并赋值。必须赋值,不赋值会报错; 3、对变量进行操作,不可以对变量进行修改,但是可以对变量的属性进行修改; 4、同一个变量名,不能在const中重复使用 function: 1、隐藏步骤;隐藏步骤在环境最顶端,创建函数,初始化并赋值为函数定义; 2、执行函数,无论函数在何位置,只要可用,就可以调用; 3、function声明的函数,会在整个环境变量最顶端完成创建、初始化、赋值三位一体的操作。这样一来,不管在何处声明了函数,可以在任何地方调用函数方法。 4、重复声明和var一样会后面覆盖前面
parseInt函数检查字符串是否合法转义,会从左到右检测,当遇到没个字符串不合法时候停止检测,输出检查通过的参数转义输出,例如:parseInt('7*6'),输出7
异或运算: 1.一个数和0做XOR运算等于本身。a^0=a; 2.一个数和其本身做XOR运算等于0。a^a=0; 所以满足以下规律: a^b^a=(a^a)^b=0^b=b; 例子: 找出数组[4,1,2,1,2]中不出现重复的数字; function singleNumber(nums){ let ans=0; for(let num of nums){ ans=ans^num; } return ans; }
// 不返回 function People() {} const people = new People() // People {} // 返回数字 function People() { return 1 } const people = new People() // People {} // 返回新对象 function Animal() { return { hello: 'world', } } const animal = new Animal() // { hello: 'world' }
// getter/setter 也可以动态设置 class Hello { _name = 'lucy' getName() { return this._name } // 静态的getter get id() { return 1 } } const hel = new Hello() hel.name // undefined hel.getName() // lucy // 动态的getter Hello.prototype.__defineGetter__('name', function () { return this._name }) Hello.prototype.__defineSetter__('name', function (value) { this._name = value }) hel.name // lucy hel.getName() // lucy hel.name = 'jimi' hel.name // jimi hel.getName() // jimi
const obj = { a: { b: 1, }, c: 2, } const { a: { b }, a, } = obj
// 判断代码是否压缩 function CustomFn() {} const isCrashed = typeof CustomFn.name === 'string' && CustomFn.name === 'CustomFn'
// instanceof 的判断方式是原型是否在当前对象的原型链上面 function People() {} function Man() {} Man.prototype = new People() Man.prototype.constructor = Man const man = new Man() man instanceof People // true // 替换People的原型 People.prototype = {} man instanceof People // false
//__proto__的值就等于父类的prototype, newPuppy.__proto__指向了Puppy.prototype // 如果你访问的属性在Puppy.prototype也不存在,那又会继续往Puppy.prototype.__proto__上找,这时候其实就找到了Object.prototype了,Object.prototype再往上找就没有了,也就是null,这其实就是原型链。 //prototype.constructor是prototype上的一个保留属性,这个属性就指向类函数本身,用于指示当前类的构造函数。 // Puppy.prototype.construstor===Puppy; // 面向对象怎么能没有继承呢,根据前面所讲的知识,我们其实已经能够自己写一个继承了。所谓继承不就是子类能够继承父类的属性和方法吗?换句话说就是子类能够找到父类的prototype,最简单的方法就是子类原型的__proto__指向父类原型就行了。
// fiber原理用了requestIdleCallback,requestIdleCallback为任务调度,requestIdleCallback会在帧结束时并且有空闲时间。或者用户不与网页交互时,执行回调。react的fiber的requestIdleCallback基于requestAnimationFrame封装 // requestIdleCallback的第一个参数时callback // 当callback被调用时,回接受一个参数 deadline,deadline是一个对象,对象上有两个属性 // 1.timeRemaining,timeRemaining属性是一个函数,函数的返回值表示当前空闲时间还剩下多少时间 // 每次调用 timeRemaining() 函数判断是否有剩余时间的时候,如果浏览器判断此时有优先级更高的任务,那么会动态的把这个值设置为 0,否则就是用预先设置好的 deadline - now 去计算。 // 2.didTimeout,didTimeout属性是一个布尔值,如果didTimeout是true,那么表示本次callback的执行是因为超时的原因 // requestIdleCallback的第二个参数是options // options是一个对象,可以用来配置超时时间 function sleep(date) { let flag = true; const now = Date.now(); while (flag) { if (Date.now() - now > date) { flag = false; } } } function work() { sleep(2000); // 模拟主线程任务执行时间 requestIdleCallback(() => { console.log("空闲时间1"); sleep(1000); console.log("空闲时间1回调任务执行完成"); }); requestIdleCallback(() => { console.log("空闲时间2"); }); } btn1.addEventListener("click", work); // 执行结果:点击button -> 等待2s -> 打印 空闲时间1 -> 等待 1s -> 打印 空闲时间1回调任务执行完成 -> 空闲时间2;当sleep结束requestIdleCallback获取到主线程空闲,立马执行cb(也是在主线程执行)继续占用主线程,直到sleep结束,第二个requestIdleCallback获取主线程空闲输出空闲时间2。细看一下,此处requestIdleCallback不就是setTimeout吗,这样的功能用setTimeout也能实现,当然他们是有区别的,的我们sleep模拟占用主线程时间是可控的,但大多时候主线程work时间是不可预知的,setTimeout需要知道具体延迟时间,所以这是主要的却别
requestAnimationFrame 的回调有两个特征: 1.在重新渲染前调用。 2.很可能在宏任务之后不调用。
// https://juejin.cn/post/6844904165836062734#heading-9 // https://juejin.cn/post/6844904180885225486 // https://juejin.cn/post/6844904168017100813#heading-6 // https://juejin.cn/post/6844904174451179528#heading-12 js设计模式: 1.策略模式,通过将我们的if条件改写为一条条的策略减少了if...else的数量,看起来更清爽,扩展起来也更方便。状态模式跟策略模式很像,只是还多了一个状态,可以根据这个状态来选取具体的策略。 2.外观模式,可能我们已经在无意间使用了,就是将模块一些内部逻辑封装在一个更高级的接口内部,或者将一些类似操作封装在一个方法内部,从而让外部调用更加方便。 3.迭代器模式,在JS数组上有很多实现,我们也可以模仿他们做一下数据处理的工作,特别适合处理从API拿来的大量结构相似的数据。 4.备忘录模式,就是加一个缓存对象,用来记录之前获取过的数据或者操作的状态,后面可以用来加快访问速度或者进行状态回滚。 5.工厂模式,的名字就很直白,封装的模块就像一个工厂一样批量的产出需要的对象。常见工厂模式的一个特征就是调用的时候不需要使用new,而且传入的参数比较简单。但是调用次数可能比较频繁,经常需要产出不同的对象,频繁调用时不用new也方便很多。 6.建造者模式,是用于比较复杂的大对象的构建,比如Vue,Vue内部包含一个功能强大,逻辑复杂的对象,在构建的时候也需要传很多参数进去。像这种需要创建的情况不多,创建的对象本身又很复杂的时候就适用建造者模式。 7.单例模式,适用于全局只能有一个实例对象的场景 8.原型模式,最典型的应用就是JS本身啊,JS的原型链就是原型模式。JS中可以使用Object.create指定一个对象作为原型来创建对象 9.桥接模式,人如其名,其实就相当于一个桥梁,把不同维度的变量桥接在一起来实现功能 10.享元模式,当我们观察到代码中有大量相似的代码块,他们做的事情可能都是一样的,只是每次应用的对象不一样,我们就可以考虑用享元模式。 11.模板方法模式,其实类似于继承,就是我们先定义一个通用的模板骨架,然后后面在这个基础上继续扩展。 12.职责链模式,顾名思义就是一个链条,这个链条上串联了很多的职责,一个事件过来,可以被链条上的职责依次处理。他的好处是链条上的各个职责,只需要关心自己的事情就行了,不需要知道自己的上一步是什么,下一步是什么,跟上下的职责都不耦合,这样当上下职责变化了,自己也不受影响,往链条上添加或者减少职责也非常方便。 13.观察者模式,还有个名字叫发布订阅模式,这在JS的世界里可是大名鼎鼎,大家或多或少都用到过,最常见的就是事件绑定了,有些面试还会要求面试者手写一个事件中心,其实就是一个观察者模式。观察者模式的优点是可以让事件的产生者和消费者相互不知道,只需要产生和消费相应的事件就行,特别适合事件的生产者和消费者不方便直接调用的情况,比如异步中。 14.装饰器模式针对的情况是我有一些老代码,但是这些老代码功能不够,需要添加功能,但是我又不能去改老代码,比如Vue 2.x需要监听数组的改变,给他添加响应式,但是他又不能直接修改 Array.prototype。这种情况下,就特别适合使用装饰者模式,给老方法重新装饰下,变成一个新方法来使用。 15.适配器想必大家都用过,我家里的老显卡只有HDMI接口,但是显示器是DP接口,这两个插不上,怎么办呢?答案就是买个适配器,将DP接口转换为HDMI的就行了。这里的适配器模式原理类似,当我们面临接口不通用,接口参数不匹配等情况,我们可以在他外面再包一个方法,这个方法接收我们现在的名字和参数,里面调用老方法传入以前的参数形式。
flex布局加入border正确做法 .table { display: flex; flex-flow: row wrap; text-align: center; line-height: 2; border-width: 1px 0 0 1px; border-style: solid; border-color: #707070; box-sizing: border-box; .item { width: 50%; display: block; border-width: 0 1px 1px 0; border-style: solid; border-color: #707070; box-sizing: border-box; } .theader { background-color: rgb(228, 228, 228); @extend .item; } }