JavaScript-数据类型

2020/7/4 JavaScript
// 简单数据类型(不可改变,只能复制,按值传递)
1. Symbol
2. undefined
3. Boolean
4. Number
5. String
6. 值不可修改
7. 保存在栈内存中
8. 按值访问
9. 比较时值相等即相等
10. 复制时创建一个副本
11. 按值传递参数
12.typeof检测类型
----------------------------------------------------------------------------------------------
// 复杂数据类型(引用,可以改变,按引用传递)
1. Object
2. Array
3. Function
4. null
5. 值可以修改
6. 保存在堆内存中
7. 按引用访问
8. 比较时同一引用才相等
9. 复制其实是指针
10. 按值传递参数
11.instanceof检测类型
----------------------------------------------------------------------------------------------
// 栈和堆
1. 栈从上往下落,堆从下往上升,一般不会重合(内存)
----------------------------------------------------------------------------------------------
// 简单数据类型检测,返回值String, Number, Boolean, Object, undefined, function
1. typeOf(1) // 返回Numbner
    1. 能识别所有简单数据类型
    2. 识别函数
    3. 判断是否是引用类型(不可再细分,object)
    4. typeof null === 'object' // 历史问题
    5. typeof Symbol === 'function'
    6. typeof NaN === 'number'
    7. typeof document.all === 'undefined'
2. undefined == null // true, undefined是null派生出来的
3. NaN // 非数值
4. NaN !== NaN // true
5. isNaN(参数) // 判断参数是否是非数值,返回Boolean,会尝试把参数转为数字类型如 id = '16'
6. null 是关键词,而 undefined 是变量
7. typeof 性能比 instanceof 性能高
8. undefined 在旧版本的 IE 中是可以被改写的,如果要判断是不是 undefined,可以使用 'xx' === void 0
  1. void 0 返回的一直是 undefined,即使 undefined 被改写了,也可以是 void 10/100/1000
----------------------------------------------------------------------------------------------
// 复杂数据类型检验,typeof遇到复杂数据类型只会返回object
1. [] instanceof Array // 返回true
2. Object.prototype.toString 可以用来判断变量类型,和普通对象上的 toString 作用不一样
  1. Symbol.toStringTag 这个方法可以自定义 Object.prototype.toString 的返回值
     当你设置了 Symbol.toStringTag,再使用 Object.prototype.toString 时,返回的值
     是你设置的值,而不是原有的类型
----------------------------------------------------------------------------------------------
// 数值转换
1. Number(参数) // 参数要以数字开头, 会忽略开头0和空格,如果转换字符串则返回NaN,有两个参数的第二个为进制
2. parsetInt(参数) // 参数要以数字开头, 会忽略开头0和空格,如果转换字符串则返回NaN,有两个参数的第二个为进制
3. parseFloat(参数) // 参数要以数字开头, 会忽略开头0和空格,如果转换字符串则返回NaN,有两个参数的第二个为进制,第一个小数点有效
----------------------------------------------------------------------------------------------
// 字符串转换
1. String(参数) // 在不知道参数的类型时使用
2. toStirng() // 返回str的一个副本
----------------------------------------------------------------------------------------------
// boolean转换
1.0之外的所有数字转换为布尔值都为true
2. 除空之外的所有字符串转换为布尔值都为true
3. nullundefined转换为布尔值为false
4. 比较操作符返回值为boolean
----------------------------------------------------------------------------------------------
// 逻辑与操作符转换
逻辑与(只要有一个条件不成立则返回false)
如果操作数不是布尔值时:
1. 如果第一个操作数隐式类型转换后为true则返回最后一个操作数
2. 如果第一个操作数隐式类型转换后为false则返回第一个操作数
3. 有一个操作数是null则返回null
4. 有一个操作数是undefined则返回undefined
----------------------------------------------------------------------------------------------
// 逻辑或操作符转换
逻辑或(只要有一个条件成立则返回true)
如果操作数不是布尔值时:
1. 如果第一个操作数隐式类型转换后为true则返回第一个操作数
2. 如果第一个操作数隐式类型转换后为false则返回第二个操作数
3. 如果有两个操作数是null则返回null
4. 如果有两个操作数是undefined则返回undefined
5. 如果有两个操作数是NaN则返回NaN
----------------------------------------------------------------------------------------------
// 逻辑非操作符转换
1. 无论操作数是什么数据类型,都会返回一个布尔值
2. !!对该布尔值求反
----------------------------------------------------------------------------------------------
// 二元操作符 '+' 规则
1. 如果操作数是对象,则对象会转换为原始值
  1. 对象转为原始数据类型的值
    1. Symbol.ToPromitive
    2. Object.prototype.valueOf
    3. Object.prototype.toString
  2. 优先级按上述进行,valueOf 如果返回的不是原始值(即不是简单数据类型)的话,就会继续调用 toString
