2019秋招知识盲点总结

开始

尽管秋招尚未拿到offer(好难过),可是一些知识点仍是要总结的,既然本身选了这条路,那就必定要坚决不移的走下去......javascript

注意 new 运算符的优先级

function Foo() {
    return this;
}
Foo.getName = function () {
    console.log('1');
};
Foo.prototype.getName = function () {
    console.log('2');
};

new Foo.getName();   // -> 1
new Foo().getName(); // -> 2

经过这段代码能够看出:new Foo() 的优先级高于 new Foo.css

对于代码1 来讲:是将 Foo.getName 当成一个构造函数来执行,执行构造函数因此输出为1.html

对于代码2来讲:经过 new Foo() 建立了一个Foo的实例,经过实例访问其原型链上的 方法因此输出为2.前端

注意非匿名的当即执行函数

var foo = 1;
 
// 有名当即执行函数
(function foo() {
    foo = 10;
    console.log(foo);
})();

// 执行这段代码会输出什么呢?

// -> ƒ foo() { foo = 10 ; console.log(foo) }

// 再去访问 foo 的值
foo
// -> 1

当JS执行器遇到非匿名的当即执行函数时,会建立一个辅助的特定对象,而后将函数名称做为这个对象的属性,所以行数内部才能够访问到 foo ,但这个值是只读的,因此对其从新赋值不会生效,因此打印结果仍是这个函数,而且外部的值也没有发生改变。java

关于对象的深拷贝

  • 可使用 JSON.stringifyJSON.parse 这个两个方法node

    优势:简单web

    缺点:会忽略掉 undefined ; 不能序列化函数 ; 不能解决循环引用的对象面试

    function clone(obj) {
        return JSON.parse(JSON.stringify(obj));
    }
  • 使用递归循环赋值的方式ajax

    优势:能够处理 undefined、函数等各类状况算法

    缺点:实现相对麻烦,效率不高

    function clone(obj) {
        if(!obj || typeof obj !== 'object') {
            return;
        }
        var _obj = obj.constructor === Object ? {} : [];
        for(let key in obj) {
            if(typeof obj[key] === 'object') {
                _obj[key] = clone(obj[key]);
            } else {
                _obj[key]  = obj[key];
            }
        }
        return _obj;
    }
    
    // 或者
    
    function clone(obj) {
        if(!obj || typeof obj !== 'object')
            throw new TypeError('params typeError');
        let _obj = obj.constructor === Object ? {} : [];
        Object.getOwnPropertyNames(obj).forEach(name => {
            if(typeof obj[name] === 'object') {
                _obj[name] = clone(obj[name]);
            } else {
                _obj[name] = obj[name];
            }
        });
        return _obj;
    }
  • 使用内置 MessageChannel 对象

    优势:是内置函数中处理深拷贝性能最快的

    缺点:不能处理函数(会报错)

    function clone(obj) {
        return new Promise(resolve => {
            let {port1, port2} = new MessageChannel();
            port2.onmessage = ev => resolve(ev.data);
            port1.postMessage(obj);
        });    
    }
    
    clone(obj).then(console.log);

关于async/await , promise 异步执行顺序

想解决这个问题,就必须知道 `await` 作了什么?
刚开始觉得 await 会一直等待表达执行的执行结果,以后才会执行后面的代码。 实际上 await 是一个让出线程的标志(遇到 await 会当即返回一个 pending 状态的promise)。await后面的函数会先执行一遍,而后就会跳出整个 async 函数来执行后面js代码。等本轮事件循环执行完又跳回到 async 函数中等待await后面表达式的返回值,若是返回值为非 promise 则继续执行async后面的代码,不然将 promse 加入队列。

且看一道面试题(分析代码执行 顺序):

async function async1() {
    console.log("async1 start");
    await async2();
    console.log("async1 end");
}

async function async2() {
   console.log("async2");
}

console.log("script start");

setTimeout(function () {
    console.log("settimeout");
},0);

async1();

new Promise(function (resolve) {
    console.log("promise1");
    resolve();
}).then(function () {
    console.log("promise2");
});

console.log("script end");

OK,那接下来具体分析执行过程:

