js进阶学习笔记

163 阅读9分钟

数据类型以及判断

数据类型

  • 基本类型
String
Number
Boolean
null
undefined
Es6 新增 Symbol
  • 引用类型
Array
Function
Object
Date

判断

typeof // 返回字符串v,在null、array、object以及函数的实例(new + 函数)时,它返回的都是object。其余可以使用,缺点通常用于基本类型
instanceof  // 用于检测构造函数的 `prototype` 属性是否出现在某个实例对象的[原型链]上,返回Boolean,缺点只能用于引用类型
=== // 判断undefined null
Object.prototype.toString.call() // 最准确,但是用法繁琐了点

== 隐式转换以及类型转换

转换为数字

  • 通常爱用 '6' * 1
  • Number() 可以把任意值转换成数字,如果要转换的目标对象(一般是字符串)中有不是数字的值,则会返回 NaN
Number('1')   // 1
Number(true)  // 1
Number('123s') // NaN
Number({})  //NaN

parseInt(string, radix)

解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。

parseInt('2') //2
parseInt('2',10) // 2
parseInt('2',2)  // 二进制(01)所以返回 NaN
parseInt('a123')  // NaN  如果第一个字符不是数字或者符号就返回NaN
parseInt('123a')  // 123

parseFloat(string)

解析一个参数并返回一个浮点数。

parseFloat('123a')	//123
parseFloat('123a.01')	//123
parseFloat('123.01')	//123.01
parseFloat('123.01.1')	//123.01

toString()

将目标对象转换为字符串。

注意:null,undefined 不能调用。

Number(123).toString()	//'123'
[].toString()	//''
true.toString()	//'true'
() => {}.toString // '() => {}'

String()

String() 的作用比较强大,它可以将任何传入的值都转为字符串。

String(123)	//'123'
String(true)	//'true'
String([])	//''
String(null)	//'null'
String(undefined)	//'undefined'
String({})	//'[object Object]'
String([1, undefined, 3]) // '1,,3' String({}) // '[object Objecr]'

转换为布尔值

Boolean()

Boolean() 方法会将以下值转换为 false:

  • 0
  • ""
  • null
  • undefined
  • NaN

其余的所有值都会被转换为 true。

ToPrimitive指对象类型类型(如:对象、数组)转换为原始类型的操作。

  • 当对象类型需要被转为原始类型时,它会先查找对象的valueOf方法,如果valueOf方法返回原始类型的值,则ToPrimitive的结果就是这个值
  • 如果valueOf不存在或者valueOf方法返回的不是原始类型的值,就会尝试调用对象的toString方法,也就是会遵循对象的ToString规则,然后使用toString的返回值作为ToPrimitive的结果。
  • 如果valueOftoString都没有返回原始类型的值,则会抛出异常。
  Number([]) // 0
  Number(['10']) //10

  const obj1 = {
    valueOf () {
      return 100
    },
    toString () {
      return 101
    }
  }
  Number(obj1) // 100

  const obj2 = {
    toString () {
      return 102
    }
  }
  Number(obj2) // 102

  const obj3 = {
    toString () {
      return {}
    }
  }
  Number(obj3) // TypeError

2. 宽松相等(==)比较时的隐式转换规则

宽松相等(==)严格相等(===)的区别在于宽松相等会在比较中进行隐式转换。现在我们来看看不同情况下的转换规则。

2.1 布尔类型和其他类型的相等比较

  • 只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型
  • 根据布尔类型ToNumber规则,true转为1false转为0
  false == 0 // true
  true == 1 // true
  true == 2 // false

之前有的人可能觉得数字2是一个真值,所以true == 2应该为真,现在明白了,布尔类型true参与相等比较会先转为数字1,相当于1 == 2,结果当然是false

我们平时在使用if判断时,一般都是这样写

  const x = 10
  if (x) {
    console.log(x)
  }

这里if(x)x会在这里被转换为布尔类型,所以代码可以正常执行。但是如果写成这样:

  const x = 10
  if (x == true) {
    console.log(x)
  }

代码不会按照预期执行,因为x == true相当于10 == 1

2.2 数字类型和字符串类型的相等比较

  • 数字类型字符串类型做相等比较时,字符串类型会被转换为数字类型
  • 根据字符串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN
  0 == '' // true
  1 == '1' // true
  1e21 == '1e21' // true
  Infinity == 'Infinity' // true
  true == '1' // true
  false == '0' // true
  false == '' // true

上面比较的结果和你预期的一致吗? 根据规则,字符串转为数字,布尔型也转为数字,所以结果就显而易见了。

这里就不讨论NaN了,因为NaN和任何值都不相等,包括它自己。

null、undefined和其他类型的比较

  • nullundefined宽松相等的结果为true,这一点大家都知道

其次,nullundefined都是假值,那么

  null == false // false
  undefined == false // false
复制代码

webpack相关

-地址

更新 原型和原型链

