前端知识准备


async await

//async 函数 返回promise对象,获取到promise 值,用then
async function timeout() {
    return 'hello world'
}
timeout().then(result => {
    console.log(result);
})
console.log('虽然我在最下边,但是我是先执行的');

//await是等待的意思,它后面可以放任何表达式,通常放一个返回promise 对象的表达式。
//注意await 关键字只能放到async 函数里面,那么我们现在写一个函数 让它返回promise对象,函数的作用是5s 之后让数值乘以2
function double(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2 * num)
        }, 5000);
    } )
}

async function testResult() {
    let result = await double(40);
    console.log(result);
}

fetch方法

//异步获取数据  返回promise对象
fetch('https://jsonplaceholder.typicode.com/users')
  //将返回的promise利用.then接收 并调用promise的json方法转化
  .then(response => response.json())
  //json转化后数据仍是promise 再用then接收
  .then(data => console.log(data));
//Post 方式
fetch('https://jsonplaceholder.typicode.com/users', {
  method: 'POST',
  body: JSON.stringify({ name: '乔巴' }),
  headers: {
    'Content-Type': 'application/json'
  }
})
  //将返回的promise利用.then接收 并调用promise的json方法转化
  .then(response => response.json())
  //json转化后数据仍是promise 再用then接收
  .then(data => console.log(data));

TS中 tepe与interface 相同点及区别

type : 类型别名 可以给一个或多个数据类型(string、number、…)取一个别名;
举例: type dataType=number | string | turple

interface:接口 定义参数或方法的数据类型;
同:
都可以描述一个对象或者函数
interface和type都可以拓展,并且两者并不是互相独立的,也就是说interface可以extends type, type也可以extends interface
interface Name {  name: string;  }
interface User extends Name { age: number; }
//type 继承
type Name = { name: string; }   type User = Name & { age: number }
interface Name { name: string; } interface User extends Name { age: number; }
//相互继承
type Name = { name: string; }
interface User extends Name { age: number; }
interface Name { name: string; }
type User = Name & { age: number; }

区别:
类型别名可以用于其它类型 (联合类型、元组类型、基本类型(原始值)),interface不支持
interface 可以多次定义(相同名字多次定义) 并被视为合并所有声明成员 type 不支持
type 能使用 in 关键字生成映射类型,但 interface 不行。
 默认导出方式不同
总结:一般来说,能用interface实现,就用interface,如果不能就用type
参考来源:https://blog.csdn.net/TIAN20121221/article/details/120085998

函数柯里化(currying)

//是指把接收多个参数的函数变换成接收单一参数的函数  嵌套返回直到所有参数都被使用并返回最终结果
//是将函数从调用方式:f(a,b,c)  ==>  f(a)(b)(c)的过程
function addThreeNum (a, b, c) { return a + b + c; }
addTreeNum(6, 9 ,10);// 返回结果为25
//柯里化
function addhTreeNumCurry(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        }
    }
}
addThreeNumCurry(6)(9)(10);// 返回结果同样是25

一个未完全理解的示例 可更好的表达柯里化
求和 add(1,2,3,4) 转为为 add(1)(2)(3)(4)

function add(any) {
  //将arguments类数组转化成数组
  let args = Array.prototype.slice.call(arguments);
  // console.log(args, 'args')
  let inner = function (any) {
    args.push(...arguments)
    args.reduce(function (pre, next) {
      return pre + next;
    })
    //因为需要链式调用  所以需要将子函数return出来
    return inner;
  }
  // inner.toString = function () {
  //   return args.reduce(function (pre, next) {
  //     return pre + next;
  //   })
  // }    
  return inner;
}
console.log(typeof add(1)(2)(3)(4))
// 最后返回的结果为函数  教程中有的会隐式调用toString方法  成功返回结果  
//但自己测试未调用toString  不知结果该如何返回
//箭头函数柯里化调用方法示例
const nameList1 = [
  { mid: '哈傻k', profession: '中单' },
  { mid: '沙皇', profession: '中单' },
  { mid: '卡牌', profession: '中单' },
  { mid: '发条', profession: '中单' }]

const curring = name => element => element[name];
const name_mid = curring('mid');
console.log(name_mid)

日期处理

let now = new Date(); 
now.getFullYear() //获取年份(4 位数)
now.getMonth() //获取月份,从 0 到 11。
now.getDate() //获取当月的具体日期,从 1 到 31,这个方法名称可能看起来有些令人疑惑。
now.getHours(),getMinutes(),getSeconds(),getMilliseconds() //获取相应的时间组件。
now.getDay() //获取一周中的第几天,从 0(星期日)到 6(星期六)。

js数组常用方法28个

28个JS常用数组方法总结

