js数据类型,类型检测,类型转换

数据类型

值类型/基本数据类型(7种)
Number、String、Boolean、Null、 Undefined、Symbol(ES6),BigInt(ES11)这些类型可以直接操作保存在变量中的实际值。
对象类型/引用数据类型
标准普通对象:Object
标准特殊对象:Array、RegExp、Date、Math、Error…
非标准特殊对象:Number、String、Boolean…
可调用对象:Function

基本数据类型和引用数据类型的区别:

基本数据类型:

基本数据类型是指存放在栈(stack)中的简单数据段。

数据大小固定。占用空间小。按值存放,按值访问。

var a = 10;
var b = a; //把a的值赋值给b
b=20;
console.log(a); //10
console.log(b);//20

a的值为一个基本类型,是存储在栈中。将a的值赋给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址

下图演示了基本类型赋值的过程:
在这里插入图片描述

引用数据类型:

引用类型是存放在堆(heap)内存中的对象。

数据大小不固定。占据空间大。

栈中存放的是一个指针,该指针指向堆中该实体起始地址

var obj1 = {};
var obj2 = obj1;
obj2.name = 'abc';
console.log(obj1.name); //'abc'

obj1是一个引用数据类型,实体起始地址存储在堆中。赋值操作,是将堆内存对象在栈内存的引用地址复制了一份给了obj2,最终他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响。

下图演示了引用类型赋值的过程:
在这里插入图片描述

基本数据类型

Number类型

数字类型,包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一位数字)两种值。
NaN是一个特殊的数字类型,表示Not A Number。

特点:

1.JS进行浮点元素运算可能得到一个不精确的值。

0.1+0.2; //0.300000004;

在JS内部所有的计算都是以二进制方式计算的, 0.1+0.2 不等于 0.3 ,因为在 0.1+0.2 的计算过程中发生了两次精度丢失。第一次是在 0.1 和 0.2 转成双精度二进制浮点数时,由于二进制浮点数的小数位只能存储52位,导致小数点后第53位的数要进行为1则进1为0则舍去的操作,从而造成一次精度丢失。第二次在
0.1 和 0.2 转成二进制浮点数后,二进制浮点数相加的过程中,小数位相加导致小数位多出了一位,又要让第53位的数进行为1则进1为0则舍去的操作,又造成一次精度丢失。最终导致
0.1+0.2 不等于0.3 。
————————————
原文链接:https://blog.csdn.net/cherryfef/article/details/121473365

2.NaN与任何数进行运算得到的结果都是NaN。

3.NaN不大于不小于不等于任何数,包括它本身。

NaN == NaN //false

浮点数计算问题的解决方案:
  • 第三方库decimal.js、Math.js
  • toFixed(N)方法,四舍五入保留N位小数
console.log((0.1+0.2).toFixed(2))//'0.30'注意是字符串
  • 扩大系数法,将浮点数扩大为整数就不会有精度丢失问题了
//获取小数点后多少位扩大多少倍
const point = (num)=>{
	num=num+''  //转为字符串
	let [,char=''] = num.split('.')//char=''防止小数点后为undefined
	let len = char.length;
	return Math.pow(10,len);
}
const plus = (num1,num2)=>{
num1=+num1; //转为数字
num2=+num2;
if(isNaN(num1)||isNaN(num2))return NaN;
let max = point(num1);
max = max>point(num2)?max:point(num2);
return(num1*max+num2*max)/max
}
console.log(plus(0.1,0.2)) //0.3

String类型

