zoukankan      html  css  js  c++  java
  • 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' }
    let array = [1, 3, 5]
    
    let object_copy = object
    let array_copy = array
    
    object_copy['first_name'] = 'Steven'
    array_copy[0] = 2
    
    console.log('object["first_name"]: ' + object['first_name']) // 原变量的值发生改变
    console.log('array[0]: ' + array[0]) // 原变量的值发生改变
    console.log('object_copy["first_name"]: ' + object_copy['first_name'])
    console.log('array_copy[0]: ' + array_copy[0])
    
    /*
     * 执行结果:
     * object["first_name"]: Steven
     * array[0]: 2
     * object_copy["first_name"]: Steven
     * array_copy[0]: 2
    **/
    
    • 浅拷贝得到的新变量,是将旧变量的第一层数据 赋值 给新变量
    // 对于对象,常用的浅拷贝方式有: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
    }
    

    【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记

    版权声明:本博客属于个人维护博客,未经博主允许不得转载其中文章。
  • 相关阅读:
    IOS开发中UITableView(表视图)的滚动优化及自定义Cell
    IOS软件国际化(本地化Localizable)
    IOS三种归档(NSKeyArchieve)的总结
    在IOS中使用DES算法对Sqlite数据库进行内容加密存储并读取解密
    内存管理_缓存一致性
    JAVA volatile 关键字
    C++ STL 的实现:
    Java for LeetCode 236 Lowest Common Ancestor of a Binary Tree
    Java for LeetCode 235 Lowest Common Ancestor of a Binary Search Tree
    Java for LeetCode 234 Palindrome Linked List
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/12175121.html
Copyright © 2011-2022 走看看