JavaScript基础篇(一)数据类型的种类、判断以及转换

数据的类型的种类

  • 基本数据类型:undefinednullbooleannumberstringsymbol ( ES6 新增,表示独一无二的值) 和 BigInt ( ES10 新增)
  • 引用数据类型:Object ( Object 本质上是由一组无序的键值对组成的)。里面包含 functionarraydate 等。JavaScript 不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。

基本类型和引用类型中监听赋值变化

  • 基本类型中值定义和改变
let a = 100
let b = a
a = 200
console.log(a) // 200
console.log(b) // 100
  • 引用类型中值定义和改变
let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21

思考:为什么基本类型中 a 的值变了,b 的值确不变;而引用类型中 b 的值变了,a 的值会跟着变呢

  • 基本类型中值都是存储在栈中,而在栈中,每一个 key 都对应一个 value,如下图:(图一为 a 和 b 的初始值都是 100,图二为 a 修改之后为 200,而 b 仍然是100)。
    图一.png
  • 图二.png
  • 而引用类型的值都是存储在堆中,我们定义的 a 和 b 这两个变量可以理解为存储在栈中,但是因为它们的值属于引用类型,计算机会将这些值放在堆中,也就是 a 和 b 两个变量其实指向的 value 都是堆中的同一个内存地址。如下图:


    变量 a 赋值,value 指向堆中的内存地址1.png

    a 和 b 同时存在,都指向堆中的内存地址1.png

    改变 b 的值,即改变堆中内存地址1的值,所以 a 的值也跟着改变.png

总结:基础类型的值存储在栈中,引用类型的值存储在堆中,栈中存储的值因为都比较小、易处理,所以值的存储是分开的;而堆中存储的值可能是一个很庞大的值(如很长很长的 json ),从性能的角度考虑,浅拷贝时就会出现两个变量共用一个堆的情况。


typeof 运算符进行数据类型的判断

  • 识别所有基础类型
let a;                   typeof a // 'undefined'
const str = 'abc'        typeof str // 'string'
const n = 100            typeof n // 'number'
const b = true           typeof b // 'boolean'
const s = Symbol('s')    typeof s // 'symbol'
  • 识别函数
typeof console.log       // function
typeof function() {}     // function
  • 可以判断该类型是否是引用类型(不可再向下细分,即不能区分该引用类型是否为 array 或者 object)
typeof null             // object
typeof ['a', 'b']      // object
typeof {x: 100}       // object

延伸思考:知道堆和栈的概念以及了解 typeof 的用法之后思考什么是浅拷贝?什么是深拷贝?如何手写一个深拷贝函数?

  • 我们上面的栗子中知道对象的直接重新赋值其实共用的是一个堆中的内存地址,改了 b 之后 a 对象中的相应的属性也会改变,这里我们就可以理解为 b 是通过浅拷贝 a 生成的。那么我们想 b 有自己的内存地址,a 和 b 的值在堆中完全独立分开,互不影响就需要用到深拷贝,这也是深拷贝的基本概念。相信聪明的小伙伴应该已经理解两者之间的差别了吧!那么就让我们来手写一个实现深拷贝的函数吧:
/**
   * 深拷贝 
   * @params {Object} obj 要拷贝的对象
   */
  function deepClone(obj = {}) {
    // 如果传入的 obj 不是 object 类型直接返回
    if (typeof obj !== 'object' || typeof obj == null) {
      return
    }
    // 初始化返回结果
    let result
    // 通过 instanceof 判断传入的 obj 是数组类型还是对象类型
    if (obj instanceof Array) {
      result = []
    } else {
      result = {}
    }
    // 遍历传入的 obj
    for (let key in obj) {
      // 保证 key 不是原型的属性
      if (obj.hasOwnProperty(key)) {
        // 递归调用,保证如果这个对象中有嵌套对象将嵌套对象传入当前函数继续执行
        result[key] = deepClone(obj[key])
      }
    }
    // 返回最后得到的结果
    return result
}

上面出现了一个 instanceofhasOwnProperty,我们前面说了 typeof 可以用来判断基础类型,而 instanceof 可以用来判断引用类型,直白点就是它可以判断你是数组还是对象;而 hasOwnProperty 在很多轮子的源码中其实都会看到,主要是判断该对象的属性是自身属性还是通过原型 prototype 添加的属性,我们深拷贝的话是不拷贝对象通过原型添加的属性和方法的。最后就是递归继续遍历拷贝对象中的子对象。

// 验证我们写的函数
let obj1 = {
  age: 20,
  name: 'zhangsan',
  address: {
    city: 'wuhan'
  },
  arr: ['a', 'b', 'c']
}
let obj2 = deepClone(obj1)
obj2.address.city = 'beijing'
console.log(obj1.address.city, obj2.address.city) // wuhan beijing 互不影响

