1.JS-基础(数据类型、比较)

390 阅读8分钟

★ 数据类型

一共8种

1:【对象类型】Object

7:【原始数据类型】null undefined boolean number string symbol bigint,

其中两个symbol bigint是es6中新增的

  • Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
  • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。

Symbol

  • 场景1:扩展对象,属性名冲突问题
// // a.js =========================================
// cache['a_foo'] = Math.random()

// // b.js =========================================
// cache['b_foo'] = '123'
// console.log(cache)
  • 两个 Symbol 永远不会相等

  • Symbol 描述文本

// console.log(Symbol('foo'))
  • 使用 Symbol 为对象添加用不重复的键
// const obj = {}
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'
// console.log(obj)
  • 也可以在计算属性名中使用
const obj = {
 [Symbol()]: 123
}
console.log(obj)
  • Symbol 模拟实现私有成员
// a.js  只对外暴露 person ===============================
const name = Symbol()
const person = {
  [name]: 'zce',
  say () {
    console.log(this[name])
  }
}

// b.js =======================================
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
person[Symbol()] // 啥也没有
person.say()  // zce

★ 原始数据类型、引用数据类型的区别

1.函数调用

原始类型存储的都是值,是没有函数可以调用的,比如 undefined.toString()会报错的

image.png

'1'.toString() 虽然看着是可以使用的。但是'1' 已经被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数。

2.存储位置

原始数据类型 【栈】

引用数据类型 【堆】


★ 栈、堆的区别

1、存储数据

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

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

2、数据结构中:

  • 在数据结构中,栈中数据的存取方式为先进后出。
  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。

3、在操作系统中,内存被分为栈区和堆区:

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

★ 数据类型检测的方式有哪些

1、typeof

typeof 属于一元运算符,运算符用来判断给定对象的类型。

用法: typeof XXX

返回值 类型

console.log(typeof 2);               // number
console.log(typeof true);            // boolean
console.log(typeof 'str');           // string
console.log(typeof function(){});    // function
console.log(typeof undefined);       // undefined
console.log(typeof null);            // object
console.log(typeof []);              // object    
console.log(typeof {});              // object

其中数组、对象、null都会被判断为object,其他判断都正确。

2、instanceof

instanceof 是关系运算符,用来判断一个对象是否是另一个对象的实例。

用法: XXX instanceof 属性

返回值 Boolean值

instanceof只能正确判断引用数据类型,而不能判断基本数据类型。

其内部运行机制是判断在其原型链中能否找到该类型的原型

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false 
 
console.log([] instanceof Array);                    // true
console.log([] instanceof Object);                   // true
console.log(function(){} instanceof Function);       // true
console.log(function(){} instanceof Object);         // true
console.log({} instanceof Object);                   // true

3、constructor

console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log(([]).constructor === Object);  // !!!false!!
console.log((function() {}).constructor === Function); // true
console.log((function() {}).constructor === Object);  //!!! false!!
console.log(({}).constructor === Object); // true

constructor的两个作用:

1、判断数据的类型

2、对象的实例可以通过constructor对象访问构造函数

所以需要注意的是,一旦手动改变了原型,那通过constructor来判断数据类型就不准确了

比如:

function Fn(){};
 
Fn.prototype = new Array();
 
var f = new Fn();
 
console.log(f.constructor===Fn);    // false
console.log(f.constructor===Array); // true

4、Object.prototype.toString.call()

使用 Object 对象的原型方法 toString 来判断数据类型:

var a = Object.prototype.toString;
 
console.log(a.call(2));       //[object Number]
console.log(a.call(true));      //[object Boolean]
console.log(a.call('str'));     //[object String]
console.log(a.call([]));       //[object Array]
console.log(a.call(function(){}));   //[object Function]
console.log(a.call({}));       //[object Object]
console.log(a.call(undefined));   //[object Object]
console.log(a.call(null));      //[object Null]

