Proud of 2020 Yearning for 2021 | 掘金年度征文

596 阅读35分钟

如果不是看到满是2020 总结的讯息,我其实是不太想写总结,今年都知道是非凡的一年,非凡在哪里,大家都感同身受,我想今年也是我自己记忆深刻的一年。从哪里开始说呢,先说说我与前端的故事吧,或者说每个人与前端的故事。有的伙伴是科班生,走着读书,社团,地下室的路,有的是“野路子”,通过社会上的一些培训的路子,踏上了大前端这个坑,有的是老油条,有的是精彩少年。那么就先从分享下该怎么学习前端吧。怎么学习前端其实更像是个大纲或者指南针,也不代表带你直接到达目的地,或者一下子让你走出迷惘,我想迷惘是每个人一睁眼就在纠结的事,至少我是这样。《论语》 说道:学而不思则罔,思而不学则殆。

一、我是怎么学习前端的

正是介绍下,我给自己起个花名 vast ,在2020 年我是洋小洋同学 ,在 2021 年我是vast ,有的人不理解可能说了,前端有什么好学的,不就是使用PS 做做图片,我也不能否认。深入大众是HTML CSS JavaScript 三个名词。好那就从HTML

1、HTML

HTML 整体来说可能就是一些标签,但对于一些刚入门童鞋来说,可能对于img p 等等这些标签记得头皮发麻。那么从学习的角度来说呢 ,其实不用发慌,这些在工作中都会经常使用,学习的时候可以直接在 MDNHTML(超文本标记语言) ,当然了,如果要问,说要看视频学习嘛。我个人不怎么建议,好好的看看MDN 文档,当然了像一些复杂的标签 video 等这些,可能要深入慢慢学习,不过这要等你接触到视频播放 业务之后再说了。那么从面试的角度来说呢,你可能需要知道这几道问题

  • HTML头部高频面试题及参考答案 对这就是HTML ,其实还是要多练习,这些标签,HTML 也没什么复杂的,是进入前端的一个好的角度,下面就是另一个角度,CSS

2、CSS

CSS 的话就不像HTML 那么轻松了,如果说HTML 花费5天的话,那css 是长久研究的事儿吧,CSS 的话有的人可能喜欢看视频,这里推荐

全面系统讲解CSS + 工作应用面试一步搞定 TooooBug 老师站在面试官的角度 ,讲述了该怎么去理解样式,以及怎么更好的还原产品的思想(以及与产品撕逼)

接着还强烈建议阅读 JowayYoung 大佬的玩转CSS的艺术之美 绝对物超所值(建议必买必读),还有的话可能需要从工作中慢慢的实践了,从CSS面试的角度来说的话 我建议了解下述的21问题

  • CSS头部高频面试题及参考答案 可以看到HTML CSS 就够我们喝一壶的,要问说是这就够了吗,这其实是一个引子 至于该怎么实践,每个人的条件不一样,从读书的角度来说 其实CSS 也是需要不断的阅读书籍加实践,这个稍后讲,接下来JavaScript

3、JavaScript

js 可能不能那么简单的说了,因为它是核心,游戏中的大Boss,直接开始,我想你应该以下这些:

严格模式(现代模式)的约束

use strict当它处于脚本文件的顶部时,则整个脚本文件都将以“现代”模式进行工作。一旦进入严格模式没有回头路了

(function() {
  "use strict";

  // ...你的代码...

})();

没有类似于 "no use strict" 这样的指令可以使程序返回默认模式。

一旦进入了严格模式,就没有回头路了。

  1. 函数不能以 eval 或 arguments 作为名称;
  2. 函数的参数不能叫 eval 或 arguments;
  3. 两个命名参数不能叫同一个名称
  4. 严格模式禁止自动或隐式地创建全局变量
  5. 如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefined
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
  1. 严格模式下,函数只能声明在当前作用域的顶层
// 不报错
'use strict';
if (true) {
  function f() {}
}

// 报错
'use strict';
if (true)
  function f() {}
"use strict";

num = 5; // 错误:num 未定义

var、let 和 const 区别的实现原理是什么

var 操作符(关键字)

在使用var声明变量时,变量会被自动添加到最接近的上下文。在函数中,最接近的上下文就是函数的局部上下文。在with语句中,最接近的上下文也是函数上下文。如果变量未经声明就被初始化了,那么它就会自动被添加到全局上下文

使用let的块级作用域声明

但它的作用域是块级的,这也是JavaScript中的新概念。块级作用域由最近的一对包含花括号{}界定。换句话说,if块、while块、function块,甚至连单独的块也是let声明变量的作用域。let与var的另一个不同之处是在同一作用域内不能声明两次。重复的var声明会被忽略,而重复的let声明会抛出SyntaxError。 当let的值修改之后,之前的值就删除了

使用const的常量声明

除了let,ES6同时还增加了const关键字。使用const声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任何时候都不能再重新赋予新值。 const声明只应用到顶级原语或者对象。换句话说,赋值为对象的const变量不能再被重新赋值为其他引用值,但对象的键则不受限制。 其本质是:并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心

 const o = {
      name:'yayxs',
      age:18
    }
    Object.freeze(o)
    o.name = 'subs'
    const o3 = Object.freeze({});
    o3.name= 'yyx'
    console.log(o3.name) // undefined
    console.log(o.name) // yayxs

该尽可能地多使用const声明,除非确实需要一个将来会重新赋值的变量。这样可以从根本上保证提前发现重新赋值导致的bug。

Object.freeze()

如果想让整个对象都不能修改,可以使用Object.freeze()

JavaScript的数据类型有哪些,存储有什么区别?

在 JavaScript 中有 8 种基本的数据类型(译注:7 种原始类型和 1 种引用类型)

数据类型的种类

js 中的数据类型大体分为两大类:一是基本的数据类型(简单数据类型、原始类型)二是复杂数据类型

Tablesdescother
原始类型Number String Null Undefined Symbol Boolean(逻辑类型) BigInt(IE不支持)Symbol
复杂类型Object存储的引用

存储区别

  • 6种原始值:Undefined、Null、Boolean、Number、String和Symbol。保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值

  • 引用值是保存在内存中的对象 JavaScript不允许直接访问内存位置,因此也就不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非实际的对象本身 保存引用值的变量是按引用(by reference)访问的

typeof 是否能正确判断类型?

  • typeof 操作符适合用来判断一个变量是否是原始类型,确切的说是判断一个变量是不是 数值 字符串 布尔值 undefined 的最好方式
// 基本的数据 类型
const isNumber = 1;
const isStr = "I am string";
const isBool = true;
const isUndefined = undefined;

const isSymbol = Symbol();

const isArr = [0, 1, 2];
const isObj = {
  name: "i am obj",
};
const isNull = null;
const isFunc = () => {};


const targetArr = [
  isNumber,
  isStr,
  isBool,
  isArr,
  isObj,
  isNull,
  isUndefined,
  isSymbol,
  isFunc,
];
for (let i = 0, len = targetArr.length; i < len; i++) {
  console.log(targetArr[i], typeof targetArr[i]);
}

之所以类型是松散的,需要一种方式来检测当前数据类型

Tablesdescother
1number检测成功
I am stringstring检测成功
trueboolean检测成功
{name: "i am obj"}object检测成功
undefinedundefined检测成功
Symbol()symbol检测成功
() => {}function检测成功
[0,2,2]object失败
nullobject失败

调用typeof null返回的是"object"。这是因为特殊值null被认为是一个对空对象的引用。原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判 断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”。

数据类型的转换

数据类型转换之2字符串

let targetArr = [-1.2,0,1,NaN, "1","","0","-1","1.1","01",true,false,null,undefined,Symbol('id'), [1,2],[],{name:'yayxs'},{},()=>{}]

targetArr.forEach(item=>{
  console.log(String(item))
})
  • 空的字符串和空的数组都转为 空
  • 对象转为 [object Object]

数据类型转换之2数字

let targetArr = [-1.2,0,1,NaN, "1","","0","-1","1.1","01",true,false,null,undefined,Symbol('id'), [1,2],[],{name:'yayxs'},{},()=>{}]

targetArr.forEach(item=>{
  console.log(Number(item))
})
  • undefined 转为 NaN
  • null 转为 0

还可以通过一元运算符+

console.log(+'') // 0

数据类型转换之2布尔

let targetArr = [-1.2,0,1,NaN, "1","","0","-1","1.1","01",true,false,null,undefined,Symbol('id'), [1,2],[],{name:'yayxs'},{},()=>{}]

targetArr.forEach(item=>{
 console.log(Boolean(item))
})

let arr =[]
if(arr){
console.log('进来了')
}else{
 console.log('进来没有')
}
  • 空字符串和0 转成 false
  • 0, null, undefined, NaN, ""
  • 空数组[] 转成 true

==和===区别是什么?

  • 双等号 == 表示相等性检查,处于相等判断符号 == 两侧的值会先被转化为数字。空字符串和 false 也是如此,转化后它们都为数字 0。
  • 严格相等运算符 === 在进行比较时不会做任何的类型转换。

if语句的布尔转换

