基础概念

堆内存与栈内存

在操作系统中,内存被分为栈区堆区

原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。
栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

Undefined

undefined 是一个标识符,所以可以被当作变量来使用和赋值。

  1. (function() {
  2. var undefined = 'not is undefined';
  3. console.log(undefined); //"not is undefined"
  4. console.log(typeof undefined) // "string"
  5. })()

我们可以通过 void 0 来获得 undefined

Null

Boolean

Number

在 js 中不同进制数字的表示方式

  • 以 0X、0x 开头的表示为十六进制。
  • 以 0、0O、0o 开头的表示为八进制。
  • 以 0B、0b 开头的表示为二进制格式。

js 中整数的安全范围是多少?

安全整数指的是,在这个范围内的整数转化为二进制存储的时候不会出现精度丢失,能够被“安全”呈现的最大整数是 2^53 - 1,Number.MAX_SAFE_INTEGER

Number.MAX_SAFE_INTEGER===Math.pow(2,53)-1 //true
Number.MIN_SAFE_INTEGER===-Math.pow(2,53)+1 // true

如果某次计算结果得到了一个超过Javascript数值范围的值。那么这个值会被自动转换为特殊的 Infinity ,isFinite可以判断isFinite。

Infinity === Infinity // true

typeof NaN 的结果是什么?

NaN 意指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。

typeof NaN; // “number”

NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值

可以通过Number.isNaN进行判断。

Object.is(NaN,NaN) // true

数组去重的时候需要注意NaN,某些方法可以去除 比如 includes,有些不可以比如indexOf

isNaN 和 Number.isNaN 函数的区别?

  • isNaN 会尝试 将参数转换为数值,任何不可以被转为数值的值都会返回 true。
  • Number.isNaN:先判断是不是数字,如果是数字在判断是否为NaN,更准确。


