前端 基础

271 阅读10分钟

js for 循环算法

  1. 计算 1 + 2 + 3 + ... + 100
for (let i = 1;i < =100; i++)

利用公式

n(n+1)/2
  1. 从数组 arr = [1,2,3,4,5,6] 寻找num1+ num2 = 11的index

一、两层for循环 复杂度 n平方

二、11 - arr[i] = ? 再去找? 算法复杂度n

装箱和拆箱

装箱:把基础数据类型转化成对应的引用数据类型的操作;

拆箱:将引用类型对象转换成对应的值类型对象。

const num = 123
const num1 = new Number(123)
console.log(typeof num1) // object

//num1转换成值类型
console.log(num1.valueOf())

//js内部方法:toPrimitive(input, type)
//1.input判断是不是原始类型的值 是 直接返回
//2.valueOf 是原始类型 返回
//3.toString 是原始类型 返回
//4.报错
console.log([]+[])
console.log([]+{})
=====>
console.log([].valueOf())
console.log([].toString())
''
====>
console.log({}.valueOf())
console.log({}.toString())
[object object]
const obj = {0: 'Emir', 2: 'Alex', 1: 'Jack'};
const obj1 = new Object({0: 'Emir', 2: 'Alex', 1: 'Jack'};) // obj和Obj1的地址不同
obj === obj1 // false
obj.__proto__.constructor === obj1.__proto__.constructor // true

const num = 1 // 'number'
const num1 = new Number(1) // 'object'
num.__proto__.constructor === num1.__proto__.constructor // true

this指向

普通fuction中的this指向,对象的function的this指向? vue的method为什么不能用箭头函数?

var name = 'Tom'
var obj = {
    name: 'Jack',
    getName:function(){
        consloe.log(this.name)
    }
}
obj.getName(); //Jack
var fn = obj.getName;
fn(); // Tom window
var fn1 = obj.getName.bind(obj); //把this指向绑定给obj
fn1(); //Jack
// 结合变量提升
var name
var obj = {
    name: 'Jack',
    getName:function(){
        console.log(this.name)
    }
}
obj.getName(); //Jack
var fn = obj.getName;
// name = 'Tom'
fn();
function foo(){
    console.log(this.name)
}
var shiny={
    name:'shiny',
    foo:foo
}
function doFoo(fn){
    fn()
}
doFoo(shiny.foo)
function Foo () {
 getName = function () {
   console.log(1);
 }
 return this;
}
// b
Foo.getName = function () {
 console.log(2);
}
// c
Foo.prototype.getName = function () {
 console.log(3);
}
// d
var getName = function () {
 console.log(4);
}
// e
function getName () {
 console.log(5);
}

Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

//2
//4
//1
//1
//2
//3
//3

原文 juejin.cn/post/714090…

开发中call、apply、bind的使用场景区分

call(this,arg) apply(this, [arg]) bind(this)() 应用(类数组转数组)

// 类数组转数组 
//1.Array.prototype.slice.apply
function getArg(arg1){
    arguments.forEach(i=>console.log(i))
    const args = Array.prototype.slice.apply(arguments)
    args.forEach(i=>console.log(i))
}
// 2. Array.from(arrayLike);
// 3. for循环

Promise

1.说下 promise 的三个状态
2.怎么处理多个 promise?
一、promise 有依赖关系,c 依赖 b 的返回,b 依赖 c 的返回
二、没有依赖关系,只需要全部执行
三、没有依赖关系,需要拿到最快的返回结果
四、没有依赖关系,但在某一个出错的情况下也能拿到所有的结果

const promise = new Promise((resolve, reject) => { 
    setTimeout(() => { 
        reject('失败') 
        resolve('成功') 
    }, 3000); 
}) 
promise.then(res => console.log(res)).catch(err => console.log(err))
const promise = new Promise((resolve, reject) => { 
    resolve(new Promise((resolve, reject) => { 
        setTimeout(() => { 
            resolve('ice') 
        }, 3000); 
    })
  ) 
}) 
promise.then(res => console.log(res))