1.Array.map()  ****
2.Array.filter()  ****
3.Array.reduce()  ****
4.Array.reduceRight()
5.Array.fill()
6.Array.find()
7.Array.indexOf()  ****
8.Array.lastIndexOf()
9.Array.findIndex()
10.Array.includes()
11.Array.pop()  ****
12.Array.push()  ****
13.Array.shift()  ****
14.Array.unshift()  ****
15.Array.splice()  ****
16.Array.slice()  ****
17Array.join()
18.Array.reverse()
19.Array.sort()  ****
20.Array.some()
21.Array.every()
22.Array.from()
23.Array.of()
24.Array.isArray()
25.Array.at()
26.Array.copyWithin()
27.Array.flat()
28.Array.flatMap()
/*************************************************************************************************/
1.Array.map((el) => el * 2)//建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
2.Array.filter()//创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
3.Array.reduce(function(total,currentValue,currentIndex,arr){},initiaValue) 
  reduce() 方法对数组中每个元素按序执行 reducer 函数,reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
  //合并二元数组
  var twoArr = [['mu','zi'],['dig','big'],['lucky','jiji']];
  var oneArr = twoArr.reduce(function(total,currentValue){
    console.log(total,currentValue)
    return total.concat(currentValue);
  })
  console.log(oneArr);//["mu", "zi", "dig", "big", "lucky", "jiji"]

  // 统计单词重复次数  给初始值initiaValue
  var arr = ["apple","orange","apple","orange","pear","orange"]; 
  function getWordCnt(){ 
    return arr.reduce(function(prev,next){ 
        console.log(prev,next,prev[next]);
      prev[next] = (prev[next] + 1) || 1; 
      return prev; 
    },{}); 
  } 
  console.log(getWordCnt());
4.Array.reduceRight()//同reduce()从数组的末尾向前将数组中的数组项做累加。
5.Array.fill(value[, start[, end]])//用一个固定值填充 不包括终止索引。
  // 填充数组
  const arr = Array(3).fill(1);  // [1, 1, 1]
  const arr1 = [1,2,3,4,5].fill(0,1,3); // [1, 0, 0, 4, 5]
  // 填充类数组
  const obj = [].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}
6.Array.find(function(currentValue, index, arr),thisValue)//返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
  const list = [1, 2, 3, 4, 5];
  list.find((el) => el === 3); // 3
  list.find((el) => el > 3); // 4
  list.find((el) => el === 6); // undefined
7.Array.findIndex((el) => el === 6)//返回数组中满足提供的测试函数的第一个元素的索引。 find是返回值
8.Array.indexOf()//返回在数组中可找到一个元素的索引,不存在返回-1。
9.Array.lastIndexOf(searchElement[, fromIndex])//返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
  const list = [1, 2, 3, 4, 5];
  list.lastIndexOf(3); // 2
  list.lastIndexOf(3, 1); // -1
10.Array.includes()//是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。
*******
11.Array.pop()//删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。
12.Array.push()//将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
13.Array.shift()//删除第一个元素,并返回该元素的值。此方法更改数组的长度。
14.Array.unshift()//添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。
15.Array.splice(index,howmany,item1,.....,itemX)//删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
  let list = [1, 2, 3, 4, 5];
  list.splice(1, 2); // [2, 3]    list: [1, 4, 5]
  list2 = [1, 2, 3, 4, 5]; list2.splice(1, 2,'a','c','e'); // [2, 3]  list2:[1, 'a', 'c', 'e', 4, 5]
16.Array.slice(begin,end)//返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
  const list = [1, 2, 3, 4, 5];
  list.slice(1, 3); // [2, 3]
  list; // [1, 2, 3, 4, 5]
17Array.join()//元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
  const list = [1, 2, 3, 4, 5];
  list.join(', '); // "1, 2, 3, 4, 5"
18.Array.reverse()//元素的位置颠倒,并返回该数组。
19.Array.sort()//用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
  const array = ['D', 'B', 'A', 'C'];
  array.sort(); // :grinning: ['A', 'B', 'C', 'D']
  const array = [4, 1, 3, 2, 10];
  array.sort(); // :anguished: [1, 10, 2, 3, 4]
  array.sort((a, b) => a - b); // :grinning: [1, 2, 3, 4, 10]
20.Array.some()//测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
  const list = [1, 2, 3, 4, 5];
  list.some((el) => el === 3); // true
  list.some((el) => el === 6); // false
21.Array.every()//测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
  const list = [1, 2, 3, 4, 5];
  list.every((el) => el === 3); // false
  const list = [2, 4, 6, 8, 10];
  list.every((el) => el%2 === 0); // true
