基础

2020-07-28  本文已影响0人  吴晗君

scrollWidth,clientWidth,offsetWidth

宽度
例子

  1. scrollWidth不包含边框宽度,包括溢出当前区域需要滚动才能看到的内容
  2. clientWidth是可视区域宽度,不包括滚动条、溢出当前区域需要滚动才能看到的内容、边框
  3. offsetWidth包括滚动条、边框宽度

浏览器进程有哪些,线程有哪些?

进程:主进程(控制各子进程)、各页面单独进程(防止页面奔溃导致整个浏览器奔溃,也避免使用线程因共享内存带来的安全问题)、插件进程、GPU进程(3D渲染)
线程:渲染线程、js线程、定时触发器线程、事件线程、异步http请求线程

执行上下文、变量对象

  1. 对于每个执行上下文都有三个重要属性:作用域链、变量对象、this
  2. 全局上下文的变量对象是windowthis指向window
  3. 变量对象存储了当前上下文中的变量、函数声明。
  4. 函数执行时创建执行上下文,激活变量对象activation object。变量对象中包含实参、内部变量声明、内部函数声明。
  5. 函数执行时,顺序执行代码,改变activation object中的值

github

js作用域链是在函数声明时确定的,还是执行时确定的?

js是词法作用域,是静态的,在声明时确定,github

什么是预编译?预编译发生了什么?

就是预解释,去解析执行代码,进行变量提升。

js的变量提升

  1. var分为三个阶段:创建、初始化、赋值,其中创建和初始化会被预解释执行,初始化为undefined
  2. function也分为三个阶段,不同于var的是,函数是将整个声明语句提升,导致赋值也提升。
var a = 1
function a(){}
console.log('变量:', a)
  1. let也分为三个阶段:不同点在于,let只做了创建提升,初始化是执行到当前行代码时才做的。所以不能使用,也就是暂时性死区。
  2. const分为两个阶段:创建和初始化。创建变量,将值初始化为对应的值。
    image.png
  3. 所谓的暂时性死区就是在初始化之前不能使用该变量。

方方的回答

flatMap

let arr1 = ["it's Sunny in", "", "California"]
console.log(arr1.flatMap(v => v.split(" ")))
console.log(arr1.map(v => v.split(" ")).flat(1))

MDN

String.prototype.matchRegExp.prototype.exec区别

  1. 当有全局搜索修饰符时,match会一次性返回所有匹配结果,返回值是数组,包含了所有匹配的子字符串,exec则是返回从lastIndex位置起的第一个匹配的结果。当有分组匹配时,对match没有影响,exec返回的数组第一项为整个正则匹配的结果字符串,第二项开始会得到分组的结果,如果有多个分组匹配成功,则在数组中依次放置分组匹配结果。
var str = 'a-b-b'
var reg = /([a-z]+)-/g
console.log(reg.exec(str))
console.log(str.match(reg))

git rebasegit merge 区别

  1. git rebase branch如果没有冲突,是把当前分支的那些commit移动到了branch分支后面。
  2. git rebase test1 test2 等于以下写法
gco test2
git rebase test1

得到的结果是test2分支的修改移动到了test1分支的后面。

  1. git pull -r的时候其实是git pull --rebase,相当于git rebase remoteBranch。将本地的commit往后移动,远程分支别人提交的commit放在前面。如果有冲突的话解决冲突。
  2. 不要在公共分支rebase分支,原因?

git revertgit reset --softgit reset --hard区别?

  1. git revert是使用一个新的commit来做回滚,reset --hard直接删除之前的commit,这样会有个问题是。如果老分支中含有之前的commit,将老分支合并到该分支时,还会把原来的那些commit带上。
  2. soft还会将文件改动保留在暂存区,hard会直接将改动删除。

版本号带在文件后面和文件名是hash有什么区别?

  1. 版本号带后面,问题在于如果只变更了其中一个文件,其他文件都没有变更,那么其他文件。
  2. 如果版本号放到文件后面,那就是覆盖式发布,一旦动态生成的页面html和放在CDN上的css资源同时有改动。更新谁都不成,如果先更新html,那么在更新静态资源的间隔时间,加载到的是旧版本的css,因为新版本还没发。样式会错乱。如果先更新css,那么如果本地有缓存的用户,没有问题,但是新用户加载到的就是旧html+新css,那么就会有问题。知道发布新的html,则会去加载新的css,才会正常。

pachage.json中~^区别

