Skip to main content

js

数据类型

基本的数据类型介绍

在 JS 中共有 8  种基础的数据类型,分别为: Undefined 、 Null 、 Boolean 、 Number 、 String 、 Object 、 Symbol 、 BigInt 。 其中 Symbol  和 BigInt  是 ES6 新增的数据类型,可能会被单独问:

Symbol 代表独一无二的值,最大的用法是用来定义对象的唯一属性名。 BigInt 可以表示任意大小的整数。

值类型和引用类型的理解

值类型是直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储; 引用类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;

事件

事件冒泡 √✨

当一个事件在 DOM 元素上触发时,会冒泡到父元素,直到祖先元素

事件委托/代理 √✨

事件委托是将事件监听器添加到父元素,而不是每个子元素单独设置事件监听器,当触发子元素时,事件会冒泡到父元素,监听器就会触发。

事件委托的好处是:

  • 内存占用减少,因为只需要一个父元素的事件,而不必为每个后代都添加事件
  • 无需从已删除的元素中解绑事件,也无需将事件绑定到新元素上

事件循环机制 √

背景 js 是单线程语言,通过事件循环机制,来协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程阻塞。

一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为 macro-task(宏任务)与 micro-task(微任务)。

事件循环就是执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。

宏任务:

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

微任务:

  • process.nextTick
  • Promise
  • Async/Await(实际就是 promise)
  • MutationObserver(html5 新特性)

for 循环中变量的值

给出以下代码,输出的结果是什么?原因?

for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
console.log(i);

参考回答: 立即输出 5 个 5,并且在一秒后输出 5 个 5 每次 for 循环的时候 setTimeout 都会执行,但是里面的 function 则不会执行被放入任务队 列,因此放了 5 次;for 循环的 5 次执行完之后不到 1000 毫秒;1000 毫秒后全部执行任 务队列中的函数,所以就是输出 5 个 5。

作用域

怎么理解 this √✨

this 是当前执行上下文的一个属性,函数的调用方式决定了 this 的值,this 取值符合以下 6 个规则:

  • 在调用函数时使用 new 关键字,this 是一个全新的对象
const obj = {
name: "objName",
fn1: function () {
console.log(this);
},
};

new obj.fn1();

// fn1 {}
  • 通过 apply/call/bind 调用或者创建函数,this 就是 apply/call/bind 的第一个参数
global.name = "globalName";

const obj = {
name: "objName",
fn1: function () {
console.log(this);
},
};

obj.fn1.apply(global);
// global
  • 通过对象.函数调用时,this 就是该对象
const obj = {
name: "objName",
fn1: function () {
console.log(this);
},
};
obj.fn1();
// obj
  • 如果调用函数不符合上述规则,this 指向全局对象
  • 如果符合多个规则,则较高的规则,1 最高,4 最低,决定 this 的指向
  • 如果函数是箭头函数,this 是它被创建时的上下文
var name = "windowsName";

var a = {
name: "Cherry",

func1: function () {
console.log(this.name);
},

func2: function () {
setTimeout(() => {
console.log(this);
}, 100);
},
};

a.func2(); // Cherry

call/apply/bind 的区别 √✨

call/apply 用于调用函数,第一个参数是 this,apply 第二个参数是数组,call 则是逗号分隔,可传任意长度的参数

bind 用于创建函数,第一个参数是 this,后面的参数是逗号分隔,可传任意长度的参数

对闭包的理解 √✨

https://juejin.cn/post/6937469222251560990 闭包就是有权访问另一个函数作用域变量的函数。

作用:

  • 保护函数的私有变量不受外部干扰,形成不销毁的栈内存
  • 把函数内的值保存下来,实现方法和属性的私有化

常见的 7 个使用场景:

  • 函数 return 一个函数
  • 函数作为参数
  • IIFE,立即执行函数
  • 循环赋值
  • 使用回调函数就是使用闭包
  • 节流防抖
  • 柯里化实现

