2021 路漫漫其修远兮,吾将上下而求索 JS的数据类型 2021-01-06 数据类型转换规则及Symbol.toPrimitive  

2021-01-02

基本数据类型(原始值类型)

  •   number
    • NaN 
    • Infinity 
  •   string
  •   boolean
  •   null
  •   undefined
  •   symbol
    • 可以做为对象的属性(属性的类型不是传统的String了)
    • 创建唯一值的
  •   bigint

引用数据类型(对象类型)

  •   object
    • 普通对象
    • 数组对象
    • 正则对象
    • 日期对象
    • Math数学函数对象
  •   function

数据类型检测(js中有且只有四种)

  • typeof  检测数据类型的逻辑运算符
  • instanceof 检测是否为某个类的实例
  • constructor 检测构造函数
  • Object.prototype.toString.call 检测数据类型
/*
 * 数据类型检测 
 *   typeof 返回字符串
 *      typeof null ->"object"
 *      typeof 实现CALL的对象「函数、箭头函数、生成器函数、构造函数」 ->"function"
 *      typeof 剩下未实现CALL的对象 ->"object"
 *   
 *   ECMAScript提供的内置类型在计算机底层都是按照二进制数据存储的
 *      以对应的数字开始代表不同的类型 
 *      1:数字   010:浮点数
 *      100:字符串
 *      110:布尔
 *      -2^30:undefined
 *      000000:null
 *      000:对象
 *    设计上的缺陷
 */
// console.log(typeof typeof []); //->”string“

// 数据类型转换
// 对象转换为数字/字符串「字符串拼接、数学运算、特殊方法处理、==比较(隐式转换、显式转换)...」
//  + 首先检测对象的 Symbol.toPrimitive 这个属性,获取其原始值
//  + 如果没有这个属性,则继续调用它的valueOf,也是获取原始值
//  + 如果值不是原始值,则继续调用toString转换为字符串
//  + 再把字符串基于Number转换为数字

/* let obj = {
    name: 'xxx'
};
console.log(obj - 10); //数学运算:先把obj隐式转换为数字,再进行运算 */
/* 
let obj = {
    name: 'xxx',
    [Symbol.toPrimitive](hint) {
        // hint检测到浏览器隐式规定的转换类型:'number'/'string'/'default'
        return 10;
    }
};
console.log(obj - 10); */

详解typeof

  •  返回的结果都是字符串(引申:alter方法返回的结果也是字符串)
  • 局限性:
    • typeof null => "object"  是一个bug因为所有的值在内存中都是按照二进制存储的,typeof检测时认为前几位都是000的是object而null的前几位正好是000,但其实null是一个基本数据类型
    • typeof 不能细分对象类型(检测普通对象或数组对象等都是"object")

2021 路漫漫其修远兮,吾将上下而求索
JS的数据类型
2021-01-06
数据类型转换规则及Symbol.toPrimitive
 

let a=typeof typeof typeof[12,23];
console.log(a);  //"string"
/*
typeof [12,23] => "object"
typeof "object" => "string"
*/

详解number:NaN/isNaN/Infinity/parseInt/Number()

数字类型:

  • 普通数字
  • NaN not a number  NaN!=NaN 
  • Infinity,-Infinity
let res=parseFloat('left:200px');
if(res===200){
 alter(200);
}else if(res ===NaN){
 alrer(NaN)
}else if(typeof res==='number'){
 alter('number')
}else{
 alter('Invalid Number')
}

 把其他数据类型转化为数字的方法:

  • 强转换(利用底层机制转换) Number([value])
    • 一些隐式转换都是基于Number完成的
      • isNaN('12px')先把其他类型值转为数字再检测
      • 数学运算 '12px'-13
      • 字符串==数字  两个等于号比较多时候也是要把其他值转为数字
      • ...
  • 弱转换(基于一些额外的方法)parseInt([value])  parseFloat([value])
