js中的变量是没有类型的,只有值才有。
undefined 和 undeclared 是不同的,前者是已经申明但没赋值,后者是未定义。
var a
a // undefined
b // ReferenceError: b is not defined 报错
var a
typeof a // undefined
typeof b // undefined 不会报错,typeof有安全机制
写polyfill有用
delete 数组单元后,数组长度不会改变
数组下标可以传字符串,数字字符串会被转为数字类型(不建议使用)
字符串成员函数不会改变其原始值,会返回一个新的字符串。
let s = 'abc'
ss = s.toUpperCase()
ss === s // false
ss // ABC
// s.push('d') // 不能使用
可以使用数组形式取值,但不能使用数组形式修改对应的下标。
let s = 'abc'
s[1] // b
s.chatAt(1) // b
s[1] = d
s // abc
字符串可以借用数组的不可变更函数
let s = 'abc'
// s.join
// s.map
Array.prototype.join.call(a, '-')
Array.prototype.map.call(a, function (v) {
return v.toUpperCase() + '.'
}).join('')
对于无法使用数组可变更成员函数的,可以将字符串转数组,处理完后再转回来。
Array.prototype.reverse(s)
let ss = s.split('').reverse().join('')
ss
let a = 33.22
// 小数位数
a.toFixed(3) // 33.220
// 有效位数
a.toPrecision(3) // 33.2
// 有效但不建议写法
22..toFixed(2) // 22.00
// 无效写法
22.toFixed(2) // SyntaxError
22 .toFixed(2) // 22.00
es6
2 8 16 进制
推荐字母小写
误差
// Number.EPSILON
0.1 + 0.2
function isNumberEqual(n1, n2) {
return Math.abs(n1 - n2) < Number.EPSILON
}
let a = 0.1, 0.2
let b = 0.3
isNumberEqual(a, b) // true
最大值最小值,安全值
Number.MAX_VALUE
Number.MIN_VALUE // 无限接近0的值
Number.MAX_SAFE_INTEGER
Number.MIN_SAFE_INTEGER
整数检测
Number.isInteger(22) // true
Number.isInteger(22.22) // true
Number.isInteger(22.2) // false
安全整数
Number.isSafeInteger(Math.pow(2, 53)) //false
Number.isSafeInteger(Math.pow(2, 53) - 1) //true
32位有符号整数,53改为31
Math.pow(-2, 31)
Math.pow(2, 31) - 1
undefined 可当作变量赋值
null 不可赋值
void 运算符
返回 undefined用
function getData() {
if(!api.ready) {
return void setTimeout(getData, 300);
}
return res
}
if (getData) {
// coding...
}
等价于
if(!api.ready) {
setTimeout(getData, 300);
return
}
NaN
a = 6/ 'a'
typeof a // number
a === NaN // false NaN不等于自身
b = 'a'
isNaN(a) // 是否为非数字的值
// true
isNaN(b)
// true
Number.isNaN(a) // 判断的是数字的操作,操作中值里面带有非数字的部分则为false
// true
Number.isNaN(b)
// false
无穷数
有穷数可以去无穷数,反之不行
计算移除结果为 Infinity 或者 -Infinity
var a = 1 / 0 // Infinity
Infinity/Infinity // NaN
1/Infinity // Infinity
零值
var a = 0 / -3 //-0
var b = 0 * -3 // -0
a.toString() // '0'
a + '' // '0'
String(a) // '0'
JSON.stringify(-0) // '0'
+'-0' // -0
Number('-0') // -0
JSON.parse('-0') // -0
a == 0 // true
a === 0 // true
-0 === 0 // true
0 > -0 // false
0 < -0 // false
1/a === -Infinity // true
特殊等式
var a = 2 / 'foo'
var b = -3 * 0
Object.is(a, NaN)
Object.is(b, -0)
能使用==、===就不要使用Object.is()。性能高些。
值和引用
标量基本类型值是值复制
复合值是引用复制
不能修改对方的引用,可以修改共同值
var a = [1,2,3]
var b = a
a[3] = 4
a // [1,2,3,4]
b // [1,2,3,4]
b = [5,6,7]
a // [1,2,3,4]
b // [5,6,7]
function fn(a) {
a.push(5)
console.log(a) // [1,2,3,4,5]
a = [1,2,3]
console.log(a) // [1,2,3]
}
fn(a)
a // [1,2,3,4,5]
修改引用
var a = [1,2,3,4]
function fn(a) {
a.push(5)
console.log(a) // [1,2,3,4,5]
a.length = 0 // 清空数组
a.push(1,2,3)
console.log(a) // [1,2,3]
}
fn(a)
a // [1,2,3]
fn(a.slice()) // 传递浅副本
封装标量值到复合值,从而修改变量值
var obj = {
a:1
}
function fn(wrapper) {
wrapper.a = 2
}
fn(obj)
obj.a // 2
内部属性[[class]],内部的分类,基本类型值被称为被各自的封装对象自动包装
Object.prototype.toString.call(/abc/i)
// '[object RegExp]'
Object.prototype.toString.call([1,2,3])
// '[object Array]'
Object.prototype.toString.call({a:1})
// '[object Object]'
Object.prototype.toString.call(123)
// '[object Number]'
Object.prototype.toString.call('123')
// '[object String]'
Object.prototype.toString.call(true)
// '[object Boolean]'
Object.prototype.toString.call(null)
// '[object Null]'
Object.prototype.toString.call(undefined)
// '[object Undefined]'
封装对象包装
基本类型无.length
,.toString
属性和方法,需要通过封装对象才能访问
var a = 'abc'
a.length // 3
a.toUpperCase() // 'ABC'
不要提前自己封装,让引擎自己去操作,引擎会自己优化,自己去优化反而效率更低
自行封装对象基本类型值
var a = Object('abc')
typeof a // 'object'
a instanceof String // true
拆封
a.valueOf() // abc
原生函数作为构造函数
var a = new Array(3) // [empty*3]
var b = [undefined, undefined, undefined] // [undefined, undefined, undefined]
var c = [] // [empty*3]
var d = [,,,] // [empty*3]
var e = Array.apply(null, {length: 3}) // [undefined, undefined, undefined]
c.length = 3
a.join('-') // '--'
b.join('-') // '--'
a.map(function(v, i){return i}) // [0,1,2]
b.map(function(v, i){return i}) // [empty*3]
let ary = [1,2,3]
ary.toString() // 1,2,3
JSON.stringify 字符串化,非强制类型转换
在对象中遇到 undefined, function, symbol 时会自动忽略,数组中则转为null
不安全的JSON:
类型:undefined,function,symbol和包含循环引用(对象之间互相引用,形成无限循环)
返回值:undefined
处理不安全JSON:
定义toJSON方法来返回一个安全的JSON值
var o = {}
var a = {
b:1,
c:o,
d: function(){},
e: [1,2,3]
}
o.f = a // 创建循环引用
// JSON.stringify(a)
a.toJSON = function() {
return {
b: this.b,
e: this.e.slice(1)
}
}
JSON.stringify(a)
Object.create() 返回的对象,没有 valueof() 和 toString() 函数,无法进行强制类型转换
对象转换:
强制转换
console.log(+true) // 1
console.log(+false) // 0
console.log(+undefined) // NaN
console.log(+null) // 0
let a = new Boolean(false)
let b = new Number(0)
let c = new String('')
d = a && b && c // d String{''}
e = new Boolean(a && b && c) // true
document.all的历史以及被设置为假值以此废弃旧的代码判断,不再执行ie兼容程序。
let a = '3.14'
let b = +a // 3.14
let c = 1 + - + + - + + 1 // 2 负负得正
// 转换日期 时间戳
let d = new Date([2022, 10,1])
+d // 不建议
let t = +new Date() // 不建议
Date.now() // 建议
let tt = new Date().getTime() // 建议
~:将对象转换为数字类型并且取反。返回2的补码。
~42 // -(42+1) -43
-1:哨位值
抽象渗漏:保留了底层的细节
一般用来取代正数Math.floor()的处理
只适合32位
对负数处理与Math.floor()不同
~~12.1 // 12
~~-12.1 // -12
Math.floor(12.1) // 12
Math.floor(-12.1) // -13
// 按位操作速度快于函数
12.1 | 0 // 12
-12.1 | 0 // -12
// 同上可取代,parseInt(12.1, 10),parseInt(-12.1, 10)
转换和解析的不同
let a = '22'
let b = '22px'
Number(a) // 22
parseInt(a) // 22
Number(b) // NaN
parseInt(b) // 22
parseInt的历史问题
es5前,下面的场景会出现问题:
let h = "08"
let s = '09'
let time = parseInt(h) + ':' + parseInt(s) // 0:0
parseInt(string, radix)
,string
的第一个字符会被默认为radix
的值,0开头即为8进制。
所以es5前需要parseInt('08', 10)
这样的写法。
es5后默认radix
为10,radix
的有效值为 0-9,a-i(区分大小写) 19最高
parseInt一般用来解析数字字符串,对于非数字字符串
对于非数字字符串,parseInt会先将其强制转换为字符串。
parseInt(1/0, 19) // 18
// =>
parseInt('Infinity', 19) // 解析了i,以19为基数时值为18.
建议用Boolean或!!来让代码更清晰易读
// 强制转换JSON里面的数据
let a = [
1,
function() {}
]
JSON.stringify(a) // [1, null]
JSON.stringify(a, function(k, v) {
if(typeof v === 'function') {
return !!v
} else {
return v
}
}) // '[1, true]'
隐式强制类型转换的作用是减少冗余,让代码更简洁
var a = [1]
var b = [2,3]
a + b // '12,3'
规则:
所以以上执行数组相加,会先执行valueOf,由于无法获取基本类型值,所以将数组转为调用toString(),最后是字符串拼接。
也就是会呈现出先执行数字运算,后执行字符串运算的效果。
[] + {} // '' + '[object object]' = '[object object]'
{} + [] // 0
a + ‘’ 和 String(a)的不同
let a = {
valueOf: function() {
return 1
},
toString: function() {
return 2
}
}
a + '' // 1
String(a) // 2
a + ''
先调用valueOf
String(a)
直接调用toString
let a = [1]
let b = [2]
a - b // -1
查看传入几个布尔值
隐式强制转换
function fn() {
var sum = 0
for(let i = 0; i < arguments.length; i++) {
if (arguments[i]) {
sum += arguments[i]
}
}
return sum === 1
}
fn(true, false, false) // true
显式强制转换
function fn() {
var sum = 0
for(let i = 0; i < arguments.length; i++) {
sum += Number(!!arguments[i])
}
return sum === 1
}
fn(true, false, false) // true
选择器运算符
let a = 11
let b = 'abc'
let c = null
a || b // 11
a && b // 'abc'
c || b // 'abc'
c && b // null
function fn (a, b) {
a = a || 'hello'
b = b || 'world'
}
fn() // 'hello world'
fn('this is j', '') // 'this is j world'
function foo() {console.log(d)}
let d = 1
d && foo() // 1 短路
有和没有!!的差别
let a = 11
let b = null
let c = 'abc'
if(a && (b || c)) {
console.log('ok') // ok
}
这里(b || c) -> 'abc'
然后a && 'abc' -> 'abc'
最后'abc' => true
使用!!
let a = 11
let b = null
let c = 'abc'
if(!!a && (!!b || !!c)) {
console.log('ok') // ok
}
避免隐式转换
symbol属于特殊情况,暂时跳过
性能不需要在意
区别:==允许在比较中隐式强制类型转换。===不允许。
var a = 11
var b = '11'
a == b // true
a === b // false
规则:
let a = '12'
let b = true
a == b // false
规则:
建议不要使用 == true, == false 操作。
undefined == null
var a = [11]
var b = 11
a == b // true
var a = 'abc'
var b = Object(a)
a == b // true
a === b // false
var a = null
var b = undefined
var c = NaN
var x = Object(a)
a == x // false
var y = Object(b)
b == y // false
var z = Object(c)
c == z // false
null,undefined不能够被拆封,所以调用Object.create(null)返回对象
NaN被拆封为NaN,NaN == NaN => false
[] == ![] // true
如果在浏览器上输入[]则会是false,因为[]会先执行toString,转为’‘,最后才转为false。
根据类型判断,右边是布尔值。所以根据布尔值和其他类型规则判断,左边会转换为布尔值来和右边比较。
布尔值强制类型转换,即ToBoolean规则规定:undefined,null,false,’‘,+0,-0,NaN 是假值,其他为真值。
先执行![], 执行ToBoolean规则操作,[]为true,![]为false。
接着执行左边的[], []会调用内部的ToPrimiter去调用valueOf,valueOf因为[]是对象无法转为数字,所以交给toString去执行.
[]的toSting是’‘字符串,’'的强制类型转换为布尔值便是 false。
2 == [2] // true
'' == [null] // true
强制类型转换中,数组valueOf返回数组本身,所以数组最后会被字符串化。
0 == '\n' // true
'', ' ', '\n'
会被ToNumber转换为0
先调用ToPrimitive,是否是数字,否则则转为字符串,字符串按照字母顺序比较。
var a = [11]
var b = ['22']
a < b // true
var a = ['22']
var b = ['012']
a < b // false 字符串则按照字母顺序来比较
var a = [1,2]
var b = [0,3,4]
a < b // false
var a = {b:1}
var b = {b:1}
a < b // false,全部转为[object object]
a == b // false
a > b // false
a <= b // true 被处理为 b < a,然后结果反转。<= 实质是 !(a > b),处理为!(b < a)
a >= b // true
链式流
错误处理
promise模式
promiseApi 概述
promise缺点
备注: