JavaScript基础知识点整理(一)——数据类型、判定、转换、this指向

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

JavaScript是每一位前端开发者都必备的技能接下来会分章节文章阐述介绍每一部分的内容。

JavaScript基础整理①


1、JavaScript类型

JavaScript数据类型分为两大类

  • 原始类型number、string、nul、undefined、boolean、symbol
  • 对象类型三大类分别是内部对象本地对象和内置对象宿主对象自定义对象
    • 本地对象ES提供的需要实例化new才能够使用的对象Object、Function、Array、String、Boolean、Number、Date、Error、EvalError、RangeError、ReferenceError、syntaxError、TypeError、URIError
    • 内置对象也就是GlobalMath其中JavaScript的全局属性有infinity-代表正的无穷大的数值、NaN-指示某一个数值不是数字值、undefined-指示未定义的数值。
    • 宿主对象执行JavaScript脚本的环境所提供的对象是浏览器提供的对象所有的BOM和DOM都是宿主对象
    • 自定义对象包括对象字面量方式通过JSON创建对象、工厂模式、构造函数模式、原型模式、组合使用构造函数模式和原型模式、其他模式动态原型模式、寄生构造函数模式、稳妥构造函数模式

2、原始primitive类型

在JavaScript中存在着7种原始数值分别是

  • boolean
  • null
  • undefined
  • number
  • string
  • symbo
  • bigint

原始类型存储的都是数值没有函数可以调用不会存在如下undefined.toString()
在这里插入图片描述

注意如果采用'1'.toString()是可以正常使用这是由于'1'已经被强制转换为了String类型也就是对象类型可以调用toString方法。

JavaScript的number类型是浮点类型使用会遇到bug例如0.1 + 0.2 !== 0.3;

string类型数值不可以改变无论使用任何方法

3、对象Object类型

对象类型和原始类型不一样原始类型存储的是数值一般保存在栈上对象类型存储的是地址-指针数据保存在堆上

创建一个对象类型时候计算机会在堆内存种开辟空间保存数值开发者需要找到该内存空间这个空间会拥有一个指针地址

const a = []

对于常量a而言设定内存地址#001那么地址#001的位置保存了数值[]常量a存放了地址#001给出如下示例代码

const a = []
const b = a
b.push(1)

当我们将变量赋值给另外一个变量时复制的是原本变量的地址指针也就是说当前变量 b 存放的地址指针也是 #001。因此当我们对任一变量进行数据修改的时候等同于修改存放在地址指针 #001 上的值所以就导致了两个变量的值都发生了改变。

若函数参数是对象的时候

function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }

  return person
}
const p1 = {
  name: 'yck',
  age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
  • 首先函数传参是传递对象指针的副本
  • 达到函数内部修改参数的属性这一步p1的数值修改age从25 -> 26
  • 若重新给person分配一个对象就出现了问题如下图
    在这里插入图片描述

所以最后 person 拥有了一个新的地址指针也就和 p1 没有任何关系了导致了最终两个变量的值是不相同的。


4、类型判定

4.1、typeof

typeof对于原始类型来说除了null都可以显示正确的类型若想判定null的话可以使用过xxx === null

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

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

typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'

4.2、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直接判定类型不可以 不过可以利用如下方法

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 并不是百分之百可信的。

// true
[].constructor === Array

4.3、Object.prototype.toString.call

Object.prototype.toString.call 综合来看是最佳选择能判断的类型最完整基本上是开源库选择最多的方式。
在这里插入图片描述


5、类型转换

JavaScript中存在三种情况

  • 转换为bool
  • 转换为数字
  • 转换为字符串

在这里插入图片描述

注意图中有一个错误Boolean 转字符串这行结果我指的是 true 转字符串的例子不是说 Boolean、函数、Symblo 转字符串都是 true

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

5.1、对象转原始类型

对象在转换类型的时候会调用内置的[[ToPrimitive]]函数算法逻辑如下

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

当然可以选择重写Symbol.toPrimitive这个方法可以在转原始类型的时候优先级最高。

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

6、四则运算符

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

  • 运算中其中一方为字符串那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字那么会将它转换为数字或者字符串
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
  • 对于第一行代码来说触发特点一所以将数字 1 转换为字符串得到结果 '11'
  • 对于第二行代码来说触发特点二所以将true转为数字 1
  • 对于第三行代码来说触发特点二所以将数组通过 toString 转为字符串 1,2,3得到结果 41,2,3

6.1、比较运算符

  • 若是对象则通过toPrimitive转换对象
  • 若是字符串则通过unicode字符索引来比较
let a = {
  valueOf() {
    return 0
  },
  toString() {
    return '1'
  }
}
a > -1 // true

在上述的代码中a是对象所以通过valueOf转换为原始类型在比较数值。


7、this

首先给出常见的this调用场景

function foo() {
  console.log(this.a)
}
var a = 1
foo()

const obj = {
  a: 2,
  foo
}
obj.foo()

const c = new foo()
  • 对于直接调用foo而言不管foo函数放在何处this一定是window
  • 对于obj.foo而言只要记住谁调用了函数谁就是this所以这时候foo函数中的this指的就是obj对象
  • 对于new的方式而言this被永远绑定在了c上面不会被任何方式改变this

给出JavaScript的箭头函数中的this

function a() {
  return () => {
    return () => {
      console.log(this)
    }
  }
}
console.log(a()()())
  • 首先箭头函数没有this箭头函数的this只是取决于包裹箭头函数的第一个普通函数的this在这个例子中因为包裹箭头函数的第一个普通函数就是a所以这时的this就是window另外箭头函数使用bind方法是无效的。
  • 另外一种情况就是bind这些改变上下文的APIthis取决于第一个参数若第一个参数空则就是window

注意若对一个函数进行多次bind上下文表现如何

let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?

也可以写作另一种形式

let a = { name: 'yck' }
function foo() {
  console.log(this.name)
}
foo.bind(a)() // => 'yck'

以上就是bind的所有规则了不同的规则之间根据优先级的高低来决定this的最终指向。

首先new 的方式优先级最高接下来是bind这些函数然后是obj.foo()这种调用方式最后是 foo 这种调用方式同时箭头函数的 this 一旦被绑定就不会再被任何方式所改变。

总结如下图所示
在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: JavaScriptJava