interview

401 阅读33分钟

CSS

元素宽度和高度随着屏幕的大小变化而变化

flex 布局:flex: auto

css 如何实现分页

伪类 ::after 和 :after 的区别

  • CSS 伪元素::after 用来创建一个伪元素。通常会配合 content 属性来为该元素添加装饰内容。这个虚拟元素默认是行内元素。
  • element:after { style properties } /_ CSS2 语法 _/
  • element::after { style properties } /_ CSS3 语法 _/

position:fix 和 animation 的问题

animation 属性

less,sass,stylus 区别

div 里面放 img,因为大小问题会失真,如何处理; background-repeat:cover 是怎么处理的,放大还是怎样

有几种动画类型动画的属性

Document

触发屏幕大小变化的 DOM 事件是什么: window.resize

JS

error 的类型

  1. Uncaught ReferenceError: a is not defined
  2. Uncaught ReferenceError: Cannot access 'c' before initialization

利用事件冒泡实现一个东西?

JS 单例

调用一个类,任何时候都返回同一个实例

setTimeout 变成同步

数组 api

1、改变原数组 :splice、slice、reverse 、sort、push、pop(删除并返回数组最后一个元素)、shift(操作数组开头的第一个数据 删除)、unshift(添加) splice:增(第二个 deleteCount=0,第三个参数等为新增的元素),删(第一个和第二个参数),改(三个参数)

  • let arr = [1,2,3,4];arr.splice(1,1);console.log(arr); //splice(index,deleteCount); 第三个参数为替换删除的元素
  • [1,2,3,4].splice(1);console.log(arr); 删除 index=1 后的所有元素 //[1,3,4];
  • let arr = [1,2,3,4];arr.pop();console.log(arr); // [1,2,3]
  • let arr = [1,2,3,4];arr.shift();console.log(arr); // [2,3,4]删除数组开头第一个元素
  • let arr = [1,2,3,4];arr.unshift(0);console.log(arr); //[0, 1, 2, 3, 4] 添加元素在数组开头

2、循环方法:every、some (返回值为布尔值)、filter 、 map(返回新数组);forEach 没有返回值、reduct 3、查找元素位置: [1,2,5].IndexOf(5) // 2 返回指定元素在数组中的位置: 4、返回满足条件元素元素 :find、findIndex 返回满足条件的第一个元素的位置 index 其他方法:sort,reverse,join,concat,includes

JavaScript是一种什么类型的语言

JavaScript变量的数据类型可以修改的,而Java,C++语言使用需要在声明变量之前定义变量的数据类型。这种声明变量之前需要确认变量数据类型的称为静态语言。运行中需要检查数据类型的语言为动态语言。 JavaScript就是动态语言。TS类似于将JavaScript动态语言转化为了静态语言。

c=a在C++代码中可以把init类型的变量a赋值给了bool型的变量c,这段代码可以编译执行,C编译器会隐式的把init类型的a转化为bool类型,这种偷偷转化称为隐式类型转换,而支持隐式类型转化的语言称为弱类型语言,不支持隐式类型的语言称为强类型语言,C和JavaScript都是弱类型语言。

JavaScript是一个弱类型的、动态的语言。弱类型意味着不需要告告诉JavaScript引擎变量是什么数据类型,JavaScript引擎在运行时会自己计算出来。 动态的意味着可以让同一个变量保存不同类型的数据。

JavaScript有哪几种数据类型

  • 一共8种、7种原始数据类型,和引用数据类型
    • String、Number、Boolean、Undefined、Null、Symbol、BigInt、Object 为什么会有原始数据类型和引用数据类型因为他们在内存中存放的位置不一样。那么JavaScript引擎在运行代码的时候数据是如何存储的?

栈堆

JavaScript在存储代码时有三种类型内存空间:代码空间、栈空间、堆空间。

  • 栈空间就是调用栈。用来存储执行上下文的。执行上下文中有变量环境,初始化变量为undefined
  • 但是遇到对象类型的声明时,栈中存储的变量值为地址值1003,然后开辟堆内存,堆内存中有地址值和值得对应关系比如1003:{name:'zhangsan'},栈中的变量值指向堆内存中对应的地址值。 image.png

image.png

V8引擎是如何执行JavaScript代码的

V8引擎使用解析器和编译器将源代码翻译成机器能读懂的字节码。编译型语言比如C++会将使用编译器将源代码转化为AST到二进制文件,然后执行。解释性语言像JavaScript会使用解释器将源代码转化为AST,字节码,再执行。JavaScript源代码解释器不能理解,解释器能够理解的是AST抽象语法树,所以解释器会将

  • 如何生成AST,将JavaScript源代码token化,转化为一个个token这个过程为分词,根据语法规则将token转化为AST这个过程为解析,生成执行山下文。
  • 解释器将AST转化为字节码,将热点代码通过编译器转化为机器码并保存下次使用,进而得到了性能优化。使用解释器和编译器这种组合成为即时编译JIT。 将JavaScript源代码先解析为字节码