首先输出 "script start" ,而后当即将定时器加入异步事件队列。执行 async1() ,输出 "async1 start" ,进入 async2() ,输出 "async2" ,跳出整个 async1() 函数来执行后面js代码,执行promise执行器中的内容,输出 "promise1" ,执行resolve()回调,将then函数中的内容加入异步事件队列,接着输出 "script end" 。回到 async1() 的await等待 async2() 函数的返回值,由于返回值是一个promise实例,将promise加入异步事件队列。此时的同步代码执行完毕,轮询并从队列拿出代码放入主线程执行,因此输出 "promise2" ,继续执行 async1() 中的后续内容,输出 "async1 end" ,最后取出定时器中的内容并执行,输出 "settimeout"

综上所述:

script start
async1 start
async2
promise1
script end
promise2
async1 end
settimeout

那么再看一个例子应该会简单不少:

function testFunc() {
    console.log("testFunc..."); // 2
    return "testFunc";
}

async function testAsync() {
    console.log("testAsync...");  // 7
    return Promise.resolve("hello testAsync");
}

async function foo() {
    console.log("test start..."); // 1
    const v1 = await testFunc();
    connsole.log('hello world.'); // 5
    console.log(v1);              // 6 testFunc
    const v2 = await testAsync();
    console.log(v2);              // 9 hello testAsync
}

foo();

var promise = new Promise(resolve => { 
    console.log("promise start..");  // 3
    resolve("promise"); 
});
promise.then(val => console.log(val)); // 8 promise

console.log("test end..."); // 4

防抖和节流

  • 防抖:若是用户屡次调用且间隔小于wait值,那么就会被转化为一次调用。
  • 节流:屡次执行函数转化为,每隔必定时间(wait)调用函数 。

一个简单的防抖函数:

function debounce(func, wait) {
    let timer = null;
    return function(...params) {
        // 若是定时器存在则清除
        if(timer){
            clearTimeout(timer);
        }
        // 从新开始定时执行
        timer = setTimeout(() => {
            func.apply(this, params);
        }, wait);
    }
}

缺点:只能在最后执行,不能当即被执行,在某些状况下不适用。

改进...

function debounce(func, wait, immediate) {
    
    let timer, context, args;
    
    // 定时器
    let later = function() {
        return setTimeout(() => {
            timer = null;
            if(!immediate) {
                func.apply(context, args);
            }
        }, wait);
    }
    
    return function(...params) {
        if(!timer) {
            timer = later();
            
            // immediate 为 true,则当即执行
            // 不然 缓存上下文 和 参数
            if(immediate) {
                func.apply(this, params);
            } else {
                context = this;
                args = params;
            }
        } else {
            clearTimeout(timer);
            timer = later();
        }           
    }
}

一个简单的节流函数:

// 节流函数
// 快速的屡次执行,转化为等待wait时间再去执行
function throttle(func, wait) {
    var timer = null;
    var context = null;
    return function(...args) {
        context = this;
        if(!timer) {
            timer = setTimeout(function() {
                timer = null;
                func.apply(context, args);
            }, wait);
        }
    }
}

// 若是想让第一次调用当即执行也很是简单
仅须要将  func.apply(context, args) 提到定时器外边便可。

节流函数除了可使用定时器实现之外,固然也能够有其余方式:

// 第一次调用会被当即执行
function throttle(func, wait) {
    var prev = 0;
    var context = null;
    return function(...args) {
        var now = +new Date();
        context = this;
        if(now -prev > wait) {
            func.apply(context,args);
            prev = now;
        }    
    }
}

call、apply和bind

怎么去模拟一个call函数呢?

思路:call一个很是重要的做用就是改变上下文环境也就是this,咱们能够给用户传入的上下文对象上添加一个函数,经过这个上下文对象去执行函数,而后将这个函数删除,返回结果就能够了。

Function.prototype.myCall = function(context, ...args) {
    context = context || window;
    // 给上下文对象上添加这个函数
    context.fn = this;
    // 经过这个上下文对象去执行函数
    let result = context.fn(...args);
    // 将这个函数删除
    delete  context.fn;
    return result;
}

call既然都实现了,那么apply也是相似的,只不过传入的参数是一个数组而已。

Function.prototype.myApply = function(context, arr) {
    context = context || window;
    arr = arr || [];
    let type = {}.toString.call(arr).slice(8,-1);
    if(type !== 'Array')
        throw new TypeError('CreateListFromArrayLike called on non-object');
    context.fn = this;
    let result = context.fn(...arr);
    delete context.fn;
    return result;
}