安装1.1.x的最新版本,不超过1.2.0
^安装1.x.x的最新版本,不超过2.0.0

npm script中&&&的区别

&并行。&&串行。a&b&&c的执行顺序是a和(b+c)并行,b和c之间串行。

Nginx

文章
epoll模型、长链接和content-length的关系

进程和线程

  1. 进程和线程都是CPU工作时间段的描述,进程粒度更大。
  2. 一个进程下的单个线程复用该进程的上下文环境。
  3. CPU在切换进程时消耗的资源更多,这时候就需要开启线程,切换线程因为复用上下文,消耗能源少。
  4. 但是也不能全用线程,因为一旦一个线程崩了,因为共享上下文环境(内存),就会导致其他线程也不能正常工作。进程则是独立的,每个子进程不互相影响。所以多个独立的应用是开启独立的子进程,单个应用内开启多个线程提高工作效率。

package.jsondependenciespeerDependencedevDependencies区别

对于项目来说,没啥区别,只是用于程序员自己区分是开发环境的依赖还是生产环境的依赖。对于npm包来说,在被用户安装时,devDependencies不会被用户安装,dependencies会被安装。同时在npm2版本中peerDependence中的包会自动被安装到和依赖包同一层级而不是当前包的子目录node_modules中,解决项目中依赖同一个包而版本不同的问题。npm3后不会自动安装,但是会在命令后中提示用户。

参考

instanceof

必须是 something instanceof object,右边必须是对象,否则报错,右边是null也报错。

前端性能优化

  1. DNS预读取 X-DNS-Prefetch-Control
  2. preload预先加载各种类型的文件,用于当前页面使用,高级浏览器默认开启
  3. 压缩资源,开启gzip
  4. 小图片base64,减少请求数量。

为什么js放页面底部,css放页面上方?

  1. js会阻塞浏览器解析html文档,浏览器的domContentLoaded事件会等js执行完才进,放在上方和底部没什么影响。但是现代浏览器会边解析边渲染,如果把js放上方,会阻塞html的解析,dom解析被延迟。空白时间就会加长。如果是旧浏览器,那就没啥区别。
  2. css不阻塞html的解析,但是阻塞html的渲染。如果放到底部,由于现代浏览器从上到下边解析边渲染,会先渲染没有样式的html,再渲染有样式的html。如果是旧浏览器,等样式全部下载完再渲染,也会导致css资源加载时间较晚导致渲染时间推迟。

尾调用优化

起因在于函数调用形成调用帧,函数内嵌套函数调用,形成调用栈。外层函数帧需要等内层函数调用完毕才会消失。但是直接return函数调用结果的话,外层函数调用帧可以直接消失,因为不会有其他操作,只保留内部函数帧即可。常见的就是Fibonacci序列。

尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。

PWA

  1. 性能优化瓶颈,首屏加载速度
  2. 用户留存率低,网站在被关闭后没办法和用户建立联系。不像app可以在后台通知用户
  1. 修改index.html页面,注销掉所有service-worker
  2. 通过动态请求js,根据js内的service-worker开关变量来判定是否要注销掉所有service-worker

获取元素的相对位置和绝对位置

  1. getBoundingClientRect.left|top获取距离视口的位置
  2. getBoundingClientRect.left + document.documentElement.scrollLeft获得绝对位置

微任务和宏任务区分

microtask:promise 中的 then,MutationObserver,ie中的setImmediate
macrotask:ajax,setTimeout,setInterval,事件绑定,postMessage(MessageChannel)

children和childnotes区别

childnotes会把空白节点也算上

substr、substring、slice区别?

  1. 三者作用相同,都是获取子字符串,都不会改变原字符串。
  2. substring和slice的参数都是起始位置,结束位置,左闭右开。
  3. str.substr(起始位置, 长度),第一个参数为负数表示倒数(length + 负数),第二个参数为负数会被转成0
  4. slice两个参数为负数都会被转为倒数第n个
  5. substring两个参数为负数都会被转为0。如果第二个参数比第一个大会互换位置。因为slice是转为负数,所以不会有自动换位置的操作。

字符串中获取位置的api?

  1. '12w1w'.match('w').index // 可正则
  2. '12w1w'.search('w') // 可正则
  3. '12w1w'.indexOf('w')
  4. '12w1w'.lastIndexOf('w')

数据类型

基础数据类型

  1. String
  2. Number
  3. Boolean
  4. undefined
  5. null
  6. bigint
  7. symbol

引用类型

  1. object

ES7

  1. Array.prototype.includes
  2. 求幂运算 **