let arr = [];
let arr1 = [1];
if (arr) {
  console.log('空数组进来'); // 打印
}
if (arr.length) {
  console.log('通过长度');
}
if (arr1) {
  console.log('有数据进来'); // 打印
}
if (arr1.length) {
  console.log('有数据长度'); // 打印
}
if(0){
  console.log('数组0')
}
if('0'){
  console.log('字符串0') // 打印
}
if(null){
  console.log('null值')
}
if(undefined){
  console.log('没有定义')
}
if(NaN){
  console.log('不是一个数字')
}

空值合并运算符

let a = null

let b = 1

console.log(a??b)

for循环中的var声明

我们在 for 循环的头部直接定义了变量 i,通常是因为只想在 for 循环内部的上下文中使 用 i,而忽略了 i 会被绑定在外部作用域(函数或全局)中的事实。

for (var i = 1; i <= 5; i++) {
  // 延迟函数在循环结束执行,所有的回调在循环结束后执行
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

问题

迭代变量 i 会渗透到循环体的外部,在循环的外部打印 i 的值此时是 5,打印window.i 是 5 这种情况理论上是应该避免的.可以使用块作用域 而 js 中的几种方式是 with try/catch

with

它不仅是一个难于理解的结构,同时也是块作用域的一 个例子(块作用域的一种形式),用 with 从对象中创建出的作用域仅在 with 声明中而非外 部作用域中有效。

console.log(i); // 5
console.log(window.i); // 5

方案一 改为 let

这样迭代变量的作用域 仅在 for 循环内部块(类似于函数作用域的效果),let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let 为其声明的变量隐式地了所在的块作用域。for 循环头部的 let 不仅将 i 绑定到了 for 循环的块中,事实上它将其重新绑定到了循环 的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值。

for (let i = 0; i < 5; ++i) {
  setTimeout(() => {
    console.log(i); // 0 1 2 3 4
  }, i * 5);
}
console.log(window.i); // undefined
console.log(i); // Uncaught ReferenceError: i is not defined
for (var i = 0; i < 5; ++i) {
  let _i = i;
  setTimeout(() => {
    console.log(_i); // 0 1 2 3 4
  }, i * 5);
}
console.log(window.i); // 5
console.log(i); // 5

构建闭包环境

IIFE 会通过声明并立即执行一个函数来创建作用域

for (var i = 1; i < =5; ++i) {
 (
   function(){
     var j = i
     setTimeout(function timer(){
       console.log(j)
     },j*1000)
   }
 )()
}
console.log(window.i); // 5
console.log(i); // 5

try catch作用域机制

非常少有人会注意到 JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作 用域,其中声明的变量仅在 catch 内部有效。

for (var i = 0; i < 5; ++i) {
  try {
    throw new Error(i);
  } catch (error) {
    let _i = Number(error.message);
    setTimeout(() => {
      console.log(_i);
    }, _i * 5);
  }
}
console.log(window.i); // 5
console.log(i); // 5

方案四 利用 setTimeout 函数的第三个参数

这个参数会被当成 timer 函数的参数传入

for (var i = 0; i < 5; i++) {
  setTimeout(
    (j) => {
      console.log(j);
    },
    i * 5,
    i
  );
}
console.log(window.i); // 5
console.log(i); // 5

创建函数的方式

  • 方式一函数声明
let bar
function foo(){
	console.log(bar) // 可以访问
}

  • 方式二 函数表达式
// 函数表达式
let sum = function(a, b) {
 return a + b;
};

创建对象的方式

构造函数的语法

第一种是使用 new 操作符和 Object 构造函数,新对象通过使用 new 操作符后跟一个构造函数(constructor)来创建。

var myObj = new Object();
myObj.key = value;
let person = new Object();
person.name = "Nicholas";
person.age = 29;

对象字面量的方式

var myObj = {
key: value
// ...
};

Object.create

  1. 创建一个对象myObject 并把这个对象的 [[Prototype]] 关联到指定的对象, 利用给定的 proto 作为 [[Prototype]] 和可选的属性描述来创建一个空对象
const animal = {
  eats:true
}

const rabbit = Object.create(animal)

请你谈谈Object.assign

Object.assign ES6 定义了 Object.assign(..) 方法来实现浅复制

let dest = {
  user:'yayxs'
}
Object.assign(dest, {name:'yayxs'},{age:12})
console.log(dest)

我们也可以用 Object.assign 代替 for..in 循环来进行简单克隆,它将 user 中的所有属性拷贝到了一个空对象中,并返回这个新的对象

谈谈JS中的垃圾回收机制

JS不同于C和C++ 自动管理内存分配和闲置资源 的回收:确定哪个变量没有再被使用,然后释放占用的内存

  • 周期性 每隔一定的时间自动运行 周而复始 从一而终
  • 算法 简单的靠算法 不能够准备正确的定位某块内存是否正在使用,存在一定的不可预判性

函数中的局部变量在使用之后就不再需要了,一般来说可以正常的释放掉,但是不是唯一的,需要内部去跟踪标记,大致有两种方式

标记清理

当变量进入上下文标记一下 理论上可能会被用到 ,离开的时候再标记一下,在任何的上下文中都找不到他们了就开始清理

引用计数

记录一个变量被引用的次数,引用数+1 当当前的引用数是0的时候,就是访问不到这个变量了,存在循环引用的问题

  • 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
  • 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
  • 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript引擎不再使用这种算法,但某些旧版本的IE-仍然会受这种算法的影响,原因是JavaScript会访问非原生JavaScript对象(如DOM元素)。
  • 引用计数在代码中存在循环引用时会出现问题。
  • 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。

this的指向问题

对象方法中的this指向对象obj


function foo() {
  console.log(`指向对象obj`,this);
  console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo,
};
obj.foo(); // 2

this 实际上是在函数调用时发生的绑定,指向什么完全取决于函数在哪里被调用,this 的值是在代码运行时计算出来的,它取决于代码上下文。

调用函数中的this

function foo() {
  console.log(`this指向window`,this)
  console.log(this.a);
}

var a = 2;

foo(); // 2

  • 严格模式下是undefined
  • 非严格模式下指向的是window

箭头函数与普通函数的区别是什么?

什么是箭头函数

  • 语法简洁
  • 没有自己的this
  • 不能用作构造函数。
const agesArr = [12,13,7,8 ]

const res = agesArr.map(item=>`${item}岁`)
console.log(res) // [ '12岁', '13岁', '7岁', '8岁' ]
const fn  = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
const res1 = fn()
console.log(res1) // 6

优势

  • 关于函数的参数默认值

    • 之前
    function log(x, y) {
      y = y || 'World';
      console.log(x, y);
    }
    
    if (typeof y === 'undefined') {
      y = 'World';
    }
    
    • 现在
    function Point(x = 0, y = 0) {
      this.x = x;
      this.y = y;
    }
    
  • 写起来更短

  • 没有单独的this

  • 箭头函数使得表达更加简洁。

没有箭头函数

函数是根据如何被调用来定义这个函数的this

  • 如果是该函数是一个构造函数,this指针指向一个新的对象
  • 在严格模式下的函数调用下,this指向undefined
 function Person() {
        // Person() 构造函数定义 `this`作为它自己的实例.
        this.age = 0;

        setInterval(function growUp() {
          console.log(this);
          // 在非严格模式, growUp()函数定义 `this`作为全局对象,
          // 与在 Person()构造函数中定义的 `this`并不相同.
            // 此时的this是window 对象(浏览器环境)
          this.age++;
        }, 1000);
      }

用箭头函数

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 正确地指向 p 实例
  }, 1000);
}

var p = new Person();

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

普通函数与箭头函数有什么不同

  • 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象
let obj = {
  name: "张三",
  sayHi() {
    console.log(this); // obj 这个对象
    function sayName() { 
      console.log(this); // 是一个函数  this 指向window
    }
    sayName()
    const foo = ()=>{
      console.log(this) // obj 这个对象
    }
    foo()
  },
};
console.log(obj.name);
obj.sayHi();

  • ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
// arguments变量的写法 类似数组的对象
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest参数的写法 真正的数组
const sortNumbers = (...numbers) => numbers.sort();
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

new操作符执行时发生了什么

  1. 一个新的空对象被创建并分配给 this
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。
function User(name) {
  // this = {};(隐式创建)

  // 添加属性到 this
  this.name = name;
  this.isAdmin = false;

  // return this;(隐式返回)
}

谈谈可选链 ?. 语法

  • obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。
  • obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。
  • obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined。

不精确的计算之为什么 0.1 + 0.2 != 0.3

let res = 0.1+0.2
console.log(res)
0.30000000000000004

Js 有一种数据类型是 Number 其中有个浮点值(数值中必须包含小数点,而且小数点后面必须至少有一个数字) 浮点值的精确度最高可达 17 位小数,但在算术计算中远不如整数精确 由于这种微小的舍入错误,导致很难测试特定的浮点值

::: tip 之所以存在这种舍入错误,是因为使用了 IEEE 754 数值(双精度浮点数),这种错误并非 ECMAScript 所独有。其他使用相同格式的语言也有这个问题。

不仅仅是 JavaScript 许多其他编程语言也存在同样的问题。

PHP,Java,C,Perl,Ruby 给出的也是完全相同的结果,因为它们基于的是相同的数字格式。 :::