AST案例:

  • Babel和ESlint都是使用AST,Babel会将ES6代码转化为AST,将AST转化为ES5的AST,使用ES5的AST生成JavaScript源代码。
  • ESlint将JavaScript源代码转化为AST,使用AST检查代码规范。

浏览器执行任务是通过消息队列来排队执行的,定时器是浏览器增加了一个延时队列

数据类型的判断

为什么要进行数据类型的判断,因为JavaScript是弱类型的动态的语言,不是在声明变量之前告诉JavaScript引擎确定数据类型,而是JavaScript引擎在运行时自己计算数据类型,所以同一个变量可以保存多个数据类型。

  1. 基础数据判断:typeof
  2. 数组的判断: Array.isArray
  3. 对象的判断:Object.prototype.toString.call({}) //'[object Object]' ;
  • Object.prototype.toString.call([]) //'[object Array]' 为了避免 toString 方法被重写

去重

  • 数组去重
    • Array.from + Set(arr) Array.from(new Set([1,2,3,4,3]))
    • … + Set: …运算符可以将 Set 转换为 Array […new Set([1,2,3,4,3])]
    • for+indexOf 或 includes
  • 字符串去重:[...new Set('ababbc')].join('')
function unique(arr) {
  if (!Array.isArray(arr)) {
    console.log("type error!");
    return;
  }
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) {
      //或!res.includes(item)
      res.push(arr[i]);
    }
  }
  return res;
}

数组扁平化

方法:reduct和concat

