前端面试之梳理JS

简介: 前端面试之梳理JS

       无论我们是主动还是被动,面试总是我们不得不学会的一项技能;其中的基础又是重中之重,决定了我们是否能继续面下去。


本文主讲一些常用JS考点:


0)同步任务、异步任务、宏任务和微任务之间的关系

   同步任务就是js单线程执行语言

   异步任务包含宏任务和微任务


1)宏任务 微任务https://www.cnblogs.com/ckAng/p/11133643.html

       a. 宏任务(由宿主(JS允许的环境 一般为浏览器或者node)发起的)

           微任务(由JS自身发起)

       b. 宏任务一般是整体代码script setTimeout setInterval I/O UIrender ajax

           微任务主要是Promise.then Object.observe MutationObserver catch finally

           执行顺序优先级 同步 > 微任务 > 宏任务


2)eventLoop(事件循环)      

       a. 以JavaScript语言为例 它是一种单线程语言 所有任务都在一个线程上完成 一旦遇到大量任务或者遇到一个耗时的任务 网页就会出现假死 因为js停不下来 也就无法影响用户的行为        

       b. Event Loop应运而生 是指浏览器的一种解决JavaScript单线程运行时不会阻塞的一种机制 也就是我们经常使用异步的原理        

       c. 同步和异步任务分别进入不同的场所执行 所有同步任务都在主线程上执行 形成一个执行栈 而异步任务进入Event Table并注册回调函数 当这个异步任务有了执行结果 Event Table会将这个回调函数移入Event Queue 进入等待状态 当主线程内同步任务执行完成 会去Event Queue读取对应的函数 并结束它的等待状态 进入主线程执行(主线程不断重复上面3个步骤 也就是常说的Event Loop事件循环)        

       d. 执行栈在执行完同步任务后 查看执行栈是否为空 如果执行栈为空 就会去执行Task(宏任务) 每次宏任务执行完后 会检查微任务队列是否为空 如果不为空的话 会按照先入先出的规则全部执行完微任务后 设置微任务队列为null 然后再执行宏任务 如此循环        

       e. 整体script属于宏任务的概念


3)手写instanceof实现(用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上)

   a. 实现思路 首先instanceof左侧必须是对象 才能找到它的原型链 instanceof右侧必须是函数 函数才有prototype属性 迭代处理 左侧对象的原型不等于右侧的prototype时 沿着原型链重新赋值左侧

   b. 代码如下:

function instance_of(L, R) {
    const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol'];
    if(baseType.includes(typeof(L))) return false; // 验证如果为基本数据类型 就直接返回false
    let RP = R.prototype; // 取R的显式原型
    L = L.__protp__; // 取L的隐式原型
    while(true) { // 循环判断prototype是否出现在实例对象的原型链上
        if(L === null) return false;
        if(L === RP) return true;
        L = L.__protp__;  // 向上一层原型链查找
    }
}

4)new的过程

   a. 创建一个空对象 将它的引用赋给this 继承函数的原型

   b. 通过this 将属性和方法添加至这个对象

   c. 最后返回this指向的新对象 也就是实例 (如果没有手动返回其他的对象)


5)this指向

   a. 第一准则是 this永远指向函数运行时所在的对象 而不是函数被创建是所在的对象

   b. 普通的函数调用 函数被谁调用 this就是谁 构造函数的话 如果不用new操作符而直接调用 那即this指向window 用new操作符生成对象实例后 this就指向了新生成的对象

   c. 匿名函数或 不处于任何对象中的函数指向window

   d. 如果是call apply等(改变函数作用域) 指定的this是谁就是谁 每个函数都包含两个非继承而来的方法call apply  在js中 两者作用是一样的 都是为了改变某个函数运行时的上下文(context)而存在的 换句话说 就是为了改变函数体内部this的指向



 

6)call apply bind (获取另一个对象的指向 就能使用它的方法与变量)

   代码如下:

