【学习笔记】JavaScript 基础 - 数据类型、运算符、类型转换

数据类型

原始(Primitive)类型

  • 原始(Primitive)值通常叫作栈数据(一旦开了个房间、不可能在这个房间里对其进行修改)数组

  • 原始类型存储的都是值,是没有函数能够调用的,如 undefined.toString() 会报错。通常咱们看到的 '1'.toString() 能够调用是由于实际上它已经被强制转换成了 String 类型也就是对象类型,因此能够调用 toString 函数 image.png安全

  • 在 JS 中,存在着如下几种原始值,分别是:markdown

    number(typeof undefined === "undefined")
    string(typeof '' === 'string')
    boolean(typeof true === 'boolean')
    null(typeof null === 'object')
    undefined(typeof null === 'undefined')
    symbol(typeof Symbol() === 'symbol')
    bigInt(typeof 10n === 'bigint')(没有正式发布但即将被加入标准的原始类型)函数

  • undefined 和 null 的区别post

    • null 有值,不过这个值是空值,null 表示为空,表明此处不该该有值的存在,一个对象能够是 null,表明是个空对象,null 通常用做占位
    • undefined 是未定义,表示『不存在』,彻底没有值的意思,JavaScript 是一⻔动态类型语言,成员除了表示存在的空值外,还有可能根本就不存在(由于存不存在只在运行期才知道),这就是 undefined 的意义所在
  • 除了会在必要的状况下类型转换之外,原始类型还有一些坑性能

    • typeof null 会输出 object,这是 JS 存在的一个悠久 Bug,在 JS 的最第一版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头表明是对象,然而 null 表示为全零,因此将它错误的判断为 object
    • JS 的 number 类型是浮点类型,在使用中会可能会遇到某些 Bug,如 0.1 + 0.2 !== 0.3,此处详见分析篇:为何 0.1 + 0.2 !== 0.3

引用类型

  • 通常叫作堆数据,包括:ui

    对象(Object)(typeof {} === 'object') 数组(Array)(typeof [] === 'object')
    函数(Function)(typeof function(){} === 'function')spa

  • 引用类型原始类型的区别指针

    • 由于原始值是存放在栈里的,而引用值是存放在堆里的,原始值不能够被改变,引用值能够被改变
    • 原始值的赋值是把值的内容赋值一份给另外一个变量,栈内存一旦被赋值了就不能够改变,即便给 num 从新赋值为 234,也是在栈里面从新开辟了一块空间赋值 234,而后把 num 指向了这个空间,前面那个存放 123 的空间还存在
    • 但引用值却不是这样:引用值的变量名存在栈里,可是值倒是存在堆里,栈里的变量名只是个指针并指向了一个堆空间,这个堆空间存的是一开始赋的值,当 arr1 = arr 时,实际上是把 arr1 指向了和 arr 指向的同一个堆空间,这样当改变 arr 的内容时,其实就是改变了这个堆空间的内容,天然一样指向这个堆空间的 arr1 的值也随着改变
      // num的改变对num1彻底没有影响
      var num = 123, num1 = num;
      num = 234;
      console.log(num) // 234
      console.log(num1) // 123
      
      // 只是改变了arr的值,可是arr1也跟着改变了
      var arr = [1,2,3], arr1 = arr;
      arr.push(4)
      console.log(arr) // [1,2,3,4]
      console.log(arr1) // [1,2,3,4]
      复制代码

    再来看个函数参数是对象的状况code

    function test(person) {
      person.age = 26
      person = {
        name: 'yyy',
        age: 30
      }
      return person
    }
    const p1 = {
      name: 'yck',
      age: 25
    }
    const p2 = test(p1)
    console.log(p1) // {name: "yck", age: 26}
    console.log(p2) // {name: "yyy", age: 30}
    复制代码

    (1)上面代码中,首先函数传参是传递对象指针的副本
    (2)到函数内部修改参数的属性这步,当前 p1 的值也被修改了
    (3)可是当从新为 person 分配了一个对象时就出现了分歧,请看下图,因此最 后 person 拥有了一个新的地址(指针),即和 p1 没有任何关系,致使了 最终两个变量的值是不相同的 image.png

  • 原始数据类型的值直接存放在栈中,对象为引用数据类型,它们的引用变量存储在栈中,指向于存储在堆中的实际对象。没法直接操纵堆中的数据,即没法直接操纵对象,但可经过栈中对对象的引用来操做对象,就像经过遥控机操做电视机同样,区别在于这个电视机自己并无控制按钮