模拟bind函数,bind函数应该返回一个新的函数。

Function.prototype.myBind  = function(context, ...args) {        
    // 保存当前的函数
    let func = this;
    return function F(...params) {
        if(this instanceof F) {
            return new func(...args, ...params);
        }
        return func.apply(context,[...args,...params]);
    }    
}

数组降维

function flattenDeep(arr) {
    if(!Array.isArray(arr))
        return [arr];
    return arr.reduce((prev,cur) => {        
        return [...prev, ...flattenDeep(cur)];
    },[]);
}

flattenDeep([1, [[2], [3, [4]], 5]]);

栈的压入和弹出

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的全部数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不多是该压栈序列的弹出序列。

function IsPopOrder(pushV,popV){
    if(pushV.length === 0) return false;
    var stack = []; // 模拟栈
    for(var i = 0, j = 0; i < pushV.length;){
        stack.push(pushV[i]);
        i += 1;
        // 压入栈的值须要被弹出
        while(j < popV.length && stack[stack.length-1] === popV[j]){
            stack.pop();
            j++;
            if(stack.length === 0) break;
        }
    }
    return stack.length === 0;
}

利用栈模拟队列

思路:

  • 对栈A添加数据。
  • 若是栈B为空,循环将栈A中内容弹出放入栈B,并弹出栈B最后一项
  • 若是栈B不为空,则直接弹出栈B的最后一项
var stackA = [];
var stackB = [];

function push(node){
    stackA.push(node);
}
function pop(){
    if(!stackB.length){
        while(stackA.length){
            stackB.push(stackA.pop());
        }
    }
    return stackB.pop();
}

Fetch和ajax之间的区别

fetch

  • Fetch API是基于Promise设计的
  • 容易同构(先后端运行同一套代码)
  • 语法简洁,更加语义化
  • 原生支持率不高,能够用polyfill兼容IE8+浏览器
fetch(url).then(function(response){
    return response.json();
}).then(function(data){
    console.log(data);
}).catch(function(err){
    console.log(err);
});

ajax

  • 设计粗糙,不关注分离原则
  • 基于事件的异步模型,不够友好
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function(){
    console.log(xhr.response);
}
xhr.onerror = function(){
    console.log('error');
}

xhr.send();

Fetch常见坑

  • fetch请求默认是不带cookie的,须要设置 fetch(url, {credentials: 'include'})
  • 服务器返回400,500错误码时不会reject,只有网络错误致使不能完成时,才会reject。
  • IE8, 9 的 XHR 不支持 CORS 跨域。

归并排序

将一个完整的数组分红两部分,分别对其排序,而后将两部分merge在一块儿便可。

function merge(left, right) {
    var temp = [];
    while(left.length && right.length) {        
        if(left[0] < right[0])
            temp.push(left.shift());
        else
            temp.push(right.shift());        
    }
    return temp.concat(left,right);
}

function mergeSort(arr) {
    if(arr.length === 1)
        return arr;
    var mid = (arr.length/2)|0;
    var left = arr.slice(0,mid);
    var right = arr.slice(mid);    
    return merge(mergeSort(left), mergeSort(right));
}

箭头函数的this原理

this 指向固定化,并非由于箭头函数内部有绑定 this 的机制,实际缘由是箭头函数没有本身的 this ,致使内部的this就是外层代码的 this ,也正是由于没有 this ,因此箭头函数不能用做构造函数。

js相关尺寸

iCtsdx.png

BFC原理

  • 在BFC垂直方向元素边距会发生重叠
  • 不会与浮动元素的box重合
  • 独立的容器,里外互不影响
  • 浮动元素参与计算

    自定义事件

var content = document.querySelector('.content');
    // 自定义事件
    var evt = new Event('custom');
    var customEvt = new CustomEvent('customEvt', {
        // 经过这个属性传递参数
        detail: {
            name: 'tom',
            age: 12
        }
    });
    content.addEventListener('custom', (e) => {
        console.log('自定义事件被触发,无参数...');
        console.log(e);
    });
    content.addEventListener('customEvt', (e) => {
        console.log('自定义事件被触发,有参数...');
        console.log(e);
        console.log(e.detail);
    });
    // 点击时触发这个自定义事件
    content.addEventListener('click', (e) => {
        content.dispatchEvent(evt);
        content.dispatchEvent(customEvt);
    });