function flatten(arr) {
    return arr.reduce((acc, item) => {
      return acc.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}

浅拷贝深拷贝

  • 浅拷贝 Object.assign slice concat
  • 深拷贝手写代码

快速排序

函数的防抖和节流

  • 是什么
    • 函数防抖和函数节流都是为了缓解函数被频繁调用消耗性能。
  • 函数节流:

    • 第一次方法执行后,需要计算当前时间和上一次执行时间是否大于指定时间,如果是则执行方法,如果不是则不执行方法。就像水龙开的太大了,给它稍微关小一点,让水有规律的一滴一滴的往下滴。
    • start = 0
  • 适用于函数频繁调用: 节流:scroll 等 DOM 事件

  • 防抖

    • 事件频繁触发时候,让事件在设定时间内的最后一次的触发有效,其他都无效
    • 场景:表单规则校验的时候、防止疯狂点击场景
const box = document.querySelector(".box");
const downCallBack = function () {
  console.log("----");
};
window.addEventListener("mousedown", debounce(downCallBack, 1000));
function debounce(cb, delay) {
  return function () {
    const that = this;
    const args = arguments;
    if (cb.timeoutId) {
      clearTimeout(cb.timeoutId);
    }
    cb.timeoutId = setTimeout(function () {
      cb.apply(that, args);
      delete cb.timeoutId;
    }, delay);
  };
}

变量提升

JavaScript代码执行过程:一段JavaScript代码-编译-执行

  • 变量提升是因为JavaScript代码在执行前,会先被JavaScript引擎编译
  • 编译阶段会创建执行上下文,执行上下文中包含了变量环境对象和词法环境,在变量定义的时候会把变量加入变量环境对象中,初始化为undefined,函数定义的时候会把函数存放在堆中,编译完成后
  • 代码执行阶段,在执行代码的时候遇到变量,会从对应的执行山下文的变量环境对象中查找。

执行上下文

JavaScript引擎在编译JavaScript代码的时候会创建全局执行上下文,执行到函数的时候会创建函数执行上下文

调用栈

调用栈也称为执行上下文栈,因为会创建有很多个执行上下文,全局执行上下文,多个函数自行上下文,JavaScript引擎使用调用栈这种数据结构来管理多个执行上下文。也称为栈空间。

栈溢出

是因为调用栈有数量限制。超出了会导致栈溢出 理解案例:

var a = 2;
function add(b,c){
    return b + c;
}
function addAll(b,c){
    var d = 10;
    result = add(b,c);
    return a+result;
}
addAll(3,6);
  • 变量a、add、addAll会被保存在全局执行上下文的变量环境对象中
  • JavaScript引擎在编译阶段将全局执行上下文压入栈后,开始执行全局代码
  • 执行a=2;将全局之行上下文中变量环境对象中的a=undefined改为a=2
  • 调用addAll
  • JavaScript引擎编译addAll函数,并为addAll创建函数执行上下文,包括函数执行上下文中的变量环境对象和词法环境,将addAll的函数执行上下文压入调用栈中
  • 进入函数代码执行阶段,执行d=10,将addAll执行上下文中变量环境对象中d值改为10;
  • 执行add函数,创建add函数执行上下文,并压入调用栈,当add函数返回时,add函数执行上下文从栈顶弹出,将addAll中变量环境对象的result设置为add函数的返回值9;执行return a+result;addAll函数执行上下文从栈顶弹出
  • JavaScript执行流程结束

let const变量如何影响执行上下文的

  • var声明的变量会放在执行上下文的变量环境对象汇总
  • let const声明的变量会放在执行上下文的词法环境中
  • 执行的时候var定义的变量值会在变量环境中被修改,let、const定义的会在词法环境中被修改,所以let、const定义和var定义的都是独立的。 image.png

块级作用域

块级作用域就是通过执行上下文中的词法环境的栈结构实现的。

function foo(){
 var a = 1;
 let b = 2;
 {
  let b = 3;
  var c = 4;
  let d = 5;
  console.log(a);
  console.log(b);
 }
 console.log(b);
 console.log(c);
 console.log(d);
}

作用域

作用域是通过执行上下文的变量环境对象实现的,调用栈的中每个执行上下文对应的变量环境都会有一个指向,这个指向引导了JavaScript引擎变量的查找,这个指向就是作用于作用域链。

  • 全局作用域
  • 函数作用域 foo和bar都是指向全局作用域,为什么?因为词法作用域,所以说作用域链是通过词法作用域实现的。 image.png

词法作用域

词法作用域是在代码编译阶段就决定的,跟函数是怎么被调用的就没关系了。 image.png

闭包

  • 项目中是否用到,用到有什么两点。
    • 通过闭包记录分页信息,多选数据
      • 项目案例:用户在个人信息页面,可以修改自己的行业,点击多选行业按钮,跳转选择行业列表页,用户点击了农业,使用闭包将农业存储在闭包中cacheFn.set,点击确认修改,返回到个人信息页,初始化的时候直接获取cacheFn.get
    • 用户选择时间后,将时间存储在闭包中,存储在内存中,即使用户跳转到其他页面选填其他内容,回退到该页面也可以看到之前选择的时间。
    • 也可以用于存储某种状态,让状态跳转页面回来也能保存。
  • 作用域有全局作用域和函数作用域,但是两个函数嵌套形成了作用域的交叉,根据词法作用域的规则,内部函数可以访问外部函数声明的变量,即使外部函数被销毁,内部函数可以访问其外部函数中声明的变量和参数。JS通过函数作用域属性记住创建变量的位置,这些函数的变量的集合就是闭包。在哪里找到变量就在哪里修改。 有了闭包:变量查找的过程就是在;当前作用域-闭包-全局作用域(全局执行上下文的变量环境)
function f() { 
 let value = 123; 
 return function() { alert(value); } 
} let g = f(); // g.[[Environment]] 存储了对相应 f() 调用的词法环境的引用
function makeCounter() {
    let count = 0;
    return function () {
      return count++;
    };
}
let counter = makeCounter();
alert(counter()); // 0  
alert(counter()); // 1
alert(counter()); // 2

a = ++i; i=i+1;a=i; a = i++; a=i;i=i+1; 假设返回值为a = count++;

  • 第一次:alert(counter()); function () {return count++;}; 在函数作用域中找count,没找到,从上层函数中找,隐藏的作用域已经记住了创建count变量的位置,count=0; a = count; count + 1; 让上层函数作用域count=1
  • 第二次调用alert(counter()); function () {return count++;}; 在函数作用域中找count,没找到,从上层函数中找,隐藏的作用域已经记住了创建count变量的位置,a = count=1; 返回a;修改count=count+1=2 //...
function makeCounter() {
    let count = 0;
    return function () {
      return ++count; //换一下结果是什么?
    };
}
debugger;
let counter = makeCounter();
alert(counter()); // 1  
alert(counter()); // 2
alert(counter()); // 3
/**
 * 通过闭包将跳转需要缓存的数据保存在内存中
 * get 第二个参数可以自定义获取不到对应纸的默认数值
 */
const cacheFn = () => {
  const cacheObj = {};
  return {
    add(key, value) {
      cacheObj[key] = value;
    },
    get(key, defaultValue: string | any = '') {
      return cacheObj[key] || defaultValue;
    },
    clear() {
      for (let key of Object.keys(cacheObj)) {
        delete cacheObj[key];
      }
    },
    debug() {
      return cacheObj;
    },
  };
};
export const cacheInfo = cacheFn(); //此时外部函数销毁,产生了cacheObj闭包

this

this是JavaScript为了在对象内部的方法中使用对象内部的属性,因为词法作用域无法实现这点。 this和作用域没有太多的的联系。

thid分为:

  • 全局执行上下文this:指向window
  • 函数上下文this
    • 根据函数的调用方式确定:普通调用执行window
    • 使用call、bind、apply改变this
    • 对象.方式调用
    • 构造函数调用:指向新生成的对象

this的问题

  • 嵌套函数中无法继承this
    • 解决方法是使用变量self保存this
    • ES6箭头函数
    var myObj = {
     name: 'geek',
     showTime: function(){
      console.log(this);  // 修改self=this
      funtion bar(){console.log(this)} //this指向window //修改为self
     }
    };
    myObj.showTime(); 
    
var bar = {
    myName: "geek",
    printName: function () {
      console.log(myName); // 如何访问对象内部的属性?改成console.log(this.myName);
    },
};
function foo() {
    return bar.printName;
}
let myName = "jike";
let _printName = foo();
_printName();
bar.printName(); 
// 两次都是打印全局变量

原型链

ES6 新特性

  1. 块级作用域:let const
    • 声明的变量在JavaScript引擎编译阶段放在执行上下文的词法环境中
    • 块级作用域是通过执行上下文的词法环境实现的
    • 没有变量提升
    • const声明常量,只要地址值不变就行
  2. 变量的解构赋值
  3. 模板字符串
  4. 箭头函数,函数参数默认值,函数参数的结构赋值,函数参数有默认值参数会形成单独的参数作用域
    • this 指向上层作用域的 this,箭头函数没有 this,所以自然也不能使用 call,bind,apply 调用的时候指定 this、解决了嵌套函数无法继承上层函数this的问题
    • 没有 arguments 对象,使用...rest let Fn = (...rest) => {}
    • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
    • 尾递归调用栈的优化,不会出现栈溢出(stack overflow),相对节省内存。
let Fn = (...rest) => {
  console.log(rest); //[1,2]
};
Fn(1, 2);
  1. Class 类

    • constructor 是类的默认方法,当使用 new 调用 Class 类,会被自动调

    • 类中定义的方法都会被实例对象继承、static 静态方法不会被实例对象继承

    • get set 关键字

    • 类的数据类型就是函数,类本身就指向构造函数。

    • in 可以识别类的属性。

    • new.target 为类的的本身,如果类被继承则是被继承的子类,new.target 可以确定 Class 是使用 new 进行调用的,如果不是则是 undefined

    • extends 关键字实现继承

    • super 关键字:super 作为函数,是父类的构造函数,子类的构造函数必须执行一次 super,为了执行父类的方法(子类 React 继承父类 React.Component,constructor 中必须调用 super())

      class Point {
        constructor() {
          // ...
        }
        toString() {}
        toValue() {}
      }
      // 等同于
      Point.prototype = {
        constructor() {},
        toString() {},
        toValue() {},
      };
      
      class A {
        #foo = 0;
        m() {
          console.log(#foo in this); // true
          console.log(#bar in this); // false
        }
      }
      
  2. 新增 Set Map 两个数据结构

    • Set
      • Set 是一个构造函数,类似于数组,但是Set的元素都是唯一的,传入一个可迭代对象。可用于来数组去重
      • Set内部NaN等于NaN(NaN === NaN 是 false)
      • Set实例的循环方法:for of,forEach,键名和键值都是同一个值。
      • Set可以并集(Union)、交集(Intersect)和差集(Difference)。
      • 一般用于去重,或者查看数据结构中是否有某个元素
      let a = new Set([1,2,3,4]);
      let b= new Set([3,4,5,6,7]);
      let bingji = new Set([...a,...b]);
      let jiaoji = new Set([...a].filter(aitem=>b.has(aitem)));
      let diff = new Set([...a].filter(aitem=>!b.has(aitem))); //a相对于b的差集
      
    • Map
      • Map的键可以是任何类型,就是Hash 结构。区别于Object的值只能是字符串
      • 用途:做单例,判断该类型是否has有值,有的话就不需要调用方法,直接取值,没有调用方法处理值,将处理后的值set到Map
      • 也可以用于封装渲染组件,key对应组建名称,值对应组件
      • Map实例可以使用forEach遍历 let map = new Map(); map.forEach((item,key,map)=>{});
      • Map转数组 [...map]
        const myMap = new Map()
          .set(true, 7)
          .set({foo: 3}, ['abc']);
        [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
        
      • 数组转Map
         new Map([
           [true, 7],
           [{foo: 3}, ['abc']]
         ])
         // Map {
         //   true => 7,
         //   Object {foo: 3} => ['abc']
         // }
        
  3. Symbol 原始数据类型

    • 创建一个独一无二的指

    • 用于给对象创建匿名的独一无二属性名。所以现在属性名有两种类型,字符串和symbol类型

    • 用于单例,私有属性,可以消除魔术字符串(消除在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值)。不能使用for-in循环出来,只能使用Object.getOwnPropertySymbols(obj)

    • Symbol函数的参数只是当前Symbol值的描述,为了让我们自己知道是哪个Symbol。

      // 没有参数的情况
      let s1 = Symbol();
      let s2 = Symbol();
      s1 === s2 // false
      // 有参数的情况
      let s1 = Symbol('foo');
      let s2 = Symbol('foo');
      s1 === s2 // false
      s1.description // foo
      
      const mySymbol = Symbol();
      const a = {};
      
      a.mySymbol = 'Hello!';
      a[mySymbol] // undefined Symbol值作为属性名称不能使用.运算符获取,.运算符后只能是字符串类型
      a['mySymbol'] // "Hello!"
      
  4. 新增字符串 API

    • includes(),startsWith(),endsWith()
  5. 对象的 API Object 方法

    • Object.is(v1,v2) 判断两个值是否相等 等价于===
    • Object.assign() 合并两个对象
    • Object.keys(obj) 返回对象的属性名组成的数组
    • Object.values(obj) 返回对象属性值组成的数组
    • Object.entries(obj) 返回属性名和属性值为键值对的数组
  6. 新增数组 API Array.from() 转化一个类数组或可迭代数组为一个真数组 Array.prototype.includes(searchE,fromIndex) 从数组的哪个索引开始找数组中是否包含该值

异步解决方案

  • 请求没回来之前,但是已经超过 10s,需要执行请求超时怎么怎么处理 Promise.race([接口请求,Promise封装的定时器])
Promise.race([
    new Promise((resolve) => {
      window.getValue('recomed', (res) => {
        resolve(res.value || 'success');
      });
    }),
    new Promise((resolve) => {
      setTimeout(() => {
        resolve('success'); // 超时使用默认的平铺
      }, 10000);
    }),
  ])
  • 项目中promise 一般用来做什么?
    • 接口调用,流程处理

gernerator函数yied

async await

async&await

github.com/dwqs/blog/i… segmentfault.com/a/119000001…

  • await 之前都是立即执行
  • await 做了什么?
    • 实际上 await 是一个让出线程的标志。
    • await 后面的表达式会先执行一遍,
    • 将 await 后面的代码加入到 microtask 中
  • async await 本身就是 promise+generator 的语法糖,所以 await 后面的代码是 microtask。
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
// 等价于
async function async1() {
    console.log('async1 start');
    Promise.resolve(async2()).then(() => {
    console.log('async1 end');
})

async 怎么处理返回值

  • 从结果中可以看到 async 函数返回的是一个 promise 对象,
  • 如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象

当调用一个 async 函数时

  1. 会返回一个 Promise 对象。
  2. 当这个 async 函数返回一个值时, Promise 的 resolve 方法会负责传递这个值; 当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

await 在等什么?

等一个 Promise,等待 Promise 处理完成

await 的返回值是什么?

Promise 正常处理(fulfilled),其回调的 resolve 函数参数作为 await 表达式的值。 若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。 new Promise 第一个回调时同步的 返回值的得出

事件循环

JavaScript语言本身没有事件循环,事件循环是浏览器V8引擎用于执行各个任务的一套机制。遇到异步的任务V8引擎会将任务维护进一个微任务队列。

宏队列:定时器、微队列: .then 执行顺序: 同步(new Promise,then 的注册) 事件循环 微队列 宏队列 注意点: 放进队列不代表被执行 什么时候被注册到微队列 确定什么时候放进队列中? 定时器中的回调: 什么时候被放进宏队列?

  1. 定时器被调用
  2. 时间到了 第一个.then 回调什么时候被执行? 前面 promise 状态确认 状态看返回值(返回值是同步得出的) 第二个.then 什么时候被放进微队列
  3. 看第一个.then 状态是否确认 状态看返回值

node 事件循环机制

  • NodeJs 10 版本之前都是根据官网 nodejs 的事件循环机制处理异步代码
  • NodeJs 新版本 12 版本后,是根据浏览器的机制来处理异步代码 所以有时候会出现 node 端执行的代码和浏览器端执行代码输出结果不一致 同步代码执行完毕;才会进入事件轮训,先进入 poll 阶段 libuv 是 node 事件循环的引擎 libuv 分为 6 个阶段 机制:(每个阶段都相当于是一个数组,放着异步回调函数) 同步代码执行完毕 poll:
  • 同步代码执行完后,进入的第一个阶段
  • 获取新的 I/O 事件,适当条件下 node 将会被阻塞在这里
  • 控制着从往上跳还是往下轮训
  1. poll 队列有 I/O 回调: 执行完队列任务,进入 check 阶段,并往下轮训 poll -> check -> 往下依次轮训
  2. poll 阶段没任务,timers 中有东西,直接跳到 timers 队列 poll -> timers -> 往下依次轮训
  3. 如果 poll 和 timers 都没有东西,进入 check 阶段执行 setImmediate 往下依次轮训 poll -> check check 没有,poll 阶段阻塞 塞入队列的顺序 往前塞入队列,往后清空队列 微任务什么时候执行
  • node 中任何微任务都在两个宏队列切换的时候执行
  • 两个宏队列进行切换的时候会去检查是否有微队列 process.nextTick 这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列. 当每个阶段完成后, 如果存在 nextTick 队列,就会清空队列中的所有回调函数, 并且优先于其他 microtask 执行 nextTick -> microtask nextTick 的问题: server.listen 先执行 ;server.on 里面的回调当用户请求过来了,才会将回调塞到 poll 队列中; 请求一次塞入一次 process.nextTick 会阻塞,所以尽量不适用;如果 nextTick 内的回调逻辑写的很复杂,会导致请求的 I/O 回调一直无法执行; 所以 process.nextTick 使用有风险, 所以使用 setImmediate();只有高版本的 node 才有 I/O 回调指:fs 读写产生的回调

宏任务和微任务

  • js 引擎将异步任务分为宏任务和微任务
  • 宏任务优先级低
  • 微任务优先级高

微任务:

  • nexttick
  • then - 宏任务
  • 定时器
  • 异步代码指的是里面回调函数的代码

XSS

跨站脚本攻击:Cross Siting Scripting为了区分CSS取名为XSS,黑客往HTML或则DOM中注入恶意脚本,用来攻击用户。

恶意脚本能干什么?

  • 窃取cookie信息:js代码可以拿到cookie信息,然后通过fetch加上cors跨域功能,将数据发送给恶意服务器,用来模拟用户登录。
  • 窃听用户行为:监听鼠标事件
  • 修改DOM伪造假的登录窗口

使用httpOnly保护重要信息,对输入的内容进行过滤或则转码。

TS

JavaScript变量的数据类型可以修改的,而Java,C++语言使用需要在声明变量之前定义变量的数据类型。这种声明变量之前需要确认变量数据类型的称为静态语言。运行中需要检查数据类型的语言为动态语言。 JavaScript就是动态语言。TS类似于将JavaScript动态语言转化为了静态语言。

是JS高级 如何编译成

ts 数据类型

number、string、boolean、null、undefined、array、enum(枚举)、any 、void(函数返回值为 undefined 以外都不行)、object 12. 联合类型(string|number )、类型断言(let a: 123 赋值变量,断言为 number 类型)

  • 接口: 约束对象的类型,接口是对象属性和方法的描述
    • interface 定义接口
      • 接口描述对象类
        • interface Iobj={}
      • 接口描述函数类
        • (source: string, subscribe: string): boolean

接口扩展(继承)

interface A extends B,C{}

类的基本使用同 ES6 和类属性方法的约束

  1. 类的定义
  2. 类的继承
    1. 重写: 子类重写父类方法
  3. 类的多态
  4. 父类型引用指向子类型的实例 ===> 多态
  5. 子类型引用指向父类型实例 ===>子类型没有新的方法才可以
  6. 类的修饰符
  7. public private protected readonly
  8. public 公共的
  9. private 私有的,外面属性方法不可见
  10. protected 类型,子类以外不可见
  11. readonly 只能读不能改
  12. 类的存取器
  13. get set
  14. 类的静态属性
  15. static 关键字 (类本身的属性而不是实例对象上的)
  16. 抽象类
  17. abstract 关键字
  18. 不能被实例化
  19. 抽象类必须包含抽象方法,
  20. 只能子类继承并实现该抽象方法

函数

  • 函数赋值的约束
  • let fn:(x:string,y:string):string = function(x:string,y:string):string{}
  • 函数参数和返回值的约束
  • 返回值 function():string{}
  • 默认参数 function(x:string= "A")
  • 可选参数 function test(x?:string)
  • 剩余参数 function test(x?:string,...args:string[])
  • 函数重载
  • 重新进行更加严格的约束

泛型

  1. 函数泛型参数
  2. 函数多个泛型参数
  3. 泛型接口
  4. 泛型类
  5. 泛型约束

d.ts 声明文件

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。 声明语句: 如果需要 ts 对新的语法进行检查, 需要要加载了对应的类型说明代码 declare var jQuery: (selector: string) => any; 声明文件: 把声明语句放到一个单独的文件(jQuery.d.ts)中, ts 会自动解析到项目中所有声明文件 下载声明文件: npm install @types/jquery --save-dev 有了这个声明文件才能使用这个库相关的语法提示 console.log($('body'))

ts 作用

变量数据类型的约束 实现面向对象的功能

js 项目如何引入 ts

webpack 引入相关库的 d.ts 声明文件

react 项目如何使用 ts 开发

创建一个 react+ts 项目:$ npx create-react-app my-app --typescript 将 ts 添加到 react 项目中 npm install --save typescript @types/node @types/react @types/react-dom @types/jest 接下来,将任何文件重命名为 TypeScript 文件 (例如 src/index.js 重命名为 src/index.tsx )并 重新启动开发服务器!

vue 项目如何引入 ts 开发

使用 vue 脚手架 3 或者 4, 并选择手动指定配置,因为目前不支持

使用 VuePress 搭建在线文档网站

浏览器

浏览器interview - 掘金 (juejin.cn)

LocalStorage和SessionStorage的区别

模块化

CommonJs 可以动态的模块,运行的时候可以加载模块

ES6模块化 import 和 require 区别

  1. require 是服务端模块化方案 commonJS 的实现方案。浏览器端无法识别
  2. import 是 es6 AMD/CMD

React

如何监控数据的更新,hook 怎么监控(react)

那么在 hook 中如何监控数据更新呢?

受控组件和非受控组件,onSubmit 获取列表信息是怎么处理的

input 框: 受控组件通过,一定是在 state 中定义数据,通过 onChange 修改 state 数据来控制表单数据的改变

Redux

action creator 创建一个工厂函数的数据模块 store 集中存放数据,根据 store.getState 获取数据,store.dispatch 更新数据 触发了 reducers 的函数根据 prevState 和 action 修改数据并存入 store 中

react 虚拟 Dom Diff 算法

节点对比 对比节点类型,一样,在对比属性,不一样就删掉节点添加新的节点 组件对比 列表对比 添加 key 好处:提高效率,减少重排重绘

SPA 技术:

局部更新,网址变

useEffects 的作用?

路由两种模式

  1. hash
    1. 缺点 1:路径比较丑
    2. 缺点 2:会导致锚点失效,锚点到#后面找到,但是 hash 模式#后面没有
    3. 优点:出现比 history 早,兼容性比 history 好
  2. history 路由的分类 后端路由 key function 前端路由 key component

MVC MVVM

react和vue有什么区别吗?你可以这样说!

引出 mvc 和 mvvm 的概念。 讲解 react 和 vue 的底层思想。 说出他们的优点和缺点。 实践:你在 xx 项目中,因为 xx 问题所以选择 xx 框架。 最后说出结论。 例如作者自己对这个问题的解答如下: 1). mvc 和 mvvm 具体是指 xxxxxxx,他们的区别是 xxxx,各方的优缺点 xxxx。 baijiahao.baidu.com/s?id=159627…

  • MVVM(前端架构):
    • M: Model 模型
    • V: View 视图
    • VM: ViewModel 视图模型,是连接 view 和 model 的桥梁
    • 两个方向数据传递:
      1. 【模型】转化为【视图】
      2. 将后台传递的数据转化为看到的页面
      3. 通过数据绑定实现
      4. 【视图】转化为【模型】
      5. 将页面转化为后台数据
      6. 通过 DOM 事件监听实现
    • 两个方向都实现称为【双向数据绑定】
    • 在 MVVM 架构下视图和模型是不能直接通信额,他通过 ViewModel 来通信
  • MVC(后端架构):
    • M: Model 模型
    • V: View 视图
    • C: Controller 控制器 页面业务逻辑
    • 特点:
    • 使用 MVC 架构是为了将 M 和 V 的代码分离
    • MVC 是单向通信;View -> Controller -> Model
    • React 只是 MVC 中的 V
  • 为什么会有 MVVM 架构? 早期后台有 MVC 架构,但是前端没有,产生很多后台代码在浏览器上,出现很多兼容性问题,jquery 但是由于代码可扩展性,可维护性比较差,才出现了 MVVM 架构,通过数据的双向绑定,大大提高了开发效率 VUE 就是基于 MVVM 模型实现的一个架构 M: 指 js 中的数据,比如对象,数组等 V:指的是页面视图 viewModel: vue 的实例对象 vue 数据双向绑定: cn.vuejs.org/v2/guide/re… 2). vue 的底层是用 xxxx 实现的,另外碰到数组的话因为有 xx 缺陷,vue 的底层是重写了关于数组的八个函数等等。 3). react 的 jsx 功能强大,灵活性强,但是代码必须要规范,每个人都有自己的代码风格。 4). 因为项目的迭代更新很快,便于多人开发,所以我选择的是 xx 框架。 其实用任何框架都要根据真实环境下的各种因素结合,并不是哪个框架就是强无敌,拿起来直接黏贴复制一把梭的。

webpack

webpack是一个项目打包工具,项目优化工具。 一、webpack的配置: webpack的使用主要围绕他的1)entry入口文件,2)output输出文件目录,3)loaders,正常webpack只支持JS和JSON两种文件类型,其他的比如css,less这些都需要下载一个loader将这些文件类型进行转化。比较常用的babel-loader,css-loader,style-loader,ts-loaderurl-loader,thread-loade,file-loader处理图片和字体等用于对文件进行转化,postcss-loader:不同的浏览器有不同的css样式前缀为了自动补齐css前缀使用的loader,url-loader对较小的资源自动base64处理,less-loader就是讲less转化为css等等。还有4)plugins插件,用于对打包文件的优化,常用是CleanWebpackPlugins清理output中的目录,压缩JS插件,提取相同模块代码的插件,HtmlWebpackPlugin生成html文件自动引入打包的js css文件,还可以进行代码优化比如去除注释,去空格换行符等 5)Mode指定构建环境,配置多个环境,就是根据定义的环境变量去加载不同的webpack.config.js文件,在启动命令的时候配置package.json中的scripts命令,不同的环境启动命令用webpack执行不同的webpack配置文件。

  • webpack进项项目优化
    • output contenthash内容发生变化重新加载,没有变化的访问缓存
    • thread-loader、HappyPack每次 webapck 解析一个模块,HappyPack 会将它及它的依赖分配给 worker 线程中;多线程打包
    • 打包体积:文件压缩、提取公共代码、Externals:对一些依赖包比如echarts使用cdn的方式引入,不打包到bundler中、分包DLLPlugin将基础包打包成一个文件比如react,react dom等、tree shaking只将用到的方法打包,而不是全部的、code splitting 代码分割、提取重复代码,将一个大的 js 文件拆分为多个小的 js 文件、 babel-loader 只能转换ES6用到的语法,使用core-js按需加载使用了哪些 ES6 语法就引入哪些,而不是全部都引入
    • 开启缓存,提升二次构建的效率
    • 小于1M的文件自动转base64 二、webpack的原理