// 写法
obj.myFun.call(db,'成都','上海');
obj.myFun.apply(db,['成都','上海']);
obj.myFun.bind(db,'成都','上海')();
obj.myFun.bind(db,['成都','上海'])();
// 手写call es5
Function.prototype.call = function(content) {
    content = content || window; // 给执行上下文 添加默认值
    content.fn = this; // 给content添加一个方法 指向this
    var args = []; // 保存参数集合
    for(let i = 1; i < arguments.length; i++) {
        args.push(arguments[i]);
    }
    var res = args.length > 1 ? eval('content.fn(' + args + ')') : content.fn(); // 执行fn
    delete content.fn; // 删除fn方法
    return res;
}
// 手写apply es6
Function.prototype._Apply = function(content, args) {
    content = content || window;
    args = [];
    if(!(args instanceof Object)) { // 如果第二个参数不是对象的实例 就返回一个错误
        throw new TypeError("Create List From Array Like called on non-object");
    }
    content.fn = this; // 显示绑定函数的this
    const res = arguments[1] ? content.fn(...arguments[1]) : content.fn()
    delete content.fn; // 删除fn方法
    return res;
}
// 手写 bind es6
Function.prototype._bind = function(content = window) {
    const slice = Array.prototype.slice;
    const thatFunc = this; // 保存调用函数的this 也就是原函数
    const args = [].slice.call(arguments, 1); // 获取第一个参数之后剩余的参数数组
    return function() {
        const funcArgs = args.concat(slice.call(arguments)); // 合并返回的新函数的参数
        return thatFunc.call(content, ...funcArgs); // 返回新函数调用的结果
    }
}

   其实就是通过一个中间函数fn 将接受的参数arguments封装返回 就实现了当前作用域使用其他函数的内容

 

7)for in 和for of

   a. for in {

       (即可遍历对象 也可遍历数组)

       遍历数组的问题(1.index索引为字符串型数字 不能直接进行几何运算 2.遍历顺序有可能不是按照实际数组的内部顺序 3.使用for in会遍历数组所有的枚举属性 包括原型 例如上溯的原型方法和method和name属性 所以for in更适合遍历对象 不要使用for in遍历数组)

   }

   b. for of 遍历数组 不可遍历对象(取索引用[index, val])


8)变量比较时 对象的隐式转换(valueOf 或者 toString转换为原始类型的值比较)

   a. 如何声明a 令 a==1&&a==2&&a==3 返回true

   b. a为对象 对象和原始数据类型比较会找对象的 valueOf方法(返回对象的原始值)如果没有则找toString()方法

   

 

9)判断汉字和字母

   a. ASCII码比较 './images/ASCII码比较.jpg'

   b. 面试题(js判断输入内容长度(不限中英文和数字))(https://www.jb51.net/article/48202.htm)



 

10)数据类型

   a. 基础数据类型(string number boolean undefined null symbol bigint)

       symbol(独一无二的值 作为属性名命名 可以保证不重名)

      https://www.runoob.com/w3cnote/es6-symbol.html

      https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt

   b. 引用数据类型(function object array)



 

11)箭头函数

   a. 箭头函数不能用作构造器 和new一起使用会抛出错误(箭头函数没有自己的this 而是继承父作用域中的this)

   b. 箭头函数没有prototype属性(因为不能通过new关键字调用)

   c. 不绑定this 在箭头函数出现之前 每个新定义的函数都有他自己的this值

   d. 用剩余参数语法表示 而不是arguments

   e. 防止变量提升

   f. 由于箭头函数没有自己的this指针 通过call或apply方法调用一个函数时 只能传递参数(不能绑定this)


12)gengerator函数(生成器)

   a. 在JavaScript中 一个函数一旦开始执行 就会运行到最后或遇到return时结束 运行期间不会有其他代码能够打断它 也不能从外部再传入值到函数体内

   b. 而gengerator函数的出现使得打破函数的完整运行成为了可能 其语法行为与传统函数完全不同

   c. https://www.cnblogs.com/rogerwu/p/10764046.html

       function关键字与函数名之间有一个*号(紧挨function关键字)

       函数体内使用yeild表达式 定义不同的内部状态(可以有多个yeild)

       直接调用Gengerator函数并不会执行 也不会返回运行结果 而是返回一个遍历器对象(Iterator Object)

       依次调用遍历器的next方法 遍历Gengerator函数内部的每一个状态



 

