读书笔记-你不知道的js(中卷)

类型

值和类型

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]

强制类型转换

  • 返回基本类型
  • 动态类型语言的进行时
  • 将对象强制转化为string是通过ToPrimitive抽象完成的

ToString

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)

ToNumber

Object.create() 返回的对象,没有 valueof() 和 toString() 函数,无法进行强制类型转换

对象转换:

  1. 转换为基本类型值
  2. 非基本类型值,则强制转换

强制转换

  1. 抽象操作ToPrimitive,内部操作DefaultValue
  2. 检查是否有 valueOf 函数
  3. 检查是否有 toString 函数
  4. 产生 TypeError 错误
console.log(+true) // 1
console.log(+false) // 0
console.log(+undefined) // NaN
console.log(+null) // 0

ToBoolean

假值对象

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)

解析数字字符串

转换和解析的不同

  • 解析从左到右,遇到非数字字符就停止。
  • 转换不允许出现非数字字符,否则会失败返回NaN.
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'

规则:

  1. 如果其中一个是对象,则调用ToPrimitive抽象操作,调用[[DefaultValue]],转为数字的上下文。
  2. 如果无法使用valueOf取得基本类型值,则转而调用toString()。
  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

隐式类型转换为布尔值

  • if
  • while
  • for 第二个条件
  • ?:
  • || &&
|| &&

选择器运算符

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

规则:

  1. type(x)是数字,type(y)是字符串,则x == ToNumber(y)
  2. type(x)是字符串,type(y)是数字,则ToNumber(x) == y
字符串和布尔值比较, 布尔值转为数字,布尔值优先级最高。
let a = '12'
let b = true
a == b // false

规则:

  1. type(x)是布尔类型,则ToNumber(x) == y
  2. type(y)是布尔类型,则x == ToNumber(y)

建议不要使用 == true, == false 操作。

undefined null

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

then 鸭子类型
promise 问题
  • 调用过早
  • 调用过晚
  • 未调用
  • 调用次数过少、过多
  • 未能传递参数
  • 吞掉异常
    无法使用try-catch捕获异常
  • 信任问题

链式流

错误处理

  • 绝望的陷阱
  • 未捕获的错误
  • 成功的坑

promise模式

  • Promise.all([])
  • Promise.race()
  • 变体
  • 并发迭代

promiseApi 概述

  • new Promise
  • Promise.resolve
  • then…catch…
  • promise.all()
  • promise.race()

promise缺点

  • 顺序错误处理
  • 单一值(不算缺点
  • 单一决议
  • 惯性
  • 无法取消
  • 性能

生成器

打破完整运行
  • 输入和输出
  • 多个迭代器
生成器生成值
  • 生产者与迭代器
  • iterable
  • 生成器迭代器
异步迭代生成器
生成器+Promise
  • 支持promise的Generator Runner
  • 生成器中的promise并发
生成器的委托
  • 为什么
  • 消息委托
  • 异步委托
  • 递归委托
生成器并发
形实转换程序
之前的生成器

程序性能

性能测试与调优

高级异步模式

备注:

  1. 委托和promise及生成器需要反复阅读和练习查看效果,这里面的设计思想非常重要。
  2. 三本书前两本技术含量比较高,第三部基本是全面两本的复习和部分重复叙述(水文字凑数),建议大概快速翻看一下即可。生成器部分有增加了更详细的描述,不熟悉的话可以再次看下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值