JS 至今有 8 种数据类型
- number
- string
- boolean
- symbol
- undefined
- null
- object
- bigint
数组、函数、日期不是数据类型,它们是特殊的对象
number
JS 里 number 是由64位浮点数组成的
写法
整数、小数、科学记数法、八进制、十六进制、二进制
特殊值
- 正0 和 负0
- 无穷大 Infinity、+Infinity、-Infinity
- 无法表示的数字 NaN ( not a number ),NaN是一个数字
- NaN != NaN
64位浮点数
存储方式
- 浮点就是浮动的点,意思就是小数点会乱动
- 123.456 可以表示为 1.23456e10^2
- 也可以表示为 12345.6e10^-2
- 符号占1位
- 指数占11位 ( -1023~1024 )
- 有效数字占52位 (开头的1省略)
范围和精度
范围(忽略指数位)
- 指数拉满、有效数字拉满,得到最大二进制数字
- Number.MAX_VALUE: 1.7976931348623157e+308
- 指数负方向拉满、有效数字最小1,得到最小值
- Number.MIN_VALUE: 5e-324
精度(有效数字)
- 最多只能到52+1个二进制位表示有效数字
- 2^53 对应的十进制是 9 后面 15 个零
- 所以15位有效数字都能精确表示
- 16位有效数字如果小于 90 开头,也能精确表示
- 9110000000000001 就存不下来
string
每个字符两个字节
写法
- ''单引号、""双引号、``反引号
- 引号不属于字符串的一部分
转义
- \' 表示 '
- \" 表示 "
- \n 表示换行
- \r 表示回车
- \t 表示 tab 制表符
- \\ 表示 \
- \uFFFF 表示对应的 Unicode 字符
- \xFF 表示前 256 个 Unicode 字符
多行字符串
用 ``反引号可以在字符串内回车
字符串的长度
- string.length
- '123'.length // 3
- '\n\r\t'.length // 3
- ''.length // 0
- ' '.length // 1
字符串的下标
- string[index]
- let s = 'hello';
- s[0] // "h"
- 下标从 0 开始,最后的下标为长度减一
base 转码
- window.btoa 正常字符串转为 Base64 编码的字符串
- window.atob Base64 编码的字符串转为原来的字符串
boolean
只有两个值,ture 和 false
- 否定运算 !value
- 相等运算 1 == 2、1 != 2、3 === 4、3 !== 4
- 比较运算 1 > 2、1 >= 2、3 < 4、3 <= 4
五个 falsy 值
- falsy 就是相当于 false 但又不是 false 的值
- 分别是 undefined、null、0、NaN、''
- '' 和 ' ' 是不一样的
- 除了五个 falsy 值和 false ,其他都为真
undefined 和 null
它们没有本质的区别
- 如果一个变量声明了,但没有赋值,那么默认值就是 undefined,而不是 null
- 如果一个函数,没有写 return,那么默认 return undefined,而不是 null
- 习惯上,把非对象的空值写为 undefined,把对象的空值写为 null
symbol
- Symbol() 函数会返回 symbol 类型的值
- 每个从 Symbol() 返回的 symbol 值都是唯一的
- 一个 symbol 值能作为对象属性的标识符
bigint
- bigint 可以表示任意大的整数
- 可以用在一个整数字面量后面加 n 的方式定义一个 bigint
- 不能用于 math 对象中的方法;不能和任何 number 实例混合运算
- bigint 变量在转换成 number 变量时可能会丢失精度
object
object 是数据类型中唯一一种复杂类型
可以包含其他的数据类型,也包括自己
定义
- 无序的数据集合
- 键值对的集合
写法
- let obj = { 'name': 'abc', 'age': 18 } // 简单写法
- let obj = new Object({'name': 'abc'}) // 正规写法
- console.log({ 'name': 'abc, 'age': 18 }) // 直接匿名对象,在firefox里,如果不写console.log 那就不是对象了,只是个标签,内容是冒号后面的值
细节
- 键名是字符串,不是标识符,可以包含任意字符
- 引号可省略,省略之后就只能写标识符
- 即使省略了引号,键名也还是字符串
属性名:每个 key 都是对象的属性名(property)
属性值:每个 value 都是对象的属性值
所有属性名会自动变成字符串
- let obj = { 1: 'a', 3.2: 'b', 1e2: true, 1e-2: true, .234: true, 0xFF: true }; Object.keys(obj) => [ "1", "3.2","100", "0.01", "0.234","255" ]
- Object.keys(obj) 可以得到 obj 的所有 key
- JS 会先把键名1e2变成数字,再把数字结果变成字符串
- 最保险的方法就是加上引号
变量名做属性名
- let p1 = 'name'
- let obj = { p1 : 'frank'} 这样写,属性名为 'p1'
- let obj = { [p1] : 'frank' } 这样写,属性名为 'name'
对比
- 不加 [ ] 的属性名会自动变成字符串
- 加了 [ ] 则会当做变量求值
- 值如果不是字符串,则会自动变成字符串
对象的隐藏属性
- JS 中每一个对象都有一个隐藏属性
- 这个隐藏属性储存着其共有属性组成的对象的地址
- 这个共有属性组成的对象叫做原型
- 也就是说,隐藏属性储存着原型的地址
代码示例
- var obj = {}
- obj.toString() // 不报错
- 因为 obj 的隐藏属性对应的对象上有 toString()
除了字符串,symbol 也能做属性名
- let a = Symbol()
- let obj = { [a]: 'Hello' }
对象属性的增删改查
- 改和增属于写,查属于读
- 改和增的时候只能针对自身,不会看对象的原型链
- 查的时候会看原型链
删除对象的属性
删除属性名和属性值
- delete obj.xxx 或 delete obj['xxx']
- delete 会把属性名和属性值都删掉
- delete 只能删属性,不能删对象
- obj.xxx = undefined 或 obj['xxx'] = undefined
- undefined 只会使属性值为空,对属性名无影响
- JS 中一个属性被删两次不会报错
是否在对象中做属性名(判断属性名)
- 'xxx' in obj // ture | false
- 意思就是 'xxx'是不是 obj 的属性名
属性名是否在对象中,且是值为 undefined(判断属性值)
- 'xxx' in obj && obj.xxx === undefined
- 'xxx' 在 obj 里面,但是值为 undefined
注意 obj.xxx === undefined
- 不能断定 'xxx' 是否为 obj 的属性
- 因为 undefined 只能判断属性值
var obj = { name:'jack' , age:18 } delete obj.name | obj [name] obj // {age: 18} 'age' in obj // true 'name' in obj // false
var obj = { name:'jack' , age:18 } obj.name | obj [name] = undefined obj // {name: undefined, age: 18} 'name' in obj && obj['name'] === undefined // true
let obj = {} let obj2 = {x:undefined} obj.x === undefined // true obj2.x === undefined // true 'x' in obj // false 'x' in obj2 //true
查看所有属性(读属性)
查看自身所有属性
- Object.keys(obj) // 查看 obj 的属性名
- Object.values(obj) // 查看 obj 的属性值
- Object.entries(obj) // 查看 obj 的属性名和属性值
- 不会查看共有属性
查看自身+共有属性
- console.dir(obj) // 以目录的形式打出
- 或者自己依次用 Object.keys 打印出 obj.__proto__ (不推荐)
判断一个属性是自身的还是共有的
- obj.hasOwnProperty('toString') // 查看自身是否有这个属性
原型
每个对象都有原型
- 原型里存着对象的共有属性
- 比如 obj 的原型就是一个对象
- obj.__proto__ 存着这个对象的地址
- 这个对象里有 toString / constructor / valueOf 等属性
对象的原型也是对象
- 所以对象的原型也有原型
- obj = {} 的原型即为所有对象的原型
- 这个原型包含所有对象的共有属性,是对象的根
- 这个原型也有原型,是 null
查看属性方法
- 中括号语法:obj['key']
- 点语法:obj.key
- 坑新人语法:obj[key] // 变量 key 值一般不为 'key'
- 优先使用中括号语法,点语法会产生误导,让你以为 key 不是字符串
- obj.name 等价于 obj['name'] obj.name 不等价于 obj[name]
- 简单来说,这里的 name 是字符串,而不是变量
var obj = { name:'jack' , age:18 } obj.name // "jack" obj['name'] // "jack" obj[name] // undefined obj [ console.log('age') ] // undefined // log的返回值是undefined
修改或增加属性(写属性)
直接赋值 let obj = {name: 'frank'} // name 是字符串
- obj.name = 'frank' // name 是字符串
- obj['name'] = 'frank'
obj[name] = 'frank'// 错,因 name 值不确定- obj['na'+'me'] = 'frank'
- let key = 'name'; obj[key] = 'frank'
- let key = 'name';
obj.key = 'frank'// 错 因为 obj.key 等价于 obj['key']批量赋值
Object.assign(obj, {age: 19, gender: 'man'}) // assign 就是赋值的意思
Object.assign( obj,{age:19,gender:'man'} ) obj // {name: undefined, age: 19, gender: "man"}
修改或增加共有属性
无法通过自身修改或增加共有属性
- let obj = {}, obj2 = {} // 共有 toString
- obj.toString = 'xxx' 只会在改 obj 自身属性
- obj2.toString 还是在原型上
如果非要修改或增加原型上的属性
- obj.__proto__.toString = 'xxx' // 不推荐用 __proto__
- Object.prototype.toString = 'xxx'
- 一般来说,不要修改原型,会引起很多问题
修改隐藏属性
不推荐使用 __proto_
let common = {apple:'red',bananer:'yellow'} let look = {fruit:'color'} look.__proto__ = common console.dir(look) // fruit: "color" __proto__: apple: "red" bananer: "yellow" __proto__:
推荐使用 Object.create
let common = {apple:'red',bananer:'yellow'} let look = Object.create( common, {fruit: { value: 'color'} } ) console.dir (look) // fruit: "color" __proto__: apple: "red" bananer: "yellow" __proto__:
- 规范大概的意思是,要改就一开始就改,别后来再改
- let look = Object.create ({'name': 'jack'}) // {} // __proto__ // name: "jack" // 属性在原型
- let cook = new Object ({'name': 'joy'}) // {'name': 'joy'} // 属性在自身
增删改查总结
删
- delete obj ['name'] | delete obj.name
- 'name' in obj // 是判断 "name" 是否为 obj 的属性名
- obj.hasOwnProperty('name') // 是判断 "name" 是否是 obj 的自身属性
查
- Object.keys(obj) // 查对象所有属性名
- console.dir(obj) // 以目录形式打出
- obj['name'] // 查对象属性
- obj.name // 记住这里的 name 是字符串
- obj[name] // 记住这里的 name 是变量
改
- obj['name'] = 'jack' // 改自身
- Object.assign(obj, {age:18, ...}) //批量改自身
- obj.__proto__['toString'] = 'xxx' // 改共有属性(不推荐)
- Object.prototype['toString'] = 'xxx' // 改共有属性改原型
- obj.__proto__ = common // 改原型(不推荐)
- let obj = Object.create(common) // 改原型
- 所有 __proto__ 代码都是强烈不推荐写的
增
- 基本同改:已有属性则改;没有属性则增。
变量声明
变量声明指定了值也指定了类型,但值和类型都可以随时变化
三种声明方式
- var a = 1
- let a = 1 //变量声明
- const a = 1 //常量声明
区别
- var 是过时的、不好用的方式
- let 是新的,更合理的方式
- const 是声明时必须赋值,且不能再改的方式
var 变量提升
JS 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升。
- let 的「创建」过程被提升了,但是初始化没有提升
- var 的「创建」和「初始化」都被提升了
- function 的「创建」「初始化」和「赋值」都被提升了
- const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程
let 声明
- 遵循块作用域,即使用范围不能超出 { }
- 不能重复申明
- 可以赋值,也可以不赋值
- 必须先声明再使用,否则报错
- 全局声明的 let 变量,不会变成 window 的属性
- for 循环配合 let 有奇效
const 声明
跟 let 几乎一样,除了声明时就要赋值,赋值后不能改
类型转换
number 转 string
- String(n)
- n + ''
string 转 number
- Number(s)
- parseInt(s) / parseFloat(s) ES6之后 parseInt 后面的数字不用加,10
- s - 0 / +s
x 转 boolean
- Boolean(x)
- !!x (!! 取原始布尔值)
x 转 string
- String(x)
- x.toString()
- 1.toString() 会报错,JS 认为1.是小数的开始,后面需要加数字,解决方法1:(1).toString() ;解决方法2:1..toString()
JavaScript 秘密花园中记录了 JS 中的各种奇葩事情
*本文为鲲游北冥的原创文章,著作权归本人和饥人谷所有,转载务必注明来源