为何引用值要放在堆中,而原始值要放在栈中?
(1)堆比栈大,栈比堆的运算速度快;对象是一个复杂的结构且能够自由扩展,如数组能够无限扩充、对象能够自由添加属性
(2)相对而言原始类型比较稳定且它只占据很小的内存,不将原始类型放在堆是由于是为了避免影响栈的效率,且经过引用到堆中查找实际对象是要花费时间的,而这个综合成本远大于直接从栈中取得实际值的成本,因此原始类型值直接存放在栈中

运算符

算术运算符(算术运算符的优先级是从左到右的)

  • +:数学上的相加功能、拼接字符串(字符串和任何数据相加都会变成字符串)
  • /*///%:f分别对应数学上的相减、相乘、相除、取余功能
  • =:赋值运算符,优先级最低
  • ():和数学上同样,加括号的部分优先级最高
  • ++:自加 1 运算,当写在变量前时是先自加 1 再执行运算,写在变量后时是先运算再自加 1
  • --:用法和 ++ 同样,不过是减法操做
  • +=:让变量自加多少
  • 相同的还有 -=、/=、*-、%= 等等

比较运算符

  • 比较运算符有 > 、< 、>= 、<= 、!= 、== 不严格等于、===严格等于
  • 不严格等于和严格等于的区别:当比较两个数据时,是否先转化成同一个类型的数据以后再进行比较。不严格等于就是说这两个数据进行了转化后值相同则整两个数据相等;而严格等于则是两个数据不进行数据转化也相等

注:NaN 不等于任何数据包括它自己,null 和 undefined 就等于它自己

逻辑运算符

  • 逻辑运算符主要是 && 和 ||(与和或)
  • && 的做用:只有是 true 时才会继续日后执行,一旦第一个表达式就错了后面的第二个表达式根本不执行。若表达式的返回结果都是 true 则这里 && 的返回结果是最后一个正确的表达式的结果
  • || 的做用:只要有一个表达式是 true 则结束,后面的就不走了且返回的结果是这个正确的表达式的结果,若都是 false 则返回结果就是 false
  • 通常来讲,&& 有当作短路语句的做用,由于运算符的运算特色,只有第一个条件成立时才会运行第二个表达式,因此能够把简单的 if 语句用 && 来表现出来
  • 通常来讲,|| 有当作赋初值的做用,有时但愿函数参数有一个初始值,在不使用ECMA6 的语法的状况下,最好的作法就是利用 || 语句

    注意:这里有一个缺点,当传的参数是一个布尔值且传的是 false,则 || 语句的特色就会忽略掉所传的这个参数值而去赋成默认的初始值,因此为了解决这个弊端,就须要利用 ES6 的一些知识

默认为 false 的值:undefined、null、" "、0、-0、false、NaN

类型转换

显示类型转换

  • typeof 能返回的类型一共有 6 种:numner、string、boolean、undefined、object、function
    • 数组和 null 的都返回 'object'
    • NaN 属于 number 类型:虽然是非数,可是非数也是数字的一种
  • Number(mix):该方法能够把其余类型的数据转换成数字类型的数据
  • parseInt(string, radix):该方法是将字符串转换成整型数字类型的
    • 第二个参数 radix 是可选择的参数
    • 当 string 里既包括数字又包括其余字符时会从左到右只会转换数字部分,遇到其余非数字的字符就中止,即便后面还有数字也不会继续转换
    • 当 radix 不为空时,该函数可用来做为进制转换,radix 做用则是把第一个参数的数字当成几进制的数字来转换成十进制(radix 参数的范围是 2 - 36)
  • parseFloat(string, radix):这个方法和 parseInt 相似,将字符串转换成浮点类型的数字,一样是碰到第一个非数字型字符中止,但因为浮点型数据有小数点,因此它会识别第一个小数点以及后面的数字,但第二个小数点则没法识别
    • 一旦数字变得足够大,其字符串表示将以指数形式呈现,以下,此时获得的是 1
      // String(100000000000000000000000) -> "1e+23"
      parseInt(100000000000000000000000);  // 1
      复制代码
  • toString(radix):它是对象上的方法,任何数据类型均可使用,转换成字符串类型
    • 一样 radix 基底是可选参数,当为空时仅仅表明将数据转化成字符串
    • 当写了 radix 基底时则表明要将这个数字转化成几进制的数字型字符串
    • undefiend 和 null 没有 toString 方法
  • String(mix):把任何类型转换成字符串类型
  • Boolean(mix):把任何类型转换成布尔类型

隐式类型转换

  • isNaN():这个方法能够检测数据是否是非数类型,这中间隐含了一个隐式转换,先将传的参数调用 Number 方法,再看结果是否是 NaN,该方法能够检测 NaN 自己
isNaN(NaN) // true
isNaN('abc') // true
isNaN(123) // false
复制代码
  • 算术运算符
    • ++ n:先将数据调一遍 Number 后,再自加 n
    • n ++:虽然是执行完后才自加 n,但执行前就调用 Number 进行类型转换
    • 一样一目运算符也能够进行类型转换:+、-、*、/ 在执行前都会先转换成数字类型再进行运算
  • 逻辑运算符也会隐式调用类型转换
    • && 和 || 都是先把表达式调用 Boolean 换成布尔值再进行判断,不过返回的结果仍是自己表达式的结果
    • !取反操做符返回的结果也是调用 Boolean 方法后的结果
    • 转 Boolean:在条件判断时除了 undefinednullfalseNaN''0-0,其余全部值都转为 true,包括全部对象

转换规则

原始值 转换为数值 转换为字符串 转换为布尔值
number / 0 -> "0",5 -> "5" 除了 0、-0、NaN 都为 true
string " " -> 0,"1" -> 1,"a" -> NaN / 除了空字符串都为 true
undefined NaN "undefined" false
null 0 "null" false
[] 0 " " true
[10,20] NaN "10,20" true
{} NaN "[object, Object]" true
{a: 1} NaN "[object, Object]" true
function() {} NaN "function(){}" true
true 1 "true" true
false 0 "false" false
Symbol NaN "function Symbol() { [native code] }" true
Symbol() 抛错 "Symbol()" true

引用类型转换为原始类型

  • 引用类型在转换类型时会调用内置的 [[ToPrimitive]] 函数
  • ToPrimitive(obj, preferredType):JS 引擎内部转换为原始值
  • ToPrimitive(obj, preferredType) 函数接受两个参数:obj 为被转换的对象,preferredType 为但愿转换成的类型(默认为空,接受的值为 Number 或 String)
  • 在执行 ToPrimitive(obj, preferredType) 时若第二个参数为空且 obj 为 Date 的实例时,此时 preferredType 会被设置为 String,其余状况下 preferredType 都会被设置为 Number,若 preferredType 为 Number,ToPrimitive 执行过程以下:
    • 若 obj 为原始值,直接返回
    • 不然调用 obj.valueOf(),若执行结果是原始值则返回
    • 不然调用 obj.toString(),若执行结果是原始值则返回
    • 不然抛异常
  • 若 preferredType 为 String,将上面的第 2 步和第 3 步调换,即:
    • 若 obj 为原始值,直接返回
    • 不然调用 obj.toString(),若执行结果是原始值则返回
    • 不然调用 obj.valueOf(),若执行结果是原始值则返回
    • 不然抛异常

== 运算规则

一些常规和很是规的转换状况

// 常规
"0" == null  // false
"0" == undefined // false
"0" == NaN // false
"0" == 0 // true
"0" == "" // false
false == null // false
false == undefined // false
false == NaN // false
false == {} // false
"" == null // false
"" == undefined // false
"" == NaN // false
"" == {} // false
0 == null // false
0 == undefined // false
0 == NaN // false
0 == {} // false

// 很是规
"0" == false // true
false == 0 // true
false == "" // true
false == [] // true
"" == 0 // true
"" == [] // true
0 == [] // true
复制代码

对 == 两边的值认真推敲,如下两个原则能够有效地避免出错,这时最好用 === 来避免不经意的强制类型转换
(1)若两边的值中有 truefalse,千万不要使用 ==
(2)若两边的值中有 []"" 或者 0,尽可能不要使用 ==

总结

  • undefined == null,结果是 true 且它俩与全部其余值比较的结果都是 false
  • String == Boolean,须要两个操做数同时转为 Number
  • String/Boolean == Number,须要 String/Boolean 转为 Number
  • Object == Primitive,须要 Object 转为 Primitive(具体经过 valueOf 和 toString 方法)

JS中 ==、=== 和 Object.is() 的区别

==:等于,===:严格等于,Object.is():增强版严格等于

const a = 3; 
const b = "3"; 
a == b;    // true
a === b;   // false,由于*a*,*b*的类型不同 
Object.is( a, b );  //false,由于*a*,*b*的类型不同 
复制代码

=== 这个比较简单,只须要利用下面的规则来判断两个值是否恒等

若类型不一样,就不相等
若两个都是数值且是同一个值,那么相等 有一个是 NaN 就不相等 若两个都是字符串且每一个位置的字符都同样,那么相等;不然不相等 若两个值都是一样的 Boolean 值,那么相等 若两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;不然不相等。 若两个值都是 null 或者都是 undefined,那么相等

Object.is() 其行为与 === 基本一致,不过有两处不一样:

+0 不等于 -0
NaN 等于自身

+0 === -0 //true
NaN === NaN // false
Object.is(0, -0) // false
Object.is(+0, -0) // false
Object.is(0, +0) // true
Object.is(-0, -0) // true
Object.is(NaN, 0/0) // true
Object.is(NaN, NaN) // true

Object.is('foo', 'foo');     // true
Object.is(window, window);   // true
 
Object.is('foo', 'bar');     // false
Object.is([], []);           // false
 
const foo = { a: 1 };
const bar = { a: 1 };
Object.is(foo, foo);         // true
Object.is(foo, bar);         // false
 
Object.is(null, null);       // true
复制代码

Object.is() 在严格等于的基础上修复了一些特殊状况下的失误,具体来讲就是 +0-0NaNNaN

function objectIs(x, y) {
    if(x === y) {
        // 运行到 1/x === 1/y 时 x 和 y 都为 0,但 1/+0 = +Infinity,1/-0 = -Infinity 是不同的
        return x !== 0 || y !== 0 || 1 / x === 1 / y;
    } else {
        // NaN === NaN 是false,在这里作个拦截,x !== x 必定是 NaN, y 同理
        // 两个都是 NaN 时返回 true
         return x !== x && y !== y;
    }
}
复制代码

扩展

为何会有 BigInt 的提案?

  • JavaScript 中 Number.MAX_SAFE_INTEGER 表示最大安全数字,计算结果是 9007199254740991,即在这个数范围内不会出现精度丢失(小数除外)
  • 可是一旦超过这个范围,JS 就会出现计算不许确的状况,这在大数计算时不得不依靠一些第三方库进行解决,所以官方提出了 BigInt 来解决此问题
  • bigInt 类型能够用任意精度表示整数,使用 bigInt 能够安全地存储和操做大整数,甚至能够超过数字的安全整数限制,bigInt 是经过在整数末尾附加 n 或调用构造函数来建立的

{} + [] 的结果是什么?

详见:juejin.cn/post/696307…

['1','2','3'].map(parseInt)的返回值是什么?

详见:juejin.cn/post/696291…

相关文章
相关标签/搜索