一个数字以其二进制的形式存储在内存中,一个 1 和 0 的序列。但是在十进制数字系统中看起来很简单的 0.1,0.2 这样的小数,实际上在二进制形式中是无限循环小数。

解决

console.log(+res.toFixed(2));

请注意,toFixed 总是返回一个字符串。它确保小数点后有 2 位数字。如果我们有一个电子购物网站,并需要显示 ¥ 0.30,这实际上很方便。对于其他情况,我们可以使用一元加号将其强制转换为一个数字:

let sum = 0.1 + 0.2;
alert(sum.toFixed(2)); // 0.30
let sum = 0.1 + 0.2;
alert(+sum.toFixed(2)); // 0.3

for..of, for..in 和 forEach,map 的区别

for 循环

这个是最最基础的操作。我们可以通过循环数组的下标,来依次访问每个值:

// 获取数组的长度
const len = arr.length;
for (let i = 0; i < len; i++) {
  // 输出数组的元素值,输出当前索引
  console.log(arr[i], i);
}

for in

for-in语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。,循环会遍历 所有属性,不仅仅是这些数字属性

for (const key in window) {
  document.write(`${key}-------`);
}
var anotherObject = {
  a: 2,
};

// 创建一个关联到 anotherObject 的对象
var myObject = Object.create(anotherObject);
for (var k in myObject) {
  console.log('found: ' + k);
}
// found: a
'a' in myObject; // true

for-of

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,不能获取当前元素的索引,只是获取元素值,但大多数情况是够用的。而且这样写更短,通常情况下用于处理数组

for (let val of arr) {
  // ele是元素
}

forEach

通过取forEach方法中传入函数的第一个入参和第二个入参,我们也可以取到数组每个元素的值及其对应索引,会遍历数组中的所有值并忽略回调函数的返回值

arr.forEach((item, index) => {
  // 输出数组的元素值,输出当前索引
  console.log(item, index);
});

map

map 方法在调用形式上与 forEach 无异,区别在于 map 方法会根据你传入的函数逻辑对数组中每个元素进行处理、进而返回一个全新的数组。 所以其实 map 做的事情不仅仅是遍历,而是在遍历的基础上“再加工”。当我们需要对数组内容做批量修改、同时修改的逻辑又高度一致时,就可以调用 map 来达到我们的目的:

const newArr = arr.map((item, index) => {
  // 输出数组的元素值,输出当前索引
  console.log(item, index);
  // 在当前元素值的基础上加1
  return item + 1;
});

个人推荐如果没有特殊的需要,那么统一使用 for 循环来实现遍历。因为从性能上看,for 循环遍历起来是最快的。

可迭代对象有哪些特点

  • 数组可以迭代
  • 字符串可以迭代

实现了 Symbol.iterator 方法的对象,一般是使用for of 来进行迭代

什么是类数组array-like

是有索引和 length 属性的对象,所以它们看起来很像数组。

如何转为真正的数组

Array.from(arrayLike);

Array.from 方法接受对象,检查它是一个可迭代对象或类数组对象,然后创建一个新数组,并将该对象的所有元素复制到这个新数组。

Set Map WeakSet WeakMap的区别

首先我们知道一点,什么是 Set 什么是 Map 等这几个又是什么玩意,我们知道和对象差不多,但是对象的 key 只能是字符串 或者 Symbl类型

let id = Symbol("id");
console.log(typeof id);
let obj = {
  0: "0",
  "0": "123",
  [id]: "12",
};

console.log(obj); //  {0: "123", Symbol(id): "12"}

Map

拿这时候就可以搞一个Map 了 摆脱这种束缚,也就是说 Map 的 key 可以是对象类型。好的那我们还是先写一个简单的Map

let doms = document.getElementsByTagName("h1");
console.log(doms);

let map = new Map();
[...doms].forEach((h1) => {
  map.set(h1, "测试");
});
console.log(map);

console.log(map.get(doms[0])); // 测试
console.log(map.size);
let key = {
  name: "yayxs",
};
let key1 = {
  age: "18",
};
let map = new Map();

map.set(key, "name").set(key1, "age");
console.log(map.keys());
console.log(map.values());
let map = new Map([
  ["pingguo", 19],
  ["xiangjiao", 20],
]);

for (let k of map.keys()) {
  console.log(k);
}
for (let v of map.values()) {
  console.log(v);
}
for (let e of map) {
  console.log(e);
}
let obj = {
  name: "yayxs",
  age: "20",
};
let map = new Map(Object.entries(obj));
console.log(map);
let obj  = {
  '1':'yayxs',
  1:'yayxs'
}
console.log(obj)
let id = Symbol('id')

let obj = {
  [id]:'yayxs'
}
console.log(obj)

以上都是使用map的姿势,或者说我们再来看看map的迭代

const map  = new Map()

console.log(map)
map.set('1','str')
map.set(0,'num')
map.set(true,'boo')

console.log(map.keys())
console.log(map.values())
console.log(map.entries())

然后是mapforEach 方法

map.forEach( (value, key, map) => {
  console.log(`${key}: ${value}`); 
});

从一个对象创建一个map

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John

WeakMap

WeakMap 的键必须是对象,WeakMap 不支持迭代以及 keys(),values() 和 entries() 方法

let map = new Map();
map.set("1", "yayxs");
console.log(map);
let map = new WeakMap();
map.set("1", "yayxs");
console.log(map); // Identifier 'map' has already been declared
let obj = {
  name: "yayxs",
};

let arr = [obj];
obj = null;

console.log(arr[0]);

如果我们在 weakMap 中使用一个对象作为键,并且没有其他对这个对象的引用 —— 该对象将会被从内存(和map)中自动清除。

Set

let set = new Set();

let zhangsan = {
  name: "zhansan",
};
let lisi = {
  name: "lisi",
};

let wangermazi = {
  name: "wangermazi",
};

set.add(zhangsan);
set.add(lisi);
set.add(wangermazi);
set.add(zhangsan);

console.log(set);

for (let val of set) {
  console.log(val);
}
console.log(set.keys());

WeakSet

  • 与 Set 类似,但是我们只能向 WeakSet 添加对象(而不能是原始值)
  • 对象只有在其它某个(些)地方能被访问的时候,才能留在 set 中
  • 跟 Set 一样,WeakSet 支持 add,has 和 delete 方法,但不支持 size 和 keys(),并且不可迭代

谈谈对象的Object.keys,values,entries

let obj = {
  key1:'1',
  key2:'2',
  key3:'3'
}

console.log(Object.keys(obj)) // ["key1", "key2", "key3"]
console.log(Object.values(obj))
console.log(Object.entries(obj))

rest参数arguments变量

rest参数

Rest 参数必须放到参数列表的末尾

function sum (a,b,...rest){
  let sum = 0
  for(let ele of rest){
    sum = sum +ele
  }
  console.log(sum)
}

sum(1,2,3,4)

arguments

function foo(...rest){
console.log(rest)
console.log(Array.isArray(rest))
}

function bar(){
  console.log(arguments)
  console.log(Array.isArray(arguments))
}

foo(1,2)
bar(3,4)

如果我们在箭头函数中访问 arguments,访问到的 arguments 并不属于箭头函数,而是属于箭头函数外部的“普通”函数。

谈谈JS中的垃圾回收机制

JS不同于C和C++ 自动管理内存分配和闲置资源 的回收:确定哪个变量没有再被使用,然后释放占用的内存

  • 周期性 每隔一定的时间自动运行 周而复始 从一而终
  • 算法 简单的靠算法 不能够准备正确的定位某块内存是否正在使用,存在一定的不可预判性

函数中的局部变量在使用之后就不再需要了,一般来说可以正常的释放掉,但是不是唯一的,需要内部去跟踪标记,大致有两种方式

标记清理

当变量进入上下文标记一下 理论上可能会被用到 ,离开的时候再标记一下,在任何的上下文中都找不到他们了就开始清理

引用计数

记录一个变量被引用的次数,引用数+1 当当前的引用数是0的时候,就是访问不到这个变量了,存在循环引用的问题

  • 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
  • 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
  • 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript引擎不再使用这种算法,但某些旧版本的IE-仍然会受这种算法的影响,原因是JavaScript会访问非原生JavaScript对象(如DOM元素)。
  • 引用计数在代码中存在循环引用时会出现问题。
  • 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。

闭包及作用闭包有哪些使用场景?优缺点是什么

function foo() {
  var a = 2;
  function bar() {
    console.log(a); // 2
  }
  bar(); // bar()涵盖foo()作用域的闭包
}
foo();


function foo(){
    var a = 2
    function bar(){
        console.log(a)
    }
    return bar
}

var baz = foo()

baz()

在定时器、事件监听器、 Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)>任务中,只要使 用了回调函数,实际上就是在使用闭包!

应用场景

模块,当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时 就产生了闭包。

JS中的声明提升

var声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。这个现象叫作“提升”

提升让同一作用域中的代码不必考虑变量是否已经声明就可以直接使用。可是在实践中,提升也会导致合法却奇怪的现象,即在变量声明之前使用变量。

打个比方,这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动” 到了最上面。这个过程就叫作提升。

function fn1() {
  var name = 'Jake';
}

// 等价于:
function fn2() {
  var name;
  name = 'Jake';
}
foo(); // 1
var foo;
function foo() {
  console.log(1);
}
foo = function () {
  console.log(2);
};

