1.Type.md

Deep-js-fundations

Type

js类型的描述

语言特性: 动态语言, 在运行时确定类型, 所以与静态语言不同的是,在初始化的时候并不需要我们指定变量的类型.

分类:

  1. 基本类型
    • undefined
    • string
    • number
    • boolean
    • symbol
    • null
  2. 引用类型
    • object
    • function
    • array
  3. 草案阶段的类型
    • bigInt

bigInt: 可以表示无限大的数, 例如:

1
2
var n = 42n;
typeof n === 'bigint';

typeof

使用typeof可以判断的类型有

  • undefined
  • number
  • string
  • boolean
  • object
  • symbol
  • function

这里要注意一个特殊的例子:

1
typeof null === 'Object'

请写一个增强的判断js类型的函数.要求可判断引用类型(arr, funciton, Data)

1
2
3
4
5
const isType = (value) => {
return Object.prototype.toString.call(value).match(/(?<= )\w+/)[0]
// String Number Null Undefined Object Date RegExp Symbol Boolean Function
}
// 这里涉及了正则的一个知识,就是匹配以xxx开头的字符串(不包含xxx), 那么就要使用正则的前置断言(?<=[这是xxx的内容])

undefined vs undeclared

这是两个不相等的关系.undeclared:在我可以触及的作用域从未创建过。undefined: 我肯定这是一个变量, 只是现在还没有值.

而在ES6语法中,我们在let定义一个变量之前去使用它,那么就会报错,这是临时死区的概念: 在定义这个变量之前的scoped都是临时死区, 无法访问。

1
2
typeof a; // referentceError.
let a;

NaN 与 isNaN

NaN 并不是指“Not A Number”, 虽然它的翻译确实如此.

NaN指的是一个无效的number, Invalid number.

我认为就是经过运算后无法转换成数字类型的都是NaN.

1
2
3
4
5
6
let myAge  = Number(42) // 42
let myCode = Number('tx123') // NaN
myAge - 'Some' // NaN
NaN === NaN // false
isNaN(myCode) // true
Number.isNaN(myCode) // false

negative Zero

让我们看一下-0的特性

1
2
3
4
5
6
7
8
let a= -0
a === -0
a.toString() // '0'
a === 0 // true
a > 0 // false
a < 0 // false
Object.is(a, -0) // true
Object.is(a, 0) // false
  1. -0 等于0
  2. -0 不大于0
  3. -0 不小于0
  4. 只能用Obect.is进行判断

应用:

1
2
3
4
5
6
7
Math.sign(-3) // -1
Math.sign(3) // 1
Math.sign(0) // 0
// 判断正负的函数
function sign (v) {
return v !== 0 ? Math.sign(v) : Object.IS(V, -0) ? -1 : 1;
}

Object.is polyfill

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// TODO: define polyfill for `Object.is(..)`
if (Object.is || true) {
Object.is = (a, b) => {
// 工具函数
const isNegZero = v => v == 0 && (1 / v) == -Infinity
const isNaN = v => v !== v
const xNegZero = isNegZero(a)
const yNegZero = isNegZero(b)

if (xNegZero || yNegZero) {
return xNegZero && yNegZero
} else if (isNaN(a) && isNaN(b)) {
return true
} else {
return x === y
}
}
}

fundational Object

use New Keyword

  1. object
  2. array
  3. function
  4. data
  5. regexp
  6. error

don’t use new Keyword

  1. string
  2. number
  3. boolean
1
2
3
4
var sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 6));
// expected output: 8
Function.length // 数组参数的长度

抽象运算符(Abstract Operation:ToPrimitive)

如果我们要使用到基础类型, 那么我们就会通过一个算法去转换它, 成为我们需要的类型

number

  1. valueOf()
  2. toString()

->to number
原则: 把所有值转换为number后返回

  1. Object: 调用valueOf方法
  2. String: 调用toPrimitive
  3. 调用顺序是: valueOf()->toString()
  4. 但是要注意, 所有的valueOf都是返回this
    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
    // 最普遍的例子
    “” -> 0
    0” -> 0
    -0” -> -0
    009” -> 9
    3.1415926” -> 3.1415926
    0.” -> 0
    .0” -> 0
    “.” -> NaN
    0xaf” -> 176
    // 特殊字符
    false-> 0
    true-> 1
    null-> 0
    undefind-> 0
    // 特殊例子
    [''] -> 0
    [0''] -> 0
    ['-0'] -> -0
    [null] -> 0
    ['undefind'] -> 0
    [1, 2, 3] -> NaN
    [[[]]]-> 0
    // 如果你重写了valueOf()方法,那么会应用到强制转换上
    {
    valueOf() {
    return 3;
    }
    }
    num = {valueOf() {return 3}}
    num + 1 // 4

String

  1. toString()
  2. valueOf()

->TO String:
原则: 把所有的值强制转化为String后返回

  1. Object: 调用toString方法
  2. String: 调用toPrimitive
  3. 调用顺序是: toString()->valueOf()

看个例子:

1
2
3
4
5
6
7
8
[]->""
[1, 2, 3]-> '1, 2, 3'
[null, undefined]-> '.'
[[[], [], []], []]-> '...'
[...]->'...'