2. 如果其中一个操作数是字符串的话,另一个操作数也会转换成字符串,进行字符串拼接
3. 否则,两个操作数都将转换为数字或NaN,进行加法操作
4. 示例
  1. [] + []
    1. 返回 ''
    2. 因为 [][Symbol.ToPromitive] 不存在
    3. 继续执行 Object.prototype.valueOf,返回 [],不是原始值
    4. 继续执行 Object.prototype.toString,返回 ''
  2. [] + {}
    1. 返回 '[object Object]'
    2. [] 上面已解释,{}[Symbol.ToPromitive] 不存在
    3. 继续执行 Object.prototype.valueOf,返回 {},不是原始值
    4. 继续执行 Object.prototype.toString,返回 '[object Object]'
  3. {} + []
    1. 这个如果是在非浏览器的控制台中手动输入时,返回值和第 2 题结果一样,如果是在浏览器控制台中输入时则返回的是 0
    2. 在控制台中 {} 会被认为是一个语句,也就是说 {} + [],就变成了 +[]
    3.+[] 相当于 +'',所以返回的是 0
  4. {} + {}
    1. 这个如果是在非浏览器的控制台中手动输入时,返回值是 '[object Object][object Object]'
    2. 如果是在 chore 浏览器的控制台中手动输入时,返回值也是 '[object Object][object Object]'
    3. 如果是在非 chore 的浏览器的控制台中手动输入时,返回是 NaN
      1. 原因是第一个 {} 被当成了语句,所以 {} + {} 相当于 +{}
      2.+{} 相当于 +'[object Object]',所以返回的是 NaN
----------------------------------------------------------------------------------------------
// 可选链
const user = {
  address: {
    street: '街道',
    getNum() {
      return '80号'
    }
  }
}
1. 常规获取
  const street = user && user.address && user.address.street
  const num = user && user.address && user.address.getNum && user.address.getNum()
2. 可选链获取
  const street = user?.address?.street
  const num = user?.address?.getNum?.()
----------------------------------------------------------------------------------------------
// 空值合并运算符
1. 常规
  const b = null
  const a = b || 5 // 当 b 隐式转换是 true 时取 b,否则取 5
2. 空值合并运算符
  const b = null
  const a = b ?? 5 // 只有 b 是 null 或 undefined 时才会取 5,而常规中 b 的值是 0 或空字符或 false 都会取 5
----------------------------------------------------------------------------------------------
// truly和falsely
1. !!变量 === true // truly变量
2. !!变量 === false // falsely变量
3. if里判断就是这两种变量
----------------------------------------------------------------------------------------------
// js错误类型
1. syntaxError // 语法错误
    1. 符号漏打、多大、少打、错打
    2. 使用了不符合语法的变量名
    3. 语句写错,没写完
    4. 有些能被捕获,有些不能
      1. 不能捕获,解析时就会报错
        try {
          var ccc jjj
        } catch(e) {
          console.log(e)
        }
      2. 可以捕获
        try {
          new Function('var ccc jjj')
        } catch(e) {
          console.log(e)
        }
2. referenceError // 变量引用异常或未定义
  1. 一个不存在的变量被引用时发生的错误
3. typeError // 类型使用错误
  1. 值的类型非预期类型时发生的错误
4. ranggeError // 递归爆栈,深度太深错误
  1. 当一个值不在其所允许的范围或集合中就会抛出此错误
    try {
      new Array(Number.MAX_VALUE)
    } catch (e) {
      console.log(e)
    }
5. 捕获错误
    try {
      code
    } catch(ex) {
      code
    } finally {
      code
    }
6. throw new Error('err') // 抛出错误
7. debugger // 打断点
8. Error 是基础的错误对象,其他错误对象均继承这个
  1. name,错误名
  2. message,错误文本消息
  3. stack,错误堆栈信息