13)手写promise.all


       代码如下:

// 手写promise.all
function promiseAll(args) {
    return new Promise((resolve, reject) => {
        const promiseResults = [];
        let iteratorIndex = 0;
        let fullCount = 0;
        for(const item of args) {
            let resultIndex = iteratorIndex;
            iteratorIndex += 1;
            Promise.resolve(item).then(res => {
                promiseResults[resultIndex] = res;
                fullCount += 1;
                if(fullCount === iteratorIndex) {
                    resolve(promiseResults);
                }
            }).catch(err => {
                reject(err);
            });
        }
        if(iteratorIndex === 0) {
            resolve(promiseResults);
        }
    });
}
if(!Promise.all) {
    Promise.all = promiseAll;
}

14)shim和polyfill有什么区别

   a. 一个shim是一个库 它将一个新的api引入到一个旧的环境中 而且仅靠旧环境中已有的手段实现

   b. 一个polyfill就是一个用在浏览器API上的shim 我们通常的做法是先检查当前浏览器是否支持某个API 如果不支持的话 就加载对应的polyfill 然后新旧浏览器就都可以使用这个API了

   c. 示例:

if(!Number.isNaN) {
   Number.isNaN = function(num) {
   return(num !== num); }
 }

15)设计模式

   a. 观察者模式和发布订阅模式的区别(https://www.jianshu.com/p/594f018b68e7

   b. 单例模式(保证一个类仅有一个实例 并提供一个全局的访问点) {

       (无非是用一个变量来标志当前是否已经为某个类创建对象 如果是 则在下一次获取该类的实例时 直接返回之前创建的对象)

       (只需要生成一个唯一对象的时候 比如说页面登录框 只可能有一个登录框 那么你就可以用单例的思路去实现他 当然不用单例的思想实现也行 那带来的结果可就是你每次要显示登录框的时候都要重新生成一个登录框并显示(耗费性能) 或者是不小心显示了两个登录框)

       (https://www.cnblogs.com/yonglin/p/8080836.html)

   }

   c. 策略模式 (应用 促销方案 表单验证) {

       (策略模式就是将一系列算法封装起来 并使他们相互之间可以转换 被封装起来的算法具有独立性 外部不可改变其特性)

       (改变多个if-else的问题)

       (https://zhuanlan.zhihu.com/p/146500964)

   }

   d. 工厂模式 {

       (工厂模式是我们最常用的实例化对象模式了 使用工厂方法代替new操作的一种模式)

       (因为工厂模式就相当于创建实例对象的new 我们经常要根据类Class生成实例对象 如A a = new A() 工厂模式也是用来创建实例对象的 所以以后new时就要多个心眼 是否可以考虑使用工厂模式 虽然这样做 可能多做一些工作 但会给你系统带来更大的可扩展性和尽量少的修改量)

   }

   e. 纯函数 {

       (如果函数的调用参数相同 则永远返回相同的结果 它不依赖于执行期间 函数外部任何状态或数据的变化 必须只依赖于其输入参数)

       (该函数不会产生任何可观察的副作用 例如网路请求 输入和输出设备或数据突变(mutation))

       (一个函数的返回结果只依赖于它的参数 并且在执行过程里面没有副作用 我们就把这个函数叫做纯函数)

       (const a = 1; const foo = (x, b) => x + b; foo(1, 2) // =3)

   }



 

16)继承(原型链继承 构造函数继承 组合继承 原型式继承 寄生式继承 寄生组合式继承)

   (https://www.cnblogs.com/ranyonsue/p/11201730.html

 

17)进程和线程 (火车与车厢)


相关文章
|
1月前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
27天前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
1月前
|
JSON JavaScript 前端开发
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
37 2
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
|
29天前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
42 5
|
27天前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
68 1
|
1月前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
36 4
|
1月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
114 1
|
1月前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
41 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
2月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
183 2
|
2月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
49 0
下一篇
DataWorks