即使是else语句,但是 var被提升至函数sayHi 的顶部

function sayHi() {
  // var phrase // 第一步
  if (false) {
    var phrase = "Hello";;
  }
  // if (false) {
  //   phrase = "Hello"; 第二步
  // }
  console.log(phrase);
}
sayHi();

函数优先 其次变量

谈谈你所了解的立即调用函数表达式

这种模式很常见,几年前社区给它规定了一个术语:IIFE,代表立即执行函数表达式(Immediately Invoked Function Expression);

var a = 2;
(function foo() {
  var a = 3;
  console.log(a); // 3
})();
console.log(a); // 2

创建 IIFE 的方法

这种模式很常见,几年前社区给它规定了一个术语:IIFE,代表立即执行函数表达式(Immediately Invoked Function Expression);

var a = 2;
(function foo() {
  var a = 3;
  console.log(a); // 3
})();
console.log(a); // 2

简单说说promise

  • 问题一 Promise构造函数是同步还是异步执行
  • 问题二:then中的方法呢 ?
  • 问题三:promise如何实现then处理 ?

回调的问题

  • 代码臃肿。
  • 可读性差。
  • 耦合度过高,可维护性差。
  • 代码复用性差。
  • 容易滋生 bug。
  • 只能在回调里处理异常。
let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
$.ajax({
    url:url1,
    error:function (error) {},
    success:function (data1) {
        console.log(data1);
        $.ajax({
            url:url2,
            data:data1,
            error:function (error) {},
            success:function (data2) {
                console.log(data2);
                $.ajax({
                    url:url3,
                    data,
                    error:function (error) {},
                    success:function (data3) {
                        console.log(data3);
                    }
                });
            }
        });
    }
});

大家都在说promise ,面试的时候也基本是必问的问题,一般知识点的话,一些网上的参考文章是不会过时的,比如像一些配置性的文章大家可能喜欢看最近的文章。我们将从基本概念 原理 面试 去看这些问题。首先要说的就是promise 一般我们就是读文档,从简单到难。

Promise 是个对象

Promise instanceof Object;
true;

代表一个异步操作的最终完成或者失败

假设现在我们需要这样一件事情:创建一个音频文件,这就需要我们准备两个回调函数,一是成功的时候调用,二是失败的时候调用

那这样我们就需要准备两个回调函数,有了promise 之后,我们就可以这样

const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);

对象的构造器语法

const p = new Promise((resolve,reject)=>{
    
})

传给peomise 的函数会自动执行

const p = new Promise(() => {
  console.log('函数立即执行');
});

Promise是一个构造函数,使用new操作符返回一个promise对象构造函数接收一个excutor函数作为参数,excutor 函数有两个函数类型的参数 resolve 和 reject,这两个函数都是js 尝试去调用的

返回的这个p 有内部两个属性,起初的时候是 pending undefined

[[PromiseState]]: "pending"
[[PromiseResult]]: undefined

常见的异步任务有哪些

异步任务有两种类型:微任务(microtask)和宏任务(macrotask)。不同类型的任务会被分配到不同的任务队列中。当执行栈中的所有任务都执行完毕后,会去检查微任务队列中是否有事件存在,如果存在,则会依次执行微任务队列中事件对应的回调,直到为空。然后去宏任务队列中取出一个事件,把对应的回调加入当前执行栈,当执行栈中的所有任务都执行完毕后,检查微任务队列中是否有事件存在。

  • 队列是先进先出 首先进入队列的任务首先执行
  • JS引擎中没有其他任务 才执行任务队列

微任务队列(ES8术语)

属于微任务的事件包括但不限于以下几种:

  • Promise.then
  • Object.observe
  • process.nextTick

宏任务

属于宏任务的事件包括但不限于以下几种:

  • setTimeout
  • setInterval
  • setImmediate
  • IO

setTimeout Promise Async/Await 的区别

Async/await是以更舒适的方式使用promise的一种特殊语法,同时它也非常易于理解和使用。

async

async function foo() {
  return 1;
}

foo().then(res=>{console.log(res)})

await

  1. 如果我们尝试在非 async 函数中使用 await 的话,就会报语法错误
  2. await 不能在顶层代码运行

JS的模块化开发

了解模块化

模块的概念

将一个复杂的程序依据一定的规则或者说是规范封装成几个块,或者是文件,并进行组合在一起,块的内部数据与实现是私有的,只是向外暴露一些接口或者是方法与其他的模块进行通信。

为什么要引入模块化
  • 降低复杂度,提高解耦性
  • 部署方便
  • 避免命名冲突
  • 更好的分离,按需加载
  • 更高的维护性
随之而来的问题
  • 请求过多
  • 依赖模糊
  • 难以维护

CommonJS 服务器执行环境

规范说明

每个文件都可以当做一个模块,CommonJS 规范概述了同步声明依赖的模块定义。这个规范主要用于在服务器端实现模块化代码组织,但也可用于定义在浏览器中使用的模块依赖。在服务器端:模块的加载运行时同步的,不能直接运行在浏览器端。

模块的暴露

暴露的本质:对象(exports 对象)

module.exports =  一个值
exports.xxx =  一个值

需要注意的是:

  • 当 exports 和 module.exports 同时存在的时候,module.exports 会盖过 exports
  • 当模块内部全部是 exports 的时候, 就等同于 module.exports
  • 最后 我们就可以认定为 exports 其实就是 module.exports 的子集。
// moduleA.js
console.log("moduleA.js执行");

module.exports = "string123";
// index.js
const ma = require("./moduleA");
console.log("index执行");

console.log("moduleA.js的执行结果", ma); // {} 默认是{}

AMD 浏览器执行环境

最古老的模块系统之一, Asynchronous Module Definition中文名是异步模块。它是一个在浏览器端模块化开发的规范,由于不是 js 原生支持,使用 AMD 规范进行页面开发需要用到对应的函数库,也就是大名鼎鼎的RequireJS实际上 AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出。

define('moduleA', ['moduleB'], function(moduleB) {
  return {
    stuff: moduleB.doStuff();
  };
});

解决的问题

  • 多个 JS 文件可能有依赖的关系,被依赖的文件需要早于依赖它的文件加载到浏览器
  • JS 加载的时候浏览器会停止页面的渲染,加载的文件越多,页面失去响应的时间越长
  • 异步前置加载

通用模块定义

通用模块定义(UMD,Universal Module Definition)它与 AMD 和 CommonJS 都兼容

(function(root, factory) {
  if (typeof define === "function" && define.amd) {
    // AMD。注册为匿名模块
    define(["moduleB"], factory);
  } else if (typeof module === "object" && module.exports) {
    // Node。不支持严格CommonJS
    // 但可以在Node这样支持module.exports的
    // 类CommonJS环境下使用
    module.exports = factory(require(" moduleB "));
  } else {
    // 浏览器全局上下文(root是window)
    root.returnExports = factory(root.moduleB);
  }
})(this, function(moduleB) {
  // 以某种方式使用moduleB

  // 将返回值作为模块的导出
  // 这个例子返回了一个对象
  // 但是模块也可以返回函数作为导出值
  return {};
});

CMD 的 seaJS

define(id, deps, factory)

因为CMD推崇一个文件一个模块,所以经常就用文件名作为模块id;
CMD推崇依赖就近,所以一般不在define的参数中写依赖,而是在factory中写。

factory有三个参数:
function(require, exports, module){}

一,require
require 是 factory 函数的第一个参数,require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口;

二,exports
exports 是一个对象,用来向外提供模块接口;

三,module
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

demo
// 定义模块  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
});

// 加载模块
seajs.use(['myModule.js'], function(my){

});

ES6 模块

  • 模块代码只在加载后执行。
  • 模块只能加载一次
  • 模块是单例
  • 模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互
  • 模块可以请求加载其他模块
  • 支持循环依赖

总结

由于模块支持特殊的关键字和功能,因此我们必须通过使用 <script type="module"> 特性(attribute)来告诉浏览器,此脚本应该被当作模块(module)来对待

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue-vast-admin</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

4、JavaScript进阶

5、做一个读书人

文章写到这儿,我需要说一点非常重要的事儿了 读书读书读书读书读书 做技术如果想要走的远,或者人生想要更上一层楼,的话读书必须的,弱弱的说以一句,放下短视频吧 在很久之前我做了一期视频

记得特别清楚,那是做了一晚上的视频哈哈,因为当初还不怎么熟练PS PR Au , 有一些书挺不错的,分享如下:

HTML

其实关于html 部分呢,我们可以从一些诸如MDN 或者其他线上的文档方式阅览,一遍实操一遍看文档,会比较快进入****结构****的大门

  • *《HTML5 权威指南》*

介绍学习需要的预备知识和 HTML、CSS 和 JavaScript 的进展等

  • *《HTML5 程序设计(第 2 版)》*

HTML5 历史背景、新的语义标签及与以往 HTML 版本相比的根本变化

CSS

接着就是咱们的样式表,也就是我们所谓的CSS ,CSS 是我们的画笔,使我们是充满 的魅力,时而炫酷,时而安谧。

  • *《CSS 设计指南》*

  • *《精通 CSS 高级 web 标准解决方案(第 2 版)》*