变量提高

var foo = 3; // 不在同一个做用域

function hoistVariable() {
    // 内部变量提高致使 foo 的初始值为undefined
    // 因此 foo = 5;
    var foo = foo || 5;
    console.log(foo); // 5
}

hoistVariable();

上边的比较简单,看一个函数和变量同名,关于变量提高的小问题。

var a = 6;
function b(){
    console.log(a); // @1
    var a = 4;
    function a(){
        alert(4);
    }
    console.log(a); //@2
}
b();
  • 由于JavaScript中的函数是一等公民,函数声明的优先级 最高(高于变量提高),会被提高至当前做用域最顶端,因此在 @1 输出的是 function a(){alert(4);}
  • 接下来执行 a=4; 这一句,从新对 a 进行赋值。
  • 函数已被提高,因此不考虑,因此在 @2 这里天然会输出 4

若是还不能理解?且看预编译后的代码:

var a;
a = 6;
function b(){
    var a; 
    a = function a(){ // 函数先提高
        alert(4);
    }
    console.log(a); // @1
    a = 4;
    console.log(a); // @2    
}
b(); // 结果已经很是明了了

POST和GET的区别

  • POST对请求参数的长度没有限制,而GET若是请求参数过长,会被浏览器截断。
  • GET请求参数会直接暴露在URL上,因此不适合用来传递敏感信息。
  • GET请求能够被浏览器主动缓存,而POST请求不能够,除非手动设置。
  • GET请求在浏览器回退时是无害的,而POST会再次提交请求。
  • GET请求产生的URL能够被浏览器缓存,而POST不能够。

通讯类

1. 先后端如何通讯?

  • ajax
  • WebSocket
  • CORS

2. 如何建立ajax?

// 建立xhr对象
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');

// GET 请求
xhr.open('GET',url,true);
xhr.send();

// POST 请求
xhr.open('POST',url,true);
// 表单数据 , 也能够提交json数据,相应的content-Type: application/json
xhr.setRequestHeader('content-Type', 'application/x-www-from-urlencoded');
xhr.send(dataArr.join('&'));

xhr.onload = function() {
    if(xhr.status === 200 || xhr.status === 304) {    
        var data = xhr.responseText;
        // 拿到数据
    } else {
        // 出问题了
    }
}

3. 跨域通讯的几种方式?

  • JSONP:利用 script 标签的跨域能力,服务返回一个js函数调用,数据做为函数的一个参数来传递。

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url; // 跨域地址
    document.head.appendChild(script);
    
    //有能耐把我这辈子都安排了,否则少他妈扯淡。
    setTimeout(function() {
        document.head.removeChild(script);
        script = null;
    });
    
    // 接收数据
    function jsonpCallback(data) {
        console.log(data);
    }
  • WebSocket:不受同源政策限制。

    var ws = new WebSocket('wss://echo.websocket.org');
    ws.onopen = function(e) {
        ws.send('hello...');
    }
    ws.onmessage = function(e) {
        var data = e.data;
    }
    ws.onclose = function() {
        console.log('close...');
    }
  • Hash:利用 location.hash 来传值。 缺点:数据直接暴露在url中,大小、类型都有限制。

    一、父窗体能够把信息写在子窗体的href的hash上,子窗口经过监听hashchange事件获取信息。

    二、子窗体改变父窗体的hash值,那么就要借助第三个子窗体,第三个子窗体是第二个子窗体的子窗体。(第三个子窗体要与父窗体同源)

    三、第二个子窗体把信息设置在第三个子窗体的hash值上,而后第三个子窗体改变父窗体的hash值,从而实现跨域。

    iPQsr6.png

    // 父窗体
    var son = document.getElementByTagName('iframe');
    son.src = son.src + '#' + data;
    
    // 子窗体
    window.onhashchange = function() {
        var data = window.location.hash;
    }
  • postMessage :语法:window.postMessage(msg,targetOrigin)

    // 窗口A 发送
    BWindow.postMessage('发送的数据', 'http://B.com');
    
    // 窗口B 接收
    window.addEventListener('message', (event) => {
       event.origin: // http://A.com
       event.source; // AWindow
       event.data;   // '发送的数据' 
    });
  • CORS: 跨域资源共享。

    fetch(url, {
        method: 'get',
        // 头信息配置
    }).then(() => {});

