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"

 

posted on 2019-10-24 21:57  minoz  阅读(172)  评论(0编辑  收藏  举报