介绍了 CSS 的基本概念和最佳实践

  • *《CSS 揭秘》*

47 个 css 技巧让你在面对各种 css 问题的时候游刃有余;背景与边框、形状、 视觉效果、字体排印、用户体验、结构与布局、过渡与动画等

  • *《CSS 权威指南(第 3 版)2006》*

理论和例子的比例安排的合理化,比较容易阅读;会告诉我们 CSS 是什么、有什么可以做什么涵盖 CSS2.0

  • *《Head First HTML and CSS, XHTML》*

较好的一本入门书

  • *《CSS 世界》* 作者 张鑫旭

以“流”为线索,从结构、内容到美化装饰等方面,全面且深入地讲解前端开发人员必须了解和掌握的大量的 CSS 知识点

JavaScript

其实我们会发现,很多****面试中****比较热的词汇,包括不限于像闭包 作用域 原型链 等等,这些在不同的书中,多多少少,深浅总有涉及与描述。主动汲取总比被动接受要好,与其埋怨生活、自怨自艾;不如探索书中的奥秘,回归知识的海洋,与同事或者身边人“谈笑风声”……

  • *《JavaScript 高级程序设计》 又名:前端红宝书*

讲解了JS 语言中各个组成部分;其中包括 DOM、事件模型等;基本是 ES5 的内容

  • *《JavaScript 权威指南》- 犀牛书*

  • *《JavaScript DOM 编程艺术》*

透彻阐述了平稳退化等一批至关重要的 JavaScript 编程原则和实践;可以跟着敲一些 DEMO

  • *《你不知道的 JavaScript 上、中、下》*

上卷主要介绍了,作用域与闭包,以及 this 和对象原型(理解这些才是才能通往框架等流行技术)

中卷主要讲了类型语法异步和性能

下卷主要讲了对 ES6 以及未来发展的趋势展望

  • *《高性能 JavaScript》*

  • *《JavaScript 语言精粹》*

  • *《网道 JavaScript 教程 阮一峰》*

  • *《ES6 标准入门 阮一峰》*

  • *《深入理解 ES6》*

数据结构与算法

  • *《学习 JavaScript 数据结构与算法》*

介绍常用的算法

  • *《数据结构与算法 JavaScript 描述》*

计算机网络

  • *《HTTP 权威指南》*

那么请问地址栏输入 URL 发生了什么,哈哈

  • *《图解 HTTP》*

  • *《图解 TCP/IP》*

框架

  • *《深入 React 技术栈》*

  • *《深入浅出 Vue.js》*

Node

  • *《Node.js 实战(第 2 版)》*

  • *《深入浅出 Node.js》*

性能优化

  • *《Web 性能权威指南》*

设计模式

关于前端,你说要不要了解设计模式,我觉得还是挺重要的,包括像发布者订阅模式 等等,那关于设计模式的有什么书可以拿来读呢

  • *《JavaScript 设计模式和开发实战》*

  • *《JavaScript 设计模式》*

6、计算机网络

前边四点可以说是基本的点,4点相辅相成,也是前端的基本,接下来打算说说网络

经典的五层模型

  • 分层管理:应用层、传输层、网络层、数据链路层

    • 应用层:决定向用户提供应用服务时的通信活动 FTP DNS HTTP
    • 传输层:网络俩你接种两台计算机之间的数据传输 TCP UDP
    • 网络层:处理网络上的数据包
    • 数据链路层:链接网络的硬件部分

http的发展

版本特点年份
HTTP/0.91.只有一个GET 2 .没有HEADER 3.关闭TCP链接1990
HTTP/1.01.增加了命令 2.多字符集支持1996年5月
HTTP/1.11.增加持久连接2. pipeline 3. 增加host1997年1月
HTTP21.二进制传输 2,多个请求不再按顺序来 3. 头信息压缩以及推送提高效率的功能

TCP三次握手

为了准确无误传送数据,TCP采用三次握手 ,网络传输的原因,数据包丢失了,服务端不知道是否接收信息

GTE vs POST

标准参考

W3school:www.w3school.com.cn/tags/html_r…

项目GETPOST
后退按钮/刷新无害数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
书签可收藏为书签不可收藏为书签
缓存能被缓存不能缓存
编码类型application/x-www-form-urlencodedapplication/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
历史参数保留在浏览器历史中。参数保留在浏览器历史中。
对数据长度的限制是的。当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。无限制
对数据类型的限制只允许 ASCII 字符。没有限制。也允许二进制数据。
安全性与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET !POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。
可见性数据在 URL 中对所有人都是可见的。数据不会显示在 URL 中。

副作用和幂等的概念

副作用指对服务器上的资源做改变,搜索是无副作用的,注册是副作用的。

幂等指发送 M 和 N 次请求(两者不相同且都大于 1),服务器上资源的状态一致,比如注册 10 个和 11 个帐号是不幂等的,对文章进行更改 10 次和 11 次是幂等的。因为前者是多了一个账号(资源),后者只是更新同一个资源。

在规范的应用场景上说,Get 多用于无副作用,幂等的场景,例如搜索关键字。Post 多用于副作用,不幂等的场景,例如注册。

技术上的不同

  • Get 请求能缓存,Post 不能
  • Post 相对 Get 安全一点点,因为Get 请求都包含在 URL 里(当然你想写到 body 里也是可以的),且会被浏览器保存历史纪录。Post 不会,但是在抓包的情况下都是一样的。
  • URL有长度限制,会影响 Get 请求,但是这个长度限制是浏览器规定的,不是 RFC 规定的
  • Post 支持更多的编码类型且不对数据类型限制

报文上的区别

结论:GET 和 POST 方法没有实质区别,只是报文格式不同。

GET 和 POST 只是 HTTP 协议中两种请求方式,而 HTTP 协议是基于 TCP/IP 的应用层协议,无论 GET 还是 POST,用的都是同一个传输层协议,所以在传输上,没有区别。

报文格式上,不带参数时,最大区别就是第一行方法名不同

POST方法请求报文第一行是这样的POST /products/create HTTP/1.1

GET方法请求报文第一行是这样的 GET /products?name=zs&age=18 HTTP/1.1

是的,不带参数时他们的区别就仅仅是报文的前几个字符不同而已

带参数时报文的区别呢? 在约定中,GET 方法的参数应该放在 url 中,POST 方法参数应该放在 body 中

举个例子,如果参数是 pname='小米9', pprice=3500

GET 请求方法的报文如下: 在这里插入图片描述

POST 请求方法的报文如下: 在这里插入图片描述

现在我们知道了两种方法本质上是 TCP 连接,没有差别,也就是说,如果我不按规范来也是可以的。我们可以在 URL 上写参数,然后方法使用 POST;也可以在 Body 写参数,然后方法使用 GET。当然,这需要服务端支持。

  • get 用于向服务器查询某些信息。必要时,需要在GET请求的URL后面添加查询字符串参数
https://jsonplaceholder.typicode.com/posts?_offset=1&_limit=2
  • post 用于向服务器发送应该保存的数据。每个POST请求都应该在请求体中携带提交的数据,而GET请求则不然

WebSocketAjax的区别是什么,怎么实现

Ajax

异步js与xml 有效利用js和dom 局部web页面的替换异步通信手段,只更新部分页面 XMLHttpRequest 存在大量请求发生

websocket

实现的一套新协议以及api,作为html5的一部分,可以互相发送数据,发起方还是客户端

  • 推送功能 服务器直接发送数据
  • 减少通信量 一直保持连接状态 减少开销 通信量减少‘
  • Upgrade websocket

websocket 的应用场景有哪些

Web Socket(套接字)的目标是通过一个长时连接实现与服务器全双工、双向的通信。不能再使用http://或https://,而要使用ws://和wss://。前者是不安全的连接,后者是安全连接

谈谈你对IP的理解

ip协议 位于网络层(IP地址和MAC地址)

  • ip地址可以变化
  • mac基本不会改变

谈谈你对TCP的理解

tcp 位于传输层,提供字节流服务,采用三次握手策略,采用 synack 标志 。主要确保数据传输的可靠性

谈谈对http的理解及基本结构

肯定是从客户端开始建立通信,是不保存的协议,**无状态的 ** 不做持久化处理 持久连接 (为了减少重复连接断开的开销)

缺点

  • 通信使用明文 不加密 内容可能被窃听
  • 不验证通信方身份
  • 无法证明报文的完整性
  • 存在安全漏洞

http一样位于应用层,提供域名到ip的解析服务,从域名到ip,从ip到域名

httphttps协议的基本概念、区别、工作原理

通过通信加密,http 协议中是没有什么加密机制 的,与SSL 建立通信组合使用 便有个一个https

  • http超文本传输协议
  • https 超文本传输安全协议
    • 因为和ssl通信,会适当变慢,加密解密消耗资源
    • 购买证书要花钱

ssl提供加密处理,证书手段,第三方机构颁发 md5 SHA-1 散列校验,https不是一个新的协议

ssl 是应用最为广泛的安全技术

HTTP2 和 HTTP1 有什么区别

  • http2.0 一直正在推进
    • 速度体验
    • 多路复用
    • TLS义务化
    • 协商
    • 客户端
    • 流量控制
    • websocket

