JavaScript学习笔记(八) 数据类型

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: JavaScript学习笔记(八) 数据类型

1、数据类型


(1)六种数据类型


JavaScript 存在六种数据类型,分别是 Number,String,Boolean,Null,Undefined 和 Object


除了 Object 是引用类型之外,其余都是原始类型(又称基本类型),其中 Null 和 Undefined 是比较特别的两个


(2)内存模型


当一个方法执行时,会建立一个内存栈,这个方法中定义的变量都会放入栈中,方法调用完成,栈随即销毁


栈中存放的是原始类型的值以及引用类型的引用变量,引用变量的值指向堆内存中的对象地址


当对象被创建时,引用变量储存在栈中,对象本身储存在堆中,也就是说对象不会随着方法调用结束而销毁


实际上对象的销毁由垃圾回收机制决定,只有当对象没有被任何引用变量引用时,才会被销毁


(3)按值传递


在 JavaScript 中所有的函数参数都是按值传递的,但是由于内存模型的原因,会出现一些有趣的行为


  • 当我们在函数中修改基本变量时,不会修改传入的值
  • 当我们在函数中修改引用变量时,将会修改传入的值(根据内存模型的解释,想一想是为什么)

let number = 123 // 基本类型
let object = { // 引用类型
    name: 'Steve',
    age: 18
}
function changeNumber(num) {
    num = 456
}
function changeObject(obj) {
    obj.age = 20
}
changeNumber(number)
changeObject(object)
console.log(number)
console.log(object.age)
/*
 * 执行结果:
 * 123
 * 20
**/


2、类型检测


(1)typeof


用于检测一个变量的类型,需要注意的是,对于 null 和 array 类型的变量,typeof 返回 object


另外,typeof 对于几乎所有类型的对象都会返回 object,也就是说无法用 typeof 准确判断一个对象的类型


let a = 0
let b = 'hello'
let c = true
let d = null
let e = undefined
let f = {}
let g = []
let h = function(){}
console.log('a: ' + typeof a)
console.log('b: ' + typeof b)
console.log('c: ' + typeof c)
console.log('d: ' + typeof d)
console.log('e: ' + typeof e)
console.log('f: ' + typeof f)
console.log('g: ' + typeof g)
console.log('h: ' + typeof h)
/*
 * 执行结果:
 * a: number
 * b: string
 * c: boolean
 * d: object
 * e: undefined
 * f: object
 * g: object
 * h: function
**/


(2)Object.prototype.toString.call()


用于检测一个变量的类型,可以解决 typeof 不能检测 null 和 array 的问题

let a = 0
let b = 'hello'
let c = true
let d = null
let e = undefined
let f = {}
let g = []
let h = function(){}
function typeOf(value) {
    return Object.prototype.toString.call(value).slice(8, -1)
}
console.log('a: ' + typeOf(a))
console.log('b: ' + typeOf(b))
console.log('c: ' + typeOf(c))
console.log('d: ' + typeOf(d))
console.log('e: ' + typeOf(e))
console.log('f: ' + typeOf(f))
console.log('g: ' + typeOf(g))
console.log('h: ' + typeOf(h))
/*
 * 执行结果:
 * a: Number
 * b: String
 * c: Boolean
 * d: Null
 * e: Undefined
 * f: Object
 * g: Array
 * h: Function
**/


(3)instanceof


用于判断某个实例是否属于某种类型,可以解决 typeof 不能准确判断对象类型的问题

var Message = function(descrition) { // 构造函数
    this.detail = descrition
}
let message = new Message('Hello')
let object = new Object()
console.log(message instanceof Message)
console.log(message instanceof Object)
console.log(object instanceof Message)
console.log(object instanceof Object)
console.log(Message instanceof Object)
/*
 * 执行结果:
 * true
 * true
 * false
 * true
 * true
**/


instanceof 的原理是判断右边对象的原型对象是否存在于左边对象的原型链上

明白了原理后,我们也能自己实现一个 instanceof

function myInstanceOf(left, right) {
    let lValue = left.__proto__
    let rValue = right.prototype
    while (true) {
        if (lValue === null) return false
        if (lValue === rValue) return true
        lValue = lValue.__proto__
    }
}


3、类型转换


(1)强制类型转换