9. EvalError,现在不会出现这个错误,历史遗留的错误
  1. 它产生的原因是当不是直接被调用而是被赋值时会产生
  try {
    new eval()
    eval = function() {}
  } catch(e) {
    console.log(e) // 现在会抛出 TypeError
  }
10. InternalError,只有 firefox 支持
  1. 它产生的原因是过多的 case 语句、正则表达式中括号过多、递归过深等
11. URIError,URI 处理函数而产生的
  1. 如果出现 URIError 一定是 URI 处理函数产生的
    try {
      encodeURI('\uD800')
    } catch(e) {
      console.log(e)
    }
  2. 但是 URI 处理函数产生的错误不一定是 URIError
    try {
      decodeURIComponent('\ud*00')
    } catch(e) {
      console.log(e)
    }
12. AggregateError,包含多个错误信息的错误
  Promise.any([
    Promise.reject(new Error('error1')),
    Promise.reject(new Error('error2')),
  ]).catch(e => {
    console.log(e instanceof AggregateError) // true
    console.log(e.message)
    console.log(e.name)
    console.log(e.errors) // 多了这个属性
  })
13. catch 的不一定是 Error 对象,在 js 中 throw 可以抛出基本类型的数据,尽量抛出准确的错误,如 TypeError('类型错误')
14. 捕获到错误的思考
  1. 是否致命,会不会导致连带错误
  2. 是否会影响用户操作
  3. 是否需要将错误信息反馈给用户
  4. 是否将错误上报
  5. 是否需要抛出错误
15. 在 es5 中想自定义错误类型时,需要手动调用一下 Error.captureStackTrace() 这个方法,否则没有 tack 属性
16 错误类型怎么判断
  1. instanceof // 注意 AggregateError 浏览器兼容问题,使用时可能是 undefined
  2. constructor // 可以被改写
  3. Error.prototype.name // 可以被改写
17. 异常捕获
  1. try catch,可疑区域
  2. window.onerror,全局没有处理的错误,已处理的错误不会捕获
    1. 我们常见到的 script error 这个错误,但是具体的错误信息却是 null
       原因是引用了跨域的 js,然后 js 报错了,如果想要得到具体的错误信息,
       则 js 文件服务器需要增加 cors 头,即 Access-Control-Allow-Origin: '*',
       然后如果是通过 <script crossorigin="anonymous"> 标签引入的,则需要增加 crossorigin="anonymous" 属性,
       如果是动态加载的 js,则 script.crossOrigin = true
  3. window.addEventListener('error'),这个和 window.onerror 差不多,但是它可以捕获静态资源的错误
      在第三个参数中需要配置成捕获,默认是冒泡,不然捕获不到静态资源的错误,
      捕获到的静态资源错误没办法区分 404 还是 500,需要结合服务端日志
  4. unhandledrejection/rejectionhandled,捕获 promise 错误
    1. 两个都可以通过 addEventListener 去进行监听
    2. unhandledrejection 当 promise 被 reject 时并且没有对应的 catch 处理,此时就会触发此事件
    2. rejectionhandled 当 promise 被 reject 时,虽然有 catch 处理,但是 catch 处理又不在同步上下文中,
       当被 catch 时,也会触发此事件
        window.addEventListener('rejectionhandled', e => {
          console.log(e)
        })
        const p = new Promise((resolve, reject) => {
          reject('error')
        })
        setTimeout(() => {
          p.catch(e => {
            console.log(e)
          })
        }, 1000)
  5. XMLHttpRequest/fetch/axios,网络请求,都有自带的错误处理事件,如 onerror,catch 等
  6. React ErrorBoundary,react 异常
    1. 子组件的渲染
    2. 生命周期函数
    3. 构造函数
    4. 主要能捕获上面的 3 种异常,对应自定义事件如 onClick 里面的异常是捕获不到的
  7. Vue errorHandler,vue 异常
18. 异常上报
  1. 使用 sendBeacon 上报的优势
    1. 数据发送可靠
    2. 数据异步传输
    3. 不影响下一个导航的载入
  2. 使用 gif 上报的优势
    1. 图片 scr 属性可以直接跨域访问
    2. 相比 png/jpg 来说 gif 体积最小,合法的 gif 只要 43 个字节
    3. 一般采用 1*1 像素透明色来上报,不存储色彩空间的数据,节约体积