//parseInt处理的值是字符串,从字符串左侧开始查找有效数字字符(遇到非有效数字字符停止查找)->如果处理的值不是字符串,需要先转化为字符串然后开始查找
//Number 直接调用浏览器最底层的数据类型检测机制完成
//Number中 true 1 false 0
//Number中 null 0  undifined NaN 空字符串0
//Number中 字符串中必须保证都是有效数字才会转化成数字,否则都是NaN
// 0 NaN null undefined 空字符串转为布尔都为假
parseInt("") //NaN
Number("") //0
isNaN("") //隐式转换先把“”转完数字(Number) isNaN(0) false
parseInt(null) //NaN
Number(null) //0
isNaN(null)  //false
parseInt("12px) //12
Number("12px") //NaN
isNaN("12px") //true
parseFloat("1.6px")+parseInt("1.2px")+typeof parseInt(null) //2.6number
isNaN(Number(!!Number(parseInt("0.8")))) //true
typeof !parseInt(null)+!isNaN(null)//booleantrue

let result =10 +false +undefined+[]+'tencent'+null+true+{};
console.lof(result)
//10+0=>10
//10+NaN=>NaN
//NaN+[]=>NaN+""=>"NaN"
//"NaNtencentnulltrue[object Object]

2021 路漫漫其修远兮,吾将上下而求索
JS的数据类型
2021-01-06
数据类型转换规则及Symbol.toPrimitive
 

 Number({})=>Number("[object Object]")=>NaN  

Number([])=>Number("")=>0 

  ({}).toString()它调用Object上的toString

  ([]).toString()  它调用Array上的toString

==号的一些比较规则:

规律:

1.对象=字符串 对象转为字符串

2.null==undefined 但是和其他值不相等

3.剩下两边都不同都转为数字

//对象==布尔   都转为数字再比较
[]==true 
//number([]) =>number("")=>0
0==1
false

2021-01-06

数据类型转换规则及Symbol.toPrimitive

18.2.5 parseInt (string , radix) 
20.1 Number Objects

let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result);
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
let arr = [27.2,0,'0013','14px',123];
arr = arr.map(parseInt);
console.log(arr);
JS运行机制:堆(Heap)/栈(Stack)/上下文(EC)/全局对象(GO)/变量对象(VO/AO)
var a = 12;
var b = a;
b = 13;
console.log(a);
 
-----------------
 
var a = {n: 12};
var b = a;
b['n'] = 13;
console.log(a.n);
 
-----------------
 
var a = {n: 12};
var b = a;
b = {n: 13};
console.log(a.n);
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b);
var x = [12, 23];
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
fn(x);
console.log(x);
/*
 * 对象转换为数字或者字符串
 *   1.查找对象的 Symbol.toPrimitive 
 *   2.对象.valueOf()  原始值:numberstringoolean
ullundefinedsymboligint
 *   3.对象.toString() 变为字符串
 *   4.字符串转换数字 Number(str)
 * 
 * ==相等  ===绝对相等
 *   ==在比较的时候,如果两边类型不一致,则转换为相同的数据类型
 *     NaN==NaN  false    Object.is(NaN,NaN)->true
 *     null==undefined -> true    null===undefined -> false   null&undefined和其他任何值比较都是不相等的
 *     对象==字符串   对象转换为字符串
 *     剩余的情况都是转换为数字
 * 
 *   ===类型不一致,不会转换,直接false
 */

// 第一类:隐式进行数据类型转换的时候进行处理的
/* var a = {
    i: 0
};
// valueOf / toString
a[Symbol.toPrimitive] = function () {
    // this->a
    return ++this.i;
};
// a[Symbol.toPrimitive]()
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
} */

/* var a = {
    i: 0,
    [Symbol.toPrimitive]() {
        return ++this.i;
    }
};
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
} */

/* var a = [1, 2, 3];
// a.shift() ->1
a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
} */

// 第二类:ES6 数据劫持 
/* let obj = {};
Object.defineProperty(obj, 'name', {
    // 以后当我们操作对象的name属性的时候(获取或者设置),触发getter/setter
    get() {
        return '逗你玩';
    },
    set(value) {
        console.log(value);
    }
}); */

/* // var a = 12; //全局上下文中,基于var/function声明变量,也相当于给window设置了属性 window.a=12
var i = 0;
Object.defineProperty(window, 'a', {
    get() {
        return ++i;
    }
});
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
} */

//============JS中的数据类型转换
/*
 * 把其它的数据类型转换为number类型 
 *    例如:==比较、数学运算(+不仅仅是数学运算,还有字符串拼接)...
 * 
 * 显式转换方案:
 *    Number([val]) -> 隐式转换一般调取的都是这个方法  「浏览器有自己的特殊处理,针对于每一种情况都有详细的规则」
 *    parsetInt/parseFloat([val])
 *       parsetInt([val],[radix])处理机制
 *         [val] 必须是一个字符串,如果不是,则也要默认转换为字符串
 *         [radix]不设置(或者写的是零):正常都是按照10处理的,如果字符串是以”0x“开始的,默认值是16...
 *       先在[val]中,找到所有符合[radix]进制的内容(从左到右查找,直到遇到不符合的为止「不论后面是否还有符合进制的,都不在查找了」),然后再把找到的内容看做[radix]进制,转换为十进制
 *       [radix]范围  2~36,除了0以外(0->10/16),不在这个范围内,结果都是NaN
 */