① String -> Number:parseInt(string[, radix]) & parseFloat(string)


parseInt 用于将字符串解析为整数,可以指定基数;parseFloat 用于将字符串解析为浮点数


两者的原理大致相同,都是从第一个非空格字符开始解析,直至字符串末尾或者遇到一个无效的数字字符


若解析成功,则返回数字;若第一个非空格字符不是数字或符号,则返回 NaN


let a = parseInt('   123abc')
let b = parseFloat(' 123.456abc')
console.log(a)
console.log(b)
/*
 * 执行结果:
 * 123
 * 123.456
**/


② Any -> Number:Number()


转换规则如下:

转换规则
Number 直接输出
String 若字符串为空,则转换成 0
若字符串中包含有效的整数格式,则转换成十进制数
若字符串中包含有效的浮点格式,则转换成浮点数值
若字符串中包含有效的十六进制格式,则转换成十六进制数
其余情况,转换成 NaN
Boolean 若为 true ,转换成 1
若为 false,转换成 0
Null 转换成 0
Undefined 转换成 NaN
Object 调用 Object 的 valueOf() 方法,按照上述规则转换
若为 NaN,调用 Object 的toString() 方法,按照上述规则转换


let a = Number(123)
let b = Number('456')
let c = Number(true)
let d = Number(false)
let e = Number(null)
let f = Number(undefined)
let g = Number(new Date())
let h = Number(new Array())
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(g)
console.log(h)
/*
 * 执行结果:
 * 123
 * 456
 * 1
 * 0
 * 0
 * NaN
 * 1578556849729
 * 0
**/


③ Any -> String:String()

转换规则如下:

转换规则
Number 转换成数字值
String 直接输出
Boolean 若为 true ,转换成 true
若为 false,转换成 false
Null 转换成 null
Undefined 转换成 undefined
Object 按特定的规则转换



let a = String(123)
let b = String('asdf')
let c = String(true)
let d = String(false)
let e = String(null)
let f = String(undefined)
let g = String({})
let h = String([123, 'asdf', true, false, null, undefined])
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(g)
console.log(h)
/*
 * 执行结果:
 * 123
 * asdf
 * true
 * false
 * null
 * undefined
 * [object Object]
 * 123,asdf,true,false,,
**/


(2)自动类型转换


① 条件判断

在进行条件判断时,会将变量的值按照规则自动转换成布尔值,转换的规则如下:

转换规则
Number 0、NaN 转换成 false
其余情况转换成 true
String 空字符串转换成 false
其余情况转换成 true
Null 转换成 false
Undefined 转换成 false
Object 转换成 true



let a = 123
let b = ''
let c = null
let d = undefined
let e = {}
a ? console.log('true') : console.log('false')
b ? console.log('true') : console.log('false')
c ? console.log('true') : console.log('false')
d ? console.log('true') : console.log('false')
e ? console.log('true') : console.log('false')
/*
 * 执行结果:
 * true
 * false
 * false
 * false
 * true
**/


② 数值运算

  • 对于 + 操作符,转换规则如下:
    若操作数都不是字符串或对象,则通过 Number() 函数转换成 Number 类型
    若操作数中存在字符串或对象,则通过 String() 函数转换成 String 类型
let a = 1 + true
let b = 1 + null
let c = 1 + undefined
let d = '1' + true
let e = '1' + null
let f = '1' + undefined
let g = '1' + 1
let h = {} + true
let i = [] + false
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(g)
console.log(h)
console.log(i)
/*
 * 执行结果:
 * 2
 * 1
 * NaN
 * 1true
 * 1null
 * 1undefined
 * 11
 * [object Object]true
 * false
**/


  • 对于 -*/% 操作符,都先通过 Number() 函数转换成数值再进行运算

基于数值运算的隐式转换规则,我们可以得到数字与字符串相互转换的方便捷径

// number -> string
let number = 123
let string = number + ''
console.log(typeof string)
console.log(string)
/*
 * 执行结果:
 * string
 * 123
**/


// string -> number
let string = '123'
let number = string - 0
console.log(typeof number)
console.log(number)
/*
 * 执行结果:
 * number
 * 123
**/


③ 相等运算

在使用 == 运算符比较两个变量是否相等时,如果变量的类型不同,则会先进行隐式转换后再比较,规则如下:

顺序 变量 1 变量 2 操作
1 NaN * 返回 false
2 Null Undefined 返回 true
3 Null Null 返回 true
4 Undefined Undefined 返回 true
5 Null 除 Null、Undefined 外 返回 false
6 Undefined 除 Null、Undefined 外 返回 false
7 Boolean * 将 Boolean 转换成 Number 后比较
8 Object Object 当两个对象指向同一内存地址时,才会相等
9 Object Number、String 将 Object 转换成原始值 (Number/String) 后比较
首先使用 valueOf() 方法
若对象能转换成原始值,则返回结果
否则使用 toString() 方法
若对象能转换成原始值,则返回结果
否则抛出 TypeError 异常
10 Number Number 直接比较
11 String String 直接比较
12 String Number 将 String 转换成 Number 后比较


练习几道题目:


false == 0   // true
false == '0' // true
false == []  // true
false == {}  // false
123 == [123] // true
2 == { valueOf(){ return 2 }} // true
1 == { valueOf: function(){ return [] }, toString: function(){ return 1 }} // true

由于使用 == 进行判断的时候会进行隐式转换,所以在项目中一般使用 ===


4、赋值、浅拷贝和深拷贝


对于基本类型,赋值、浅拷贝和深拷贝都是将旧变量的值 复制 给新变量,新旧变量之间互不影响

let number = 159
let string = 'hello'
let number_copy = number
let string_copy = string
number_copy = 258
string_copy = 'hi'
console.log('number: ' + number) // 原变量的值不会改变
console.log('string: ' + string) // 原变量的值不会改变
console.log('number_copy: ' + number_copy)
console.log('string_copy: ' + string_copy)
/*
 * 执行结果:
 * number: 159
 * string: hello
 * number_copy: 258
 * string_copy: hi
**/


对于引用类型:

  • 赋值后新旧变量指向同一个内存地址,此时改变新变量会影响旧变量的值