{}-> ‘[Object Object]’
{toString() {return 'X'}}-> "X"

->toBoolean

  1. false
    • “”
    • 0/-0
    • null
    • NaN
    • false
    • undefined
  2. true: !false

coercion rules

大概有以下规则

  1. ‘+’: 如果为加号, 只要有一边是字符串, 则会把两边作为字符串处理
  2. ‘-‘: 只会把运算符两边作为数字来处理
  3. 布尔值:
    • !!
    • 0: false/ !0: true
    • Boolean(value)

常见的触发场景:

  1. 使用模板字符串
  2. 字符串拼接(使用+号)
  3. 对对象的某个属性进行数学运算的时候, 会触发
    1
    2
    3
    4
    5
    6
    const addAStudent = number => number + 1;
    addAStudent(student.count) // 171
    // solution1
    addAStudent(+student.count)
    // solution2
    addAStudent(Number(student.count))

    隐式转换

    访问字符串的长度值,就是一种隐式转换:
    string是基本类, 而这个基本类并不是一个对象,那么js会判定你想要把string当做一个对象访问,以便获取到String的长度值。
    “someText”.length

在所有语言, 我们都要处理类型转换。而在js中,我们遵循的基本原则就是aka(讲道理,我理解就是上面说的三种转换)

边界转换事件

  1. ‘’ -> number
  2. boolean -> number
  3. 3>2>1 || 1>2>3
    边界转换发生在字符串和数字转换之间

强制转换的应用

每个语言都要处理类型转换, 而每个语言都存在边际转换的情况,我们要做的是掌握这些边际问题,然后应用起来。

但是我们可以通过一些编码风格防止这些边界情况,例如使用typescript.以及,在你的代码中使用全等===。

营造学习文化

通过code review的方式, 让团队形成这样一种文化, 有助于帮助成员更好的了解他手中的工具, 当成员收到了代码审查,应该认真的去看,如果你发现了一些愚蠢的事,那么你应该让他过来,认真的说出这件事,让他避开那个边界情况做这样的事,是让大家成为更好的开发者。
你写的代码就像你在跟代码阅读者沟通, 他们应该可以理解。不然你在做的东西就是shit.浪费时间。

code communication

  1. 好的代码本身就是注释
  2. 但是注释本身也是必要的,特别是在一些复杂的业务逻辑上,在入口函数就说明它的入参结构, 功能, 代码组织方式,会减少很多维护者阅读代码的时间

隐式转换

  1. 隐式转换在一些写静态语言的人看来,是一种魔法,他们无法理解,为什么要做这样的事,所以认为这是js语言的弱点。
  2. 隐式转换是一种抽象概念。不是所有抽象都是好的,但是有些抽象是必要的。
  3. 提升代码清晰度的一个方法是,抽象不必要的细节,让阅读者关注我们需要他关注的地方。最常见的例子是: 比较运算符
  4. 我希望你是拥有分析能力的工程师,而不是写代码的猴子

理解特性

原则1: 有用的判断依据是, 你需要读者专注的点是重要的

原则2: 危险的是, 你写的东西如果有让人看不懂的地方,那么这就是危险的

原则3: 让读者能看得懂你的代码,并理解它

== 与 ===

两者区别:
=== 会检测类型与值, == 只会检测value, 那么意味着会触发强制转换

== 算法

  1. 优先转换成数字进行比较
  2. 若为非数字的原语(string, boolean, object, array), 那么会优先转换为原语

边际问题:

1
2
[] == false;
[] == true;

你应该避免进行以下的比较

  1. 使用 == 比较 “”/0/“ ”
  2. 使用 == 比较非原语(boolean/string/number)
  3. 如果要使用到 == true or == false,那么我更建议你去使用 === 做比较

优先使用 ==的情况

遵循两个原则

  1. 明确类型,比不知道类型好
  2. 静态类型(Typescript)不是唯一的方式让你去明确你的类型的方法

如果了解 == 双方的类型

  1. 场景1: == 不适用与不知道类型的比较场景, 当你不确定比较元素的类型的时候,不要使用==
  2. 场景2: == 适用于你知道两者的类型,并且期望它进行转换的场景(当你确定类型一样的情况下 == 与 === 是一样的情况)
  3. 场景3: 两者元素类型不一样的时候,使用 === 这样的逻辑注定错误

总结: 如果类型确定,那么优先使用 == 将是你最好的选择

如果不了解 === 双方的类型

  1. 建议: 如果你不能确认比较双方的类型是什么,那么你不够了解你的代码,建议重构, 基准是你能明白类型是什么
  2. 类型不能确定会让你代码的阅读者困惑, 所以当你需要给代码阅读者明显的信号,让他注意这个地方的时候,请使用 ===
  3. 如果类型处于不确定的状态下, 使用 === 真的是一个保护措施

总结: 如果你代码中,不能确定类型,那么使用 === 保护你的代码时必要措施
当然,如果你代码里都是 === 那么,你要表达的东西是,我不相信(不知道)每一个比较两边的类型

我们写的代码应该是类型确定和明显会让代码质量更高,如果类型确定,那么优先使用== ,如果类型不确定,那么优先使用 === 保护你的代码