字符串类型,字符串可以使用双引号(")、单引号(')或反引号(`)表示。

特点:

1.在JS中字符串需要使用引号引起来,单引号和双引号不能混用。

2.引号不能嵌套,可以使用\进行转义。

//正确的语法
var str1 = 'I am a "programmer"';    // I am a "programmer"
var str2 = "I'm a 'programmer'"; // I'm a 'programmer'
 
//常见的错误语法
var str1 = 'I'm a programmer';   //单引号错误用法
var str2 = "I'm a "programmer""; //双引号错误用法
var str3 = 'I am a programmer"; //单双引号混用

转义符

var strl = 'I\'m a programmer';   // I'm a programmer
var str2 = 'I am a\nprogrammer'    // I am a(换行)programmer
var str3 = 'C:\\JavaScript\\';   // C:\JavaScript\
var str4 = '\x61bc';        // abc
var str5 = '\u597d学生';      //好学生

Boolean类型

布尔类型,该类型只有两个值,true和false。主要用来做逻辑判断

特点:

1.隐式转换:true会转换成1,false会转换成0。

1 + true; // 2 true会被隐式转化为1

2.0; -0;0n;NaN; null; undefined;‘’,“”,``都会转换成false。

Null类型

空指针对象,只有一个值。null类型也是空的对象引用。

特点:

1.typeof检测, null 值时会返回 object。

typeof null; //object

2.undefined值派生自null值。出现场景:对象不存在时。

undefined==null; //true

Undefined类型

只有一个值,即undefined值。

特点:

1.使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。

var un;

console.log(un); //undefined

Symbol类型

Symbol 指的是独一无二的值,这是ES6新增的数据类型。每个通过 Symbol() 生成的值都是唯一的。

特点:

1.Symbol 类型永远不相等,即便创建的时候传入相同的值。

let symbol1 = Symbol();

let symbol2= Symbol();

console.log(symbol1==symbol2); //false

2.不支持语法:“new Symbol()”。

BigInt类型

Javascript 中的任意精度整数,可以安全存储和操作大整数。即使超出 Number 能够表示的安全整数范围。是 chrome 67中的新功能。

我们前面提到的number数据类型大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。ES2020 引入了一种新的数据类型 BigInt,来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

特点:

1.BigInt 类型的数据必须添加后缀n。

100; //number类型

100n;//Bigint类型

2.BigInt 与普通整数是两种值,它们之间并不相等。

100 == 100n; //false

3.BigInt 并不是一个构造函数,所以,不能使用 new BigInt() 的方式来构建实例。

引用数据类型

Object类型

我们将在JS中除了基本数据类型以外的都称为对象类型。里面包括我们常用的对象(Object)、数组(Array)、函数(Function),以及特殊类型正则(RegExp)和日期(Date)等…

ECMAjavascript中的对象其实就是一组数据和功能的集合。

特点:

引用数据类型是保存到堆内存中的,每创建⼀个新的对象,就会在堆内存中开辟出⼀个新的空间。

数据类型检测

typeof 操作符

typeof 操作符用于检测一个值的数据类型。

  • 所有的数据类型值,在计算机底层都是按照“64位”的二进制值进行存储的!
  • typeof是按照二进制值进行检测类型的二进制的前三位是零,认为是对象,然后再去看有么有实现call方法,如果实现了,返回function’,没有实现,则返回object’,除了function无法细分其他对象typeof [] // 输出 “object”
  • null是64个零 typeof null ->object’ 局限性
  • 检测未被声明的变量,值是’undefined’
typeof "Hello, World!" // 输出 "string"
typeof "" // 输出 "string"
typeof 42 // 输出 "number"
typeof NaN // 输出 "number"
typeof Infinity // 输出 "number"
typeof 10n //输出"bigint"
typeof true // 输出 "boolean"
typeof function() {} // 输出 "function"
typeof undefined // 输出 "undefined"
typeof {} // 输出 "object"
typeof [] // 输出 "object"
typeof null // 输出 "object"
typeof new Number()//输出 "object"

console.log(a)  //Uncaught ReferenceError:a is not defined
console.log(typeof a) //'undefined'
  • 应用场景:检测除null以外的其他原始值类型、笼统的检测是否为对象
if(obj!==null && /^(object|function)$/.test(typeof obj)){//obj是一个对象类型}

instanceof 操作符

instanceof可以用来细分对象,只能正确判断引用数据类型,而不能判断基本数据类型。instanceof运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的prototype属性

  1. 先检测 构造函数 是否拥有 Symbol.hasInstance 方法「ES6+之后,Function.prototype设置了Symbol.hasInstance这个方法,所以函数都具备这个属性」;
    如果有这个方法:构造函数Symbol.hasInstance返回的值就是我们要的结果!+ 我们正常情况下重写是无效的-> Array[Symbol.hasInstance]=function(){…}+但是基于class创建的自定义类,可以重写其Symbol.hasInstance方法
class MySpecialArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance) && instance.length > 0;
  }
}

console.log([] instanceof MySpecialArray); // false
console.log([1, 2, 3] instanceof MySpecialArray); // true
  1. 如果没有这个方法,则按照原型链进行查找:按照实例的proto 一直向上找,直到找到0biect.prototype为止,只要在原型链上.现了“构造函数.prototype",说明当前实例属于它,结果返回true;如果没找到,结果就是false;
