zoukankan      html  css  js  c++  java
  • JS 数据类型

    1、JS数据类型:

      原始类型:存放在栈中,占内存小,读取快

    • boolean
    • null
    • undefined
    • number
    • string
    • symbol

      引用类型:可以动态分配内存,可以存大数

    • object --- 引用类型

      栈:栈中存放的引用类型其实是该引用类型在堆中的地址

    10
    'abc'
    true
    0xabba
    0xabbb

      堆:堆中存放的才是真正的引用类型内容;当引用类型中嵌套引用类型时,还是存放下一层引用类型的地址

    0xabba {a: 1, b: 2}
    0xabbb {name: 0xabbd, age: 27}
    0xabbc [1, 2, 3, 4]
    0xabbd ['kimi', 'bob']

        Tips1not defined属于未定义,undefined属于定义未赋值。typeof都返回undefined.

                  2js中的string是不可变的,无论你在 string 类型上调用何种方法,都是创建并返回一个新的字符串(并销毁原来的字符串) 

    var a = 'aaa';
    c = a.toUpperCase();
    c === a;    // false

    2、字面量:

    • 字符串字面量
    • 数组字面量
    • 对象字面量
    • 函数字面量

      eg:对象字面量 - 创建对象的一种简单容易阅读的方法。

    var obj = {
        a: 'aaa',    // a是属性,'aaa'是属性值
        b: 'bbb',
        c: 'ccc'
    }
    
    obj.a;       // "aaa"
    
    obj['a'];    // "aaa"

      eg:函数字面量:由4部分组成。

    • 关键词 function
    • 函数名,但是可有可无。
    • 包含在括号内的参数,当然参数也是可有可无的,括号不能少。
    • 是一组包裹在大括号的语句块,也就是函数要执行的具体代码,当然不写代码也没问题,{} 是必须要的。
    function() {};        // Uncaught SyntaxError: Function statements require a function name
    
    (function() {});        // ƒ () {} 
    
    var fun = function() {};
    fun;                        // ƒ () {}
    
    
    var fun = {fn: function () {}};
    fun;                        // { fn: f() }
    
    
    var fun = function test() {};
    fun;                        // ƒ test() {}
    
    var fn = function test1() { console.log(test1); };
    
    console.log(fn);    // ƒ test1() { console.log(test1); }
    
    fn();    // ƒ test1() { console.log(test1); }
    
    console.log(test1);    // Uncaught ReferenceError: test1 is not defined

    3toString()String()new String()

    • toString():返回string类型(string返回该字符串的一个副本),nullundefined没有这个方法;
    • String():返回string类型,能将任何类型的值转换成字符串,在不确定转换的值是不是nullundefined时可以使用;
    • + ' ':返回string类型
    • new String():返回引用类型

      var abc = 'abc',

          str = abc.toString(),

          str1 = String(abc),

          str2 = new String(abc);

      //判断下面输出

    str === abc    // true
    str1 === abc   // true
    str2 === abc   // false
    typeof str     // string
    typeof str1     // string
    typeof str2     // object
    String(null);    // "null"
    String(undefined);    // "undefined"
    null + '';    // "null"
    undefined + '';    // "undefined"

      Tips

    • 'abc'.toString()abc.length可以直接调用的原因:只要引用字符串的属性或方法,JavaScript就会将字符串值通过new String(s)的方式转换成对象,这个对象继承了字符串的方法,并用来处理属性的作用。一旦属性引用结束,这个新创建的对象就会销毁(其实在实现上并不一定创建和销毁这个临时对象,然而整个过程看起来就是这样的)
    类似代码
    
    var a1 = new String('abc');
    var a2 = a1.substring(0);
    a1 = null;
    console.log(a2);    // abc
    • 存取字符串,数字或布尔值的属性时创建的临时对象称作包装对象.

      同字符串一样,数字和布尔值也具有各自的方法:通过Number和Boolean构造函数创建一个临时对象,这些方法的调用均来自这个临时对象;

      null和undefined没有包装对象:访问它们的属性会造成一个错误

    var aa = "test";
    aa.len = 5;
    
    aa.len;    // undefined

          由于包装对象使用完毕会自己销毁,所以添加的属性也读取不到。

    • 2.toString()会报错的原因:解释器将.判断成了小数点,真正解释成了(2.)toString(),因此报错。可以改成:
    2..toString();    // "2"
    (2).toString();    // "2"
    2 .toString();  // 加个空格     // "2"
    2.0.toString();    // "2"

    4、深浅拷贝:

    • 浅拷贝:直接赋值,改变其中一个变量的值,另外一个也跟着改变
    let obj1 = {name: '小明', age: 15};
    let obj2 = obj1;
    
    obj2.name = '小红';
    
    console.log(obj1);    // {name: "小红", age: 15}
    console.log(obj2);    // {name: "小红", age: 15}
    • 一层拷贝:只会拷贝所有对象属性值到心得对象中。如果还是对象则拷贝地址
    let a = {id: 1};
    let b = {...a, c: 2};
    
    console.log(b);    // {id: 1, c: 2}
    console.log(a);    // {id: 1}
    
    let b = Object.assign({}, a);
    let b = a.concat();
    let b = a.splice('-');
    ...
    • 深拷贝:
    1. let b = JSON.parse(JSON.stringify(obj)) :可以满足开发过程中90%的深拷贝
    let a = {
        q: function() { console.log(1); },
        w: null,
        e: undefined,
        d: Symbol('abc')
    }
    
    let b = JSON.parse(JSON.stringify(a));
    
    b;    // {w: null}

      缺点:对函数、undefined、Symbol序列化后会丢失

      2. 递归遍历:如果有特殊情况(有函数、undefined、Symbol),需要递归遍历进行深拷贝。

      3.MessageChannel:API允许我们创建一个新的消息通道,并通过它的两个MessagePort属性发送数据

        和webwork相似。数据是深拷贝且不会丢失undefined,循环引用也可以。但是对函数和Symbol还是不行。

    // 有undefined + 循环引用
    let obj = {
        a: 1,
        b: {
            c: 2,
            d: 3
        },
        f: undefined
    }
    
    obj.c = obj.b;
    obj.e = obj.a;
    obj.b.c = obj.c;
    
    function deepCopy(obj) {
        return new Promise((resolve) => {
            const { port1, port2 } = new MessageChannel();
            port2.onmessage = ev => resolve(ev.data);
            port1.postMessage(obj);
        });
    }
    
    deepCopy(obj).then((copy) => {    // 请记住"MessageChannel"是异步的这个前提!!
        let copyObj = copy;
        console.log(copyObj, obj); 
        console.log(copyObj == obj); 
    });

    Test:

    function test(person) {
        person.age = 26;
        person = {
            name: 'yyy',
            age: 30
        }
        return person;
    }
    
    const p1 = {
        name: 'abc',
        age: 25
    }
    
    const p2 = test(p1);
    
    p1;    // {name: 'abc', age: 25}
    p2;    // {name: 'yyy', age: 30}    p2在调用test方法时,传入p1为参数,p1的age属性被重写。 后面代码person相当于是重新创建了一个person,返回的是新创建的

    5、typeof:

    • 使用typeof是可以准确判断基本数据类型的
    typeof false;     // boolean
    typeof null;     // object
    typeof undefined;     // undefined
    typeof 2;     // number
    typeof NaN;     // number
    typeof Infinity;     // number
    typeof '2';     // string
    typeof Symbol('22');     // symbol
    
    typeof Date;     // funtion
    typeof [2,2,3];     // object
    typeof {a:1};     // object
    • typeof null ----> object,这是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。
    • 对象、数组、函数都是引用类型,要较准确的判断各个引用类型就需要使用instanceof
    null instanceof Object;        // false

    6准确类型的判断方法

    • 引用类型的类型校验用 instanceof
    let Person = function() {};
    let p1 = new Person();
    p1 instanceof Person;    // true
    
    let str = 'hello';
    str instanceof String;     // false
    str.__proto__     // String
    // str字面量有__proto__属性是因为转换成包装对象。但是本身是个基本类型,使用不了instanceof
    typeof str;    // string
    
    let str1 = new String('hello');
    str1 instanceof String;     // true
    •  原始类型不能直接通过instanceof判断,需要通过Symbol.hasInstance加层判断
    class PrimitiverString {
        static [Symbol.hasInstance](x) {
            return typeof x === 'string';
        }
    }
    
    console.log('hello' instanceof PrimitiverString);    // true
    •  Object.prototype.toString.call()
    Object.prototype.toString.call(2);    // "[object Number]"
    Object.prototype.toString.call('aa');    // "[object String]"
    Object.prototype.toString.call(true);    // "[object Boolean]"
    Object.prototype.toString.call(Symbol('aaa'));    // "[object Symbol]"
    Object.prototype.toString.call(new Date());    // "[object Date]"
    Object.prototype.toString.call(new RegExp());    // "[object RegExp]"

    7、Object.prototype.toString.call()、toString()、toString.call()

    • 所有的对象都会继承Object.prototype.toString这个方法。而在每个子类都会改写这个方法。
    • Array、Function的原型上都已经改写了这个方法。
    • 每个对象上调用toString方法时会先调用自身的toString方法,如果找不到会沿着原型链网上找,如果一直没找到最终会找到Object.prototype.toString这个方法。

      具体看每个对象调用toString方法的结果:

      • 对象 object
    var obj = {a: 1};
    
    obj.toString();    // "[object Object]"
    Object.prototype.toString.call(obj);    // "[object Object]"

      Tips:  

    Object.prototype.toString()在toString方法被调用时,会执行下面步骤
      • 获取this对象的[[Class]]属性的值(怎么看这个的取值??

      • 计算出三个字符串"[object", 第一步的操作结果Result[1], 以及"]"连接后的新字符串

      • 返回第二部操作结果Result(2)。

        [[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性。在规范中,[[Class]]是这么定义的:

          内部属性 描述[[Class]]一个字符串值,表明了该对象的类型。即:

          1、获取对象的类名(对象类型)。2、然后将[object、获取的类名]组合并返回

    Object.prototype.toString.call({});    // "[object Object]"
    Object.prototype.toString.call([]);    // "[object Array]"
    Object.prototype.toString.call(function() {});    // "[object Function]"
    Object.prototype.toString.call('');    // "[object String]"
    Object.prototype.toString.call(1);    // "[object Number]"
    Object.prototype.toString.call(true);    // "[object Boolean]"
    Object.prototype.toString.call(null);    // "[object Null]"
    Object.prototype.toString.call(undefined);    // "[object Undefined]"
    
    // toString()方法能识别以下类型是因为引擎给它们设置好了 toStringTag 标签
    Object.prototype.toString.call();    // "[object Undefined]"   相当于Object.prototype.toString.call(undefined)
    Object.prototype.toString.call(new Date());    // "[objectDate]"
    Object.prototype.toString.call(/at/);    // "[object RegExp]"
      • 自己创建的类不能识别,toString()找不到toStringTag属性时只好返回默认的Object标签
    class ValidatorClass {}
    
    Object.prototype.toString.call(new ValidatorClass());    // "[object Object]"
    
    // 可以加上 toStringTag 属性让它识别
    class ValidatorClass {
        get [Symbol.toStringTag]() {
            return 'Validator';
        }
    }
    
    Object.prototype.toString.call(new ValidatorClass());    // "[object Validator]"

       用Object.prototype.toString.call()判断类型可简写为toString.call()来判断

      但是要注意如果已经定义了toString函数就不可以!

    function toString() {
        console.log('qqq');
    }
    
    toString();    // 'qqq'
    toString.call({});    // 'qqq'
    toString.call([]);    // 'qqq'

     8、toString、valueOf:

      基本类型的valueOf会返回自身的原始类型,而Array、Function、Object则返回自身,Date返回时间戳

    // object
    var obj = {a: 1};
    obj.valueOf();    // {a: 1}
    obj.toString();    // "[object Object]"
    
    // array
    var arr = [1, 2];
    arr.valueOf();    // [1, 2]
    arr.toString();    // "1, 2"
    
    // function
    var fun = function() {};
    fun.valueOf();    // ƒ () {}
    fun.toString();    // "function() {}"
    
    // date
    var date = new Date();
    date.valueOf();    // 1572592931967
    date.toString();    // "Fri Nov 01 2019 15:22:01 GMT+0800 (中国标准时间)"
    
    // string
    var str = 'abc';
    str.valueOf();    // "abc"
    str.toString();    // "abc"
    
    // number
    var num = 222;
    num.valueOf();    // 222
    num.toString();    // "222"
    
    // boolean
    var bool = true;
    bool.valueOf();    // true
    bool.toString();    // "true"
    
    // null & undefined  无法调用,报错
    
    // regExp
    var reg = /cat/g;
    reg.valueOf();    // /cat/g
    reg.toString();    // "/cat/g"
    
    // window
    var win = window;
    win.valueOf();    // window
    win.toString();    // "[object Window]"
    
    // error
    var err = new Error('abc');
    err.valueOf();    // Error: abc
        at <anonymous>:1:11
    err.toString();    // "Error: abc"
  • 相关阅读:
    Undefined symbols for architecture armv7错误解决方法
    一个iOS 框架介绍:MKNetworkKit
    objective-c之各种数值
    利用openURL,在IOS应用中打开另外一个应用
    hackerrank---List Comprehensions
    LeetCode --- Plus One
    Vim学习与总结
    LeetCode --- Pascal's Triangle II
    LeetCode --- Gray Code
    LeetCode --- Two Sum
  • 原文地址:https://www.cnblogs.com/minozMin/p/11735242.html
Copyright © 2011-2022 走看看