ES8

  1. asyncawait
  2. 求幂运算 **
  3. Object.values
  4. Object.entries
  5. padStart、padnd

有哪些遍历对象的方法

  1. Object.keys获取非原型链上的所有可枚举属性,ES5中的prototypes默认可枚举,ES6 class默认不可枚举。
  2. for...in 获取包括原型链上的所有可枚举属性
  3. Object.entries获取非原型链上的所有可枚举属性,
  4. Object.getOwnPropertyNames获取非原型链上的所有非symbol属性,包括不可枚举属性
  5. Object.getOwnPropertySymbols获取非原型链上的所有symbol属性,包括不可枚举属性
  6. Reflect.ownKeys = Object.getOwnPropertySymbols + Object.getOwnPropertyNames
    以上得到的数组顺序都相同。
  7. for...in循环出的是keyfor...of循环出的是value

importrequire区别

  1. import值的引用,require是值的复制,import引入的变量会被影响。
  2. require是运行时加载,import是编译时输出接口

call、apply、bind使用场景

1.call常用于继承,实例属性
2.apply可用于求最值
Math.max.apply(undefined, [1,2,3])
3.bind常用于传递参数

如何防止递归函数被复写

arguments.callee可以读取到当前函数

如何团队提效

函数防抖和函数节流

防抖:只有最后一次会被执行,输入框输入内容搜索

function debounce (fn = () => {}, timeout = 300) {
  let timeoutID = null
  
  return function (...args) {
    if (timeoutID) {
      clearTimeout(timeoutID)
    }

    timeoutID = setTimeout(() => {
      // 这里的this是上下文中的this
      // 但是函数直接执行,this取的是window
      fn.call(this, ...args)
    }, timeout)
  }
};

const onInput = debounce(function (...args) {
  console.log(this)
  console.log(...args)
})


window.input.addEventListener('input',function (...args) {
  onInput.call(this, ...args)
}, false)

节流:一段时间内只触发一次,CF游戏按住鼠标一直射击,子弹匀速打出


function throttle (fn = () => {}, timeout = 300){
  let lastTime = null
  let timeoutID = null

  return function (...args) {
    const now = Date.now()
    // 如果之前执行过并且还没到预定时间
    if (lastTime && now < timeout + lastTime) {
        clearTimeout(timeoutID)
        timeoutID = setTimeout(() => {
            fn.call(this, ...args)
        }, timeout)
    } else {
        // 第一次进入则直接执行,并初始化最近一次执行时间
        lastTime = now
        fn.call(this, ...args)
    }
  }
}

function shot () {
  console.log('shot', this)
}

const throttledShot = throttle(shot, 100)

gun.on('shot', function(...args){
    throttledShot(...args)
})

基本思路,注意这里的没有考虑好this指向的问题。

图片懒加载

var num = document.getElementsByTagName('img').length;
var img = document.getElementsByTagName("img");
var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历

lazyload(); //页面载入完毕加载可是区域内的图片

window.onscroll = throttle(lazyload, 500);

function lazyload() { //监听页面滚动事件
    var seeHeight = document.documentElement.clientHeight; //可见区域高度
// 这里是因为旧版本谷歌浏览器只支持document.body.scrollTop
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
    for (var i = n; i < num; i++) {
        // offsetTop是节点离其最近的position非static的元素或者body
        if (img[i].offsetTop < seeHeight + scrollTop) {
            if (img[i].getAttribute("src") == "default.jpg") {
                img[i].src = img[i].getAttribute("data-src");
            }
            n = i + 1;
        }
    }
}

函数式编程

  1. 纯函数:没有副作用,不依赖除参数外的变量,不改变参数及函数外的变量,任何时候调用得到相同结果
  2. 细粒度拆分步骤,进行函数组合
  3. 模块化,可复用

柯里化

当函数参数很多的时候,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

function currying (fn, ...args1) {

    return function (...args2) {

        return fn(...args1, ...args2)

    }
}

function add (x, y) {
  return x + y
}

var increment = currying(add, 1)

increment(2) === 3

作用域链

当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

闭包

在函数内使用函数外的变量。

跨域解决方案、JSONP

CORS兼容到ie8,ie8和ie9是通过XDomainRequest实现,注意如果要跨域带cookie则xhr.withCredentials = true

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 

// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true"); 

// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");

参考地址

call实现