console.log(9 instanceof Number); // false
console.log(true instanceof Boolean);   // false 
console.log('libo' instanceof String);  // false  
console.log([] instanceof Array);    // true
console.log([12] instanceof Object);    // true
console.log(function(){} instanceof Function);   // true
console.log({} instanceof Object);   // true
// 注意以下两组
console.log(typeof null); //object
console.log(null instanceof Object); //false !!!
console.log(typeof NaN);   //number
console.log(NaN instanceof Number);  //false !!!
  • 基于instanceof检测数据类型,其实“不是很靠谱”;
    无法检测原始值类型
    无法区分是否为“标准普通对象”
    一但原型链被重构,检测的结果是不准确
    真实项目中,偶尔用于初步检测是否为特殊对象,例如:检测是否为正则、日期对象等

使用constructor属性

constructor 属性返回一个指向创建了该对象原型的函数引用。需要注意的是,该属性的值是那个函数本身。例如:

function Fruit(){}
var a = new Fruit
a.constructor === Fruit    // true

constructor不适合用来判断变量类型。

其一,它是一个属性,非常容易被伪造:

var a = new Fruit
a.constructor = Array
a.constructor === Fruit    // false

其二,constructor指向的是最初创建当前对象的函数,是原型链最上层的那个方法:

function Apple(){}
Apple.prototype = new Fruit

function BadApple(){}
BadApple.prototype = new Apple

(new BadApple).constructor === Fruit   // true
Fruit.constructor === Function       // true

与instanceof类似,constructor只能用于检测引用对象,对基本数据类型无能为力。

4 console.log(([]).constructor === Array); //true
5 console.log((function() {}).constructor === Function);//true
6 console.log(({}).constructor === Object); //true

与instanceof不同的是,在访问基本数据类型的属性时,JavaScript会自动调用其构造函数来生成一个对象。例如:

