前端基础 JS篇02

这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战

前言

之前都是忙于业务开发,对前端的基本知识都是用到什么查什么,很少有时间沉下心来自己归纳,总结下基础,有时候,用到什么,觉得值得留意一下的东西,也都是自己笔记里记录下,也不怎么及时整理,然后随着时间的推移,陆陆续续记录的东西都是乱七八糟的,都没整理过,这次,写这个前端的基础系列,整理一下

打算以轻松,闲谈的调调来聊聊,文笔不好,大家将就的看

快速回顾

上文讲了 JS中的内置类型,基本类型 和对象

  1. 基本类型 又叫原始类型
  2. 对象类型 又叫引用类型
  3. 原始类型存储的是值,对象类型存储的是地址(指针)

typeof , instanceof

这2个家伙,在实际业务开发中,用的频率不高,但是讲JS基础,还是要拎出来讲讲的

MDN 中对 typeof 的解释如下

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

typeof 是否能正确判断类型? 本来以为可以检测出所有的类型,后来实操中发现并不是

typeof 对于原始类型来说,除了 null 都可以显示正确的类型

typeof 1 // 'number' 
typeof '1' // 'string' 
typeof undefined // 'undefined' 
typeof true // 'boolean' 
typeof Symbol() // 'symbol'

另类
typeof(null) // 'object'
复制代码

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

typeof(toString) // 'function'
typeof console.log // 'function'

typeof [] // 'object' 
typeof {} // 'object' 

复制代码

如果我们想判断一个对象的正确类型, typeof 就有些 不来事了

这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的。

MDN 中对 typeof 的解释如下

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

关于原型链,我自己的理解是,把原型链比作是一个基类,而 instanceof 就像这个基类的一个判断类型的方法,通过定义对象的方法,得到一个对象,这个对象的类型就可以用 它的方法instanceof 来判断。

const Person = function() {}; 
const p1 = new Person(); 
p1 instanceof Person; // true 

var str = 'hello world';
str instanceof String; // false

var str1 = new String('hello world');
str1 instanceof String; // true

instanceof 就是 原型链的一个判断类型的方法
复制代码

对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的

image.png

那咋搞? 还是有办法让 instanceof 判断原始类型的

class PrimitiveString {
    static [Symbol.hasInstance](x) {
        return typeof x === 'string' 
    } 
}

console.log('hello world' instanceof PrimitiveString) // true
复制代码

解释下: 你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

常态下 基本 typeof 和 instanceof 一梭子开干即可,但是要扫下基础,那就还是过一遍吧

类型转换

在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

转Boolean

在条件判断时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。

对象转原始类型

对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是原始类型了,那就不需要转换了
  • 如果需要转字符串类型就调用 x.toString(),转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用 valueOf,结果不是基础类型的话再调用 toString
  • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
  • 如果都没有返回原始类型,就会报错

当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。

let a = { 
    valueOf() { 
        return 0 
    }, 
    toString() { 
        return '1' 
    },
    [Symbol.toPrimitive]() {
        return 2
     } 
 } 
 
1 + a // => 3
复制代码

四则运算符

加法运算符不同于其他几个运算符,它有以下几个特点:

  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串(隐式转换,这个应该不用多说了
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11' 
true + true // 2 
4 + [1,2,3] // "41,2,3"
复制代码

如果你对于答案有疑问的话,请看解析:

  • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
  • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
  • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 1,2,3,得到结果 41,2,3

在JS中,true就是1,false 就是 0, 所以

image.png

另外对于加法还需要注意这个表达式 'a' + + 'b',这个平时不太常见,也讲一下 因为 + 'b' 等于 NaN,所以结果为 "aNaN",别问为什么是这样,因为控制台打印出来就是这样

image.png

你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。

image.png

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字(这个我熟,这个我经常在代码中使用,有时候我就用 * 代替 parseInt方法来做

4 * '3' // 12 
4 * [] // 0 
4 * [22, 33] // NaN
复制代码

比较运算符

  1. 如果是对象,就通过 toPrimitive 转换对象
  2. 如果是字符串,就通过 unicode 字符索引来比较
let a = { 
    valueOf() { 
        return 0
    },
    toString() { 
        return '1' 
    } 
} 

a > -1 // true
复制代码

在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。

总结

  1. typeof 和 instanceof
  2. 类型转换

ps: 干巴巴的文字讲了还是比较枯燥,并且难以印象深刻的,大家还是日常使用中,多多体会

猜你喜欢

转载自juejin.im/post/7033404005271535653
今日推荐