isNaN(“a”) // true
**Number.isNaN(“a”

~操作符

~ 返回 2 的补码,并且 ~ 会将数字转换为 32 位整数,因此我们可以使用 ~ 来进行取整操作。
~x 大致等同于 -(x+1)。

parseInt vs Number

parseInt:解析允许字符串中含有非数字字符,解析按照从左到右的顺序,如果遇到非数字就停止。
Number:如果遇到非数字,就会解析失败返回NaN

生成随机数的各种方法?

《JS - 生成随机数的方法汇总(不同范围、类型的随机数)》

0和-0

  1. 通过 1/0 和 1/-0 进行判断,Infinity 和 -Infinity。
  2. Object.is(0,-0) // false

    格式化价格

    1. var num = new Number('1.111e+84')
    2. num = num.toLocaleString() // 会以每三位为一组,以逗号间隔

    String

    Unicode 和 UTF-8 之间的关系

    Unicode 是一种字符集合,现在可容纳 100 多万个字符。每个字符对应一个不同的 Unicode 编码,它只规定了符号的二进制代码,却没有规定这个二进制代码在计算机中如何编码传输。
    UTF-8 是一种对 Unicode 的编码方式,它是一种变长的编码方式,可以用 1~4 个字节来表示一个字符。

    Symbol(Es6)

    Symbol 代表创建后独一无二且不可变的数据类型,它的出现我认为主要是为了解决可能出现的全局变量冲突的问题。

    BigInt(Es10)

    BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。

    问题

    undefiend与undeclared

    undefined:已在作用域中声明但还没有赋值的变量.
    undeclared:还没有在作用域中声明过的变量

对于 undeclared 变量的引用,浏览器会报引用错误,如 ReferenceError: b is not defined 。但是我们可以使用 typeof 的安全防范机制来避免报错,因为对于 undeclared(或者 not defined )变量,typeof 会返回 “undefined”。

null 和 undefined 的区别

  • undefined和null都是基本数据类型,这两个基本数据类型分别都只有一个值

null

  1. null代表空对象
    • 主要用于赋值给一些可能会返回对象的变量,作为初始化。
    • 当又一个数据不在需要时,可以通过为其赋值null解除引用
  2. typeof null 的值为object
  3. 作为对象原型链的终点。

undefined

  1. 代表的含义是未定义
    1. 函数相关
      1. 调用函数,没有被传递的参数的默认值
      2. 函数的默认返回值。
    2. 变量被声明后的默认值。
    3. 对象没有赋值的属性
    4. typeof 未定义的变量 值为undefined
  2. undefined不是一个保留字

window.null 的值是 undefined。 typeof window.null 为 “undefined”
window.undefined 的值是 undefined。 typeof window.undefined 为 “undefined”
undefined == null // false
undefined === null // false

ndefined值是派生自null值的。

ES3之前其实是没有原始值undefined这个值的,第三版引入这个值,其实是为了正式区分空对象指针(后面我们会介绍到这指的就是null)与未经初始化的变量

JavaScript深入理解之undefined与null

类型转换

其他值->字符串

https://262.ecma-international.org/11.0/#sec-tostring
规范的 9.8 节中定义了抽象操作 ToString ,它负责处理非字符串到字符串的强制类型转换。

  • Null 和 Undefined 类型 ,null 转换为 “null”,undefined 转换为 “undefined”,
  • Boolean 类型,true 转换为 “true”,false 转换为 “false”。
  • Number 类型的值直接转换,不过那些极小和极大的数字会使用指数形式。
  • Symbol 类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误。
  • 对普通对象来说,除非自行定义 toString() 方法,否则会调用 toString()(Object.prototype.toString()),来返回内部属性 [[Class]] 的值,如”[object Object]”。如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。

其他值->数字值

有时我们需要将非数字值当作数字来使用,比如数学运算。为此 ES5 规范在 9.3 节定义了抽象操作 ToNumber。
(1)Undefined 类型的值转换为 NaN。
(2)Null 类型的值转换为 0。
(3)Boolean 类型的值,true 转换为 1,false 转换为 0。
(4)String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。
(5)Symbol 类型的值不能转换为数字,会报错。
(6)对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive 会首先(通过内部操作 DefaultValue)检查该值是否有valueOf() 方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。
如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。

Symbol 值的强制类型转换?

  • ES6 允许从符号到字符串显式强制类型转换,然而隐式强制类型转换会产生错误
  • Symbol 值不能够被强制类型转换为数字(显式和隐式都会产生错误)
  • 可以强制类型转换为布尔值(显式和隐式结果都是 true )。 ```javascript let a = Symbol.for(“a”)

a.toString() // “Symbol(a)” String(a) // “Symbol(a)” a + “1” // Cannot convert a Symbol value to a string

Number(a) // Cannot convert a Symbol value to a number a + 1 // Cannot convert a Symbol value to a number

Boolean(a) // true !a // false

  1. <a name="zzb3Z"></a>
  2. ## 其他值到布尔类型的值的转换规则?
  3. ES5 规范 9.2 节中定义了抽象操作 ToBoolean,列举了布尔强制类型转换所有可能出现的结果。<br />以下这些是假值:<br />• undefined<br />• null<br />• false<br />• +0、-0 和 NaN<br />• ""<br />假值的布尔强制类型转换结果为 false。从逻辑上说,假值列表以外的都应该是真值。
  4. <a name="xdjdz"></a>
  5. ## 什么情况下会发生布尔值的隐式强制类型转换?
  6. 1. **if (..) **语句中的条件判断表达式。
  7. 1. **for** ( .. ; .. ; .. ) 语句中的条件判断表达式(第二个)。
  8. 1. **while (..) **和 **do..while(..)** 循环中的条件判断表达式。
  9. 1. **? :** 中的条件判断表达式。
  10. 1. **逻辑运算符** ||(逻辑或)和 &&(逻辑与)**左边的操作数(**作为条件判断表达式)。
  11. <a name="Yf6Bk"></a>
  12. ## {} 和 [] 的 valueOf 和 toString 的结果是什么?
  13. {} 的 valueOf 结果为 {} ,toString 的结果为 "[object Object]"<br />[] 的 valueOf 结果为 [] ,toString 的结果为 ""
  14. <a name="tuudL"></a>
  15. ## == 操作符的强制类型转换规则?
  16. 1. **字符串**和**数字**之间的相等比较,将字符串转换为**数字**之后再进行比较。
  17. 1. **其他类型**和**布尔**类型之间的相等比较,**先**将**布尔值转换为数字**后,再应用其他规则进行比较。
  18. 1. **null 和 undefined** 之间的相等比较,**结果为真**。其他值和它们进行比较都**返回假值**。
  19. 1. **对象**和**非对象之间**的相等比较,**对象先调用 ToPrimitive 抽象**操作后,再进行比较。
  20. 1. 如果一个操作值为** NaN **,则相等比较**返回 false**( NaN 本身也不等于 NaN )。
  21. 1. 如果两个操作值**都是对象**,则比较它们是不是指向同一个对象。如果两个操作数都**指向同一个对象**,则相等操作符**返回 true**,否则,返回 false。
  22. ```javascript
  23. "[object Object]" == {} //true
  24. [] == {} // false => "" == "[object Object]" false

如何将字符串转化为数字,例如 ‘12.3b’?

  1. 使用 Number() 方法,前提是所包含的字符串不包含不合法字符。
  2. 使用 parseInt() 方法,parseInt() 函数可解析一个字符串,并返回一个整数。还可以设置要解析的数字的基数。当基数的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。
  3. 使用 parseFloat() 方法,该函数解析一个字符串参数并返回一个浮点数。
  4. 使用 + 操作符的隐式转换。

===、==、Object.is

  • 使用双等号进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
  • 三等号判等(判断严格),比较时不进行隐式类型转换,(类型不同则会返回false)。
  • Object.is 在三等号判等的基础上特别处理了 NaN 、-0 和 +0 ,保证 -0 和 +0 不再相同,但 Object.is(NaN, NaN) 会返回 true。

如何将浮点数点左边的数每三位添加一个逗号,如 12000000.11 转化为『12,000,000.11』?

  1. // 方法一
  2. function format(number) {
  3. return number && number.replace(/(?!^)(?=(\d{3})+\.)/g, ",");
  4. }
  5. // 方法二
  6. function format1(number) {
  7. return Intl.NumberFormat().format(number)
  8. }
  9. // 方法三
  10. function format2(number) {
  11. return number.toLocaleString('en')
  12. }

思考

  1. ({}+{}).length // "[object Object][object Object]".length 30
  2. ([]+[]).length // "" 0

操作符

+ 操作符什么时候用于字符串的拼接?

根据 ES5 规范 11.6.1 节,如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+ 将进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作,该抽象操作再调用[[DefaultValue]],以数字作为上下文。如果不能转换为字符串,则会将其转换为数字类型来进行计算。

简单来说就是,如果 +其中一个操作数是字符串(或者通过以上步骤最终得到字符串),则执行字符串拼接否则执行数字加法。
那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

  1. 1 + "1" // 11
  2. "1" + 1 // 11
  3. var c ={valueOf:()=>1}
  4. c + 1 // 2
  5. var d ={valueOf:()=>"1"}
  6. c + 1 // 11
  7. var e ={toString:()=>"1"}
  8. e + 1 // 11
  9. var f ={toString:()=>1}
  10. f + 1 // 2

为什么 0.1 + 0.2 != 0.3?如何解决这个问题

当计算机计算 0.1+0.2 的时候,实际上计算的是这两个数字在计算机里所存储的二进制0.1 和 0.2 在转换为二进制表示的时候会出现位数无限循环的情况。js 中是以 64 位双精度格式来存储数字的,只有 53 位的有效数字超过这个长度的位数会被截取掉这样就造成了精度丢失的问题。这是第一个会造成精度丢失的地方。在对两个以 64 位双精度格式的数据进行计算的时候,首先会进行对阶的处理,对阶指的是将阶码对齐,也就是将小数点的位置对齐后,再进行计算,一般是小阶向大阶对齐,因此小阶的数在对齐的过程中,有效数字会向右移动,移动后超过有效位数的位会被截取掉,这是第二个可能会出现精度丢失的地方。当两个数据阶码对齐后,进行相加运算后,得到的结果可能会超过 53 位有效数字,因此超过的位数也会被截取掉,这是可能发生精度丢失的第三个地方。
对于这样的情况,我们可以将其转换为整数后再进行运算,运算后再转换为对应的小数,以这种方式来解决这个问题。
我们还可以将两个数相加的结果和右边相减,如果相减的结果小于一个极小数,那么我们就可以认定结果是相等的,这个极小数可以
使用 es6 的 Number.EPSILON

精度丢失可能出现在进制转换和对阶运算中

详细资料可以参考:

ToPrimitive

ECMAScript7规范中的ToPrimitive抽象操作