继承

JS 如何实现继承

继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联(原型对象指针),这样,一个对象就可以通过委托访问另一个对象的属性和函数

理解原型继承

https://juejin.cn/post/6934498361475072014

所有 js 对象都有一个proto属性,指向它的原型对象,当试图访问一个对象的属性时,如果没有在该对象上找到,就会搜寻该对象的原型,以及该对象的原型的原型,层层向上搜索,直到找到一个名字匹配的属性或者到达原型链的末尾。

const obj = { a: 1 };

obj.toString(); // 原型链上找到的toString方法

变量

变量提升 √✨

声明变量会提升到当前作用域的顶部

null、undefined、未声明变量之间的区别 √✨

未声明变量会脱离当前作用域,成为全局作用域下定义的变量,在严格模式下,给未声明的变量赋值,会抛出错误

undefined 是声明变量但未赋值。

null 是声明变量赋值 null,表示空值

== 和 === 的区别是什么? √✨

==是在类型转换后再比较,===是严格相等进行比较

Set、Map、WeakSet 和 WeakMap 的区别? √✨

https://es6.ruanyifeng.com/#docs/set-map

  • set:类似于数组,成员的值都是唯一的,没有重复的值

  • weakSet:不重复的值的集合,成员只能是对象,弱引用,如果不被其他对象引用,会被垃圾回收机制自动回收该对象占用的内存。

  • map:类似于对象,键可以是各种类型的值,包括对象

  • weakMap:生成键值对的集合,键只能是对象,null 除外,键名所引用的对象是弱引用,如果不被其他对象引用,会被垃圾回收机制自动回收该对象占用的内存。

ajax

ajax

ajax 是创建异步 web 应用的一种技术。

通过 ajax,web 应用可以异步向服务器发送数据

jsonp 原理 √✨

通过 script 标签发送跨域请求,通过 callback 查询参数

同源策略

同源策略可防止 JS 发起跨域请求,源被定义为 URI、主机名、端口号的组合。

同源策略可以防止页面上的恶意脚本访问另一个网页的敏感数据。

模块化

CommonJS、AMD、CMD、ES6 的了解

https://juejin.cn/post/6844903541853650951

  • CommonJS:

    • 定义模块:一个文件就是一个模块,每一个模块都是一个单独的作用域
    • 模块输出:模块只有一个出口,把需要导出的内容放到 module.exports 对象
    • 加载模块:用 require 方法,读取一个文件并执行,返回文件内部的 module.exports 对象
    • 用同步的方式加载模块
      • 在服务端,模块文件都存在本地磁盘,读取非常快。
      • 但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
      • 而且浏览器端加载 js 的方式是 script 标签,脚本标签天生异步,传统 CommonJS 模块在浏览器环境中无法正常加载
  • AMD:异步模块定义,在浏览器端模块化开发的规范

    • RequireJS 解决问题
      • 多个 js 文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
      • js 加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长
    • 语法
      • define 函数定义模块,require 函数加载模块
    • 原理:require 函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行。解决了依赖性问题。
    • 推崇依赖前置,在定义模块的时候就要声明其依赖的模块,用户体验好,因为没有延迟,依赖模块提前执行了
  • CMD:通用模块定义

    • SeaJS
    • define 函数参数和 RequireJS 不同
    • 推崇就近依赖,只有在用到某个模块的时候再去 require,性能好,因为只有用户需要的时候才执行
  • ES6 module

    • export 输出模块
    • import 加载模块
  • UMD

    • 是 AMD 和 CommonJS 的糅合,跨平台的解决方案
    • UMD 先判断是否支持 Node.js,存在则使用 Node.js 模块模式。再判断是否支持 AMD,存在则使用 AMD 方式加载模块

工具函数

防抖

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/5

防抖:触发高频事件后 n 秒内,函数只执行一次,如果 n 秒内高频事件再次触发,则重新计算时间