(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true
// 相当于
(new Number(3)).constructor === Number
(new Boolean(true)).constructor === Boolean
(new String('abc')).constructor === String

这种将一个值类型转换为对象引用类型的机制在其他语言中也存在,称为装箱。

但在基本数据类型中,null和undefined调用constructor会抛出TypeError异常。

null.constructor         // TypeError!
undefined.constructor    // TypeError!

因为null是JavaScript原型链的起点,undefined是无效对象,都没有构造函数,也就不存在constructor属性。

Object.prototype.toString.call() 方法

这个方法可以检测绝大多数 JavaScript 数据类型,包括内置对象。

  • 内置构造函数的原型对象上,基本上都有tostring这个方法,基本都是用来把值转换为字符串的,除0biect.prototype.tostring外,它是用来检测数据类型的;
  • 只需要把0bject.prototype.toString执行,方法中的this是谁,就是检测谁的数据类型 返回结果“[object ?]"

这里是引用

一般是自己所属的构造函数
首先会看[value]值是否有 Symbol.tostringTag 属性,有这个属性,属性值是啥,检测出来的就是啥;如果没有这个属性,才一般是按照自己所属的构造函数返回!!
具备这个属性的值:
Math[symbol.tostringTagl:‘Math’
Promise.prototypelSymbol.toStringTag]:‘Promise’
Generator函数原型链上有
Set.prototype[Symbol.tostringTagl:‘set’
Set,prototype[Symbol.tostringTag]:'Map

  • 优势:基本上属于检测最准确、最全面的方式了!能够区分null、能够检测原始值类型、能够细分对象、即便重构原型对象检测也是准确的.,
  • 弊端:写起来比较长
Object.prototype.toString.call("Hello, World!") // 返回 "[object String]"
Object.prototype.toString.call(42) // 返回 "[object Number]"
Object.prototype.toString.call(true) // 返回 "[object Boolean]"
Object.prototype.toString.call({}) // 返回 "[object Object]"
Object.prototype.toString.call([]) // 返回 "[object Array]"
Object.prototype.toString.call(function() {}) // 返回 "[object Function]"
Object.prototype.toString.call(null) // 返回 "[object Null]"
Object.prototype.toString.call(undefined) // 返回 "[object Undefined]"
Object.prototype.toString.call(/^$/) // 返回 "[object RegExp]"
Object.prototype.toString.call(new Date()) // 返回 "[object Date]"
let toString = 0bject.prototype.toString;
class Fn {}
Fn.prototype[Symbol.tostringTag]='Fn';
let f= new Fn;
console.log(tostring.call(f));//[object Fn]”不设置之前是“[object 0bject]”

Array.isArray() 方法

用于检测一个对象是否是数组。

Array.isArray([]) // 返回 true
Array.isArray({}) // 返回 false

isNaN() 函数

用于检测一个值是否为 NaN(非数字值)。

isNaN("Hello") // 返回 true
isNaN(42) // 返回 false

Number.isNaN() 方法

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

Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true

// 下面这几个如果使用全局的 isNaN() 时,会返回 true。
Number.isNaN("NaN");      // false,字符串 "NaN" 不会被隐式转换成数字 NaN。
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false

// 下面的都返回 false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");

一些封装方法

  • 检测类型通用方法
 function getType(obj) {
	let str = Object.prototype.toString.call(obj);
	let type = str.match(/\[object (\w+)\]/);
	return type[1].toLowerCase();
}
  • 检测是否为数组或类数组
function isArrayOrArrayLike(obj) {
  // 首先检查是否是数组
  if (Array.isArray(obj)) {
    return true;
  }

  // 检查是否是类数组对象
  // 类数组对象应该有 length 属性,且 length 为非负整数
  // 类数组对象的 length 属性应该是有限数值
  const length = obj.length;
  const isObject = typeof obj === 'object';
  const isFunction = typeof obj === 'function';
  const hasLengthProperty = typeof length === 'number' && length >= 0 && length === parseInt(length, 10) && isFinite(length);

  // 类数组对象不应该是函数,因为函数也有 length 属性(表示函数参数的个数)
  if (isObject && !isFunction && hasLengthProperty) {
    // 检查是否具有按索引存储的元素
    if (length === 0 || (length > 0 && (length - 1) in obj)) {
      return true;
    }
  }

  return false;
}
  • 检测是否为标准版普通对象
function isPlainObject(obj) {
  // 首先确保不是 null 和非对象类型
  if (typeof obj !== 'object' || obj === null) return false;

  // 获取对象的直接原型
  const proto = Object.getPrototypeOf(obj);
  // 检查对象的直接原型是否等于 Object.prototype
  return proto === Object.prototype||proto===null;//匹配Object.create(null)
}
  • 检测是否为空对象
//for...in
function isEmptyObject(obj){
  for key in obj{
	return false
  }
  return true;
}
//for...in 循环会检测私有的以及原型链上的可枚举属性,非常消耗性能,不包括Symbol类型的属性
for key in obj {
	if (!obj.hasOwnProperty(key)) break;//排除原型链上的属性
	console.log(key); 
}


function isEmptyObject(obj) {
  // 首先确保是一个对象
  if (typeof obj !== 'object' || obj === null) return false;

  // 检查对象自身的可枚举属性的键名数组长度是否为 0
  return Object.keys(obj).length === 0;
}

//0bject.keys(obj)获取对象“非Symbol类型”、“可枚举的”、“私有属性” 「结果:包含属性名的数组」
// 0bject.get0wnPropertyNames(obj)获取对象“非Symbol类型”、“私有属性”,不论是否是可枚举的
// 0bject.get0wnPropertySymbols(obj)获取对象“Symbol类型”、“私有属性”,不论是否是可枚举的
//ES6新增的Reflect对象 Reflect.ownKeys(obj)获取所有私有属性无论是否可枚举无论是否Symbol

数据类型转换

1. 其他类型转换为Number

Number()规则

//字符串转换为数字:空字符串变为0,如果出现任何非有效数字字符,结果都是NaN
console.log(Number(""))//0
console.log(Number("12"))//12
console.log(Number("12a"))//NaN
console.log(Number("abc"))//NaN
console.log(Number(true))//1
console.log(Number(false))//0
console.log(Number(null))//0
console.log(Number(undefined))//NaN
console.log(Number(Symbol()))//Uncaught TypeError: Cannot convert a Symbol value to a number
//BigInt去除“n”(超过安全数字的,会按照科学计数法处理)
console.log(Number(12n))//12

/**把对象转换为数字:
            + 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法
            + 再调用对象的 valueOf 获取原始值,如果获取的值不是原始值
            + 再调用对象的 toString 把其变为字符串
            + 最后再把字符串基于Number方法转换为数字
**/
console.log(Number([]))//0
console.log(Number([12]))//12
console.log(Number([1,2]))//NaN
console.log(Number({}))//NaN
console.log(Number(new String(12)))//12
console.log(Number(new Number(12)));//12

let time = new Date();
console.log(Number(time));
//首先检测 Symbol.toPrimitive 有没有,结果: 有,而且是个函数time[Symbol.toPrimitive]('number')
let arr = [10];
console. log(Number(arr));
//首先 arr[Symbol.toPrimitive] -> undefined其次 arr.valueof() 获取原始值 -> [10] 不是原始值再此 arr.tostring() 转换为字符串'10'最后再把字符串'10'转化为数字 -> 10
let num = new Number(10)
console log(Number(num));
//首先 num[Symbol.toPrimitive] -> undefined其次 num.value0f() -> 10
  • 隐式转换:浏览器默认使用Number()转换
  1. 数学运算
console.log(10-"2") //8
//相当于10-Number("2")

注: JavaScript 中的加法运算符(+)和减法运算符(-)在不同的操作数类型下会有不同的行为。

当至少一个操作数是字符串类型时,加法运算符会执行字符串拼接操作,而不是数值相加。所以,10 + ‘2’ 会将数字 10转换为字符串,然后进行字符串拼接,结果是字符串 ‘102’。对于其他类型的操作数(如布尔值、对象等),加法运算符会尝试将它们转换为数值类型,然后执行数值相加操作。例如,true + 1 的结果是数值2,因为 true 在数值上下文中被转换为 1。

而减法运算符只用于数值计算,所以当一个操作数是字符串类型时,会尝试将字符串转换为数值进行计算。在这种情况下,字符串 ‘2’ 会被转换为数值2,然后进行减法运算,结果是数值 8。
console.log(10 + ‘2’); // 输出 “102”,字符串拼接
console.log(10 - ‘2’); // 输出 8,数值计算 console.log(‘10’ + 2); // 输出 “102”,字符串拼接
console.log(‘10’ - 2); // 输出 8,数值计算

注: ‘+’出现在一个值左边,转换为数字
let num=‘10’;console.log(+num);//10

let i =‘10’; i++; console.log(i) //11

i = ‘10’ i += 1 console.log(i) //‘101’

console.log(10+[10]) //‘1010’
console.log(10+[1,10]) // ‘101,10’

  1. isNaN检测
console.log(isNaN('2')) //false
//相当于isNaN(Number('2'))
console.log(isNaN(null)) //false
//相当于isNaN(Number(null))  Number(null)=0
console.log(isNaN(undefined)) //true
//相当于isNaN(Number(undefined))  Number(undefined)=NaN
  1. ==比较
console.log(12=='12')//true
  • 显式转换:自己手动使用Number()转换

parseInt/parseFloat

规则:
parseInt( [val],[radix])

  • [val]必须是字符串,如果不是,要先隐式转换为字符串 string([val])
  • [radix]进制
  • 如果不写,或者写零: 默认是10 (特殊情况: 如果字符串是以0x开始的,默认值是16)
  • 有效范围: 2~36之间 (如果不在这个区间,结果直接是NaN)
  • 从[val]字符串左侧第一个字符开始查找,查找出符合[radix]进制的值(遇到不符合的则结束查找,不论后面是否还有符合的),把找到的内容,按照[radix]进制,转换为10进制!!
    console.log(parseInt(‘10102px13’,2)): //10
    // 找到符合二进制的1010
    把这个二进制的值转换为十进制“按权展开求和”

parseFloat(val)
没有进制可以多识别一个小数点

let arr = [27.20"0013",14px',123];
arr = arr.map(parseInt);
console.log(arr); //[27,NaN,1,1,27]
//每次迭代把(item,index)传给parseInt()
//parseInt(27.2,0) -> parseInt('27.2',0)
//'27' 把其当做10进制转换为10进制 => 27
//parseInt(0,1) //NaN 1不在进制范围内
//parseInt('0013',2)//'001'当做2进制转换为10进制1
//parseInt('14px',3)'1'当做3进制转换为10进制1*3^0 -> 1
//parseInt(123,4) -> parseInt('123',4)123当做4进制转换为10进制1*4^2+2*4^1+3*4^0 -> 16+8+3 => 27

console.log(parseInt(null)) //NaN
console.log(parseInt(undefined)) //NaN
console.log(parseInt(0013,2)) //3
//JS中遇到以0开始“数字”,会默认把其按照8进制转为10进制,然后在进行其他操作parseInt(0013,2)
//先8转10 = 11parseInt('11',2)'11'在2转10-> 2 + 1 -> 3

2. 其他类型转换为String

console.log(String(""))//''
console.log(String(null))//'null'
console.log(String(undefined))//'undefined'
console.log(String(true))//'true'
console.log(String(false))//'false'
console.log(String(Symbol()))//'Symbol()'
console.log(String(12n))//'12'
console.log(String([]))//''
console.log(String([12]))//'12'
console.log(String([1,2]))//'1,2'
console.log(String({}))//'[object Object]'
console.log(String(new String(12)))//'12'
console.log(String(new Number(12)));//'12'

'+'字符串拼接

出现左右“两边”,其中一边是字符串,或者是某些对象:会以字符串拼接的规则处理

let result = 100 + true+ 21,2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result);
//100+1+21.2+0=122.2
//122.2+undedined=NaN
//NaN+'Tencent'='NaNTencent'
//'NaNTencent'+[]='NaNTencent'
//...'NaNTencentnull9false'
console.log(10 + "10"); //"1010"
console.log(10 + new Number(10)); //20
// new Number(10)[Symbol.toPrimitive] -> undefined
// new Number(10).valueof() -> 10
// 10+10 => 20
console.log(10 + new Date()); //'10Mon Nov 06 2023 13:26:40 GMT+0800 (中国标准时间)'
// new Date()[Symboltoprimitivel('default') -> 'Mon Nov 06 2023 13:26:40 GMT+0800 (中国标准时间)'
console.log(10 + [10]); //'1010"
//[10][Symbol.toPrimitive] -> undefined`在这里插入代码片`
//[10].valueof() -> [10]
// [10].toString() ->10' => 10+'10'


console.log([]+[]);//‘’
console.log({}+{});//[object Object][object Object]
console.log([]+{});//[object Object]
console.log({}+[]);//[object Object]

String()和.toString()的区别

String() 是 JavaScript 的全局函数;
toString() 是 Object 原型的一个方法。

String() 可以将 null, undefined转化为字符串;而 toString()不可以。

String(null) // "null" 
String(undefined) // "undefined"

null.toString() // Uncaught TypeError: Cannot read property 'toString' of null 
undefined.toString() // Uncaught TypeError: Cannot read property 'toString' of undefined 

包装对象:为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,(装箱操作)以便调用方法属性,而在使用完毕后将会销毁该对象。

JavaScript也可以使用Object函数显式地将基本类型转换为包装类型:
var a = ‘abc’
Object(a) // String {“abc”}
也可以使用valueOf方法将包装类型倒转成基本类型:(拆箱)

var a = ‘abc’
var b = Object(a)
var c = b.valueOf() // ‘abc’

(1)创建String类型的一个实例;
(2)在实例上调用指定的方法;
(3)销毁这个实例;
var str = ‘abc’;
str.length // 3
// 等同于 var strObj = new String(str)
// String {
// 0: “a”,
1: “b”,
2: “c”,
length: 3,
[[PrimitiveValue]]: “abc”
// }
strObj.length // 3
strObj = null

而我们又可以知道String是Function的实例对象,Function是自身的实例对象,Function的原型对象的proto最终指向 Object,所以 String 的toString方法是继承的 Object,然后重新改造的
String.proto === Function.prototype
// true
Function.proto === Function.prototype
// true
Function.prototype.proto === Object.prototype
// true

由于 null & undefined 作为一种单独的数据类型,所以他们没有继承 Object.prototype,因此不存在
toString()的方法,报错 Uncaught TypeError: Cannot read property ‘toString’
of undefined;而 String 作为一个全局的方法,是都可以获取的。

null.proto // Uncaught TypeError: Cannot read property ‘proto’ of null
typeof null // object 尽管typeof null === ‘object’
为true,但是null 并不存在 proto属性,自然不会继承 Object 的原型方法。

不同进制之间的转换
String()只支持转为普通字符串,不支持转为相应进制的字符串;
而Number.toString(radix)可以将一个Number对象转换对应进制的字符串;其中radix可选,表示数字几基数。

// 将十进制转化为二进制 
var num = 2; 
num.toString(num, 2); // "10"

(1)1.toString()报错的问题
整数的字面量形式是没法直接自动装箱的,因为整数后面的第一个小数点,会被认为连接小数位,而toString不是数字,所以报语法错误。

1.toString() // Uncaught SyntaxError: Invalid or unexpected token 
//解决办法
(1).toString(); // 使用括号括起来
1 .toString(); // 整数后面加个空格
1..toString(); // 使用两个点
 
var num = 1;//使用变量名
num.toString(); 
//这是因为 JavaScript引擎在解释代码时,对于1.toString()认为第一个.是浮点符号;第二个为属性访问的语法,
//所以1..toString()正常;而 (1).toString() 排除了小数点的影响所以也为正常; 

(2)纯小数的小数点后面有连续6个或6个以上的0时,小数将用e表示法输出;

var num = 0.000006; //小数点后面有5个“0” 
console.log(num.toString()); //"0.000006"

num = 0.0000006 ;//小数点后面有6个“0” 
console.log(num.toString()); //"6e-7" 

(3)浮点数整数部分的位数大于21时,输出时采用e表示法:

var num = 1234567890123456789012; 
console.log(num.toString()); //"1.2345678901234568e+21"

3. 其他类型转换为Boolean

除了“0/NaN/空字符串/null/undefined”五个值是false,其余都是true
Boolean([val]) 或者 !/!!

console.log(Boolean(""))//false
console.log(Boolean(0))//false
console.log(Boolean(-0))//false
console.log(Boolean(0n))//false
console.log(Boolean(NaN))//false
console.log(Boolean(null))//false
console.log(Boolean(undefined))//false
console.log(Boolean(Symbol()))//true
console.log(Boolean([]))//true
console.log(Boolean({}))//true
console.log(![] == false); //true

4.“==”比较时候的相互转换规则

“==”相等,两边数据类型不同,需要先转为相同类型,然后再进行比较

  1. 对象==字符串
    对象转字符串「Symbol.toPrimitive -> valueOf -> toString」
console.log([22]=='22');//true
console.log({a: 1, b: 2}=='{a: 1, b: 2}')//false '[object Object]'!='{a: 1, b: 2}'
  1. null只==null或undefined和其他都不相等
console.log(null==undefined);//true
console.log(null===undefined);//false
console.log(null=='null');//false
  1. 对象==对象
    实际上是比较它们的引用是否相等。如果两个对象的引用指向同一个内存地址,那么的结果就是true,否则结果就是false。
let obj1 = {a: 1, b: 2};
let obj2 = {a: 1, b: 2};
let obj3 = obj1;

console.log(obj1 == obj2); // 输出 false,因为 obj1 和 obj2 是两个不同的对象
console.log(obj1 == obj3); // 输出 true,因为 obj1 和 obj3 的引用指向同一个内存地址
  1. NaN
console.log(NaN==NaN) //false
console.log(Object.is(NaN,NaN))//true
  1. 除了以上情况,只要两边类型不一致,剩下的都是转换为数字,然后再进行比较的

“===”绝对相等,如果两边类型不同,则直接是false,不会转换数据类型「推荐」

console.log([] == false); //true 两边都转成数字0
console.log(![] == false);//![]先bool转换false

Object在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0,NaN和NaN。

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

参考文章:
https://baijiahao.baidu.com/s?id=1773262321759146083&wfr=spider&for=pc

https://www.jianshu.com/p/ef898304ebdf

一道题
如何实现输出OK
var a;
if(a1&&a2&&a==3){
console.log(‘OK’)
}

//利用==比较会转换数据类型,而对象转数字会经历一个详细步骤:[Symbol.toPrimitive-->valueOf-->toString]重写步骤中的方法

//方法一
var a = {
	i:0,
	[Symbol.toPrimitive](){
		//this->a
		return ++this.i;
	}
}
if(a==1&&a==2&&a==3){
	console.log('OK')
}

//方法二
var a = [1,2,3]
a.toString = a.shift;
if(a==1&&a==2&&a==3){
	console.log('OK')
}

//全局上下文中获取a的值先看VO中有没有,没有再看GO(window)中有没有
//方法三
var i =0;
Object.defineProperty(window,'a'){
	get(){
		return ++i;
	}
}
if(a===1&&a===2&&a===3){
	console.log('ok')
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值