JavaScript基础知识

Jan 8, 2022|Oct 23, 2023
Asuhe
Asuhe
type
status
date
slug
summary
tags
category
icon
password

Javascript基础

数据类型

javascript中数据值的类型可以分为两大类,分别是基本类型和引用类型。基本类型都存储在栈内存上,而引用类型通常都存储在堆内存上。

基本类型

基本数据类型有七大类:
  • Number:number类型包括了整数类型和浮点数类型,所有的数字类型都可以用number表示
  • Boolean:boolean类型就只有两个值,true和false。通常用于条件判断
  • String:string类型表示字符串
  • Null:null类型一般用于给一个准备设置为引用类型的数据赋初始值,如var a = null,表示变量a在后面可能会被赋予一个引用类型的值
  • Undefined:undefined类型用于给变量当默认值,如var a;console.log(a),此时会输出undefined,我们没有在声明变量后给一个初始值时js引擎会给它默认添加undefined作为默认值
    • undefined 在 JavaScript 中不是一个保留字,它作为一个属性挂载在Windows对象上,这意味着可以使用 undefined 来作为一个变量名,但是这样的做法是非常危险的,它会影响对 undefined 值的判断。我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
  • Symbol:symbol类型被设计之初是用于解决命名冲突问题,用它作为标识符永远不会与其他变量冲突。如console.log(Symbol(1)===Symbol(1)),输出false。即便传入的变量都为1,获取的结果也是不同的
  • BigInt:BigInt是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
基本数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储

引用类型

除了基本类型以外的类型都是引用类型,如Array、Object、Function。
引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

类型判断

typeof

typeof一般用于判断基本数据类型,但是它在判断null时会被判断为object

instanceof

instanceof一般用于判断引用数据类型,它的运行原理是判断其在原型链中是否能够找到该类型的原型
instanceof简单实现

constructor

constructor有两个作用,一是判断数据的类型,二是对象实例通过 constructor 对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了

Object.prototype.toString.call()

Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型
同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。

判断数组

  • 通过Object.prototype.toString.call()做判断
  • 通过原型链做判断
  • 通过ES6的Array.isArray()做判断
  • 通过instanceof做判断
  • 通过Array.prototype.isPrototypeOf

类型转换

== 和 ===

对于 == 来说,如果对比双方的类型不一样,就会进行类型转换。假如对比 xy 是否相同,就会进行如下判断流程:
  1. 首先会判断两者类型是否相同,相同的话就比较两者的大小;
  1. 类型不相同的话,就会进行类型转换;
  1. 会先判断是否在对比 nullundefined,是的话就会返回 true
  1. 判断两者类型是否为 stringnumber,是的话就会将字符串转换为 number
  1. 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
  1. 判断其中一方是否为 object 且另一方为 stringnumber 或者 symbol,是的话就会把 object 转为原始类型再进行判断
其流程图如下:
notion image
image

其他类型到数值类型的转换规则

  • Undefined 类型的值转换为 NaN。
  • Null 类型的值转换为 0。
  • Boolean 类型的值,true 转换为 1,false 转换为 0。
  • String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。
  • Symbol 类型的值不能转换为数字,会报错。
  • 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive 会首先(通过内部操作 DefaultValue)检查该值是否有valueOf()方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。
如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。

其他类型到布尔类型的转换规则

以下这些是假值:
  • undefined
  • null
  • false
  • +0、-0 和 NaN
  • ""
假值的布尔强制类型转换结果为 false。从逻辑上说,假值列表以外的都应该是真值。

变量类型

var

在全局作用域下声明的var变量会有变量提升,同时它会被挂载到window对象上作为一个属性
notion image
image-20220708145618892

let

let 声明的变量其作用域会绑定在最近的{}花括号里面,而且不存在变量提升。
同时对于函数作用域内的同名变量会有暂时性死区的效果,暂时性死区和没有变量提升配合起来会屏蔽变量作用域的查找

const

const 变量基本特点和let 一致,但是const 所指向的那个值不允许被修改,因此const在声明之初一定要赋予一个初始值

函数

函数声明

函数声明有函数提升的效果,而函数声明表达式则没有

函数声明表达式

函数声明表达式没有函数提升效果,所以必须在赋值完成后才能调用

this指向

在使用function关键字声明的函数中,this指向的是其调用者
匿名函数没有自己的this,匿名函数中的this是匿名函数定义时的上层作用域的this