前端项目性能优化

重排、重绘、合成

修改使用JS或CSS修改了布局属性,div.style.height,会将渲染流水线重新更新一遍,消耗最大 修改使用JS或CSS修改了绘制属性,修改了背景颜色, 不会更新Layler阶段,相对于重排开销较小 如果修改了既不是布局属性也不是绘制属性,transform,会直接进入合成线程完成合成阶段,而非渲染主线程,所以开销最小。

一个网站优化重心还是后台服务器(中台、后台做的)

主页的更新

  1. 高重用组件

拆分公共组件(全局公共组件,谋个或某两个组件使用的公共组件)

代码简化,加载的速度会越快,

  1. 组件渲染优化: componentShouldUpdate 控制是否渲染

  2. 对浏览器事件进行防抖,节流的限制

  • 我们搜索框事件上当用户不停输入的时候,会发送 ajax 请求给后台进行验证,使用了防抖限制,节约了请求资源,也减少了后台开销

  • 理解: 在函数频繁触发时,在规定时间内,最后一次才生效

  • 鼠标不停进行点击事件,还有向下滚动触发的事件都会考虑使用节流来节约资源,减少开销

  • 理解: 函数频繁触发时,函数执行一次后。大于设定的事件后才能执行第二次,之后触发都要大于设定的时间。

  1. 定时器,点击事件的优化

  2. 静态资源优化

  3. 懒加载组件 +import()语法 一个域名并发连接数是6个,同时加载6个文件,其他文件就会处于pending状态,如果一次性加载太多,会影响效率,需要懒加载,让其访问的时候才加载

  4. 图片的静态资源 小的icon图base64处理,图片放到 CDN 上

