原生JavaScript灵魂拷问,你能答上多少(一)1

简介: 原生JavaScript灵魂拷问,你能答上多少(一)1

前言


传送门: 原生JavaScript灵魂拷问,你能答上多少(二)


当下的前端开发,三大框架三分天下,框架的简单、强大让我们欲罢不能,使用原生 JavaScript 越来越少。


但我认为 JavaScript 作为每一个前端工程师的立身之本,不止要学会,还要学好、学精,学再多遍都不为过。


另一方面,前端面试中,越来越重视原生 JavaScript 的考察,其所占比例也越来越高。


我抓取了牛客上今年的线上面试题和面经,大约 500 左右道题,原生 JavaScript 的难点(闭包,eventLoop,this,手撕原生JS)考察的频率非常高。


完整的分析我还正在赶工中,希望大家到时候可以来支持一下。


因此我决定整理JavaScript中容易忽视或者混淆的知识点,写一系列篇文章,以灵魂拷问的方式,系统且完整的带大家遨游原生 JavaScript 的世界,希望能给大家带来一些收获。


JS类型之问——概念与检测篇


1.JS中的数据类型有哪些?


  1. 基本数据类型:共有7种


Boolean Number String undefined null Bigint Symbol
复制代码


SymbolES6 引入的一种新的原始值,表示独一无二的值,主要为了解决属性名冲突问题。


BigintES2020 新增加,是比 Number 类型的整数范围更大。


  1. 引用数据类型:1种


Object对象(包括普通Object、Function、Array、Date、RegExp、Math)
复制代码


2.你真的懂typeof吗?


  1. typeof的作用?


区分数据类型,可以返回7种数据类型:number、string、boolean、undefined、object、function ,以及 ES6 新增的 symbol


  1. typeof 能正确区分数据类型吗?


不能。对于原始类型,除 null 都可以正确判断;对于引用类型,除 function 外,都会返回 "object"


  1. typeof 注意事项


  • typeof 返回值为 string 格式,注意类似这种考题: typeof(typeof(undefined)) -> "string"
  • typeof 未定义的变量不会报错,返回 "undefiend"
  • typeof(null) -> "object": 遗留已久的 bug
  • typeof无法区别数组与普通对象: typeof([]) -> "object"
  • typeof(NaN) -> "number"


  1. 习题


console.log(typeof(b));
console.log(typeof(undefined)); 
console.log(typeof(NaN)); 
console.log(typeof(null)); 
var a = '123abc'; 
console.log(typeof(+a)); 
console.log(typeof(!!a)); 
console.log(typeof(a + "")); 
console.log(typeof(typeof(null)));
console.log(typeof(typeof({})));
复制代码


答案


undefined // b未定义,返回undefined
undefined
number // NaN 为number类型
object
number // +a 类型转换为NaN
boolean
string
string // typeof(null) -> "object"; typeof("object") -> "string"
string
复制代码


3.什么是instanceof?你能模拟实现一个instanceof吗?


  1. instanceof 判断对象的原型链上是否存在构造函数的原型。只能判断引用类型。
  2. instanceof 常用来判断 A 是否为 B 的实例


// A是B的实例,返回true,否则返回false
// 判断A的原型链上是否有B的原型
A instaceof B
复制代码


  1. 模拟实现 instanceof


思想:沿原型链往上查找


function instance_of(Case, Constructor) {
    // 基本数据类型返回false
    // 兼容一下函数对象
    if ((typeof(Case) != 'object' && typeof(Case) != 'function') || Case == 'null') return false;
    let CaseProto = Object.getPrototypeOf(Case);
    while (true) {
        // 查到原型链顶端,仍未查到,返回false
        if (CaseProto == null) return false;
        // 找到相同的原型
        if (CaseProto === Constructor.prototype) return true;
        CaseProto = Object.getPrototypeOf(CaseProto);
    }
}
复制代码


测试:


console.log(instance_of(Array, Object)) // true
function User(name){
    this.name = name;
}
const user = new User('zc');
const vipUser = Object.create(user);
console.log(instance_of(vipUser, User)) // true
复制代码