----------------------------------------------------------------------------------------------
// 隐式类型转换
1. 当预期的类型和实际的类型不一致时就会发生隐式类型转换
2. 对象隐式转换规则
  1. Symbol.ToPromitive
    1. 有个 hint 参数,值有三种:string/number/default
    2. 值是 js 引擎自动推断的
    3. 值为 string 的情况
      1. window.alert(obj)
      2. 模板字符串 `${obj}`
      3. test[obj] = 123
    4. 值为 number 的情况
      1. 一元+,位移
      2. -*/,关系运算符
      3. Math.pow、String.prototype.slice 等内部方法
    5. 值为 default 的情况
      1. 二元+
      2. ==!=
  2. Object.prototype.valueOf
  3. Object.prototype.toString
  4. 如果 [Symbol.ToPromitive](hint) 方法存在,优先调用,无视 valueOf 和 toString 方法
  5. 否则,如果期望是 'string' 时,则先调用 toString,如果返回不是原始值,则继续调用 valueOf
  6. 如果期望是 'number''default' 时,则先调用 valueOf,如果返回不是原始值,则继续调用 toString
  7. 特殊的 Date,如果 hint 是 'default' 时,会优先调用 toString,然后调用 valueOf
----------------------------------------------------------------------------------------------
// 浅拷贝和深拷贝
1. 浅拷贝
  1. 对象
    1. ES6 扩展运算符
    2. Object.assign
    3. for in 和其他的一层遍历复制
  2. 数组
    1. ES6 扩展运算符
    2. slice
    3. concat
2. 深拷贝
  1. JSON.stringify + JSON.parse
    1. 优点:天然无污染
    2. 缺点:
      1. 只能复制普通键的属性,Symbol 类型的无能为力
      2. 循环引用对象,比如 window 不能复制
      3. 函数、Date、Rege、Blob 等类型不能复制
      4. 性能差
  2. 消息通讯
    1. window.postMessage
    2. Broadcast Channel
    3. Shared Worker
    4. Message Channel
    5. 缺点:
      1. 循环引用对象,比如 window 不能复制
      2. 函数不能复制
      3. 同步变异步
  3. 自己实现方法
    1. 循环引用难点处理(WeakMap)
    2. 爆栈问题难点处理(循环替代递归)
    3. 特殊类型(Map、Set、Blob 等)难点处理(构造函数识别)
----------------------------------------------------------------------------------------------
// 其他
1. 在判断是否相等时只有判断是不是 == null 时使用两个等号,其他使用三个等号
2. blob对象表示一个不可变、原始数据的类文件对象
3. 不同的数据类型做比较 // 和数字或布尔做比较时,不管是什么类型都会转为数字
  1. 对象类型和对象类型做比较时,比的是引用地址
  2. 对象类型和字符串类型做比较时,对象类型会先转为字符串,再和字符串做比较 // toString()
  3. 对象类型和布尔类型做比较时,两边都要转为数字,对象会先尝试调用 valueOf() 方法获取结果,如果没定义,
     再尝试调用 toString 方法获取结果,最后调用 Number()
  4. 对象类型和数字类型做比较时,对象类型会转为数字,对象会先尝试调用 valueOf() 方法获取结果,如果没定义,
     再尝试调用 toString 方法获取结果,最后调用 Number()
  5. 数字类型和布尔类型做比较时,布尔类型会转为数字 // true == 1,false == 0
  6. 数字类型和字符串类型做比较时,字符串类型会转为数字 // Number()
  7. 布尔类型和字符串类型做比较时,字符串类型会转为数字 // Number()
  8. 示例
    '[object Object]' == {} // true
    '' == [] // true
    0 == [] // true
4. if 语句中的判断是调用 Boolean(xxx),进行判断的
  1. 其值不是undefinednull的任何对象(包括其值为false的布尔对象)在传递给条件语句时都将计算为true。
     例如,以下if语句中的条件评估为truevar x = new Boolean(false);
      if (x) {
        // 这里的代码会被执行
      }
  2. Boolean 对象:如果省略或值0-0nullfalseNaNundefined,或空字符串(""),该对象具有的初始值false。
     所有其他值,包括任何对象,空数组([])或字符串"false",都会创建一个初始值为true的对象
  3. 注意不要将基本类型中的布尔值 truefalse 与值为 truefalse 的 Boolean 对象弄混了。
5. 使用 + 号转数字时,如果数字是 ES6 的 BigInt 和 Symbol 时会报错,而不是返回 NaN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
Last Updated: 2024/6/11 14:20:38
    飘向北方
    那吾克热-NW,尤长靖