22.Array.from()//对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
  const range = (n) => Array.from({ length: n }, (_, i) => i + 1);
  console.log(range(10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
23.Array.of()//创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
  Array.of(7);       // [7]
  Array.of(1, 2, 3); // [1, 2, 3]
  Array(7);          // [ , , , , , , ]
  Array(1, 2, 3);    // [1, 2, 3]
24.Array.isArray()//确定传递的值是否是一个 Array。
  Array.isArray([1, 2, 3, 4, 5]); // true
  Array.isArray(5); // false
25.Array.at()//接收一个整数值并返回该索引的项目,允许正数和负数。负整数从数组中的最后一个项目开始倒数。
  const list = [1, 2, 3, 4, 5];
  list.at(1); // 2
  list.at(-1); // 5
  list.at(-2); // 4
26.copyWithin(target[, start[, end]])//浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
  const list = [1, 2, 3, 4, 5];
  list.copyWithin(0, 3, 5); // [4, 5, 3, 4, 5]
27.Array.flat(depth)//按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
  const list = [1, 2, [3, 4, [5, 6]]];
  list.flat(Infinity); // [1, 2, 3, 4, 5, 6]
  const list = [1, 2, [3, 4, [5, 6]]];
  list.flat(1); // [1, 2, 3, 4, 5, 6]//[1, 2, 3, 4, Array(2)]
28.Array.flatMap()//映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
  const list = [1, 2, 3];
  list.flatMap((el) => [el, el * el]); // [1, 1, 2, 4, 3, 9]

js中字符串的常用方法

('string').charAt(2)//返回字符串中指定位置的字符。
('hello').concat('world','s');//连接两个或多个字符串。
('hello').indexOf("i",n)//i首次出现的位置。可从第n个字符开始查找
('hello').includes("i",n)//是否包含指定的子字符串.可从第n个字符开始查找
('hello').match(/l/g)//['l','l'] 索指定的值,或找到一个或多个正则表达式的匹配
('hello').repeat(2)//复制指定次数。 'hellohello'
('hello').replace("l", "o");('hello').replace(/l/g, "o")//替换另一些字符,或替换一个与正则表达式匹配的子串
('hello').replaceAll("l", "o")//替换所有匹配到的子字符串
('hello').search("lo")//返回匹配字符串位置索引
('hello').slice(1,2)//提取开始结束位置 end不传表示截取到结尾 start传负数表截取后几位
('heleleo').split("e",2)//按'e'分割成字符串数组 2表示最大长度
('hello').substring(1, 3)// 'el' 提取介于下标(之间)的字符
('HeLLo').toLowerCase(); //转换为小写。
('HeLLo').toUpperCase(); //转换为大写。
(' hel lo ').trim(); //删除(头尾) 空格、制表符 tab、换行符等。

js中数字的常用方法

Math.min(10,2,0,2,2,4,5,78) //取最小值
Math.max(10,2,0,2,2,4,5,78)//取最大值
Math.ceil(12.1)//向上取整ceil
Math.floor(12.1)//floor向下取整
Math.round(12.5)//round四舍五入
Math.abs(-12.5)//abs绝对值
Math.sqrt(9)//sqrt开方
Math.pow(9,2)//pow  m的n次方
Number("12.12")//12.12//Number转化为数字
parseInt("12.12")//12  //parseInt转化为数字
parseFloat("12.12aa")//12.12 //parseFloat转化为数字
(12.3).toFixed(3)//12.300//tofixed保留几位小数
Math.random()//0-1的随机数

//Number转化为数字,只能处理为整数、小数
带字母的字符串不能处理,会转为NaN
Number("12.12")//12.12
Number("12")//12
Number("12.12aa")//NaN

//parseInt转化为数字,只能处理为整数
字母开头的字符串不能处理,会转为NaN
parseInt("12.12")//12
parseInt("12")//12
parseInt("12.12aa")//12
parseInt("aa12.12")//NaN

//parseFloat转化为数字,只能处理为整数、小数
字母开头的字符串不能处理,会转为NaN
parseFloat("12.12")//12.12
parseFloat("12")//12
parseFloat("12.12aa")//12.12
parseFloat("aa12.12")//NaN

//tofixed保留几位小数
会改变成字符串类型
const num=12.12;
num.toFixed(3))//12.120
num.toFixed(1))//12.1

//random0-1的随机数
function getRandom(n,m){
return Math.trunc(Math.random()*(m-n)+n);
}
getRandom(2,14)//

描述原生 JavaScript 中有几种实现继承的方式?分别有什么优缺点?

原生 JavaScript 中通过函数的方式模拟面向对象语法,主要的继承的实现方式有4种,
原型继承、 冒充继承、组合继承、寄生组合继承

原型继承,基于原型对象的继承,子类和父类在原型链上就存在继承关系;
问题:子类在创建对象时无法直接修改父类的属性数据,
必须在创建的对象上通过访问属性的方式进行修改,编码复杂;很少独立使用
      function Person() {}
      function Student() {}
      Student.prototype = new Person()      //原型继承

冒充继承,基于 call() 辅助函数修改 this 指向的功能,在子类中通过调用父类构造函数的 call() 方法,
完成子类可以使用父类构造函数内部属性和方法的使用;
问题:原型链上没有真实继承关系,所以子类无法继承父类在原型对象上的数据和函数
      function Person(name) {}
      function Student(name, age) {
           Person.call(this, name) // 冒充继承,可以直接修改父类属性
           this.age = age
      }

组合继承,原型继承 + 冒充继承,通过原型继承让子类可以使用父类原型对象上的属性和函数,通过冒充继承可以让子类直接访问父类属性,优化操作语法;
问题:两种继承方式都访问了父类的构造函数,继承过程中造成父类构造函数的多次调用存在一定的性能问题;如果性能没有太高要求的情况下,可以通过组合继承的方式进行实现

      function Person(name) {}
       function Student(name, age) {
          //冒充继承
          Person.call(this, name)
          this.age = age
     }
    //原型继承
     Student.prototype = new Person('')

寄生组合继承,对组合继承的一种升级,将父类原型对象进行了复制得到一个空对象,子类在冒充继承的继承上,实现原型继承时继承这个空对象,就不需要让父类的构造函数再次调用,
这个空对象的创建和调用性能消耗较低,对性能上有一定的优化提升;项目中如果出现了对继承关系的频繁使用,建议可以将寄生组合继承进行封装使用,优化项目性能
        function Person(name) {}
        function Student(name, age) {
           // 冒充继承
           Person.call(this, name)
           this.age = age
         }
        // 寄生函数
       function inherit(Parent, Child){
          //寄生构造函数|空函数
        var Temp = function() {}
        // 复制 原型对象
        Temp.prototype = Parent.prototype
           // 原型继承(空构造函数,性能 优化)
          Child.prototype = new Temp()
           // 指定自己构造函数
          Child.prototype.constructor = Child
       }
       // 寄生继承:让Student继承自Parent类型
       inherit(Person, Student)