图片为什么要转 base64?

  1. 提升性能:网页上的图片都需要消耗一个 http 请求下载而来,图片的下载需要向服务器发送请求,不需要向服务器发送请求的,base64 可以随着 HTML 下载同时下载到本地,减少了 https 请求 问题: 前端性能优化 —— 减少 HTTP 请求 验证: 图片转 base64 编码可以不发送 http 请求 不再用常规图片,使用 svg

webpack 打包一般都定义了 1M 以下自动转 base64

大图片,OSS 会帮你压缩和拆分,然后放到 CDN 第三方网站手动转再使用 8. 代码分割 import()语法引入组件,返回一个 promise 9. 开启 PWA+ServiceWorkers+浏览器缓存: 让静态页面缓存在浏览器端, 用户即使离线静态页面也可以显示 10. 压缩文件: 集合 webpack 构建工具做的优化 js,css,html,图片的压缩 11. babel + core-js: 使用 babel 兼容性处理的时候,使用 core-js 按需引入需要高级语法,而不是将 es6 的语法全部引入,会导致打包的体谅变大 11. 加快首屏渲染 nodejs 服务端渲染 ssr 首页使用服务端渲染,不是使用常规的钩子去拿数据


移动端

flex 布局 单位:rem 淘宝的 flexible 布局

