前端面试之梳理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 前端开发 开发者
Vue.js的未来已来:掌握最新功能,驾驭前端开发的新浪潮!
【8月更文挑战第30天】Vue.js作为前端开发领域的明星框架,凭借其轻量级、响应式及组件化特性,深受全球开发者喜爱。它持续进化,引入新功能并优化性能,如提升渲染速度和减小打包体积,增强TypeScript支持,简化组件编写流程,进一步提升应用响应性和加载速度,改善开发者体验。活跃的社区和丰富的开源项目如“vuejs-challenges”推动其不断发展。未来,Vue.js将探索更多新特性,如宏和非虚拟DOM模式,巩固其在现代前端框架中的领先地位。
|
1天前
|
JavaScript 前端开发 测试技术
Vue.js开发者必看!Vue Test Utils携手端到端测试,打造无懈可击的应用体验,引领前端测试新风尚!
【8月更文挑战第30天】随着Vue.js的普及,构建可靠的Vue应用至关重要。测试不仅能确保应用质量,还能提升开发效率。Vue Test Utils作为官方测试库,方便进行单元测试,而结合端到端(E2E)测试,则能构建全面的测试体系,保障应用稳定性。本文将带你深入了解如何使用Vue Test Utils进行单元测试,通过具体示例展示如何测试组件行为;并通过Cypress进行E2E测试,确保整个应用流程的正确性。无论是单元测试还是E2E测试,都能显著提高Vue应用的质量,让你更加自信地交付高质量的应用。
|
1天前
|
JavaScript 前端开发 API
Vue.js 3.x新纪元:Composition API引领潮流,Options API何去何从?前端开发者必看的抉择指南!
【8月更文挑战第30天】Vue.js 3.x 引入了 Composition API,为开发者提供了更多灵活性和控制力。本文通过示例代码对比 Composition API 与传统 Options API 的差异,帮助理解两者在逻辑复用、代码组织、类型推断及性能优化方面的不同,并指导在不同场景下的选择。Composition API 改善了代码可读性和维护性,尤其在大型项目中优势明显,同时结合 TypeScript 提供更好的类型推断和代码提示,减少错误并提升开发效率。尽管如此,在选择 API 时仍需考虑项目复杂性、团队熟悉度等因素。
10 0
|
1天前
|
JavaScript 前端开发 API
解锁前端开发新境界:Vue.js携手Webpack,打造高效构建流程,你的项目值得拥有!
【8月更文挑战第30天】随着前端技术的发展,模块化与组件化趋势愈发显著。Vue.js 以其简洁的 API 和灵活的组件系统,深受开发者喜爱;Webpack 则凭借强大的模块打包能力成为前端工程化的基石。两者结合,不仅简化了组件编写与引用,还通过模块热替换、代码分割等功能大幅提升开发效率。本文将通过具体示例,展示如何利用 Vue.js 和 Webpack 构建高效、有序的前端开发环境。从安装配置到实际应用,逐步解析这一组合的优势所在。
|
1天前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
|
1天前
|
JavaScript 前端开发 开发者
决战前端之巅!Element UI与Vuetify谁才是Vue.js组件界的霸主?一场关于颜值与实力的较量!
【8月更文挑战第30天】本文对比了两款热门的Vue.js组件库——Element UI与Vuetify。Element UI由饿了么团队打造,提供多种高质量UI组件,设计简洁大方。Vuetify基于Material Design规范,支持Vue.js 2.0及3.0版本,具备前瞻性。两者均涵盖表单、导航、数据展示等组件,Element UI配置选项丰富,而Vuetify则提供了更深层的样式定制功能。开发者可根据项目需求及个人偏好选择合适的组件库。
10 0
|
1天前
|
JavaScript 前端开发 开发者
哇塞!Vue.js 与 Web Components 携手,掀起前端组件复用风暴,震撼你的开发世界!
【8月更文挑战第30天】这段内容介绍了Vue.js和Web Components在前端开发中的优势及二者结合的可能性。Vue.js提供高效简洁的组件化开发,单个组件包含模板、脚本和样式,方便构建复杂用户界面。Web Components作为新兴技术标准,利用自定义元素、Shadow DOM等技术创建封装性强的自定义HTML元素,实现跨框架复用。结合二者,不仅增强了Web Components的逻辑和交互功能,还实现了Vue.js组件在不同框架中的复用,提高了开发效率和可维护性。未来前端开发中,这种结合将大有可为。
|
19天前
|
存储 前端开发 JavaScript
前端语言串讲 | 青训营笔记
前端语言串讲 | 青训营笔记
17 0
|
3月前
|
JSON 前端开发 JavaScript
前端Ajax、Axios和Fetch的用法和区别笔记
前端Ajax、Axios和Fetch的用法和区别笔记
58 2
|
3月前
|
前端开发 JavaScript 数据库
如何实现前后端分离-----前端笔记
如何实现前后端分离-----前端笔记
下一篇
云函数