匿名函数

  1. 匿名函数只有一个形参时,可以省略(),且在函数体只有一句时可以省略{}并将该语句结果返回
    1. 匿名函数没有自己的this,因此也不能使用callapplybind等方法来改变this指向
    1. 由于箭头函数时没有自己的this的,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用
      1. 箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值
      1. 箭头函数没有prototype
      1. 箭头函数不能用作Generator函数,不能使用yeild关键字

      作用域 && 作用域链

      在ES6以前javascript并没有像其他语言一样拥有块级作用域的概念,仅有函数作用域和全局作用域这两种作用域。例如:
      而在ES6新增了letconst关键字后,由这两种关键字声明的变量其作用域会绑定在其最近的{}内。例如:
      在 JavaScript 里面,函数、块、模块都可以形成作用域(一个存放变量的独立空间),他们之间可以相互嵌套,作用域之间会形成引用关系,这条链叫做作用域链。
      当我们去查找一个变量a时,若这个变量a不在本级作用域中那么它会继续往它的上级作用域中去寻找该变量的声明。若最后找不到就报错。

      闭包

      闭包就是在一个变量对象里持有另一个变量对象里内容的引用,这时就会产生闭包。常见的表现形式就是,内部函数持有外部函数的变量,我们可以通过返回内部函数去让更外层的作用域能够访问到内部函数的父函数里的变量。

      常用对象&&对象方法

      Array对象

      静态方法

      Array.from

      Array.from()从一个可迭代对象或类数组对象中,创建一个新的数组实例。它实现的是浅拷贝。
      参数:
      arrayLike:一个可迭代对象或类数组对象
      mapFn(可选):遍历函数
      thisArg(可选):执行遍历函数时的this指针

      Array.isArray

      判断一个变量是否为数组
      参数:
      value:需要判断的变量

      Array.of

      根据传入的参数创建一个数组,浅拷贝
      参数:
      elementN:传入的参数可以为可变数量,传多少个就创建多长的数组

      实例方法

      concat

      合并多个参数,返回一个新数组。浅拷贝
      参数:
      valueN:参数可以为数组,也可以为普通参数。

      filter

      过滤出回调函数返回值为true的元素,返回一个符合条件的新数组。
      参数:
      callbackFn:遍历的回调
      thisArg(可选):回调执行时的this

      every

      当所有的元素都通过了回调函数的条件时,返回一个true的布尔值,否则返回false。
      参数:
      callbackFn:遍历的回调
      thisArg(可选):回调执行时的this

      some

      当数组中有一个元素通过了回调函数的条件时,返回一个true的布尔值,否则返回false。
      参数:
      callbackFn:遍历的回调
      thisArg(可选):回调执行时的this

      reduce

      根据遍历函数的返回值,一直传递给下次遍历函数。一般用于计算总和
      参数:
      callbackFn:遍历的回调
      initialValue(可选):回调执行时的this

      Object对象

      静态方法

      Object.keys

      返回一个数组,数组内容为对象中所有的可枚举属性的key值

      Object.getPrototypeOf

      获取一个对象的隐式原型

      Object.create

      使用传入的对象作为prototype,创建一个新的对象

      Object.assign

      从源对象中,合并源对象的可枚举属性进目标对象,并返回修改后的对象
      参数:
      target:需要修改的目标对象
      source:提供可枚举属性来源的源对象

      逻辑中断

      &&

      &&逻辑与,当前一个条件判断为真时,才会继续执行后一个条件,并返回表达式的值

      ||

      ||逻辑或,当前一个条件判断为假时,才会继续执行后一个条件,并返回表达式的值

      ? :

      ? :三目运算符,当条件值为true时,返回第一个表达式的值,否则返回第二个表达式的值

      原型链

      • 所有对象都有一个隐式原型__proto__
      • 所有函数对象都有一个原型prototype
      • 以某函数为构造函数,new出实例的隐式原型__proto__都会指向该函数的原型prototype
      • 所有函数对象都是Function的实例,包括Function自身
      • 所有函数对象的prototype都是Object的实例
      notion image
      终极原型链

      异步编程

      event-loop

      事件循环模型示意图
      notion image
      事件循环模型

      Promise

      Task && Microtask

      MDN中Task(宏任务)的描述:
      A task is any JavaScript code which is scheduled to be run by the standard mechanisms such as initially starting to run a program, an event callback being run, or an interval or timeout being fired. These all get scheduled on the task queue.
      MDN中Microtask(微任务)的描述:
      At first the difference between microtasks and tasks seems minor. And they are similar; both are made up of JavaScript code which gets placed on a queue and run at an appropriate time. However, whereas the event loop runs only the tasks present on the queue when the iteration began, one after another, it handles the microtask queue very differently.
      两者不同之处的MDN描述:
      There are two key differences.
      First, each time a task exits, the event loop checks to see if the task is returning control to other JavaScript code. If not, it runs all of the microtasks in the microtask queue. The microtask queue is, then, processed multiple times per iteration of the event loop, including after handling events and other callbacks.
      Second, if a microtask adds more microtasks to the queue by calling queueMicrotask(), those newly-added microtasks execute before the next task is run. That’s because the event loop will keep calling microtasks until there are none left in the queue, even if more keep getting added.
      react脚手架配置代理DOM渲染原理