//toString()的区别

console.log([1,2,3].toString())   // '1,2,3'
console.log(console.log.toString())  // 'function log() { [native code] }'


let fn = function(){
    console.log('aaa')
}

console.log(fn.toString())   //  function(){console.log('aaa')}

obj.toString()和Object.prototype.toString.call(obj)的区别

toString是Object的原型方法,

Array、function等类型作为Object的实例,都重写了toString方法

根据原型链的知识,不同对象调用的是对应的重写之后的toString方法

function类型返回内容为函数体的字符串

Array类型返回元素组成的字符串

所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型 因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。


★ 判断数组的方式有哪些

1、通过instanceof做判断

obj instanceof Array

2、通过Object.prototype.toString.call()做判断

Object.prototype.toString.call(obj).slice(8,-1) === 'Array';

3、通过ES6的Array.isArray()做判断

Array.isArrray(obj);

4、通过原型链做判断

obj.__proto__ === Array.prototype;

5、通过Array.prototype.isPrototypeOf【不熟悉】

Array.prototype.isPrototypeOf(obj)

★ isPrototypeOf

用于检测一个对象是否存在于另一个对象的原型链中


★ 类型转换

首先我们要知道,在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串
原始值转换目标结果
numberBoolean()除了0 -0 NaN 都为true
string除了空串都为true
null undefinedfalse
引用类型true
numberstring5=>'5'
Boolean、函数、symbol'true'
数组[1,2]=>'1,2'
对象'object Object'
string数字'1'=>1,'a'=>NaN
数组空数组=> 0;一个元素且为数字的转数字;其余NaN
null0
除了数组的引用类型NaN
Symbol报错

转Boolean()

undefined, null, false, NaN, '', 0, -0,为false

其余都为 true,包括所有对象。 注意转换是用Boolean() 、String()、 Number()方法转换的时候,用==比较的时候又是另外的结果

image.png

image.png


★ 四则运算符

加法运算符不同于其他几个运算符,它有以下几个特点:

  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"

如果你对于答案有疑问的话,请看解析:

  • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
  • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
  • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 1,2,3,得到结果 41,2,3

另外对于加法还需要注意这个表达式 'a' + + 'b'

'a' + + 'b' // -> "aNaN"

因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN

★ 比较运算符

  1. 如果是对象,就通过 toPrimitive 转换对象
  2. 如果是字符串,就通过 unicode 字符索引来比较
let a = {
  valueOf() {
    return 0
  },
  toString() {
    return '1'
  }
}
a > -1 // true

在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。

'b'>'a' // true

【比较运算的时候,如果是对象会转换成基本类型,通过toPrimitive()方法】 【Primitive ['prɪmətɪv'] 原始的】

★ toPrimitive

应用场景: 在JavaScript中,如果想要将对象转换成基本类型时,也就是所谓的拆箱时,会调用toPrimitive()。

函数结构: toPrimitive(input,preferedType?)

参数解释:

input是输入的值,即要转换的对象,必选;

preferedType是期望转换的基本类型,他可以是字符串,也可以是数字。选填,默认为number

执行过程:

如果转换的类型是number,会执行以下步骤:

  1. 如果input是原始值,直接返回这个值;

  2. 否则,如果input是对象,调用input.valueOf(),如果结果是原始值,返回结果;

  3. 否则,调用input.toString()。如果结果是原始值,返回结果;

  4. 否则,抛出错误。

如果转换的类型是String,2和3会交换执行,即先执行toString()方法。

你也可以省略preferedType,此时,日期会被认为是字符串,而其他的值会被当做Number。

总结

如果转换的类型是number,先执行valueOf(),如果没有返回原始值,再执行toString()

如果转换的类型是String,先执行toString(),如果没有返回原始值,再执行valueOf()


★ == vs ===

对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换

image.png

对于 === 来说就简单多了,就是判断两者类型和值是否相同。