call()、apply()、bind() 的用法

call()、apply()、bind() 的用法
obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined
call apply  参数不同
call bind   bind返回函数  可随时调用

什么是 cookie?

cookie 是网站应用开发中,服务器给浏览器客户端记录文本数据的一个对象;记录数据的时候有如下特点:

数据格式: key=value ,保存的数据只能是文本数据
有过期时间: expires=xxxx        
不设置过期时间,称为临时 cookie ,会话结束数据删除
设置过期时间,称为永久 cookie ,过期时间一到立即删除
可以包含路径,给不同的路径保存不同的 cookie
一个网站,大部分浏览器限制最多保存50个 key=value 键值对
一个网站,大部分浏览器限制最多保存 4K 数据


缺点 存储量太小,只有4KB
缺点 每次HTTP请求都会发送到服务端,影响获取资源的效率
缺点 需要自己封装获取、设置、删除cookie的方法
缺点 当超过单个域名限制之后,再设置cookie,浏览器就会清除以前设置的cookie。IE和Opera 会清理近期最少使用的cookie,FF会随机清理cookie

location.go(-1) 和 history.go(-1) 有什么区别

两个函数都可以用于页面回到上一页, location.go() 需要插件支持*
location.go(-1)回到上一页并且刷新页面
history.go(-1) ,回到上一页

何为 JSX ?

JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产
React "元素",你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX
中。在编译完成之后,JSX 表达式就变成了常规的 JavaScript 对象,这意味着你可以在
if 语句和 for 循环内部使用 JSX,将它赋值给变量,接受它作为参数,并从函数中返回
它

angularJs 和 React 区别

React 对比 Angular 是思想上的转变,它也并不是一个库,是一种开发理念,组件化,分
治的管理,数据与 view 的一体化。它只有一个中心,发出状态,渲染 view,对于虚拟 dom
它并没有提高渲染页面的性能,它提供更多的是利用 jsx 便捷生成 dom 元素,利用组件
概念进行分治管理页面每个部分(例如 header section footer slider

axios 是什么?怎样使用它?怎么解决跨域的问题?

axios 的是一种异步请求,用法和 ajax 类似,安装 npm install axios --save 即可使用,请
求中包括 get,post,put, patch ,delete 等五种请求方式,解决跨域可以在请求头中添加
Access-Control-Allow-Origin,也可以在 index.js 文件中更改 proxyTable 配置等解决跨域
问题

将原生的 ajax 封装成 promis

var myNewAjax=function(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(data);
xhr.onreadystatechange=function(){
if(xhr.status==200&&readyState==4){
  var json=JSON.parse(xhr.responseText);
  resolve(json)
}else if(xhr.readyState==4&&xhr.status!=200){
  reject('error');
}
}
})
}

说一下 CommonJS、AMD 和 CMD UMD

一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么
功能就能加载什么模块。
CommonJS:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的
作用域,模块输出,modules.exports,模块加载 require()引入模块。 有依赖模块,未加载会报错  不适合网络加载

AMD: 异步模块定义 
requireJS 实现了 AMD 规范,主要用于解决下述两个问题。
1.多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
2.加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。
语法:requireJS 定义了一个函数 define,它是全局变量,用来定义模块。

CMD:  amd 的优化版(seaJS)
amd是前置依赖 提前加载依赖
cmd 后置 使用时加载

UMD
CommonJS 侧重服务器
AMD 侧重浏览器
UMD 会先判断  是commonJS则 commonJS 是AMD则用AMD

JJS 的各种位置,比如 clientHeight,scrollHeight,offsetHeight ,以及 scrollTop, offsetTop,clientTop 的区别?

clientHeight:表示的是可视区域的高度,不包含 border 和滚动条
offsetHeight:表示可视区域的高度,包含了 border 和滚动条
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
clientTop:表示边框 border 的厚度,在未指定的情况下一般为 0
scrollTop:滚动后被隐藏的高度,获取对象相对于由 offsetParent 属性指定的父坐标(css
定位的元素或 body 元素)距离顶端的高度。

JS 的 new 操作符做了哪些事情

new 操作符新建了一个空对象,这个对象原型指向构造函数的 prototype,执行构造函数
后返回这个对象。

如何实现元素的垂直居中

法一:父元素 display:flex,align-items:center;
法二:元素绝对定位,top:50%,margin-top:-(高度/2)
法三:高度不确定用 transform:translateY(-50%)
法四:父元素 table 布局,子元素设置 vertical-align:center

vue的生命周期

