20230313
day-026-twenty-six-20230313-数据类型深入-symbol类型深入-typeof深入及在实战中的运用-内存类型-JavaScript进制转换
概念
API
: 所有可以供别人调用
的方法
/接口
,统称为API
-
API
全称是application programming interface
,应用程序接口; -
要学习
英语单词
,因为一般中文文档
会加一些作者的想法
,但那想法
并不一定正确
。 -
ide
就是开发工具
-
程序员三种病
- 腰稚,要坐直
- 眼睛 别晚上黑灯看手机
- 痔疮,别长久坐
数组方法
- 增删改5个
[].push()
[].pop()
[].unshift()
[].shift()
[].splice()
- 查找3个
[].indexOf()
[].lastIndexOf()
[].includes()
- 转字符串2
[].toString()
[].join()
- 截取slice
[].slice()
- 合并concat
[].concat()
- 排序/排列2个
[].sort()
[].reverse()
- 迭代/循环2个
[].forEach()
[].map()
对象
- 多组
键值对
组成的属性名和属性值集合
- 一组
键值对
就是对象
的一个成员
obj.name
- 学术说法: 访问
obj对象
中的name这个成员
- 这个操作的学术说法:
对象的成员访问
字符串
- 查找2
"string".charAt()
"string".charCodeAt()
- 截取3
"string".slice()
"string".substring()
"string".substr()
- 验证是否存在3
"string".indexOf()
"string".lastIndexOf()
"string".includes()
- 大小写转换2
"string".toUpperCase()
"string".toLowerCase()
- 替换1
"string".replace()
- 转数组split
"string".split()
Math
-
Math.abs()
-
Math.ceil()
-
Math.floor()
-
Math.round()
-
Math.random()
- Math.floor(Math.random()*(max+1-min)+min)
-
Math.max()
-
Math.min()
-
Math.sqrt()
-
Math.pow()
//2^10 -> 1024 Math.pow(2,10)//2的10次方 2**10//2的10次方
日期对象
new Date()
创建当前时间的日期对象
new Date().getFullYear()
new Date().getMonth()
new Date().getDay()
new Date().getDate()
new Date().getHours()
new Date().getMinutes()
new Date().getSeconds()
new Date().getMilliseconds()
new Date().getTime()
DOM相关方法
-
获取DOM元素
-
节点关系属性
-
增删改[自定义属性]
- createElement
- createTextNode
- appendChild
- insertBefore
- removeChild
- cloneNode
- get/set/removeAttribute
函数
- 定义&执行
- 参数
- 形参
- 实参
- 获取参数
- arguments
- 剩余运算符
- 获取参数
- 箭头函数
- 简写形式
- 返回值
JS中的操作符
循环:for 、for in 、while、do-while
判断:if-else、switch-case、三元运算符
ECMAScript
ECMAScript是JS的语法规范
-
ES3[很老的规范]
-
ES5[在ES3的基础上,新增了很多便于开发的API方法,例如:forEach/map] =>不兼容IE6~8
-
以ES5为分隔,ES5是JS的老版本语法规范!ES6是js的新语法规范。
-
ES6新语法规范[ES6+: ES6~ES12]
- ES2015:在2015年,正式发布了ES6这个版本,并且规定,每一年都在ES6这个版本的基础上,每年6月份进行更新迭代。
- 大方向不改,只是增补。
- ES2015:在2015年,正式发布了ES6这个版本,并且规定,每一年都在ES6这个版本的基础上,每年6月份进行更新迭代。
-
以后习惯用===用来比较。
数据类型检测
JS中的数据类型
- 原始值类型「基本数据类型 & 值类型」
- number 整数、小数、零、负数、NaN(不是一个有效数字,但是属于数字类型)、Infinity(无穷大的值)…
- string 字符串:“”、‘’、``(ES6中的模板字符串,更方便的实现字符串拼接)
- boolean 布尔:true/false
- null 空
- undefined 未定义
- symbol 唯一值(ES6+)
- bigint 大数(ES6+)
- 对象类型「引用数据类型」
- 标准普通对象(纯粹的对象)
- 例如:{x:10,y:20}
- 标准特殊对象
- new Array 数组
- new RegExp 正则
- new Date 日期对象
- new Error 错误对象
- Set/Map
ES6+新增的数据结构
- …
- 非标准特殊对象(包装类型对象)
- 例如:new Number(1) -> 原始值对应的对象数据类型值
- 函数对象function
- 标准普通对象(纯粹的对象)
symbol类型
- 每一次执行
Symbol(描述)
都会创建
一个唯一值
- 设置的描述仅仅是为了方便我们自己区分,但是对最后的结果没有影响!
console.log(Symbol() === Symbol()); //false
console.log(Symbol('AA') === Symbol('AA')); //false
symbol数据类型的作用
- 想创建一个和别人不相等的值(唯一值)
- 可以给对象设置一个symbol类型(唯一)的成员「属性名」
- 处理JS的一些底层机制!
给对象设置一个symbol类型属性名
-
一个对象中,其成员值(属性值/value)可以是任意类型!
-
在ES6之前,其成员(属性名/key),都是“字符串”类型!「不论是设置还是访问成员,只要成员的类型不是字符串,都先转换为字符串,再进行处理」
let arr = [10, 20]; let sym = Symbol('BB'); let obj = {0: 100, //成员:'0'1: 200, //成员:'1'length: 2, //成员:'length'true: '哈哈哈', //成员:'true'null: '呵呵呵', //成员:'null'fn: function () { }, //成员:'fn'// 此处设置中括号,仅仅是语法要求:把变量的值作为成员[arr]: '嘿嘿嘿',[Symbol('AA')]: 1000,[sym]: 2000 }; obj[arr] = '嘿嘿嘿'; // obj[String([10, 20])] -> obj['10,20'] console.log(obj); // { '10,20':'嘿嘿嘿',.... }console.log(obj[0], obj['0']); //100 100 // obj[0] -> obj['0'] 默认先把0变为字符串"0",再去对象中进行成员访问let length = 1; console.log(obj['length']); //2 console.log(obj[length]); //先获取length变量的值,再拿这个值作为对象的成员去访问 obj[length]->obj['1'] console.log(obj.length); //2「同方式一」
-
在ES6及以后,对象中的成员不单纯是字符串类型,还可以是symbol类型了!
- 设置的唯一值成员,如果后期想访问
-
设置的唯一值成员,如果后期想访问:先用变量存储创建的唯一值,然后设置和获取的时候,都基于这个变量进行操作!先用变量存储创建的唯一值,然后设置和获取的时候,都基于这个变量进行操作!
let arr = [10, 20]; let sym = Symbol('BB'); let obj = {0: 100, //成员:'0'1: 200, //成员:'1'length: 2, //成员:'length'true: '哈哈哈', //成员:'true'null: '呵呵呵', //成员:'null'fn: function () { }, //成员:'fn'// 此处设置中括号,仅仅是语法要求:把变量的值作为成员[arr]: '嘿嘿嘿',[Symbol('AA')]: 1000,[sym]: 2000 }; console.log(obj["Symbol('AA')"]); //undefined 此成员就是symbol类型,并没有给其转换为字符串 console.log(obj[Symbol('AA')]); //undefined 按照symbol类型的成员获取值,获取不到的原因是:两次是不同的唯一值 console.log(obj[sym]); //2000
-
可以基于一些API方法,获取对象中的所有成员(属性名):返回包含成员的数组集合
let arr = [10, 20]; let sym = Symbol('BB'); let obj = {0: 100, //成员:'0'1: 200, //成员:'1'length: 2, //成员:'length'true: '哈哈哈', //成员:'true'null: '呵呵呵', //成员:'null'fn: function () { }, //成员:'fn'// 此处设置中括号,仅仅是语法要求:把变量的值作为成员[arr]: '嘿嘿嘿',[Symbol('AA')]: 1000,[sym]: 2000 }; console.log(Object.keys(obj)); //获取对象的私有属性,但是只能获取 可枚举、字符串类型(非symbol类型) 的私有属性 -> ['0', '1', 'length', 'true', 'null', 'fn', '10,20'] console.log(Object.getOwnPropertyNames(obj)); // 获取对象的私有属性,但是只能获取 字符串类型(非symbol类型) 的私有属性「好处:不考虑枚举性」 -> ['0', '1', 'length', 'true', 'null', 'fn', '10,20'] console.log(Object.getOwnPropertySymbols(obj)); // -> [Symbol(AA), Symbol(BB)] 获取当前对象中,所有 symbol 类型的私有属性!!「好处:不考虑枚举性」 // 获取对象所有的私有属性「不论类型、不论是否可以枚举」 // let keys = Object.getOwnPropertyNames(obj); // keys = keys.concat(Object.getOwnPropertySymbols(obj)); let keys = Reflect.ownKeys(obj); //这一个操作,代替了上面两个操作「ES6新提供的方法」 keys.forEach(key => {console.log(key); //迭代的每个成员console.log(obj[key]); //迭代的每个成员值 });
-
- 设置的唯一值成员,如果后期想访问
对象属性的可枚举性
- 对象中的每个成员都有自己的规则,其中一个规则是:是否可“枚举”「enumerable」
- 所谓枚举,可以理解为列举、遍历!
- 在JS中,对象中的某个成员,只要能够被for/in循环到,这样的成员就是可枚举的,反之则是不可枚举!
- “一般”情况下:
- 内置的成员都是不可枚举的(浅颜色)
- 自定义的成员都是可枚举的(深颜色)
- 在自己手动修改了某个属性的可枚举性,以自己修改并生效的可枚举性为准
- “一般”情况下:
数据类型检测的办法
- typeof
- 语法
typeof 值
- null的学术名词:空对象指针
- 语法
- instanceof
- constructor
- Object.prototype.toString.call
变量声明过程
let obj={x:10};
- 先创建值
- 如果是原始值,直接存储在栈内存中
- 如果是对象类型值,首先开辟一个堆内存空间[有一个16进制的内存地址 假设0x000],把成员存储到空间中
- 声明变量
- 等号赋值[指针指向]
typeof数据类型检测的底层机制
- 特点1:返回的结果是字符串,字符串中包含了对应的数据类型
typeof 值
返回这个值的数据类型- 字符串
- 字符串中包含了相关的数据类型
- typeof typeof typeof [1,2,3]//‘string’
- 特点2:按照计算机底层存储的二进制进行检测「效率高」
- 000 对象
- 1 整数
- 010 浮点数
- 100 字符串
- 110 布尔
- 000000… null
- -2^30 undefined
- ……
- 特点3:typeof null -> “object”
- typeof按照二进制进行检测的时候,认为以“000”开始的就是对象类型
- 因为null存储的是64个零,所以被识别为对象,导致:typeof null -> “object”
- 如果检测出来是对象,再去看是否实现了call方法;如果实现了,说明其是一个函数对象,返回“function”;
- 如果没有实现call,都返回“object”;
- typeof按照二进制进行检测的时候,认为以“000”开始的就是对象类型
- 特点4:typeof 对象 -> “object” && typeof 函数 -> “function”
- typeof不能检测null,也无法对“对象”进行细分(除函数对象外)
- 特点5:typeof 未被声明的变量 -> “undefined”
typeof在实战中的运用
-
检测除null以外的原始值类型「性能高」
-
笼统的校验是否为对象
// 笼统的检测一个值是否为对象 const isObject = function isObject(value) {let type = typeof value; //这个值的检测结果// 是对象的条件:不能是null,并且基于typeof检测的值是 'object'/'function' 中的一个return value !== null && (type === 'object' || type === 'function'); };console.log(isObject(10)); //false console.log(isObject(null)); //false console.log(isObject([])); //true console.log(isObject({})); //true console.log(isObject(function () { })); //true
-
检测是否为函数 => if(typeof obj===“function”){…}
-
处理浏览器兼容「ES6+语法规范,都不兼容IE」
if (typeof Symbol !== 'undefined') {// 当前浏览器支持Symbol// ... }
- 兼容问题主要是由于浏览器内核引起
- IE浏览器内核:Trident
- Edge浏览器内核:Chromium「虽然都是微软搞的,但是Edge不是IE」
- 谷歌浏览器内核:webkit「分支:blink」
- Safari浏览器内核:webkit
- 火狐浏览器内核:Gecko
- 移动端浏览器 && 国产浏览器:现在基本上都是webkit内核
- 兼容问题主要是由于浏览器内核引起
内存
内存类型
计算机的内存:
- 运行内存[虚拟内存] -> 内存条
- 物理内存[硬盘存储]-> 硬盘
在计算机的编程语言中,我们声明的变量
& 数据类型值
,都存储在计算机的 虚拟内存
中
- 都是以2进制的格式进行存储[而且存储64位(之前有32位)]
- 不管是堆内存还是栈内存都是存在虚拟内存中
内存大小单位
1B 1个字节[一个字母/数字,是一个字节、一个汉字是两个字节]
1KB 1024B=1KB
1MB 1024KB=1MB
1GB 1024MB=1GB
1TB 1024GB=1TB
进制概念
- 计算机中有一个非常重要的概念:进制的概念
- 进制的种类 2进制~36进制
- 2进制:0 1
- 3进制:0 1 2
- 8进制:0~7
- 10进制:0~9
- 16进制:0~9A-F
- …
- 36进制:09AZa~z
- 在JS中编写的number类型的值的数字基本上都是10进制的
- 进制的种类 2进制~36进制
把10进制转2进制
-
整数部分
-
除以2,取余数,用本次“商值”继续除以2…直到商数为0结束
-
把所有取的余数“倒过来”拼接
let num = 18; console.log(num.toString(2)); //toString中支持传递进制,把数字转换为指定进制的字符串 '10010' /* 18 / 2 = 9 余 09 / 2 = 4 余 14 / 2 = 2 余 02 / 2 = 1 余 01 / 2 = 1 余 1 ..... */
-
-
小数部分
-
乘以2,取整数部分,然后继续乘以2…
-
直到乘积是1,这样取完整只剩0,则停止相乘
let num = 0.3; console.log(num.toString(2)); //'01001100110011001....' /* 0.3 * 2 = 0.6 0 0.6 * 2 = 1.2 1 0.2 * 2 = 0.4 0 0.4 * 2 = 0.8 0 0.8 * 2 = 1.6 1 0.6 * 2 = 1.2 1 0.2 * 2 = 0.4 0 0.4 * 2 = 0.8 0 0.8 * 2 = 1.6 1 ..... */
-
问题:一定会有无限循环的
- 所有编程语言中,只要是浮点数(小数),就可能存在“丢失精准度”的问题,因为:浮点数在转换为2进制的时候,可能出现无限循环的值,但是计算机只能存储64位…
-
思考问题
-
为什么
0.1+0.2!==0.3
?-
console.log((0.1 + 0.2) === 0.3); //false
-
原因:
0.1 + 0.2=0.30000000000000004
?「以小组为单位探索这个问题」const the01All = (0.1).toString(2) const the02All = (0.2).toString(2) const the03All = (0.3).toString(2) const the034All = (0.30000000000000004).toString(2) console.log(the01All.slice(0,51),'0.1') console.log(the02All.slice(0,51),'0.2') console.log(the03All.slice(0,51),'0.3')//0.1与0.2的二进制相加得到0.3的二进制表示。但JavaScript中浮点数是有位数的,最多50位有效数字。故而0.3的二进制表示得到的是与0.30000000000000004相同的表示,而0.30000000000000004二进制表示方式中,反转成十进制时,得到的是0.30000000000000004。 console.log(the034All.toString(2).slice(0,51),'0.30000000000000004') console.log(the034All.toString(2).slice(0,51)===the03All.slice(0,51),'比较')
-
-
怎么解决精准度丢失的问题?
- 使用bigint数据类型计算
- 使用string进行计算及存储
案例
题目
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);
map()语法
/*map的语法:+ 数组中有多少项,就迭代多少次,对应的:传递进来的回调函数,就要被执行多少次+ 每一次函数执行,都会把当前迭代这一项的值和索引传递给函数+ 回调函数返回啥,就是把数组当前迭代这一项替换为啥「原始数组不变,以新数组返回」*/
let arr = [10, 20, 30];
arr = arr.map((item, index) => {// 执行三次// @1 item=10 index=0 return 0;// @2 item=20 index=1 return 20;// @3 item=30 index=2 return 60;return item * index;
});
console.log(arr); //[0,20,60]
parseInt()语法及进制转换
/*parseInt([value]):必须要确保[value]是个字符串,如果不是,则先把其转换为字符串;然后从字符串左侧开始查找,找到有效的数字字符(10进制的),直到遇到一个非有效数字字符结束(不论后面是否还有)则停止查找;把找到数字字符转换为数字(10进制的),如果一个都没找到,则结果是NaN!*/
console.log(parseInt('10px')); //'10' -> 10
console.log(parseInt('10.2px')); //'10' -> 10
console.log(parseInt('px10')); //'' -> NaN
console.log(parseInt(null)); //'null' -> NaN
console.log(parseInt('')); //->NaN
console.log(parseInt(true)); //->'true' -> NaN/*parseInt([value],[radix]):+ 如果没有写[radix],或者写的0,则默认是10进制「有特殊情况:如果字符串是以0x开始,则默认值是16」+ 如果[radix]的范围不在2~36之间,则直接返回NaN+ 从左侧[value]字符串中,找出所有符合[radix]进制的字符,最后把这些字符,看做[radix]进制,转为10进制!*/
console.log(parseInt('14635px'));
/* parseInt('14635px',10)找到所有符合10进制的字符 '14635' 最后转为10进制 14635*/console.log(parseInt('14635px', 5));
/* 找到所有符合5进制的字符 '14'把其看做5进制,转10进制的数字「知识点:把N进制转10进制 -> 按权展开求和」把N进制转10进制(个位数权重是0,十位数是1,百位数是2...) --> ... + 百位数*N^2 + 十位数*N^1 + 个位数*N^01*5^1 + 4*5^0 = 5 + 4 = 9*/
/*parseInt也是一个函数;数组有五项,所以迭代五次;每一次迭代,都把parseInt函数执行,并且把当前迭代这一项及其索引传递给parseInt;最后只要分析出parseInt每一次执行的结果,我们就知道最终数组每一项都是啥了!第一次迭代 parseInt(27.2,0) => 27parseInt('27.2',10)找到符合10进制的字符 '27'把其转换为10进制的数字 27第二次迭代 parseInt(0,1) => NaN没有1进制第三次迭代 parseInt('0013',2) => 1找到符合2进制的字符 '001'把其转换为10进制的数字 0*2^2 + 0*2^1 + 1*2^0 = 0+0+1 第四次迭代 parseInt('14px',3) => 1找到符合3进制的字符 '1'把其转换为10进制的数字 1*3^0第五次迭代 parseInt(123,4) => 27parseInt('123',4)找到符合4进制的字符 '123'把其转换为10进制的数字 1*4^2+2*4^1+3*4^0 = 16+8+3 => 27*/
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);//[ 27, NaN, 1, 1, 27 ]
变种:
let arr = [27.2, 0, 0013, '14px', 123];
arr = arr.map(parseInt);
console.log(arr);/*
只看第三项 parseInt(0013,2)浏览器看到 0013 后,首先会默认把其转换为10进制(8->10)0*8^3+0*8^2+1*8^1+3*8^0 = 0+0+8+3 = 11parseInt(11,2)parseInt('11',2)找符合2进制的字符 '11'把其转换为10进制的值 1*2^1+1*2^0 = 2+1 = 3
这一块有一个特殊的知识点:在JS中,以“0”开始的“数字”,浏览器会默认把其作为8进制转10进制
*/
进阶参考
- ES6官方规范 - 全英文,工作时要花一两年来啃
- 把数字转成进制数
- ES13官方规范 - 全英文,工作时慢慢追,一年一版,改url可以到另一版
更多推荐
20230313
发布评论