实现原理:每次触发事件时都取消之前的延时调用方法

function debounce(fn) {
let timer = null;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, 500);
};
}

节流

节流:高频事件触发,但在 n 秒内,只会执行一次,节流会稀释函数的执行频率

实现原理:每次触发事件时都判断当前是否有等待执行的延时函数

function throttle(fn) {
let canRun = true;
return () => {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, 500);
};
}

深度优先遍历和广度优先遍历

深度优先遍历,沿着树的深度遍历树的节点,尽可能深的搜索树的分支,属于盲目搜索,无法保证搜索到的路径是最短路径

广度优先遍历,沿着图的宽度遍历节点,属于盲目搜索,无法保证搜索到的路径是最短路径

对 Promise 的理解

Promise 是一个在未来某个时间产生结果的对象,可能处于以下三种状态:fulfilled、rejected、pending。

优点:

  • 避免回调地狱
  • then 方法简单易读
  • all/allsetted 并行执行异步代码

请解释下面代码为什么不能用作 IIFE:function foo(){}(),需要做出哪些修改才能成为 IIFE

IIFE 代表立即执行函数,JS 解析器会把 function foo(){}()解析成 function foo(){}和(),function foo(){}是函数声明,()调用函数却没有指定名称,会抛出 Uncaught SyntasxError:Unexpected token 错误

修改方式是再加一对括号,(function foo(){})()或者(function foo(){}()),这样些不会暴露到全局作用域,可以省略函数的名称。

判断浏览器型号

  • 功能检测,'geolocation in navigator',不会在不支持的浏览器出现奔溃和错误
  • UA 字符串,很难解析并且可能存在欺骗性,例如,Chrome 会同时作为 Chrome 和 Safari 进行报告,不建议使用

优化

单页应用是什么,怎么对 SEO 友好

单页应用指的是客户端渲染,浏览器从服务器加载初始页面,整个 web 应用所需的脚本和样式文件。当用户导航到其他页面时,不会触发页面刷新。

优点:

  • 用户感知响应快
  • http 请求减少,不必下载相同的资源

缺点:

  • 首次资源加载过多,初始页面加载时间较长
  • 不利于 SEO,这个可以通过在服务器渲染来优化

性能优化

代码层面:

  • 防抖和节流(resize,scroll,input)。
  • 减少回流(重排)和重绘。
  • 事件委托。
  • css 放 ,js 脚本放 最底部。
  • 减少 DOM 操作。
  • 按需加载,比如 React 中使用 React.lazy 和 React.Suspense ,通常需要与 webpack 中的 splitChunks 配合。

构建方面:

  • 压缩代码文件,在 webpack 中使用 terser-webpack-plugin 压缩 Javascript 代码;使用 css-minimizer-webpack-plugin 压缩 CSS 代码;使用 html-webpack-plugin 压缩 html 代码。
  • 开启 gzip 压缩,webpack 中使用 compression-webpack-plugin ,node 作为服务器也要开启,使用 compression。
  • 常用的第三方库使用 CDN 服务,在 webpack 中我们要配置 externals,将比如 React, Vue 这种包不打倒最终生成的文件中。而是采用 CDN 服务。

其它:

  • 使用 http2。因为解析速度快,头部压缩,多路复用,服务器推送静态资源。
  • 使用服务端渲染。
  • 图片压缩。
  • 使用 http 缓存,比如服务端的响应中添加 Cache-Control / Expires 。

严格模式

  • 无法意外创建全局变量
  • 会使静默失败的赋值抛出异常
  • 视图删除不可删除的属性时会抛出异常
  • 要求函数的参数名唯一
  • 全局作用域下,this 的值为 undefined
  • 捕获了一些常见的编码错误,并抛出异常
  • 禁用令人困惑或欠佳的功能

浏览器的垃圾回收机制

  • 标记清除:标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。
  • 引用计数:它把对象是否不再需要简化定义为对象有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。