beforeCreate、created、 
beforeMount、mounted、
beforeUpdate、 updated、
beforeDestroy、destroyed
状态 描述
beforeCreate(创建前):vue实例初始化之前调用 此阶段为实例初始化之后,此时的数据观察和事件配置都还没有准备好,而此时的实例中的data和el还是underfined状态,不可用的,dom元素也未加载,此时使用html片段代码我们加上ref属性,用于获取DOM元素的操作会报错,详细效果请使用代码测试。
created(创建后):vue实例初始化之后调用 beforeCreate之后紧接着的钩子就是创建完毕created,此时我们能读取到data的值,但是DOM还没有生成,所以属性el还是不存在的,dom元素也未加载。
beforeMount(载入前):挂载到DOM树之前调用 此时的$el成功关联到我们指定的DOM节点,但是此时的DOM元素还未加载,如果此时在DOM元素中绑定数据使用{{name}}后里边的name不能成功地渲染出我们data中的数据
mounted(载入后):挂载到DOM树之后调用 挂载完毕阶段,到了这个阶段数据就会被成功渲染出来。DOM元素也加载出来了,html片段代码我们加上ref属性,可以获取DOM元素。
beforeUpdate(更新前):数据更新之前调用 当修改Vue实例的data时,Vue就会自动帮我们更新渲染视图,在这个过程中,Vue提供了beforeUpdate的钩子给我们,在检测到我们要修改数据的时候,更新渲染视图之前就会触发钩子beforeUpdate。html片段代码我们加上ref属性,用于获取DOM元素。Dom元素上的数据还没改变。
updated(更新后):数据更新之后调用 此阶段为更新渲染视图之后,此时再读取视图上的内容,已经是最新的内容。 此时加载的DOM元素上的数据更新了。
beforeDestroy(销毁前):vue实例销毁之前调用 调用实例的destroy()方法可以销毁当前的组件,在销毁之前,会触发beforeDestroy钩子。
destroyed(销毁后):vue实例销毁之后调用 成功销毁之后,会触发destroyed钩子,此时该实例与其他实例的关联已经被清除,它与视图之间也被解绑,此时再修改name的值,试图不在更新,说明实例成功被销毁了。
image.png

call 的使用

//改变this 指向
var Dog = {
    getName:function(){
        return this.name;
    }
}
var dog1 = {
    name:'大黄'
}
Dog.getName.call(dog1); //大黄
//////////////////////////////////////////////////////////////////////////////////////////////////////////
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");
'Bill Gates,Seattle,USA'

手写call 源码

var Person = {
  name: '',
  sayName: function () {
    return this.name;
  }
}
var person1 = {
  name: '刘德华',
}
Function.prototype.myCall = function (context) {
  if (typeof this !== 'function') {
    throw new Error('error')
  }
  // context 调用myCall时传入的对象
  context = context || window;
  //拿到参数 拿到第一个以外的其他参数 调用方法的参数
  var args = [...arguments].slice(1);
  //当调用时  this 指向Person.sayName
  //context 指向person1
  context.fn = this; //改变Person.sayName方法中this指向
  var result = context.fn(args)
  return result;
}
Person.sayName.myCall(person1); //刘德华

splice slice 区别

// splice主要能实现原[数组]的删除、替换,插入
slice不改变原数组 splice会改变原数组

splice删除:  color.splice(1,2) (删除color中的1、2两项);
splice插入:  color.splice(1,0,‘yellow’,‘black’) (在color键值为1的元素前插入两个值);
splice替换:  color.splice(1,2,‘yellow’,‘black’) (在color中替换1、2元素);
[slice]可以取出数组中的任意值,并返回一个新的数组

slice

 语法
array.slice(start, end);
参数
start: 必需。起始元素,使用负数可从数组结尾处规定位置。
end:可选。截止元素(截取不包含此元素)

splice

语法
array.splice(index, howmany, item1, …, itemX);
参数
index: 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany:必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, …, itemX: 可选。向数组添加的新项目。
var sss = [1,2,3,4,5,6,7,8,9]
var fff = sss.splice(1,2,'aa','bb','vvv')
console.log(fff) //(2) [2, 3]
console.log(sss) //(10) [1, 'aa', 'bb', 'vvv', 4, 5, 6, 7, 8, 9]
var aaa = sss.slice(2,4); //删除的开始下标(包含)结束下标(不包含)
console.log(aaa ) //['bb', 'vvv']
console.log(sss) //(10) [1, 'aa', 'bb', 'vvv', 4, 5, 6, 7, 8, 9]

预编译 this指向 箭头函数中this

易错总结:
预编译流程
var a = function(){}   function a(){} 区别
在     var a = 123;   function a(){};    前后分别打印a的结果
function fn(a,c){
  console.log(a);
  a = 123;
  console.log(a);
  console.log(c);
  function a(){}
  if(false){
    var d = 678;
  }
  console.log(d);
  console.log(b);
  var b = function(){}
  console.log(b);
  function c(){}
  console.log(c);
}
fn(1,2)

输出结果

sentry-5.7.1.min.js:2 ƒ a(){}  //重点注意 当参数赋值给函数时 变量会被覆盖
sentry-5.7.1.min.js:2 123
sentry-5.7.1.min.js:2 ƒ c(){}
sentry-5.7.1.min.js:2 undefined
sentry-5.7.1.min.js:2 undefined
sentry-5.7.1.min.js:2 ƒ (){}
sentry-5.7.1.min.js:2 ƒ c(){}
undefine

//其中易错

window.name = 'wn';
var b = {name:'bb',say:function(){console.log(this.name)}}
var c = {name:'aa',say:function(fun){   fun()  }}
c.say(b.say)
// 结果输出 wn  

箭头函数没有自己的this 会找外层this 因此也不能做构造函数

var name = '111';
var obj = {
  name: '222',
  say: ()=>{
    console.log(this.name)
  }
}
obj.say() // 输出'111'
function fn(a){
    console.log(a);     //根据AO对象中的数据第一个打印的是:fn()
    // 变量声明+变量赋值(只提升变量声明,不提升变量赋值)
    var a = 123;        // 执行到这时,由于变量赋值是不提升的,所以函数被123覆盖了
    console.log(a);     // 123
    // 函数声明
    function a(){};     // 这里被提升上去了,可以忽略
    console.log(a);     // 123
    // 函数表达式
    var b = function(){};
    console.log(b);     // 根据AO对象中的数据:fn()
    // 函数
    function d(){};
}
//调用函数
fn(1);
global = 100;
function test(){
    console.log(global);        // 根据AO对象中的数据:undefined
    var global = 200;       // 执行到这时,200覆盖了undefined
    console.log(global);        // 200
    var global = 300;
}
test();
var global;