HTTPS是如何进行加密的 谈谈https的原理?为什么 https 能保证安全?

加密处理和认证 添加了加密以及认证机制的http称为 https 。SSL采用公开秘钥加密

非对称加密

一把私有秘钥 一把公开密钥

CA机构

  • 提出公开秘钥的申请
  • 分配给一个公开的秘钥
  • 服务器送给客户端
  • 客户端拿到数字证书(证书)

URL vs URI

  • url统一资源定位符 指定文档所在地址的URL 网页地址 输入的网页地址
  • uri 统一资源标识符
    • u 协议方案 ftp http mailto telnet file
  • url是uri的子集
https://www.baidu.com/s?ie=utf-8&wd=vue&tn=40025628_10_hao_pg
  • https:// 协议方案名

  • www.baidu.com 服务器地址

  • 443 服务器端口号

  • wd= 查询字符串

谈谈http 报文

用于http协议交互的信息被称为http报文,大体分为请求行 状态行 首部字段 其他

内容编码

常见的内容编码

  • gzip
  • compress
  • deflate
  • identity
mime机制
  • multipart/form-data 表单文件上传使用
  • multipart/byteranges 状态码 206

使用对象的集合时候需要在首部字段增加Content-type

HTTP的请求报文由哪几部分组成

  • 请求方法
  • 请求的URI
  • http的版本号
  • 可选的请求首部字段
  • 内容实体
GET https://jsonplaceholder.typicode.com/posts?_offset=1&_limit=2 HTTP/1.1
User-Agent:Fiddler Everywhere
Host:jsonplaceholder.typicode.com
Content-Type:text/plain

HTTP的响应报文由哪几部分组成

  • http的版本号 状态码 原因短语
  • 响应的日期
  • 首部字段
  • 资源实体的主题
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  }
]

基本是由 协议版本 状态码 原因短语 可选的响应首部字段 主体

HTTP请求方法

HTTP 定义了一组请求方法, 以表明要对给定资源执行的操作。指示针对给定资源要执行的期望动作. 虽然他们也可以是名词, 但这些请求方法有时被称为HTTP动词. 每一个请求方法都实现了不同的语义, 但一些共同的特征由一组共享:: 例如一个请求方法可以是 safe, idempotent, 或 cacheable.

方法名称用法
GETGET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.
HEADHEAD方法请求一个与GET请求的响应相同的响应,但没有响应体.
POST用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用.
PUTPUT方法用请求有效载荷替换目标资源的所有当前表示。
DELETEDELETE方法删除指定的资源。
CONNECTCONNECT方法建立一个到由目标资源标识的服务器的隧道。
OPTIONSOPTIONS方法用于描述目标资源的通信选项。
TRACETRACE方法沿着到目标资源的路径执行一个消息环回测试。
PATCHPATCH方法用于对资源应用部分修改。

cookie状态管理

正是因为http 是无状态的,存储的是会话信息,*通过在请求和响应 ** 的报文中

不难发现cookie 的组成就是有图片中展示的 几种参数构成的

Set-Cookie: __cfduid=df6eaa5fb405d992b43fb3487389a612c1607999890; expires=Thu, 14-Jan-21 

通知客户端保存Cookie

首部字段名说明首部类型
Set-Cookie开始状态管理所使用的Cookie信息响应首部字段
Cookie服务器接收到的Cookie信息请求首部字段

Set-Cookie 字段的属性

属性说明
NAME=VALUE名称值
expires=DATE有效期
path=PATH文件目录作为Cookie的适用对象
domain=域名
Secure
HttpOnly加以限制

缺点

  1. 特定域限制

  2. 浏览器会限制

  3. 不会占用太多磁盘空间

  4. 总数大多有限制,超出的话删除之前的,或者随机删除

  5. 大小限制 4096 字节

  6. 数据不是保存在安全的环境中

http常见的状态码

状态码当客户端向服务器发请求,描述返回的请求结果,实际使用的只有14种左右

1xx信息状态接收的请求正在处理
2xx成功状态正常处理完毕
3xx重定向状态附加操作
4xx客户端错误服务器无法处理请求
5xx服务器端错误服务器处理请求出错

浏览器需要执行某些特殊的处理

  • 300 "multiple choices"
  • 301 "moved permanently" 永久性重定向 被分配新的URI
  • 302 "found" 临时性重定向 希望用户本次能使用新的URI访问 不是被永久的移动
  • 303 "see other" 客户端应该是get 方法获取资源
  • 304 "not modified" 和重定向没啥关系 附带条件请求
  • 305 "use proxy"
  • 307 "temporary redirect" 临时重定向 不会从POS变成GET
  • 308 "permanent redirect"

客户端错误

  • 400 "bad request" 语法错误,需要修改请求的内容
  • 401 "unauthorized" 需要认证信息
  • 402 "payment required"
  • 403 "forbidden" 被服务器拒绝 未获得文件系统的访问授权
  • 404 "not found" 没有找打请求资源

服务器错误

  • 500 "internal server error" 存在bug 服务端执行时错误

代理

  • 缓存代理 代理服务器或客户端本地磁盘内保存的资源副本,节省通信流量通信时间
  • 透明代理

http首部

首部分为请求首部和响应首部,并且部分首部两种通用,接下来我们就来学习一部分的常用首部。

通用首部字段

请求报文响应报文 都会使用的首部

字段名说明
Cache-Control控制缓存的行为
Connection浏览器想要优先使用的连接类型,连接的管理
Date创建报文日期时间
Pragma报文指令
Via代理服务器相关信息
Transfer-Encoding传输编码方式
Upgrade要求客户端升级协议
Warning在内容中可能存在错误,错误通知
  • cache-control 操作缓存的工作机制

    Cache-Control: no-cache
    Cache-Control: private,max-age=0,no-cache
    
    指令参数说明
    no-cache再次验证
    no-store
    max-age响应的最大age值
  • connection 控制不再转发给代理 管理持久连接

    ConnectionKeep-Alive在旧版的http协议上维持持久连接,增加Keep-Alive
  • Date

    Date: Tue, 15 Dec 2020 05:32:53 GMT
    

请求首部字段

请求首部字段是从客户端发送请求报文中所使用的字段