Token刷新,怎么刷新token,如何优化多个并发请求同时token(jwt token)过期的问题。解决方案一:前端解析jwt token),在request拦截中判断是否过期,使用单例模式缓存下refresh token promise,并发请求都等待着歌单例的promise返回结果后再继续请求;二、使用队列,将过期的请求都存储到队列中,等refresh token promise返回结果后再从队列中取出,重新执行请求后返回结果。

数组操作

  • 在数组的尾部添加一个元素 push
  • 在数组的头部添加一个元素 unshift
  • 从数组的尾部弹出一个元素 pop
  • 从数组的头部弹出一个元素 shift

1.自己实现一个数组切割的chunk函数,chunk长度可改变。

[1,2,3,4,5,6,7,8] => [[1,2],[3,4],[5,6],[7,8]]

2.计算1到100的值,用reduce函数实现。有没有更简便的方法?(对应到算法中△)
3.有一个父级路由path的集合,例如,['google/home', 'google/my', ’google/my/settings', google/favorite'], 现在有一个子级或孙子级的path,例如'google/my/settings/version/1.2.1',在这集合里面找出最匹配的父级路径。这个path必然可以在path集合中找到父路径。

function getParentPath (){
    const myList = ['google/home', 'google/my', ’google/my/settings', google/favorite']
    const paths = 'google/my/settings/version/1.2.1'.split('/')
    while(!myList.includes(path.join('/') && paths.length){
      paths.pop()
    }
    return path.join(',')
}

js 递归

递归怎么写,用递归写一个阶乘函数。

function f(n){
    if (n <= 1) return 1
    return n * f(n-1)
}

这样写有什么问题? 有没有遇到:Maximum call stack size exceeded 这样的错误?

会导致调用栈一直无法释放,容易导致栈溢出问题。

优化思路:使用尾递归/循环。

尾递归即尾调用自身。es6规定尾调用,可以释放调用栈。但chrome并没有释放,而safari可以释放。所以推荐使用循环的方式解决。

怎么查看函数的调用栈有没有释放?

dom树的加载过程

  • 在浏览器中输入url,交给DNS域名解析,找到IP,向服务器发起请求,还有缓存,http协议,TCP等等
  • 服务器返回数据,浏览器接收文件(html,css,js,img),二进制文件

html:二进制转换为html -> index.html

  • 构建dom树:html解析器
  • token-〉node-〉dom
  • token词法解析,例如:识别到div开始/div结尾。通过词法解析之后生成HTMLDivElement Node节点对象。
  • 生成节点对象以后,得到一个根元素为Document的DOM树。

解析html的过程:

  • 1.遇到link的外部css,遇到css的代码会进行css的下载,但不影响dom树的构建
  • 2.遇到script标签的时候,会先去执行js的内容,直至脚本执行完成,再继续构建Dom

底部引入Javascript的原因,或者说在头部引用,但加上async、defer或者window.onload

  • 解析器遇到设置了async的script时,开始下载脚本并继续解析文档。脚本会在它下载完成后尽快执行,但是解析器不会停下来等它下载,不会影响dom的渲染。async的设置,会使得script脚本异步的加载并在允许的情况下执行 。async的执行,并不会按着script在页面中的顺序来执行,而是谁先加载完谁执行。
  • defer脚本会在文档渲染完毕后,DMOContentLoaded事件调用前执行。脚本会被延迟到整个页面都解析完毕后再运行。如果有多个设置了deferscript标签存在,则会按照顺序执行所有的script
  • DOMContentLoaded事件的触发并不受async脚本加载的影响,在脚本加载完之前,就已经触发了DOMContentLoaded
  • async的执行是加载完成就会去执行,而不像defer那样要等待所有的脚本加载完后按照顺序执行。
  • HTML文档被加载和解析完成,此时会触发DOMContentLoaded事件,页面上所有的资源(图片,音频,视频等)被加载以后才会触发load事件。

推荐的应用场景

defer

如果你的脚本代码依赖于页面中的DOM元素(文档是否解析完毕),或者被其他脚本文件依赖。
例:1. 评论框 2. 代码语法高亮 3. polyfill.js

async

如果你的脚本并不关心页面中的DOM元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据。
百度统计 如果不太能确定的话,用defer总是会比async稳定。。。


提问:下面的脚本执行结果是什么?为什么会这样?

<script type="module">
  alert(typeof button);
</script> 
<script> 
  alert(typeof button); 
</script> 
<button id="button">Button</button>

第二个脚本实际上在第一个脚本之前运行!所以我们先看看undefined,然后再看看object。type="module"脚本总是defer延迟的。

构建css树:css解析器

  • 1.每个css文件解析为样式表对象CSSStyleSheet,每个对象都包含CSSRule,CSSRule包含选择器和声明对象,以及其他与css语法对应的对象。
  • Token解析:css的词法及语法文法。
  • Node->cssom

构建render树:渲染树=dom树 + css树

布局Layout与绘制paint: 计算对象之间的大小,距离确定每个节点在屏幕上的确切坐标;映射浏览器屏幕绘制,使用UI线程绘制每个节点。

reflow回流:当元素属性发生改变且影响布局时(宽度、高度、内外边距等),产生回流,相当于刷新页面

repaint重绘:当元素属性发生改变且不影响布局时(背景颜色、透明度、字体样式等),产生重绘,相当于不刷新页面,动态更新内容。

重绘不一定引起回流,回流必将引起重绘。

dom事件

事件对象:事件触发时候自动创建的,封装了事件发生的元素和属性信息。比如鼠标事件对象上,怎么获取是在什么元素上触发了事件?e.target

  • 事件触发
  • 事件冒泡
  • 事件委托(过多的事件监听非常消耗内存,所以需要事件委托)

获取页面中ID名为demo的元素,并且给它添加点击事件监听,打印出这个元素的文字内容,并且阻止它向上冒泡。

const domObj = document.getElementById('demo')
domObj.addEventListener('click', function(e){
    e.stoPropagation()
    console.log(e.innerText)
}, true) // true 捕获, false 冒泡
domObj.addEventListener('click', function(e){
    console.log(e.target)
}, true) 

提问:简单自己实现一个addEventListener,用trigger方法代替dom事件触发。怎么收集事件监听和管理事件监听?
提问:addEventListener第三个参数是什么含义?

true表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件;
false表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件。

如果传{passive: true}是什么意思?
{passive: true}浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为.等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。所以passive:true的意思就是告诉浏览器,不会阻止默认事件,你放心的滚动吧,不用等待了。

css

盒子模型: 普通盒模型 怪异盒模型 css3盒模型属性怎么写
选择器优先级
鼠标事件对象中有哪些属性 事件对象scrollHeight,clientHeight的区别
Div内部滚动,鼠标在上面移动,计算鼠标的垂直方向的移动距离。现在想要鼠标往上移动到Div顶部时,如果能滚动的话,自动往上滚动,向下也一样。怎么实现?
实现一个动画效果。

div 默认高度是32px,当hover上去之后,高度由div内部的内容决定。并且给高度过渡加一个动画效果?categories内容不固定。

<!----html------>
<div class="categories">
  <ul class="categories_ul">
    <li v-for="cat in categories" :key="cat">{{ cat }}</li>
  </ul>
</div>
<!----html------>
<!----js------>
// 在组件创建时(可利用浏览器空闲时间),模拟创建和ul、li一样的dom,设置相同的style,得到高度
<!----js------>
.categories {
  position: absolute; // 悬浮抽屉效果
  top: 100px;
  height: 32px;
  display: flex;
  overflow: hidden;
  transition: all 0.2s;
  z-index: 999;
  &_ul {
    flex: 1;
  }
  &:hover {
    height: auto; // auto 没有动画效果 需要设置
    background: #fff;
  }
}

实现一个div高度为浏览器视窗高度的100% - 100px,div内部滚动,并且有一个操作区一直吸附于div的可视区域底部。

<div style="height: clc(100vh - 100px)">
<div style="position:sticky;"></div> // sticky/fiexd/flex
</div>

安全方面

Xss攻击,Vue和react什么代码写法能引起xss攻击。怎么避免xss攻击。
什么是ddos攻击
Cookie有哪些属性,什么是crsf攻击