对于引用类型:
赋值后新旧变量指向同一个内存地址,此时改变新变量会影响旧变量的值
let object = { first_name: 'Steve', last_name: 'Jobs' 


  • 浅拷贝得到的新变量,是将旧变量的第一层数据 赋值 给新变量
// 对于对象,常用的浅拷贝方式有:Object.assign
// 对于数组,常用的浅拷贝方式有:Array.from、slice、concat
let object = {
    name: {
        first_name: 'Steve',
        last_name: 'Jobs'
    },
    age: 18
}
let array = [[1, 3], 5, 7]
let object_shallow_copy = Object.assign({}, object)
let array_shallow_copy = Array.from(array)
object_shallow_copy['name']['first_name'] = 'Steven'
object_shallow_copy['age'] = 20
array_shallow_copy[0][0] = 2
array_shallow_copy[2] = 11
console.log('object["name"]["first_name"]: ' + object['name']['first_name'])
console.log('object["age"]: ' + object['age'])
console.log('array[0][0]: ' + array[0][0])
console.log('array[2]: ' + array[2])
console.log('object_shallow_copy["name"]["first_name"]: ' + object_shallow_copy['name']['first_name'])
console.log('object_shallow_copy["age"]: ' + object_shallow_copy['age'])
console.log('array_shallow_copy[0][0]: ' + array_shallow_copy[0][0])
console.log('array_shallow_copy[2]: ' + array_shallow_copy[2])
/*
 * 执行结果:
 * object["name"]["first_name"]: Steven
 * object["age"]: 18
 * array[0][0]: 2
 * array[2]: 7
 * object_shallow_copy["name"]["first_name"]: Steven
 * object_shallow_copy["age"]: 20
 * array_shallow_copy[0][0]: 2
 * array_shallow_copy[2]: 11
**/


自己来实现一个浅拷贝函数:

function shallowCopy(object) {
    if (typeof object !== 'object') return
    let newObject = object instanceof Array ? [] : {}
    for (let key in object) {
        if (object.hasOwnProperty(key)) {
            newObject[key] = object[key]
        }
    }
    return newObject
}


  • 深拷贝得到的新变量,是将旧变量的所有数据(无论嵌套多深)都 复制 给新变量,新旧变量之间互不影响
// 最简单的方法就是 JSON.parse(JSON.stringify()),但是这种方法有一定的缺陷
// 如果属性中有 undefined、NaN、Infinity、function 类型,那么会转换为 null
// 如果属性中有 RegExp、Error 对象,那么会转换为空对象
let object = {
    name: {
        first_name: 'Steve',
        last_name: 'Jobs'
    },
    age: 18
}
let array = [[1, 3], 5, 7]
let object_deep_copy = JSON.parse(JSON.stringify(object))
let array_deep_copy = JSON.parse(JSON.stringify(array))
object_deep_copy['name']['first_name'] = 'Steven'
object_deep_copy['age'] = 20
array_deep_copy[0][0] = 2
array_deep_copy[2] = 11
console.log('object["name"]["first_name"]: ' + object['name']['first_name'])
console.log('object["age"]: ' + object['age'])
console.log('array[0][0]: ' + array[0][0])
console.log('array[2]: ' + array[2])
console.log('object_deep_copy["name"]["first_name"]: ' + object_deep_copy['name']['first_name'])
console.log('object_deep_copy["age"]: ' + object_deep_copy['age'])
console.log('array_deep_copy[0][0]: ' + array_deep_copy[0][0])
console.log('array_deep_copy[2]: ' + array_deep_copy[2])
/*
 * 执行结果:
 * object["name"]["first_name"]: Steve
 * object["age"]: 18
 * array[0][0]: 1
 * array[2]: 7
 * object_deep_copy["name"]["first_name"]: Steven
 * object_deep_copy["age"]: 20
 * array_deep_copy[0][0]: 2
 * array_deep_copy[2]: 11
**/


自己来实现一个深拷贝函数:

注意,这种简单的实现还是会有一定的缺陷


function deepCopy(object) {
    if (typeof object !== 'object') return
    let newObject = object instanceof Array ? [] : {}
    for (let key in object) {
        if (object.hasOwnProperty(key)) {
            let item = object[key]
            newObject[key] = typeof item === 'object' ? deepCopy(item) : item
        }
    }
    return newObject
}


目录
相关文章
|
2月前
|
存储 JavaScript 对象存储
js检测数据类型有那些方法
js检测数据类型有那些方法
141 59
|
1月前
|
存储 JavaScript 前端开发
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
JavaScript 数据类型分为基本数据类型和引用数据类型。基本数据类型(如 string、number 等)具有不可变性,按值访问,存储在栈内存中。引用数据类型(如 Object、Array 等)存储在堆内存中,按引用访问,值是可变的。本文深入探讨了这两种数据类型的特性、存储方式、以及检测数据类型的两种常用方法——typeof 和 instanceof,帮助开发者更好地理解 JavaScript 内存模型和类型检测机制。
80 0
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
|
1月前
|
JavaScript 前端开发 开发者
【干货拿走】JavaScript中最全的数据类型判断方法!!!!
【干货拿走】JavaScript中最全的数据类型判断方法!!!!
23 1
|
2月前
|
JavaScript 前端开发 API
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
|
1月前
|
存储 JavaScript 前端开发
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
19 0
|
2月前
|
存储 前端开发 JavaScript
前端基础(三)_JavaScript数据类型(基本数据类型、复杂数据类型)
本文详细介绍了JavaScript中的数据类型,包括基本数据类型(Number、String、Boolean、Undefined、Null)和复杂数据类型(Object),并解释了如何使用`typeof`操作符来识别变量的数据类型。同时,还讨论了对象、函数和数组等复杂数据类型的使用方式。
52 2
|
2月前
|
JavaScript 前端开发
JavaScript基础知识-基本数据类型和引用数据类型
关于JavaScript基础知识的文章,主要介绍了基本数据类型和引用数据类型。
42 2
JavaScript基础知识-基本数据类型和引用数据类型
|
1月前
|
JavaScript 前端开发
【干货分享】JavaScript学习笔记分享
【干货分享】JavaScript学习笔记分享
57 0
|
2月前
|
Web App开发 前端开发 JavaScript
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
|
3月前
|
JavaScript 前端开发
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
这篇文章是JavaScript基础与实战教程的第一部分,涵盖了JavaScript的基本语法、标识符、数据类型以及如何进行强制类型转换,通过代码示例介绍了JS的输出语句、编写位置和数据类型转换方法。
JavaScript基础&实战(1)js的基本语法、标识符、数据类型