登录验证流程

传统session方式

用户通过浏览器post 传入用户名、密码给服务端
服务端验证通过后,返回给前端session
前端再发送请求时将带有session的cookie一同传入
后端进行验证是否正确及到期时间
成功后将新数据返回给前端
***弊端:***
大型项目会有多个服务器,服务器间识别session不容,造成无法验证通过

token方式

解决session痛点
前端登录后将用户信息加密传给redis(缓存),
生成数据的id称为token,存于redis
用户发送情况,携带token,  redis 统一验证后可调用其他服务

最新 JWT方式

图片懒加载原理

给所图片不设置src或给默认图。
给img标签增加data-src属性,用于存放真实src
页面滚动或变化时判断图片与可以窗口关系
若图片可见,则把data-src的值赋值给src
优化,给全局图片是否加载完成增加一个flag

vue2/3实现数据双向绑定 说说区别

vue2 Object.defineProperty监听属性,渲染后再增加属性不会被监听
vue3 new Proxy直接监听对象 增加属性后可被监听


        /**
         * 点击按钮 内容显示你好  h1内同步input内容
          
          

点击 */ //vue2 方式 let data = {text:''}; const input = document.getElementsByName('input'); const h1 = document.getElementsByName('h1'); const btn = document.getElementsByClassName('btn'); //input 绑定输入事件 改变data input.addEventListener('input',function(e){ data.text = e.target.value; }); //监听data的text属性 //只能监听某个属性 在页面渲染后在给对象加属性则不会被监听 //原因是Object.defineProperty的局限性 Object.defineProperty(data,'text',{ get:function(){ //属性被访问时执行 return data.text; }, set:function(newValue){//属性被修改时执行 h1.innerText = newValue; input.value = newValue; } });

VUE3 方式

        
        const data2 = {text:''};
        const effect = function(){
          document.body.innerText = data.text;
        }

        //使用proxy实现对象监听  后期加属性也会被监听
        const obj = new Proxy(data,{
          get(target,key){
            return target[key];
          },
          set(target,key,newValue){
            target[key] = newValue;
            effect();
            return true;
          }
        });

        btn.onclick = function(){
          obj.text = 'hello!'
        }

判断数组的几种方法

var arr = [];
Array.isArray();
console.log( arr instanceof Array )
console.log( arr.constructor === Array )
console.log( Object.prototype.toString.call(arr) === '[object Array]' )

数组方法 unshift、shift、push、pop

let arr = [4,5,6];
arr.unshift(2,3); //  头部插入 返回长度5  arr:[2,3,4,5,6]
arr.shift();// 删除首项  返回被删除的首相  arr:[3,4,5,6]
arr.push(7,8);  //  尾部插入 返回长度6  arr:[3,4,5,6,7,8]
arr.pop()// 删除末项  返回被删末项8  arr:[3,4,5,6,7]

forEach map filter区别

forEach map filter
原数组 改变 不变 不变
返回值 数组,return的内容,无return 则返回undefined 数组,返回满足条件的item
// forEach  没有返回值  主要用于改变原数组值
//forEach中的return充当continue使用,只用于控制是否跳出当前循环;
let arrOne = [
        {name:'小明', age: 14},
        {name:'小华', age: 11},
        {name:'小红', age: 15},
        {name:'小黄', age: 17},
    ]
    arrOne.forEach((item,index,arr) => {
        console.log(item,index,arr);
        if(item.age == 11) return;
        item.name =  item.name + 'update'
    })
    console.log(arrOne)

image.png
// map 有返回值 处理后的值返回到新数组中 不改变原数组
// 不写返回值则默认返回 undefined 插入到新数组中
  let arrOne = [
        {name:'小明', age: 14},
        {name:'小华', age: 11},
        {name:'小红', age: 15},
        {name:'小黄', age: 17},
    ]
    let arrNew = arrOne.map((item,index,arr) => {
        console.log(item,index,arr);
        if(item.age>14)return item.age + 10
    })
    console.log(arrOne)
    console.log(arrNew)
//
image.png
//filter主要用于过滤当前数组,找出符合条件的元素,返回一个新的数组,不会改变原数组
let arrOne = [
        {name:'小明', age: 14},
        {name:'小华', age: 11},
        {name:'小红', age: 15},
        {name:'小黄', age: 17},
    ]
    let arrNew = arrOne.filter((item, index, arr) => {
        // 值,索引,原数组
        console.log(item, index, arr);
        return item.age > 11
    })
    console.log(arrOne)
    console.log(arrNew)

image.png

栈 filo 先进后出

class Stack {
  //构造器
  constructor() {
    this.items = [];
  }
  //入栈 栈顶添加
  in(value) {
    this.items.push(value);
  }
  //出栈
  out() {
    if (this.size() === 0) return null;
    return this.items.pop();
  }

  //弹栈(只取出栈顶元素  不删除)
  top() {
    if (this.size() === 0) return null;
    return this.items[this.items.size() - 1];
  }
  //栈的长度
  size() {
    return this.items.length;
  }
}

虚拟dom