4.如何区分数组与对象?使用instanceof判断数组可靠吗?


  1. ES6 提供的新方法 Array.isArray()
  2. 如果不存在Array.isArray()呢?可以借助Object.prototype.toString.call() 进行判断,此方式兼容性最好


if (!Array.isArray) {
    Array.isArray = function(o) {
        return typeof(o) === 'object' 
               && Object.prototype.toString.call(o) === '[object Array]';
    }
}
复制代码


  1. instanceof 判断


判断方式


// 如果为true,则arr为数组
arr instanceof Array
复制代码


instanceof 判断数组类型如此之简单,为何不推荐使用那?


instanceof 操作符的问题在于,如果网页中存在多个 iframe ,那便会存在多个 Array 构造函数,此时判断是否是数组会存在问题。


更详细的内容可以参考博文:JavaScript为啥不用instanceof检测数组


5.如何判断一个数是否为NaN?


NaN 有个非常特殊的特性, NaN 与任何值都不相等,包括它自身


NaN === NaN // false
NaN == NaN // false
复制代码


鉴于这个独特的特性,可以手撕一个比较简单的判断函数


function isNaN(x) {
    return x != x;
}
复制代码


  • 全局函数 isNaN 方法:不推荐使用。MDN 对它的介绍是:isNaN 函数内包含一些非常有趣的规则。


但为了避免一些面试官出一些冷门题目,咱们来稍微了解一下 isNaN 的有趣机制:会先判断参数是不是 Number 类型,如果不是 Number 类型会尝试将这个参数转换为 Number 类型,之后再去判断是不是 NaN


举个例子:


// 为什么对象会带来三种不同的结果
// 是不是很有趣
// 具体原因可以参考类型转换篇
console.log(isNaN([])) // false
console.log(isNaN([1])) // false
console.log(isNaN([1, 2])) // true 
console.log(isNaN(null)) // false
console.log(isNaN(undefined)) // true
复制代码


isNaN 的结果很大程度上取决于 Number() 类型转换的结果,关于 Number 的转换结果,后面会专门有一部分来介绍。


  • Number.isNaN (推荐使用)


isNaN() 相比,Number.isNaN() 不会自行将参数转换成数字,只有在参数是值为 NaN 的数字时,才会返回 true


6.如何实现一个功能完善的类型判断函数?


Object.prototype.toString.call([value]) ,可以精准判断数据类型,因此可以根据这个原理封装一个自己的 type 方法。


toString.call(()=>{})       // [object Function]
toString.call({})           // [object Object]
toString.call([])           // [object Array]
toString.call('')           // [object String]
toString.call(22)           // [object Number]
toString.call(undefined)    // [object undefined]
toString.call(null)         // [object null]
toString.call(new Date)     // [object Date]
toString.call(Math)         // [object Math]
toString.call(window)       // [object Window]
复制代码


JS类型之问——类型转换篇


7.toString 和 valueOf 方法有什么区别?


  1. 基础:这两个方法属于 Object 对象,是为了解决 JavaScript 值运算与显示的问题。为了更适合自身功能,很多 JavaScript 内置对象都重写了这两个方法。
  2. toString(): 返回当前对象的字符串形式;valueOf() : 返回该对象的原始值
  3. 各个类型下两个方法返回值情况对比


类型 valueOf toString
Array[1,2,3] 数组本身[1, 2, 3] 1,2,3
Object 对象本身 [object Object]
Boolean类型 Boolean值 "true"或"false"
Function 函数本身 function fnName(){code}
Number 数值 数值的字符换表示
Date 毫米格式时间戳 GMT格式时间字符串


  1. 调用优先级隐式转换时会自动调用 toStringvalueOf 方法,两者优先级如下:


  • 强制转化为字符串类型时,优先调用 toString 方法
  • 强制转换为数值类型时,优先调用 valueOf 方法
  • 使用运算符操作符情况下,valueOf 优先级高于 toStirng
  • 对象的类型转换见下一问。


8.你知道对象转换成原始值是什么流程吗 (ToPrimitive)?