// parseInt('12px') ->  parseInt('12px',10) -> 在字符串中找到所有符合10进制的内容 ‘12’ -> 最后把'12'当做看做10进制,转换为10进制 -> 12
// parseInt('12px',1) -> NaN
// console.log(parseInt(null)); //->parseInt('null',10) -> NaN  

// 把其它进制转换为10进制?
// '10101'  2机制 -> 10进制
// 1*2^0 + 0*2^1 + 1*2^2 + 0*2^3 + 1*2^4
// 4^-1 -> 1/4    4^-3 -> 1/(4^3)


// 把一个函数作为值传递给另外一个函数执行(实参):回调函数
// parseInt(27.2,0) 
//    parseInt('27.2') -> 27
// parseInt(0,1)
//    NaN
// parseInt('0013',2)
//    '001' 看做2进制 转换为10进制
//    1*2^0 -> 1
// parseInt('14px',3)
//    '1' 看做3进制 转换为10进制
//    1*3^0 -> 1
// parseInt(123,4)
//    parseInt('123',4)
//    '123' 看做4进制 转换为10进制
//    3*4^0 + 2*4^1 + 1*4^2 -> 3+8+16 -> 27
/* let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr); */

// 数据中有多少项,就迭代多少次,每一次执行回调函数(item当前迭代项 index索引),支持回调函数返回值,返回啥就把当前项替换成啥,原始数组不变,以新数组返回!!
/* let arrNew = arr.map(function (item, index) {
    return '@';
}); */

/*
 * 把其它数据类型转换为布尔:
 *   只有”0/NaN/null/undefined/空字符串“ 转换为false,其余都是true
 *   例如:
 *     if(1){} 
 *     ! 取反
 *     !! 转换为布尔
 *     ...
 */


/*
 * ”+“还存在字符串拼接 
 *   +两边都有值,有一边出现字符串或者对象,则为字符拼接
 *     特殊:{}+10 -> 10  {}看做代码块(ES6 块级上下文),真正运算的只有 +10 ->10
 *          ({}+10) -> '[object Object]10'
 *   +只有一边或者++x再或者x++,都是数学运算
 *     +'10' -> 10
 *     10+(++x) -> 先把x累加1,然后和10运算
 *     10+(x++) -> 先拿x的值和10运算,最后再x累加1
 *     
 *     x++ !== (x+=1 == x=x+1)
 */
/* let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result); */

// 10+{} -> "10[object Object]"  原本是想把{}变为数字,但是Symbol.toPrimitive/valueOf/toString,调到toString变为字符串,此时符合了有一边变为字符串了,则是字符串拼接

/* let x = '10';
console.log(++x); //->11

x = '10';
x += 1; //->x=x+1
console.log(x); //->'101' */

//=======================
/*
 * JS运行的环境:
 *   + 浏览器
 *   + webview  WebApp(Hybride混合APP) 
 *   + node.js
 *   + ...
 * 
 * 浏览器能够运行JS代码,是因为提供了代码运行的环境:栈内存(Stack)
 *   + 栈内存也是从计算机的内存分配出来的一块内存
 *   + 执行环境栈 E(execution)C(context)Stack
 *   
 * 执行代码的过程中,为了区分是在哪个环境下执行(全局/函数/块...),首先会产生一个执行上下文:EC
 *   + EC(G) 全局上下文,全局代码在这执行
 *   + EC(X) 某个函数的执行上下文
 */

/* var a = 12;
var b = a;
b = 13;
console.log(a); */


/* var a = {
    n: 12
}; //a -> 0x0001
var b = a;
b = {
    n: 13
}; //b -> 0x0002
console.log(a.n); //=>12 */


// 课后思考:
var a = {
    n: 1
};
var b = a;
a.x = a = {
    n: 2
};
console.log(a.x);
console.log(b);
// a.x 成员访问,优先级20「优先计算的」
//  a.x=a=?  先处理a.x=?
//  a=a.x=?  先处理a.x=?

/* var a = 12,
    b = 12;
// var a=12;  var b=12; */

/* var a = b = 12;
// var a;  b没有var
// 1.创建值12
// 2.连等操作是按照从右到左
//    b -> 12
//    a -> 12 */

// https://developer.mozilla.org/zh-CN/docs/web/javascript/reference/operators/operator_precedence

 

2021 路漫漫其修远兮,吾将上下而求索
JS的数据类型
2021-01-06
数据类型转换规则及Symbol.toPrimitive
 

2021 路漫漫其修远兮,吾将上下而求索
JS的数据类型
2021-01-06
数据类型转换规则及Symbol.toPrimitive