安全类

  • CSRF,跨站请求伪造(Cross-site request forgery)

    iP1PXt.png

    防护措施:

    Token验证(请求必须携带Token)

    Referer 验证(验证请求的来源是否可信)

  • XSS(cross-site scripting 跨站脚本攻击)

    原理:注入脚本

    防护措施:对用户的输入作验证

渲染机制类

1. 什么是DOCTYPE及做用?

用来声明文档类型和DTD规范的。

DTD(document type definition)文档类型定义,是一系列的语法规则,用来声明XML或(X)HTML的文件类型,浏览器会使用它来决定文档类型,决定使用何种协议来解析,以及切换浏览器模式。

2. 常见的doctype有哪些?

  • HTML5 <!DOCTYPE html>
  • HTML4.01 Strict 严格模式 (不包含展现性或弃用的元素)
  • HTML4.01 Transitional 传统(宽松)模式(包含展现性或弃用的元素)

页面性能类

提高页面性能的方式?

  • 资源压缩合并,减小HTTP请求
  • 非核心代码异步加载

    异步加载方式:

    1. 动态脚本加载
    2. defer (HTML解析完顺序执行)
    3. async (加载完当即执行)
  • 利用浏览器缓存
  • 使用CDN
  • 预解析DNS

    <meta http-equiv="x-dnns-prefetch-control" content="on">
    <link rel="dns-prefetch" href="//host_name_to_prefetch.com">

错误监控

  • 前端错误分类:代码(运行)错误 资源加载错误
  • 错误捕获方式

    运行错误捕获:(1)try...catch (2)window.onerror

    资源加载错误 :(1)object.onerror(资源错误不冒泡) (2)performance.getEntries() (3)Error事件捕获(在事件流捕获阶段处理错误)

  • 跨域js运行错误也是能够捕获的,捕获的错误:script error
  • 上报错误原理

    利用Image对象上报

    // 利用Image标签上报错(简单、不须要借助其余库)
    (new Image()).src = 'http://www.baidu.com/test?error=xxx';

二分法查找

function binarySearch(arr,val,leftIndex,rightIndex) {
    if(leftIndex > rightIndex){ return; }
    var midIndex = (leftIndex + rightIndex) / 2 | 0;
    var midVal = arr[midIndex];
    if(val > midVal) {
        return binarySearch(arr,val,midIndex+1,rightIndex);
    }else if(val < midVal) {
        return binarySearch(arr,val,leftIndex,midIndex-1);
    }else{
        return midIndex;
    }
}

连续最长不重复字符串

在一个字符串中找出连续的不重复的最大长度的字符串,解决这类问题的思路:

  • 利用循环叠加字符串,直到出现重复为止
  • 每一次叠加,记录下来最大长度的字符串
// 连续最长不重复字符串
function getMaxLenStr(str) {
    var cur = [];
    var maxLenStr = '';
    for(var i = 0; i < str.length; i++) {
        if(!cur.includes(str[i])) {
            cur.push(str[i]);
        } else {
            cur = []; // 置为空
            cur.push(str[i]);
        }
        
        // 存储最大长度的字符串
        if(maxLenStr.length < cur.length) {
            maxLenStr = cur.join('');
        }        
    }
    return maxLenStr;
}

getMaxLenStr('ababcabcde'); // abcde

和上面这道题有同样思路的是:求一个数组当中,连续子向量的最大和。

无非是将 对比字符串的长度 改成 对比值大小

function FindGreatestSumOfSubArray(arr) {
    let sum = arr[0];
    let max = arr[0];
    for(let i = 1; i < arr.length; i++) {
        if(sum < 0) {
            sum = arr[i];
        }else{
            sum += arr[i];
        }
        // 记录最大值
        if(max < sum) {
            max = sum;
        }
    }
    return max;
}

面试题:解码字符串并输出

阿里的一道面试题:给定一个编码字符,按编码规则进行解码,输出字符串

编码规则:coount[letter] ,将letter的内容count次输出,count是0或正整数,letter是区分大小写的纯字母。