instanceof 进行数据类型的判断

instanceof 其内部机制是通过原型链来判断的,如果对原型和原型链不理解可能比较绕,下一部分会去记录原型和原型链,到时候再来提。这里先总结:

  • instanceof 左为实例,右为构造函数。例如 A instanceof B,用来判断 A 是否为 B 的实例,返回 boolean 值。instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
  • instanceof 可以精准判断引用数据类型 ArrayFunctionObject,而基本数据类型不能被 instanceof 精准判断,因为它本身不是一个实例对象
console.log(2 instanceof Number);                    // false
console.log(new Number(2) instanceof Number)         // true
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log(Symbol('protein') instanceof Symbol)     // false 不支持语法:"new Symbol()"
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                  // true
new Date() instanceof Date;                          // true
new RegExp() instanceof RegExp                      // true  
null instanceof Null                               // 报错
undefined instanceof undefined                    // 报 错

Object.prototype.toString.call()

好吧,摊牌了!前面介绍的两种感觉都不是很满意对吧,王炸要来了。使用 Object 对象的原型方法 toString,使用 call 改变 this 指向

const 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(Symbol()))       // [object Symbol]
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 Undefined]
console.log(a.call(null));          // [object Null]
console.log(a.call(new Date()))     // [object Date]
console.log(a.call(new Error()))    // [object Error]
console.log(a.call(new RegExp()))   // [object RegExp]

思考:为什么这样就能区分呢?

追根溯源我们还是要先了解 toString 方法 的用法,toString 方法返回反映这个对象的字符串,如下栗子:

console.log("jerry".toString());//jerry
console.log((1).toString());//1
console.log([1,2].toString());//1,2
console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
console.log(function(){}.toString());//function (){}
console.log(null.toString());//error
console.log(undefined.toString());//error

那么新的问题来了:同样是检测对象 obj 调用 toString 方法,obj.toString() 的结果和Object.prototype.toString.call(obj) 的结果不一样,这是为什么?

这是因为 toStringObject 的原型方法,而 ArrayFunction 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(Function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串.....),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 上原型 toString 方法。


数据类型的转换

字符串转 Number
// 传统写法
var a = '1'
console.log(typeof a)
console.log(typeof Number(a)) // 普通写法
console.log(typeof +a) // 高端写法
Number 转字符串
var a = 1
console.log(typeof a.toString())
快速转 Boolean
var a = 0
console.log(typeof a)
console.log(typeof Boolean(a)) // 普通写法
console.log(typeof !!a) // 高端写法
console.log(!!a) // false
跟着(抄袭)大佬整理一份类型转换表:
原始值 转换目标 结果
number Boolean 除了 0、-0、NaN 都为 true
string Boolean 除了空字符串 ' ' 都为 true
undefined、null Boolean false
引用类型 Boolean true
number string 6 => '6'
Boolean、函数、Symbol string 'true'
数组 string [1, 2] => '1, 2'
对象 string '[ object, Object ]'
string number '6' => 6, 'p' => NaN
数组 number [] => 0, [ '6' ] => 6, 其他都是 NaN
非数组的引用类型 number NaN
null number 0
Symbol number 抛错
== 和 === 运算符

思考:为啥会有 ===== 呢,啥时候用 ==,啥时候用 ===

来个栗子瞅瞅吧:

100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true

可以看出 == 运算符对类型判断非常不严谨,不推荐使用,日常开发中除了 ==null 之外其余情况一律都用 === 。至于为啥 ==null 可以用呢,你咋这么多为什么?算了,你长得帅你有理,看如下栗子:

const obj = {x: 100}
if (obj.a == null) {}
// 相当于
if(obj.a === null || obj.a === undefined){}
四则运算符
  • 加法 + 运算符的特点
    运算中其中一方为字符串,那么就会把另一方也转化成字符串,如果一方不是字符串或者数字,那么会将他转化成数字或者字符串。
console.log(1 + '1'); // 11
console.log(true + true); // 2
console.log(4 + [1, 2, 3]); // 41, 2, 3
console.log('a' + + 'b'); // aNaN 因为 + 'b' 为 NaN
  • 非加法运算符来说只要其中一方不是数字就会被转化成数字,说实话感觉这个基本用的不多,可能更多出现在面试题里...
console.log(2 * '1'); // 2
console.log(2 * []); // 0
console.log(2 * ['2']); // 4
console.log(2 * [1, 2]); // NaN

本文部分内容参考于掘金大佬的文章:https://www.juejin.im/post/6844904201735110669
如果有理解错误或不足的地方,欢迎大家提出修改意见,此整理仅供学习参考!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341