又捋了一下,仅做笔记

  • 先下一个定义 proto [[Prototype]] 隐式原型这些名词是同一个东西

  • 显式原型 prototype是同一个东西

  • 任何构造函数prototype上的_proto_都指向Object的prototype ,除了Object自身外,因为Object自己prototype上的_proto_指向null,null类似于一个跳出条件,避免造成死循环

  • 任何构造函数的constructor都指向Function,等价于任何构造函数的_proto_都指向与Function的prototype,包括Function自身 (Function.constructor === Function Object.proto === Function.prototype)

  • 下图摘录只掘金的大佬,感谢

c9f757548808b852131a471462e993b2.png

手写new

        function P(name, age) {
            this.name = name
            this.age = age
                // return [1]
        }
        let a = new P('老斋', '38')  // 常规操作
            // 手写操作
        function handleNew(o, ...arg) {
            //创建一个空对象 将对象的隐式原型和构造函数的显式原型接起来
            let obj = {}
            obj.__proto__ = o.protoType;
            // 等价于 let obj = Object.create(o.prototype)
            // 改变构造函数的this指向,并且传参
            let result = o.apply(obj, arg);
            // 加一下判断是因为,new实例化函数,如果返回值是引用类型,则会返回引用类型的值,如果是普通类型,则忽略return
            // 例如当前P没有返回值(返回值是普通类型undefined)则控制台上 P {name: '老斋', age: '38'},如果有返回值return [1], 则控制台上 [1]
            // 为满足以上条件。如果不加判断,要么直接把返回值抛出去,要么直接把obj这个对象抛出去,都是不合理的。
            return result instanceof Object ? result : obj
        }
        let b = handleNew(P, '唠嗑', 51)
        console.log(a);
        console.log(b);

Event Loop

浏览器内核(渲染进程)

从前文我们得知,进程和线程是一对多的关系,也就是说一个进程包含了多条线程。

而对于渲染进程来说,它当然也是多线程的了,接下来我们来看一下渲染进程包含哪些线程。

  • GUI渲染线程

    • 负责渲染页面,布局和绘制
    • 页面需要重绘和回流时,该线程就会执行
    • 与js引擎线程互斥,防止渲染结果不可预期
  • JS引擎线程

    • 负责处理解析和执行javascript脚本程序
    • 只有一个JS引擎线程(单线程)
    • 与GUI渲染线程互斥,防止渲染结果不可预期
  • 事件触发线程

    • 用来控制事件循环(鼠标点击、setTimeout、ajax等)
    • 当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中
  • 定时触发器线程

    • setInterval与setTimeout所在的线程
    • 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的
    • 计时完毕后,通知事件触发线程
  • 异步http请求线程

    • 浏览器有一个单独的线程用于处理AJAX请求
    • 当请求完成时,若有回调函数,通知事件触发线程

在前端开发中我们会通过setTimeout/setInterval来指定定时任务,会通过XHR/fetch发送网络请求, 接下来简述一下setTimeout/setIntervalXHR/fetch到底做了什么事

我们知道,不管是setTimeout/setIntervalXHR/fetch代码,在这些代码执行时, 本身是同步任务,而其中的回调函数才是异步任务。

当代码执行到setTimeout/setInterval时,实际上是JS引擎线程通知定时触发器线程,间隔一个时间后,会触发一个回调事件, 而定时触发器线程在接收到这个消息后,会在等待的时间后,将回调事件放入到由事件触发线程所管理的事件队列中。

当代码执行到XHR/fetch时,实际上是JS引擎线程通知异步http请求线程,发送一个网络请求,并制定请求完成后的回调事件, 而异步http请求线程在接收到这个消息后,会在请求成功后,将回调事件放入到由事件触发线程所管理的事件队列中。

当我们的同步任务执行完,JS引擎线程会询问事件触发线程,在事件队列中是否有待执行的回调函数,如果有就会加入到执行栈中交给JS引擎线程执行

宏任务和微任务

  • 宏任务:setTimeout,setInterval,Ajax,DOM事件,全局script标签
  • 微任务:Promise,async/await
  1. 先执行宏任务,再执行当前宏任务内的微任务,(在此过程中将遇到的微宏任务依次推入到eventqueue,先进入eventqueue的任务会被先执行)
  2. 执行完当前宏任务内的所有微任务之后,接着再取出一个宏任务,同样把在此期间产生的回调入队。再把当前的微任务队列清空。以此往复。
  3. 当然在微宏任务中同步任务会优先执行,而整体的scrpt标签就是第一个执行的宏任务。异步任务包含宏任务和微任务

每个红任务执行过程中,会间隔一次GUI的页面渲染

我们可以将每次执行栈执行的代码当做是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行), 每一个宏任务会从头到尾执行完毕,不会执行其他。

我们前文提到过JS引擎线程GUI渲染线程是互斥的关系,浏览器为了能够使宏任务DOM任务有序的进行,会在一个宏任务执行结果后,在下一个宏任务执行前,GUI渲染线程开始工作,对页面进行渲染。

作者:云中桥
链接:juejin.cn/post/684490…
来源:稀土掘金
著作权归作者所有。感谢大佬,仅作记录