ES6实现

  Function.prototype.call1 = function (context, ...rest) {
    // 哪个函数调用call,this就指向它
    context = context || window
    const fn = Symbol('fn')

    context[fn] = this
    const result = context[fn](...rest)
    delete context[fn]

    return result
  }

ES5实现错误示例

  // 错误示例
  Function.prototype.call2 = function (context) {
    // 哪个函数调用call,this就指向它
    context = context || window
    // 防止覆盖上下文中的属性
    const fn = '_fn' + Math.random()
    let args = []
    for (let i = 1; i < arguments.length; ++i) {
      args.push(arguments[i])
    }
    
    context[fn] = this
    // 不能用join:用了之后函数参数就变成一个了,参数值是join后的字符串
    // const result = eval('context[fn](args.join())')

    // 不能直接使用args的原因:
    // [1,2,3].toString()     ===> '1,2,3'
    // ['1',2,3].toString()   ===> '1,2,3'
    // ['abc',1,2].toString() ===> 'abc,1,2'  ===> abc is not defined
    // const result = eval('context[fn](' + args + ')')
    
    delete context[fn]

    return result
  }

ES5正确示例

  Function.prototype.call3 = function (context) {
    context = context || window
    const fn = '_fn' + Math.random()

    context[fn] = this
    let args = []
    for (let i = 1; i < arguments.length; ++i) {
      args.push('arguments[' + i + ']')
    }

    const result = eval('context[fn](' + args + ')')
    delete context[fn]

    return result
  }

github
掘金评论区

apply实现

call的注意点没什么区别,详见call错误示例

    Function.prototype.apply1 = function (context, arr) {
      context = context || window
      arr = arr || []
      const fn = '_fn' + Math.random()
      context[fn] = this
      

      let args = []
      for (let i = 0; i < arr.length; i++) {
        args[i] = 'arr[' + i + ']'
      }
      const ret = eval('context[fn](' + args + ')')
      delete context[fn]

      return ret
    }

    let a = ['name',18,3]
    function test(name, age) {
      return {
        name: name,
        age: age
      }
    }

    console.log(test.apply1(null, a))

bind实现

    Function.prototype.bind2 = function (context) {
      const self = this
      // const outerArgs = [...arguments].slice(1)
      const outerArgs = Array.prototype.slice.call(arguments, 1)
      function noop (){}
      function bound (...args) {
        return self.apply(this instanceof self ? this : context, outerArgs.concat(args))
      }
      noop.prototype = self.prototype

      bound.prototype = new noop()

      return bound
    }

    var value = 1
    let obj = {
      value: 2
    }
    function testBind (a, b) {
      console.log(this.value)
      console.log(a, b)
      this.yy = 'yy'
    }

    testBind.prototype.xx = 'xx'

    const boundFn = testBind.bind2(obj, 3)

    // boundFn(4)

    let instance = new boundFn(5, 6)
    console.log(instance.xx)
    console.log(instance.yy)

数据的判定方式

Object.prototype.toString.call()

4种方式的问题

websocket握手过程

参考

前端持久化的方式、区别

sessionStorage
端口号不同没关系,但是域名、协议必须一样。
生命周期是打开至关闭一个tab页。
在一个页面内通过a标签、window.open打开新页面时,新页面能够共享sessionStorage的值,但是拿到的只是初始化的值,在两个页面
各自改变值不会影响其他页面。如果是新打开的页面,则为一个新的session,值和其他页面无关。

localStorage
遵循同源策略:协议、域名、端口号必须全部一样。4K

cookie
遵循同源策略:协议、域名、端口号必须全部一样。5M,随请求发送,占用带宽。

indexedDB
阮一峰
张鑫旭 indexedDB vs web SQL Database

什么是关系型数据库,什么是非关系型数据库

数据库

为什么需要索引

CSDN

回调函数的劣势

  1. 嵌套多层,难以理解
  2. 难以维护,容易出bug

如何实现有过期时间的localstorage

将过期时间存储在数据中,读取的时候判断一下,如果过期就执行删除逻辑。

Promise和async的区别

asyncgenerator函数的语法糖,需要不断执行迭代器的next方法,返回一个promise对象。

文件上传

掘金

浏览器事件先捕获后冒泡

知乎

事件委托优缺点

个人博客

this指向

个人博客

异步编程优缺点

异步编程优缺点

mouseover和mouseenter的区别

mouseover和mouseenter的区别

DOM