真机 bug 只能看真机,必须给调试机,连接到电脑,用模拟器调试 vconsole 用于移动端调试

可以直接在手机上,显示控制台调试 华为模拟器 抓包调试 手机配 ip ,看报错

调用原生 app: app 和网页的混合开发 还是 react native 我们的页面嵌套到他们 app,问清楚再回答

em、rem,vw,vh

项目

跨页面数据存储

  • 前后两个页面url Params参数带过去
  • 闭包
  • redux

代码检查 codeView 怎么做?

用到 lodash 哪些库?

别人写了一个组件 你使用的时候需要触发自己写的方法 应该怎么办

版本更新问题

版本更新,用户界面还是没有更新,如何处理

  • 解决: 加上时间戳,或者,按钮显示新旧版本切换

前端开发版本管理: 原因:缓存会导致新发布的资源因为缓存问题无法成功载入 文件名加入版本号或者时间戳的 md5 值: 方法 1): 文件名加上版本号;版本号的来源: 自动生成的版本号:将时间戳、动态 hash 等自动生成的信息作为版本号,在打包脚本中配置好,自动生成版本信息。 方法 webpack 打包静态资源文件:webpack 中配置 contenthash,作为 output 输出文件名,即可自动生成版本信息

2G 大文件传输问题

印象深刻的 bug

你作为项目负责人应该怎么建立项目

自己处理的比较有成就的事情

前后端分离结合 dorker 的方案

做一个兄台的项目,设计十几个业务模块,在前后端分离结合 dorker 方案上出了一个方案,代码推到发布平台上后,每个模块添加不同的额外端口,这个项目