实例:

  • const s= 3[a]2[bc]; decodeString(s); // 返回 ‘aaabcbc’
  • const s= 3[a2[c]]; decodeString(s); // 返回 ‘accaccacc’
  • const s= 2[ab]3[cd]ef; decodeString(s); // 返回 ‘ababcdcdcdef’

解题过程...

  • 思路:

    使用栈这种数据结构,若是push的内容为‘]’,则循环pop字符,直到碰到’[‘,而后将pop

出来的字符串按规则整理后,从新push进栈中,最后将栈内的内容拼接成字符串输出便可。

  • 代码:

    const s = '2[a2[c]]ef';
    function decodeString(str) {
        let stack = []; // 存储字符串的栈
        for (let i = 0; i < str.length; i++) {
            let cur = str[i];
            if (cur !== ']') {
                stack.push(cur);
            } else { // 弹出
                let count = 0;
                let loopStr = [];
                let popStr = '';
                while ((popStr = stack.pop()) !== '[') {
                    loopStr.unshift(popStr);
                }
                count = stack.pop();
                // 添加结果
                let item = '';
                for (let i = 0; i < count; i++) {
                    item += loopStr.join('');
                }
                stack.push(...(item.split('')));
            }
        }
        return stack.join('');
    }
    console.log(decodeString(s)); // accaccef

排序算法时间复杂度

  • 元素的移动次数与关键字的初始排列次序无关的是:基数排列。
  • 元素的比较次数与初始序列无关是:选择排序。
  • 算法的时间复杂度与初始序列无关的是:选择排序。

BOM和DOM

BOM 即浏览器对象模型,BOM没有相关标准,BOM的核心对象是window对象。

DOM即文档对象模型,DOM是W3C标准,DOM的最根本对象是document(window.document), 这个对象其实是window对象的属性,这个对象的独特之处是惟一一个既属于BOM有属于DOM的对象。

http和WebSocket的区别:

  • 相同点:

都是创建于tcp链接之上,经过tcp协议来传输数据。

  • 不一样点:

HTTP是一种单向的协议,即客户端只能向服务器端请求信息。request永远等于response,而且这个response是被动的,不能主动发起。一旦有一个任务超时,就会阻塞后续的任务(线头阻塞)。

HTTP协议是无状态的,如使用轮询、long poll都须要将身份鉴别信息上传。

WebSocket真正的全双工通讯,只须要一次链接,这样就避免了HTTP的无状态性,服务器端能够主动推送消息到达客户端。

http2.0

  • 二进制分帧
  • 多路复用,这样就避免了线头阻塞
  • 服务器端推送,客户端请求HTML,服务器能够主动推送js、css那些客户端可能会用到的东西,这样就避免了重复发送请求
  • 头压缩

O(n)复杂度去重

function unique(arr) {
    let obj = {};
    let ret = [];
    for(let i = 0; i < arr.length; i++) {
        let cur = `${typeof arr[i]}-${arr[i]}`;
        if(!obj[cur]) {
            ret.push(arr[i]);
            obj[cur] = true;
        }
    }        
    return ret;
}

var arr = [1, '1', 3, 3, 4];
unique(arr);
// [1, '1', 3, 4]

判断是否相同

function isSame(a, b) {
    if(a === b) return true;
    if(typeof a !== typeof b) return false;
    
    // 若是均为object类型,判断属性个数是否相同
    if(typeof a === 'object' && typeof b === 'object') {
        let aLen = Object.getOwnPropertyNames(a).length;
        let bLen = Object.getOwnPropertyNames(b).length;
        if(aLen !== bLen) return false;
    }
    return Object.getOwnPropertyNames(a).every(key => {
        if(typeof a[key] === 'object') {
            return isSame(a[key], b[key]);
        }
        return a[key] === b[key];
    });
}


后记一波:

付出了总归是有回报的。

  • 电话面试拿到了百度云事业部实习生的offer。
  • 南京现场面试得到了上海微盟的offer。
  • 参加北京的面试获得了迅雷的offer。签约那天恰好是中秋节(2018/9/24),多是一份最棒的中秋大礼了吧😁。决定去迅雷了,因此,秋招之路到此结束......

但,学习是永无止境的。(先从作好总结开始)

相关文章
相关标签/搜索