1.什么是虚拟dom?
  vue 2.x 才有 
  本质是js对象  可跨平台
  是将真实的com创建成js对象
2.在vue中做了什么?
  vue的渲染过程 浏览器只认识  html css js
  将真是dom转化成虚拟dom(js对象)
  在更新的时候做对比
3.虚拟dom是如何提升vue的渲染效率?
  // vue的两大核心  1.组件化  2.数据驱动
  局部更新
  将直接操作dom的地方 拿到新旧两个js对象之中去做比较
  原理:数据改变后形成新的url, 新url会形成新的虚拟dom(js对象) ,
  数据更新就是将新老 虚拟dom(新老js对象) 对比较,将发生变化 的地方进行局部更新。
  此过程对比 直接销毁原dom,再重新创建新dom的过程,效率大大提升
vue 虚拟dom创建流程

Diff中的patch()

问题目的是写出 vue 如何将虚拟dom转化成真实dom
vue的渲染 → 初始化 更新


// 初始化 patch( container, vnode)

function createVnodeElement() {
  //虚拟dom生产的三要素
  let tag = vnode.tag    //目标元素 要创建的元素标签
  let attrs = vnode.attrs ? vnode.attrs : {}  //属性
  let children = vnode.children || [];  //子节点

  if (!tag) return null;

  //创建对应dom
  let elem = document.createElement(tag);

  //给对应dom添加属性
  for (let attrName in attrs) {
    if (attrs.hasOwnProperty(attrName)) {
      elem.setAttribute(attrName, attrs[attrName])
    }

  }

  //将子元素(通过递归)添加到目标元素上
  children.forEach(function (childVnode) {
    elem.appendChild(createVnodeElement(childVnode));
  })

  return elem;
}


function updateChildren(vnode, newVnode) {
  // 比较新老节点,将发生变化的节点进行替换
  let children = vnode.children || [];
  let newChildren = newVnode.children || [];

  children.forEach(function (childrenVnode, index) {
    let newChildrenVnode = newChildren[index];
    if (childrenVnode === newChildrenVnode) {
      //子节点没有变化 递归比较
      updateChildren(childrenVnode, newChildrenVnode)
    } else {
      //子节点发生变化  替换旧节点
      replaceNode(childrenVnode, newChildrenVnode)
    }
  });


}

vue2的响应式原理

vue2的响应式原理

需要发布 及 数据劫持

vue3的响应式原理

v-if 与 v-show 的区别与使用场景

// v-if 不满足条件不会渲染dom  单词判断
// v-show 隐藏dom  display:block/none  不能用于权限操作

vue中data() 为什么是函数

闭包 保持每一个组件都有自己的作用域 确保各个组件中数据互不干扰

new 关键字创建对象流程

function Dog() {
  this.name = "可可";
  this.eat = function () {
    console.log("骨头")
  };
}
let taidi = new Dog();
console.log(taidi);

/*
1.创建一个空对象
2.原型链连接到原型
3.绑定this指向,执行构造函数
4.返回一个对象
*/

let keji;
let obj = new Object();
obj.__proto__ = Dog.prototype;
let result = Dog.call(obj);
if (typeof result == "object") {
  keji = result; //引用类型
}else{
  keji = obj; //值类型
}

console.log(obj)

深拷贝 deepclone

function deepClone ( obj = {} ){
  if(typeof obj !== 'object' || obj == null  ) return boj;
  let result = obj instanceof Array ? [] : {};
  for (key in obj ){
      let v = obj[key];
      if(obj.hasOwnProperty(key)  ){ result[key] = deepClone(v)  }
  }
  return result;
}
/**
  1.创建空对象
  2.循环原对象
  3.引用类型属性,则递归调用 deepClone 方法
  4.基础类型属性,直接赋值
  5.return对象
**/
function deepClone(obj){
        let newobj = null;
        if(typeof(obj) == 'object' && obj !== null){ 
            newobj = obj instanceof Array? [] : {};   
            for(var i in obj){   newobj[i] = copy(obj[i]) }
        }else{ newobj = obj }    
      return newobj;
   }
  const deepClone = (source)=>{
    const targetObj = source.constructor === Array ? [ ] : { } ;
    for( let keys in source ){
      if( source[ keys ] && typeof source[ keys ] === 'object' ){
        targetObj [ keys ] = deepClone( source[ keys ] );
      } else {
        targetObj [ keys ] = source[ keys ];
      }
    }
  }

节流防抖

节流 某个函数在一定时间间隔内只执行第一次回调。


    
点击测试

防抖 无视短时间内重复回调 只执行最后一次

function debounceFun(callback, delay) {
  let timmer;
//利用闭包保持timmer
  return function (v) {
    clearTimeout(timmer);
    timmer = setTimeout(function () {
      callback(v);
    }, delay)
  }
}

const test = debounceFun(function (e) { console.log(e) }, 1000);

节流是在某段时间内只执行首次回调,而防抖动通常是只执行末次回调。

https://www.jianshu.com/writer#/notebooks/52520049/notes/106474305/preview

cookies,sessionStorage ,localStorage的区别?

sessionStorage 和 localStorage 是HTML5 Web Storage API 提供的,相当于本地数据,减少服务器请求。

相同:都是保存在浏览器端,且同源的。

区别:
1.cookie数据始终在同源的http请求中携带(即使不需要)。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
2.cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。
3.存储大小限制也不同,cookie数据不能超过4k,sessionStorage和localStorage  可达到5M 及更多,根据浏览器而定。
4.数据有效期不同:
  sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
  localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
  cookie:设置的cookie过期时间内一直有效,无视窗口或浏览器关闭。