DOM是文档对象模型,作用是将网页转化为一个对象,从而可以使用脚本来操作网页的内容。

  1. parentNode.append(多个节点)
  2. parentNode.prepend(多个节点)
  3. parentNode.appendChild(单个节点)
  4. parentNode.insertBefore(newNode, 某个子节点)
  5. node.before(单个节点) 在节点前新增节点
  6. node.after(单个节点) 在节点后新增节点
  1. parentNode. removeChild(单个自节点)
  2. node.remove(),移除自己
  3. node.replaceWith(新节点) 用新节点替换当前节点
  4. node.

append和appendChild区别

  1. append() 方法可以直接追加字符串为文本节点,比如 append("text") ,appendChild() 不行
  2. append() 方法支持追加多个参数,appendChild() 只能追加一个
  3. append() 方法没有返回值,而 appendChild() 会返回追加进去的那个节点
  4. 和 append() 同时期加入 DOM 规范的方法还有 prepend() 、before()、after() 等
  5. jQuery 中存在的 appendTo() 方法并没有和 append() 一起加入到 DOM 规范里

BOM

BOM
BOM

proxy作用

  1. 拦截属性读取,只读属性读取时报错
  2. 相比于Object.defineProperty,多了很多拦截器,比如apply、deleteProperty等
  3. 也可以拦截对数组的操作

css文件会阻塞DOM解析吗?会阻塞DOM渲染吗?会阻塞js执行吗?

  1. 不会阻塞DOM解析,文档对象模型(DOM)树和css对象模型(CSSOM)树是并行解析的
  2. 谷歌浏览器会阻塞DOM渲染,等CSS文件完全加载完毕才会执行渲染,避免重复计算布局、渲染。各浏览器的渲染步骤
  3. 会阻塞js执行,因为css文件和js都有可能改变样式,这就可能导致多次渲染,所以先等css加载完毕再执行js。JS 也有可能会去获取 DOM 的样式,所以 JS 会等待样式表加载完毕。
  4. 参考文章

浏览器是边解析边渲染吗?

不是,会等所有内容解析执行完成再渲染。
segmentfault.com

DOMContentLoaded和onload区别?

  1. DOMContentLoaded是等页面上html和js解析完成,不等待图片、css文件加载完成
  2. 有 defer 属性的脚本会阻止 DOMContentLoaded 事件,直到defer脚本被加载并且解析完成。
  3. onload是等所有资源加载执行完成
  4. 页面生命周期文章

js会等样式文件加载完成再执行,但是一旦将script标签往上移动,就会立即执行,因为script标签没有样式要等,而DOMContentLoaded事件不等css样式文件的加载。

<link rel="stylesheet" href="css.php">
<script>
document.addEventListener('DOMContentLoaded',function(){
    console.log('3 seconds passed');
});
</script>

错误处理

stopImmediatePropagationstopPropagationpreventDefaultreturn false

  1. stopImmediatePropagation用于阻止调用后续的监听相同事件的函数
  2. stopPropagation用于停止冒泡
  3. preventDefault用于阻止默认事件发生
  4. return false

addEventListener('click', fn, useCaptcha)

useCaptcha默认为false,表示在冒泡阶段接收事件,如果设置为true,则是捕获阶段接收事件

document.querySelector('.parent').addEventListener('click', () => {
  console.log('设为false,冒泡阶段才接收,后面输出;设为true,捕获阶段就接收,因为事件流是先捕获,所以会先于子节点输出')
}, false)

document.querySelector('.child').addEventListener('click', () => {
  console.log('111')
})

addEventListener 的passive是怎么回事?

总的来说,在鼠标滚轮或者触摸滑动时,浏览器必须等touchmovemousemove执行完才能知道要不要阻止屏幕滚动,这样就会导致屏幕滚动会卡顿。所以加入了该参数来显式声明不会有preventDefault的行为。是一种性能优化。
紫云飞

已刷面经

基础习题

commonjs和esmodules的区别

  1. commonjs引入非引用类型变量时,变量变化时,外部模块内得到的值还是旧的,这是因为只执行commonjs的模块只是在被引入的时候执行一次,后面全部到installedModules[moduleId]. exports中取。所以取到的是值的拷贝。所以引入引用类型变量时,是会跟随者一起变化。因为拷贝的是引用类型变量的地址
  2. esmodule引入变量时,注意分两种情况。如果是通过export default导出的非引用类型,则也是导出值的拷贝,通过export {}导出的,则是导出值的引用,在其他模块取到的是一个地址。
  3. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
  4. CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立模块依赖的解析阶段
    参考地址
上一篇 下一篇

猜你喜欢

热点阅读