请求首部作用
Accept能正确接收的媒体类型,用户代理可处理的媒体类型
Accept-Charset能正确接收的字符集
Accept-Encoding能正确接收的编码格式列表
Accept-Language能正确接收的语言列表
Expect期待服务端的指定行为
From请求方邮箱地址
Host服务器的域名
If-Match两端资源标记比较
If-Modified-Since本地资源未修改返回 304(比较时间)
If-None-Match本地资源未修改返回 304(比较标记)
User-AgentHTTP客户端程序的信息
Max-Forwards限制可被代理及网关转发的次数
Proxy-Authorization向代理服务器发送验证信息
Range请求某个内容的一部分
Referer表示浏览器所访问的前一个页面
TE传输编码方式
  • Accept

    Accept: application/json, text/plain, */*
    - 文本文件
    text/html text/plain text/css application/xhtml+xml application/xml
    - 图片文件
    image/jpeg image/gif imag/png
    - 视频文件
    video/mpeg video/quicktime
    - 二进制文件
    application、octet-stream application/zip
    

响应首部字段

响应首部作用
Accept-Ranges是否支持某些种类的范围
Age资源在代理缓存中存在的时间
ETag资源标识
Location客户端重定向到某个 URL
Proxy-Authenticate向代理服务器发送验证信息
Server服务器名字
WWW-Authenticate获取资源需要的验证信息

实体首部字段

实体首部作用
Allow资源的正确请求方式
Content-Encoding内容的编码格式
Content-Language内容使用的语言
Content-Lengthrequest body 长度
Content-Location返回数据的备用地址
Content-MD5Base64加密格式的内容 MD5检验值
Content-Range内容的位置范围
Content-Type内容的媒体类型,对象类型
Expires内容的过期时间
Last_modified内容的最后修改时间

http认证方式

  • BASIC基本认证
  • DIGEST摘要认证
  • SSL客户端认证
  • FormBase基于表单认证

7、设计模式

前言

不知不觉这已经是前端厚说的第四大篇了,在前端的圈子里很少有人分享设计模式相关的,东西至于为什么分享这篇,我想我有几点要说的

  • 1 设计模式是大学里的导师才会提及的,在实际的开发中很少用到,会写代码不算的优秀,会组织代码才行
  • 2 面试的时候面试你的不仅仅是前端开发者,一些“上了年纪的”领导基本都会问你设计模式,因为这是他们基本功
  • 3 正是计算机网络和设计模式,操作系统这些核心的本才是区别能否走的长久

好啦,html css javascript 那么本篇就来说一说设计模式

什么是设计模式?

在面向对象的软件设计过程中针对特定问题的简洁优雅的解决方案

或者说是一种编程的套路,让代码更好的维护等

常见的设计模式有哪些?

一般我们所说的设计模式差不多有23 中。比较有js 特色的就是 原型模式 以及 vue中不识庐山真面目的观察者模式

构造器模式

首先我们来聊一聊第一个设计模式,那就是构造器模式 可能看文章的小伙伴对这个名字有点陌生,让我们把时光拉到小学的时候,或者中学,此时你是班级里的班长,老师班主任的二把手,接着让你出一版班级里的花名册。这时候就是你大展身手的时候了。

  • 学生一 班花 王二花
  • 学生二 班草 李二蛋
  • 等等……

那你说好办

 const wangerhua = {};

      wangerhua.name = "weh";
      wangerhua.age = 16;
      wangerhua.sex = "女";

      const lierdan = {};

      lierdan.name = "led";
      lierdan.age = 15;
      lierdan.sex = "男";

为了在你的女神三菜 面前耍一手,你熬夜写了班级全部的40 多人,眼看花名册即将完事,你满怀期待的发给你的女神,打算先让她看看……,但是呢,作为旁观者的我们呢,发现是有点问题的,这样熬夜效率不高,虽然也能造出一个花名册,但是此举并不会得到女神的芳心。改进下吧

 function Student() {
        this.name = "wangerhua";
        this.age = "16";
        this.sex = "女";
      }
      // 然后通过`new` 关键词 活生生的`new` 出 一个王二花

但是现在是写死的肯定不行,把三菜 放在何处对吧,那好办

  // 然后通过`new` 关键词 活生生的`new` 出 一个王二花

      function Student(name,age,sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
      }

      const 王二花 = new Student('','','')
      const 李二蛋 = new Student('','','')

或者我们使用最舒服的方式es6class 方式

class Student {
        constructor(name,age,sex) {
          this.name = name;
          this.age = age;
          this.sex = sex;
        }
      }

工厂模式

其核心目的是为了实现无脑的传参。将创建对象的过程单独封装.较快的生成几类对象

  function Persion(name,age,profession,work){
            this.name = name
            this.age = age
            this.profession  = profession
            this.work = work
        }
  • 第一个简单的工厂
 function Factory(name,age,profession){
            let work; // 是干什么的
            switch (profession) {
                case 'coder':
                 work = ['摸鱼']
                    break;
                    case 'coder':
                 work = ['摸鱼']
                    break;
                case 'Fisherman':
                    work = ['捕鱼']
                default:
                    break;
            }
            return new Persion(name,age,profession,work)
        }
  • 第二个工厂

    我们设想一个场景,我记得笔者在高二 年级的时候是文理分科的,我想大多在文章的你是理科生,好的那我们就来看一下这个工厂

      // name 你的名字
          // choose 你的选择
          function factory(name, choose) {
            let stu = [];
            if (type === "理科生") {
              stu = ["敲代码", "摸鱼"];
            } else {
              stu = ["琴棋", "书画"];
            }
            return new Student(name, stu);
          }
    
          const yayxs = factory("洋小洋同学", "理科生");
    

单例模式(vuex中应用)

  • 定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  • 应用:我们的vuex 就是应用了单例模式保证全局store 还有浏览器的window对象
  • 思路:是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象

比如我们设想一个场景,我们的压岁钱我们自己存着,里边存着自己的收入但是我们已经花的只剩下10块了,我的妈妈眼里我的压岁钱还是那么多(只是因为又new了一次)


        function 压岁钱(){
            this.money = 100000
        }
        const 我的压岁钱 = new 压岁钱()
        我的压岁钱.money = 10// 我的妈妈
        const 我的妈妈的概念里的压岁钱 = new 压岁钱()

        我的妈妈的概念里的压岁钱.money = 100000

也就是说不管我们怎么new 返回的都是同一个实例,

 class SingletonMode {
            sayHi(){
                console.log('你好,我是单例模式')
            }
            static isHaveInstance(){
                if(!SingletonMode.isHaveInstance){ // 不存在采取重新创建
                    SingletonMode.instance = new SingletonMode()
                }

                return SingletonMode.instance
            }
        }

我们还是简单的说一下vuex 中,如果我们不能控制全局一个vuex 的话,那我们的数据就会乱了套了,不是吗

let vue
export function install (_vue){
    if(Vue && _vue===vue){
        // 首先都是先判断是否已经存在,如果已经存在的话就不重新创建了
        return
    }
    Vue = _vue
}

原型模式(创建一个对象)

在 Brendan Eich 为 JavaScript 设计面向对象系统时,借鉴了 Self 和 Smalltalk 这两门基于原型 的语言。之所以选择基于原型的面向对象系统,并不是因为时间匆忙,它设计起来相对简单,而 是因为从一开始 Brendan Eich 就没有打算在 JavaScript 中加入类的概念。

接下来我们提到的原型模式不仅仅是一个设计模式,也被称之为一种编程的泛型。

  • 作用适用于创建对象的一种模式

原型模式的实现关键,是语言本身是否提供了 clone 方法。ECMAScript 5 提供了 Object.create方法,可以用来克隆对象。因为本文主要是介绍分享设计模式相关 不会具体分析js的api

   class Coder1{

            constructor(name,age){
                this.name = name
                this.age = age
            }
            like(){
                console.log('i like mofish')
            }
        }

        function Coder2(name,age){
            this.name = name
            this.age = age
        }
        Coder2.prototype.like = function(){
            console.log('i like mofish')
        }

  • 构造函数Coder2.prototype.constructor===构造函数Coder2
  • 实例对象coderYayxs.proto // 原型对象 {like: ƒ, constructor: ƒ}

当我试图访问一个 JavaScript 实例的属性/方法时,它首先搜索这个实例本身;当发现实例没有定义对应的属性/方法时,它会转而去搜索实例的原型对象;如果原型对象中也搜索不到,它就去搜索原型对象的原型对象,这个搜索的轨迹,就叫做原型链。

或者说,我们通过es5 的语法来创建一个对象,当然比如当前的学生有个说自己名字的能力,至于这个能力是每个人都拥有的

  function Student(name,age,sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
      
      function sayName(){
          console.log(`Iam ${this.name}`)
      }
 
      }

      const 王二花 = new Student('','','')
      const 李二蛋 = new Student('','','')

这时候我们可以把通用的能力放在构造函数 的外部,然后类似这样的代码

  function Student(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
      // 引用了sayName函数
        this.sayName = sayName;
      }

      function sayName() {
        console.log(`Iam ${this.name}`);
      }

接着我们使用原型的方式,类似上文的程序员 coder 然后把通用的能力(这里暂且这样称之吧) 放在原型上

 Student.prototype.sayName = function(){
        
      }

装饰器模式(Nest.js中应用)

它的定义是“在不改变原对象的基础上,通过对其进行包装拓展,使原有对象可以满足用户的更复杂需求”。


import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

适配器模式(axios中应用)

  • 应用
    • 至于为什么说在axios 中有应用,之所以axios 是如此优秀的一个库,是因为它的api 足够统一,却带来的在浏览器 以及node 环境 统一的请求能力,这是因为在库中已经做了适配,调用者使用者只需要传入统一化的参数即可
    • 还有就是在vue 的组件化开发中使用计算属性对一些怪异的数据进行转换适配等

代理模式(Proxy)

  • 定义:在某些情况下,出于种种考虑/限制,一个对象不能直接访问另一个对象,需要一个第三者(代理)牵线搭桥从而间接达到访问目的,这样的模式就是代理模式。es6中proxy 一个Proxy 对象包装另一个对象并拦截诸如读取写入等其他的操作,简言之,拦截对象的操作。为一个对象提供一个代用品或占位符,以便控制对它的访问
  • 场景:图片的懒加载、合并http 请求
  • 其他代理:
    • 防火墙代理
    • 远程代理
    • 保护代理
    • 智能引用
  const user = {
        name: "洋小洋同学",
        age: 18,
      };

      /**
       * 参数一 target目标对象  要包装的对象(可以是函数)
       * 参数二 handler对象    代理配置 拦截操作方法
       */
      const  proxy = new Proxy(user, {
          get(user,key){

          },
           /**
         * 参数一 target目标对象
         * 参数二 prop目标属性名称
         * 参数三 val 目标属性的值
         * 参数四
         */
          set: function(user, key, val){

          }
      });

值的一说就是我们的事件代理模式,点击li 的时候让父级的ul 去代理等等,

观察者模式(vue的响应式原理)

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新,有人也称之为发布者订阅模式

class Publisher {
  constructor() {
    this.observers = [];
    console.log("init");
  }
  add(observer) {
    console.log("add");
    this.observers.push(observer);
  }
  remove(observer) {
    console.log("remove");
    this.observers.forEach((item, i) => {
      if (item === observer) {
        this.observers.splice(i, 1);
      }
    });
  }
  notify() {
    console.log("notify");
    this.observers.forEach((item) => {
      item.update(this);
    });
  }
}

class Observer {
  constructor() {
    console.log("init");
  }
  update() {
    console.log("update");
  }
}

二、我读了哪些书

三、谈谈垂直的Vue

简单反思一下,2020年,我从年初搞flutter 然后年中搞vue 后来搞react 再到最近搞vue 这一年太快了,永远都在赶仿佛赶着什么,国庆节的时候,脖子贴上了膏药,就是因为上班低着头盯着电脑 下了班自己在出租屋里盯着电脑,就是因为太快了,先说vue3

vue3

  • 首先你需要好好的看看 Composition API RFC 官网什么的就不说了
  • 其次你可以看下这个项目假使你非要用新东西写项目的话 vue-vben-admin
  • 这个项目应该有你想了解的 vue3-News

你要想写平台 网页 项目就是在电脑上看的