5.作用域不同,
  sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;
  localStorage 在所有同源窗口中都是共享的;
  cookie也是在所有同源窗口中都是共享的。

Web Storage(sessionStorage,localStorage )的优势?
存储空间更大:IE8下每个独立的存储空间为10M,其他浏览器实现略有不同,但都比Cookie要大很多。
存储内容不会发送到服务器
Web Storage提供了一套更为丰富的接口,使得数据操作更为简便。
独立的存储空间:每个域(包括子域)有独立的存储空间,各个存储空间是完全独立的,因此不会造成数据混乱。
image.png

Sass/Scss 和 Less的区别

Sass: 一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量、嵌套、运算,混入(Mixin)、继承、颜色处理,函数等),更容易阅读。Sass 3就变成了Scss(Sassy CSS)。SCSS(Sassy CSS)是CSS语法的扩展。
Less: 也是一种动态样式语言. 对CSS赋予了动态语言的特性,如变量,继承,运算, 函数.  Less 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可在服务端运行 (借助 Node.js)。

1.编译环境不一样
Sass是在服务端处理的,以前是Ruby,现在是Dart-Sass或Node-Sass,
Less是需要引入less.js来处理Less代码输出CSS到浏览器

2.变量符不一样,Less是@,而Scss是$。
3.输出设置,Less没有输出设置,Sass提供4中输出选项:nested, compact, compressed 和 expanded
4.Sass支持条件语句,可以使用if{}else{},for{}循环等等。而Less不支持

https://www.cnblogs.com/wangpenghui522/p/5467560.html

如何阻止事件冒泡和事件的默认事件

Sass: 一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量、嵌套、运算,混入(Mixin)、继承、颜色处理,函数等),更容易阅读。Sass 3就变成了Scss(Sassy CSS)。SCSS(Sassy CSS)是CSS语法的扩展。
Less: 也是一种动态样式语言. 对CSS赋予了动态语言的特性,如变量,继承,运算, 函数.  Less 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可在服务端运行 (借助 Node.js)。

1.编译环境不一样
Sass是在服务端处理的,以前是Ruby,现在是Dart-Sass或Node-Sass,
Less是需要引入less.js来处理Less代码输出CSS到浏览器

2.变量符不一样,Less是@,而Scss是$。
3.输出设置,Less没有输出设置,Sass提供4中输出选项:nested, compact, compressed 和 expanded
4.Sass支持条件语句,可以使用if{}else{},for{}循环等等。而Less不支持

介绍css盒模型

contenet + padding+ margin+ border
正常盒模型 box-sizing: content-box   width=width+左右padding+左右border
怪异盒模型 box-sizing: border-box   设定宽高后  padding、border会向内缩 占用原width

display:none 与 visibility:hidden 区别

二者均表示隐藏
display:none  不占用空间
visibility:hidden 占用空间隐藏

js中的堆和栈

堆(heap) :堆是堆内存的简称
1.堆是动态分配内存内存大小不一,也不会自动释放。

栈(stack):栈是栈内存的简称 线性结构,后进先出,便于管理
栈是自动分配相对固定大小的内存空间,并由系统自动释放 一个混沌,杂乱无章,方便存储和开辟内存空间。

写出以下代码输出结果

image.png
考察js中事件循环机制
通过任务队列完成 宏线程 微线程

宏任务:setTimeout setInterval ajax
微任务:promise

先执行同步代码 > 宏任务 > 微任务 > 宏 > 微...

image.png

闭包 this 指向

image.png

如何实现上下左右居中对齐

.cont {
  display:flex;
}
.center{
  width:200px;
  height:200px;
  background:#ccc;
  margin:auto;
}

padding、margin有什么区别

作用对象不同
padding: 作用于自身
margin:作用于外部

vw与百分比有什么区别

50%     50vw
百分比:继承于父级
vw:  只和设备宽度有关

行内元素与块元素区别

行内元素: 不能设置宽高、宽高由内容决定
块元素   独立占一行  继承父级元素宽度

如何让浏览器支持小字体

谷歌默认最小12px
利用css缩放
transform: scale(0.8);

let 与 var

1.变量提升
console.log(name);
var name = "名字";
//打印  undefined  不能正常提示报错
//改用let:报错 cannot access name before initialization

2.没有局部作用域
function fun(){
  for(var i = 0; i < 5; i++ ){
  }
  console.log(i) 
}
//会打印5  不能正常提示报错
//改用let:报错 i is not defined
3.声明覆盖
var name = "名字";
var name = "名字2";
 console.log(name ) 
//打印 名字2 不能正常报错   
//改用 let 会报错 'name' has already been declared

看到这里---
https://www.bilibili.com/video/BV1Sa41167Ri/?p=4&spm_id_from=pageDriver&vd_source=575ceee169404991816c658859de0f7f

常见笔试题整理:https://www.jianshu.com/p/c2a99ce44176

https://www.jianshu.com/p/872f8fb425ce
前端开发面试题总结之——CSS3
前端开发面试题总结之——JAVASCRIPT(一)
前端开发面试题总结之——JAVASCRIPT(二)
前端开发面试题总结之——JAVASCRIPT(三)

JavaScript 2022 最新面试题 【前端面试题】

你可能感兴趣的:(前端知识准备)