前端面试之梳理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 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
23 3
|
9天前
|
前端开发 JavaScript
前端ES5 | js —添加元素方法
前端ES5 | js —添加元素方法
|
10天前
|
JavaScript 前端开发
前端JS函数
【9月更文挑战第4天】前端JS函数
20 6
|
14天前
|
开发者 图形学 开发工具
Unity编辑器神级扩展攻略:从批量操作到定制Inspector界面,手把手教你编写高效开发工具,解锁编辑器隐藏潜能
【8月更文挑战第31天】Unity是一款强大的游戏开发引擎,支持多平台发布与高度可定制的编辑器环境。通过自定义编辑器工具,开发者能显著提升工作效率。本文介绍如何使用C#脚本扩展Unity编辑器功能,包括批量调整游戏对象位置、创建自定义Inspector界面及项目统计窗口等实用工具,并提供具体示例代码。理解并应用这些技巧,可大幅优化开发流程,提高生产力。
44 1
|
14天前
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
37 0
|
14天前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
32 0
|
14天前
|
开发者 C# Android开发
明白吗?Xamarin与Native的终极对决:究竟哪种开发方式更适合您的项目需求,让我们一探究竟!
【8月更文挑战第31天】随着移动应用开发的普及,开发者面临多种技术选择。本文对比了跨平台解决方案Xamarin与原生开发方式的优势与劣势。Xamarin使用C#进行跨平台开发,代码复用率高,可大幅降低开发成本;但因基于抽象层,可能影响性能。原生开发则充分利用平台特性,提供最佳用户体验,但需维护多套代码库,增加工作量。开发者应根据项目需求、团队技能和预算综合考量,选择最适合的开发方式。
56 0
|
14天前
|
开发者 Android开发 iOS开发
Xamarin开发者的神器!揭秘你绝不能错过的插件和工具,让你的开发效率飞跃式提升
【8月更文挑战第31天】Xamarin.Forms 是一个强大的框架,让开发者通过单一共享代码库构建跨平台移动应用,支持 iOS、Android 和 Windows。使用 C# 和 XAML,它简化了多平台开发流程,保持一致的用户体验。本指南通过创建一个简单的 “HelloXamarin” 应用介绍 Xamarin.Forms 的基本功能和工作原理。首先配置 Visual Studio 开发环境,然后创建并运行一个包含标题、按钮和消息标签的示例应用,展示如何定义界面布局及处理按钮点击事件。这帮助开发者快速入门 Xamarin.Forms,提高跨平台应用开发效率。
30 0
|
14天前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
26 0
|
14天前
|
Java 开发者 关系型数据库
JSF与AWS的神秘之旅:如何在云端部署JSF应用,让你的Web应用如虎添翼?
【8月更文挑战第31天】在云计算蓬勃发展的今天,AWS已成为企业级应用的首选平台。本文探讨了在AWS上部署JSF(JavaServer Faces)应用的方法,这是一种广泛使用的Java Web框架。通过了解并利用AWS的基础设施与服务,如EC2、RDS 和 S3,开发者能够高效地部署和管理JSF应用。文章还提供了具体的部署步骤示例,并讨论了使用AWS可能遇到的挑战及应对策略,帮助开发者更好地利用AWS的强大功能,提升Web应用开发效率。
40 0