前端面试的那些事儿(1)~JavaScript 原始数据类型
前言
自我总结面试常问的一些细节,方便不断回顾与补充。第一次发表文章,如有问题或不足之处望及时指出。
JavaScript 原始数据类型
1.1 基础数据类型
7大基础数据类型
- boolean
- null
- undefined
- number
- string
- symbol
- BigInt (Stage阶段)
1.2 boolean
只有true、false两个值
什么是falsy?
转换成false的值称为假值(falsy value),这7个值包括undefined、null、+0、-0、NaN、false、""(空字符串)
1.3 null 和 undefined
- null值表示一个空对象指针
- undefined是一个表示”无”的原始值
比较:
- null == undefined // true
- null === undefined // false
转为数值:
- null -> 0
- undefined -> NaN
null 是对象吗?
不是
typeof null 为什么是object?
原因是在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。
为什么有些地方要使用 void 0 代替 undefined
undefined在JavaScript中并不属于保留字/关键字,因此在IE5.5~8中我们可以将其当作变量那样对其赋值,于是采用void方式获取undefined则成了通用方法。
1.4 number
javascript采用IEEE754格式来表示数字,不区分整数和浮点数,javascript中的所有数字都用浮点数值表示
不让除以 0 出错,而引入了无穷大的概念
- Infinity,无穷大; => 1/+0 === Infinity
- -Infinity,负无穷大。=> 1/-0 === -Infinity
number 中经典的 0.1 + 0.2 !=0.3
原因:
- 采用 IEEE754格式的语言都有该问题
- 计算机将 0.1 0.2 表示为二进制,然后相加之后再转换成10进制
- 问题就是转换成2进制时,0.1 在二进制中是无限循环的一些数字(很多十进制小数用二进制表示都是无限循环的)
- JS 采用的浮点数标准却会裁剪掉我们的数字
- 那么这些循环的数字被裁剪了,就会出现精度丢失的问题,也就造成了 0.1 不再是 0.1 了,而是变成了 0.100000000000000002
- 0.2 在二进制也是无限循环的,被裁剪后也失去了精度变成了 0.200000000000000002
- 因此结果是 0.1 + 0.2 === 0.30000000000000004
解决方案:
检查左右两边差的绝对值是否小于最小精度
Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON
NaN
NaN(not a number)表示非数字,NaN与任何值都不相等,包括NaN本身,且任何涉及NaN的操作都会返回NaN
判断NaN更可靠的方法是,利用NaN是javascript之中唯一不等于自身的值这个特点,进行判断
function myIsNaN(value) {
return value !== value;
}
1.5 string
字符串String类型是由引号括起来的一组由16位Unicode字符组成的字符序列
Note:现行的字符集国际标准,字符是以 Unicode 的方式表示的,每一个 Unicode 的码点表示一个字符,理论上,Unicode 的范围是无限的。UTF 是 Unicode 的编码方式,规定了码点在计算机中的表示方法,常见的有 UTF16 和 UTF8。 Unicode 的码点通常用 U+??? 来表示,其中 ??? 是十六进制的码点值。 0-65536(U+0000 - U+FFFF)的码点被称为基本字符区域(BMP)
JavaScript 字符串把每个 UTF16 单元当作一个字符来处理,所以处理非 BMP(超出 U+0000 - U+FFFF 范围)的字符时,你应该格外小心。
JavaScript 这个设计继承自 Java,最新标准中是这样解释的,这样设计是为了“性能和尽可能实现起来简单”。因为现实中很少用到 BMP 之外的字符。
JavaScript 中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征。
1.6 symbol
Symbol的作用非常的专一,换句话说其设计出来就只有一个目的——作为对象属性的唯一标识符,防止对象属性冲突发生。
除了自己创建的symbol,JavaScript还内建了一些在ECMAScript 5 之前没有暴露给开发者的symbol,它们代表了内部语言行为。
Symbol 函数比较特殊,直接用 new 调用它会抛出错误,但它仍然是 Symbol 对象的构造器。
Symbol.toPrimitive
JavaScript 对象转换到基本类型值时,会使用 ToPrimitive 算法,这是一个内部算法,是编程语言在内部执行时遵循的一套规则。
An object without Symbol.toPrimitive property.
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ''); // "[object Object]"
当我们创建一个普通对象时({} 或 new Object() 的方式等),对象上是不具备 [Symbol.toPrimitive] (方法)属性的。所以,对于普通对象的到基本类型值的运算,一般按照具体场景:
- hint 值为 "string" 时,先调用 toString,toString 如果返回一个基本类型值了,则返回、终止运算;否则接着调用 valueOf 方法。
- 否则先调用 valueOf,valueOf 如果返回一个基本类型值了,则返回、终止运算;
- 否则接着调用 toString 方法。
An object with Symbol.toPrimitive property.
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == 'number') {
return 10;
}
if (hint == 'string') {
return 'hello';
}
return true;
}
};
console.log(+obj2); // 10 -- hint is "number"
console.log(`${obj2}`); // "hello" -- hint is "string"
console.log(obj2 + ''); // "true" -- hint is "default"
如果存在Symbol.toPrimitive
则优先调用否则安装上面的规则处理。
ToPrimitive 算法在执行时,会被传递一个参数 hint,表示这是一个什么类型的运算(也可以叫运算的期望值),根据这个 hint 参数,ToPrimitive 算法来决定内部的执行逻辑。
hint 参数的取值只能是下列 3 者之一:
- string
- number
- default
1.7 BigInt
目前还在stage阶段
- BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。
- 不能使用Number和BigInt操作数的混合执行算术运算,需要通过显式转换其中的一种类型。
- 出于兼容性原因,不允许在BigInt上使用一元加号(+)运算符。
1.8 类型检查
有类型就势必会有类型检查
- typeof 对于原始数据,都可以正确检测,但是null会显示object
- typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型
- instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// expected output: true
console.log(auto instanceof Object);
// expected output: true
- so,使用instanceof判断类型就是胡扯,受限极大。
- Object.prototype.toString.call(any anyType) // "[object anyType]" 这才是最准确的判断类型的方式
Object.prototype.toString
在早期版本的 JavaScript 中,“类”的定义是一个私有属性 [[class]],语言标准为内置类型诸如 Number、String、Date 等指定了[[class]]属性,以表示它们的类。语言使用者唯一可以访问[[class]]属性的方式是 Object.prototype.toString。
var o = new Object;
var n = new Number;
var s = new String;
var b = new Boolean;
var d = new Date;
var arg = function(){ return arguments }();
var r = new RegExp;
var f = new Function;
var arr = new Array;
var e = new Error;
console.log([o, n, s, b, d, arg, r, f, arr, e].map(v => Object.prototype.toString.call(v)));
// ["[object Object]", "[object Number]", "[object String]", "[object Boolean]", "[object Date]", "[object Arguments]", "[object RegExp]", "[object Function]", "[object Array]", "[object Error]"]
在 ES5 开始,[[class]] 私有属性被 Symbol.toStringTag 代替,Object.prototype.toString 的意义从命名上不再跟 class 相关。我们甚至可以自定义 Object.prototype.toString 的行为,以下代码展示了使用 Symbol.toStringTag 来自定义 Object.prototype.toString 的行为:
var o = { [Symbol.toStringTag]: "MyObject" }
console.log(o + ""); // [object MyObject]
var d = new Date;
d[Symbol.toStringTag] = "custom"
Object.prototype.toString.call(d) // "[object custom]"
1.9 类型转换
JavaScript 中类型转换有三种情况
- 转换为布尔值
- 转换为数字
- 转换为字符串
转换成布尔值
方法:
- !! 两次取反
- if() 参数会自动转
- Boolean() 构造函数转
规则:
- number -> 除 0 -0 NaN 为false ,其它都为true
- string -> 除 空字符串 为false , 其它都为true
- undefined -> false
- null -> false
- object、function、array -> true
转换为数字
方法:
- Number()
- ParseInt()
- ParseFloat()
- 一元运算符 +'1' -'1'
规则:
- string -> '1' => 1 'a' => NaN
- array -> [] => 0 [1] => 1 [any] => NaN
- null -> 0
- 除数组的引用类型 -> NaN
- Symbal -> 报错
面试题:
[] == ![]
分析:
- 0 == false
- false == false
转换为字符串
方法:
- String()
- toString()
- ''+
规则:
- number -> 转换成对应的字符串 1 =>'1'
- Boolean -> 'true'
- Object -> '[object,Object]'
[题1]:+ 'b'= NaN --> 'a'+NaN = 'aNaN'
'a' + + 'b' // -> "aNaN"
1.10 装箱操作与拆箱操作
- 原始值本身是没有方法可以调用的,除非它的包装类型
- 除了 null 和 undefined,所有的原始值都有等价的、由对象包装原始值的形式表达,通过各自的构造函数可以手动进行包装,系统也会进行隐式转换
装箱操作(原始类型转对象)
console.log("abc".charAt(0)); //a
. 运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。
每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。
我们定义一个函数,函数里面只有 return this,然后我们调用函数的 call 方法到一个 Symbol 类型的值上,这样就会产生一个 symbolObject。
var symbolObject = (function(){ return this; }).call(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
[注意] 装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。
使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。
var symbolObject = Object(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
拆箱操作(对象转原始类型)
在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即拆箱转换)。
- valueOf()方法返回指定对象的原始值,如果对象没有原始值,则valueOf将返回对象本身。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。
- toString()方法返回一个表示该对象的字符串。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o * 2
// valueOf
// toString
// TypeError
拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。
到 String 的拆箱转换会优先调用 toString(这个不同浏览器还不一样,有的还是调用valueOf不纠结)。我们把刚才的运算从 o*2 换成 String(o),那么你会看到调用顺序就变了。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
String(o)
// toString
// valueOf
// TypeError
在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
console.log(o + "")
// toPrimitive
// hello
前端面试的那些事儿(1)~JavaScript 原始数据类型的更多相关文章
- 面试官:JavaScript 原始数据类型 Symbol 有什么用?
以前提到 JavaScript 原始数据类型时,我们知道有Number,String,Null,Boolean,Undefined这几种.ES6 引入了新的基本数据类型Symbol和BigInt.今天 ...
- 【经验之谈】前端面试知识点总结03(JavaScript相关)——附答案
目录 三.JavaScript部分 1.谈谈你对Ajax的理解?(概念.特点.作用) 2.说说你对延迟对象deferred的理解? 3.什么是跨域,如何实现跨域访问? 4.为什么要使用模板引擎? 5. ...
- 「面试指南」解读JavaScript原始数据类型
JavaScript 有 7 种原始数据类型: String(字符型) Number(数值型) Boolean(布尔值型) Undefined Null Object(对象型) Symbol(符号型, ...
- 前端面试知识点锦集(JavaScript篇)
目录 1.谈谈你对Ajax的理解?(概念.特点.作用) 2.说说你对延迟对象deferred的理解? 3.什么是跨域,如何实现跨域访问? 4.为什么要使用模板引擎? 5.JavaScript是一门什么 ...
- 前端面试回顾(1)---javascript的面向对象
前言 前一阵面试,过程中发现问到一些很基础的问题时候,自己并不能很流畅的回答出来.或者遇到一些基础知识的应用,由于对这些点理解的不是很深入,拿着笔居然什么都写不出来,于是有了回顾一下这些基础知识的想法 ...
- 【前端面试】(四)JavaScript var let const的区别
视频链接: JavaScript var let const的区别 - Web前端工程师面试题讲解 参考链接: JavaScript 变量 JavaScript Let JavaScript Cons ...
- 【前端面试】(三)JavaScript相等(==)和全等(===)运算符的区别
视频链接: JavaScript相等()和全等(=)运算符的区别 - Web前端工程师面试题讲解 参考链接: JavaScript == 与 === 区别 区别 对于string.number 等基础 ...
- 【前端面试】(二)JavaScript加法运算
视频链接:JavaScript加法运算 - Web前端工程师面试题讲解 数值 + 数值 首先看菜鸟教程有关于数值对象的教程 JavaScript Number 对象 可以知道Infinity , -I ...
- 《Web 前端面试指南》1、JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
随机推荐
- PHP一致性hash
PHP提供了两种比较两个变量的方法: 松散比较使用 == or != : 两个变量都具有“相同的值”. 严格比较 === or !== : 两个变量都具有“相同的类型和相同的值”. 类型杂耍 真实陈述 ...
- 如何获取 bing 每日壁纸(超高清版)
目录 需求描述 实现方式 简单粗暴 如何下载 如何更高清 排坑指南 初级 优点 给有好奇心的孩子 进阶 接口 自动保存 网站集成 爬虫 需求描述 必应作为一个在壁纸圈做搜索引擎最优秀的站点,其每日壁纸 ...
- 透过源码分析ArrayList运作原理
List接口的主要实现类ArrayList,是线程不安全的,执行效率高:底层基于Object[] elementData 实现,是一个动态数组,它的容量能动态增加和减少.可以通过元素下标访问对象,使用 ...
- .gitattributes
.gitattributes文件是一个文本文件,文件中的一行定义一个路径的若干属性.以行为单位设置一个路径下所有文件的属性,格式如下: 要匹配的文件模式 属性1 属性2 GRLF和LF CRLF,LF ...
- iOS, Xcode11,项目提示第三方库报错无法运行 bundle format unrecognized, invalid, or unsuitable
检查你有没有把静态库和动态库配置错误!! 下图处是配置动态库的地方! 对于动态库和静态库都有使用的时候,注意把静态库设置成“Do not Embeded”
- 少儿编程Scratch第一讲:Scratch完美的初体验
素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8 都说未来是人工智能.计算机程式控制的时代,如何让青少年接触计算机编 ...
- javascript入门 之 zTree (一)
1.安装: 我用的bower工具,所以执行: bower install ztree 2.详细功能与配制,请考官方文档: http://www.treejs.cn/v3/main.php#_zTree ...
- 01-启动jmeter目录功能
1.bin :存储了jmeter的可执行程序,如启动脚本.配置程序 docs: api扩展文档存放 lib: lib\ext 存储了jmeter的整合的功能(如.jar文件程序,和第三方 ...
- C#多线程系列(2):多线程锁lock和Monitor
1,Lock lock 原型 lock 编写实例 2,Monitor 怎么用呢 解释一下 示例 设置获取锁的时效 C# 中,可以使用 lock 关键字和 Monitor 类来解决多线程锁定资源和死锁的 ...
- 分布式 and 集群
集群是个物理形态,强调个体和群体之间的联系: 同一个业务部署在多个服务器上,形成的逻辑上的整体. 分布式是个工作方式.强调请求和处理直接的分发状况: 一个业务分拆多个子业务,部署在不同的服务器上,通过 ...