对象转换成原始类型,会调用内置的 [ToPrimitive]函数

(参考博客: 从ECMA规范彻底理解 JavaScript 类型转换)


  • ToPrimitive 方法接受两个参数,一个是输入的值 input,一个是期望转换的类型 PreferredType
  1. 如果未传入 PreferredType 参数,让 hint 等于 'default',后面会将 hint 修改为 'number'
  2. 如果 PreferredTypehint String,让 hint 等于 'string'
  3. 如果 PreferredTypehint Number,让 hint 等于 'number'
  4. 返回 OrdinaryToPrimitive(input, hint)


  • OrdinaryToPrimitive(input, hint)
  1. 如果 hint'string',那么就将 methodNames 设置为 toString、valueOf
  2. 如果 hint'number',那么就将 methodNames 设置为 valueOf、toString


methodName 存储的就是当前 preferredType 下的调用优先级,如果全部调用完毕仍然未转化为原始值,会发生报错。


9.你能做出下面这个题吗?


const a = {x:1};
const b = {x:2};
const obj = {};
obj[a] = 100;
obj[b] = 200;
console.log(obj[a]);
console.log(obj[b]);
复制代码


有了第七问和第八问的知识,这个题目就不难了。 JavaScript 对象的键必须是字符串,因此分别需要将对象 ab 转换为 string 类型。具体转换流程:


// 1.执行ToPrimitive
// hint 为 string
ToPrimitive(a, 'hint String')
// 2.执行OrdinaryToPrimitive
OrdinaryToPrimitive(a, 'string')
// 3.返回methodNames
methodNames = ['toString', 'valueOf']
// 4.调用methodNames里方法
// 调用toString
a.toString() // 返回[object Object]
复制代码


对象 ab 转换后的结果都是 [object Object]obj 对象上只添加了一个属性 [object Object]


答案


200
200
复制代码


相关文章
|
17天前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
27 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
2月前
|
移动开发 前端开发 JavaScript
原生JavaScript+canvas实现五子棋游戏_值得一看
本文介绍了如何使用原生JavaScript和HTML5的Canvas API实现五子棋游戏,包括棋盘的绘制、棋子的生成和落子、以及判断胜负的逻辑,提供了详细的代码和注释。
37 0
原生JavaScript+canvas实现五子棋游戏_值得一看
|
3月前
|
Devops 持续交付 测试技术
JSF遇上DevOps:开发流程将迎巨变?一篇文章带你领略高效协同的魅力!
【8月更文挑战第31天】本文探讨了如何在JavaServer Faces(JSF)开发中融入DevOps文化,通过持续集成与部署、自动化测试、监控与日志记录及反馈机制,提升软件交付速度与质量。文中详细介绍了使用Jenkins进行自动化部署、JUnit与Selenium进行自动化测试、ELK Stack进行日志监控的具体方法,并强调了持续改进的重要性。
39 0
|
3月前
|
JavaScript 前端开发 API
从零开始学表单操作,jQuery 与原生 JavaScript 完全指南,带你轻松掌握网页交互关键!
【8月更文挑战第31天】在网页开发中,表单是实现用户互动的关键元素。无论是收集信息、提交数据还是验证输入,都需要对表单进行有效操作。本文档介绍了如何使用原生 JavaScript 和 jQuery 操作表单,包括获取表单元素、读写表单值、处理表单提交及验证等核心功能。jQuery 提供了更简洁的语法和更好的兼容性,但原生 JavaScript 在性能上有优势。选择合适的方法取决于项目需求和个人偏好。下面通过具体示例展示了两种方式的操作方法。
39 0
|
5月前
|
JavaScript 前端开发 安全
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作
|
4月前
|
JavaScript
js 一键复制到剪贴板(原生js实现)
js 一键复制到剪贴板(原生js实现)
32 0
|
5月前
|
前端开发 JavaScript 容器
程序技术好文:纯原生javascript下拉框表单美化实例教程
程序技术好文:纯原生javascript下拉框表单美化实例教程
90 0
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
97 2
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
129 4
下一篇
无影云桌面