你要是写手机网页 (注意不是app)

vue2

再来说说现在市面上大多的项目2版本的vue ,如果你真的希望接收vue的话,不建议看视频,即使是自己也是up主,如果推荐一位老师的话 coderwhy

2019年最全最新Vue、Vuejs教程,从入门到精通

不过我不建议看视频,在11月 12月2020年的后半年我喜欢上了研究大佬的代码

四、社区成果

成果的话,从哪个说起呢,就从网抑云说吧,

网易云

虽然自己使用react写了一点网易云项目,停止更新了几十天,等2021年更新的时候你再看吧,现在没必要看

以上的文章不用看 几十天不更新了

其实我想说的不是这个,而是关于音乐,我看前几天大家在分享网抑云总结,我一点开,发现压根我没挺多久,压根就没听几首歌,不过现在和读书上学的时候听的不一样的,不再听那些嗷嗷叫 无病呻吟的 的歌曲 情啊爱啊 要么短视频曲子,乱的心头发麻,之前追过 说唱新世代 后来多数听说唱。安利一首吧 九局下半 热狗

微信

微信这块有个公众号,但是只有几天每天坚持早起发文章,可能还没体会到怎么玩吧,主要是自己现在还没很多重心放上去,还有就是我自己不怎么喜欢朋友圈,微信除了聊聊天,总结没什么成果没什么收获,初步估计在2021年11月左右会搞搞微信公众号,所以你之前看到我的文章有让你扫码关注的,直接别管,也不更新没必要,占你的内存,觉得我还可,可以添加微信,

知乎

在说说知乎吧,2020年一年没怎么写,不过推广一些自己的想法效果挺好的,哪个程序员不玩知乎呢,刚下飞机,2021年5月份左右应该就要重视起来了,毕竟社区里的明星话题在知乎的挺多的,比如什么

等等吧,以上不代表本人观点,但是我给知乎提过能不能支持md格式,以后写文章应该是掘金知乎了,一把锁,扩大自己个的社区范围吧

所以知乎2021,加油

B站

B站好好说一说,首先看看数据

image-20201231161512816

自己也是UP主了,自己能分享点东西还是比较自豪的,有几个系列比较深刻

  1. 跑跑步当时相机还没有因为经济原因卖掉
  2. 还有就是我在7月2号的时候就直播读书了,或者说是录播吧,我记得但是在群里说了要不要一起读书互相监督
  3. 再有就是关于react的分享好多人问我还还更新不,我应该会更新,不过应该是准备好后

关于B站的思考,明天等适应新工作后,应该会从宏观角度分享前端的或者技能这块的东西。希望突破1000 粉丝吧,然后就是说说B站社区吧,可能很多童鞋是在b学习,不过我个人不建议看视频学习,特别是你已经知道自己欠缺什么了或者说是知道学哪些东西了,即使我自己也是up主,为什么这么说吗因为up讲的好,不代表自己能看懂也能写出来,还有就是讲得不好那你完了 哈哈 各种被忽悠,那我就简单推荐几位

技术之外的就不说了 罗翔老师 何同学 在下哲别 逗川 木鱼水心 等等,他们是我每天晚上下了班的娱乐项目,陪我一天又一天 ,整个的2020年。

对于b站的思考,我认为帮助了很多喜欢编程的同学,2021年规划的话,考虑把项目写完整之后出一套,不会放弃,毕竟自己也是重度用户啊

github

看了自己github 感触 挺深的,因为包含了我的一次次提交 我的github

  • 在2020年我提交了 733 左右
  • 目前收获的标星 157

很重要的一点是我梳理了自己前端的路线

2021年的话,我应该会从h5的角度出发写一个项目,然后规范化工程化,比如

  • 编辑器配置

  • prettier.config.js 的配置

等等吧,这些都会借鉴当前社区里优秀的项目,接着就是会维护自己的一个函数库,这些都在进行中,到时间成熟了再说吧。关于github,我想你应该好好的经营,因为这是你的无形的财富都是慢慢积累的

掘金

掘金真的的是贯穿了我的2020年,包括我什么时间发的什么文章,甚至只在掘金社区,甚至可以想到我是在什么场景下写的文档,有时候在一早,有时在深夜,不过现在来看有点文章是错的甚至是不恰当或者不全面,自己也正在在年末对之前写的文章进行更新

你好..2019..让你慢下脚步已是不太可能之事了..这一年..我从刷抖音玩微博的那个笑个不听的少年到逛B站看掘金写文章的安静男孩..这一年..我还是用着不利索的苹果6..身边也多了些优秀努力的程序员朋友..我在某个记不太清的清晨地铁上看到征文..又在一个特定班次的公交上写着感慨..把一些自己的拙见分享给他人..却不再会把生活琐事记录..2019来了很多..有些人事物过着过着也有消失不见..而我并不像大多数人一样优秀..会裁剪视频..懂得电影..打的一手好游戏..有人减肥成功..有人成家立业..偶尔无聊的时候写写开源项目..我真的是一个很失败的人..很笨而又菜的程序员..但值得自己开心的事..在2019年我找回了那个曾经的自己..那个还相信努力就有收获的自己..在2019年进步了一些..会在技术群和屏幕那头的你聊的乱七八糟..懂得看英文文档..竟然觉得代码有意思了..文字有时比言语更生动..我可能并不在意听到了什么..当认认真真看一篇文章的时候..却越发的记忆深刻..

上述正是摘抄自2019年的年度总结

2020的你想对你说:

2020年依然过得很快,你的不利索的苹果手机已经换成了12 还有就是你还给妈妈买了新手机,在2020年 你还是 掘金 b站两个app

2020年你已经剪辑了几个视频 其实读书分享那期你还是熬夜到5点 不过你会看别人的解说 木鱼啊 发条张 某幻君的2077流程向视频也还不错

2020年 你已经在减肥了 还在掘金参加沸点 活动 打卡了 5天 说不动 还能中奖 哈哈

2020年你已经不怎么混flutter 群了 因为你更多的写的是 vue

2020年 你还是一个人 这个没办法 掘金人都还有相亲呢 你好好挣钱呗 急啥

还有就是你的博客已经很系统了

学习

  • React Hooks+巩固类语法
  • 跟着大佬走Vue 3x
  • Flutter方案
  • Node.js + 上层框架

关于你在2019年提的这几点,让2020年的我告诉你

2020年 你已经使用react写项目了

2020年 vue3 都快结束 其实还不很成熟

2020年 你基本不咋写flutter

2020年 你的node 没怎么实际的写 交给2021年的你把

社区

还有就是说说整个社区,自己认识的人比较少,还是局限于自己的微信群、qq群,其实应该更多的拥抱优秀的人,单说掘金就有各路神仙,想必大家都知道我就不说了,不过要说说我见识到的 飞木鱼 还有flutter 糖果 fluttercandies 我在想着 2021年能不能认识点优秀的人 带带自己个,总得来说多个朋友多条路

五、展望

2021年第一件事应该是跳跳跳 ,其次就是平衡工作与学习的时间力度分配,然后可能就是搬家了,整个一整年的话,平常心应该会放在算法,上半年的时间应该是在Vue 好好的把中文社区的源码文章好好的看一看

在第一条的知识分享 还有promise 以及浏览器 还有 webpack node等没有涉及这些都是 2021下半年的重心吧

六、感恩

参加掘金社区的活动,我记得是第一次参加活动就中了多少人梦寐以求的礼物,感恩节的沸点 但是这次我只想感恩自己,在这2020年我基本本每天都在盯着电脑,每周花费在手机上的时间越来越少,手机有个统计每周的花在手机上的时间,我手机上没有一个短视频的app,可能是长时间盯着电脑,坐姿什么的姿势不对,国庆休息的几天我在京东 买了膏药贴贴好很多。还有就是长时间不运动自己在2020年基本差不多胖了几斤,在差不多1月之前开始了健身房,每天跑跑步,然后晚上吃吃水果啥的,我把之前做UP的补光灯和相机卖了,由于种种原因,公司在上半年拖欠工资。等等吧,自己也才是刚工作1年多吧,没有很多积蓄,一步步走着,32G的苹果6手机我使用了3年多,由于实在卡的不行,最近换了12,体重也瘦了点,有下降,过春节前再接再厉。还是告诫下大家伙,身体保重 保重身体,颈椎太重了,我承认自己在大学花在学术上的时间不够多,才会导致现在那么被动,不过我没放弃,也没有没有目标,每一小步,我都计划着,比如这周自己补补哪方面,这一个月重点在那儿,在工作和自己的下班时间怎么规划,正是摸爬滚打,在9月 10月面试还算顺利,不过还是年后吧 都说年后机会多,在202年市区蛮多的,首先呢,女朋友没有了,自己也自己搬家到了离公司比较近的地方,所以每天有充足的时间看看书,或者写写代码做做视频技术分享,能说的不能说的都选择 了不说了,能跑出去玩的不跑出去玩的时间都在出租房里了

关于我的旧手机

我的”破手机“ 每次扫个健康包都让人等着 半天不出来,在2020年12月份的前几天我终于换了,之前还给妈妈买个个手机 虽然没我的好 哈哈 我的可是12

关于我的小桌子

掘金年度征文 | 2020 与我的技术之路 征文活动正在进行中......