地铁上看的笔记1

/**
 * JS数据类型精讲和typeof底层机制
 *   原始值类型
 *      number, NaN Infinity
 *      string
 *      boolean
 *      null
 *      undefined
 *      symbol
 *          Symbol.iterator: for of 循环的底层处理方法
 *          Symbol.toStringTag: Object.prototype.toString.call()
 *          Symbol.hasInstance: instanceOf
 *          Symbol.toPrimitive: 获取原始值
 *      bigInt
 *          JS中存在最大安全数字和最小安全数字,如果超越这个范围运算就会存在问题,需要借bigInt来计算,一般是n为结尾代表bigInt数据
 *          如:9007199254740991n+1n = 9007199254740992n
 *   对象数据类型
 *      object 
 *          + {}
 *          + [] 
 *          + /^$/
 *          + new Date
 *      function 
 */

//  数据类型检测:typeof instanceof constructor Object.prototype.tostring.call() Array.isArray
// 特殊?为啥
console.log(typeof null) // 'object'
// typeof检测底层机制
//  + 所有数据类型在计算机中都是二进制存储的
//    整数:1 浮点:010 字符串:100 布尔:110 对象:000 undefined:-^30 null;000000
//  + typeof底层机制就是判断这些二进制,如果是000开头的都是对象,其中排除可执行对象
// 检测对象数据类型,除了可执行对象「函数:能够调用call方法的,如 普通函数/构造函数/箭头函数/生成器函数」检测出来的是‘function’,其他的都是‘object’

/**
 * 真正的编程从堆栈内存开始
 *   堆 Heap
 *   栈 Stack
 *     堆栈都是从电脑内存中分配出来的「优化手段:内存优化处理‘内存回收/垃圾回收机制 GC’」
 *   上下文 EC
 *   全局对象 GO:全局对象,存放浏览器默认提供JS调用的API
 *   全局变量对象 VO(G):存放全局申明的对象
 *      + 给予var/function声明的变量或函数,目前不会向VO(G)中存储了,直接存储到GO中「当作键值对」
 *      + 给予let/const声明的变量,存储在VO(G)中,和GO没啥关系
 *      + 如果不带任何声明关键字,则也是直接给GO设置的,类似于window.变量名
 */
var a = 12;

/**
 *  JS中的数据类型转换规则
 *      把原始值类型转化为Object
 *      把其他类型转化为数字
 *          + Number(value):一般是用于隐式转换
 *              + 字符串:空字符串会变为0,字符串中只要出现非数字字符结果就是NaN
 *              + 布尔:true为1 false为0
 *              + null;为0
 *              + undefined:为NaN
 *              + Symbol:报错
 *              + BigInt:正常转换
 *              + 对象:Symbol.toPrimitive->valueOf->toString
 *                  Number({}) // NaN
 *                  Number([]) // 0
 *          + parseInt/parseFloat(value)
 *              + 首先会把value转化为字符串,从左侧第一个字符串开始查找,直到找到一个非数字类型为止,把找到的结果转换为数字,如果一个都没找到,返回NaN
 *              + parseInt(value:string, radix)
 *                  radix是进制,有效取值范围为2~36,如果不传,默认是十进制「如果字符串是0x开始的,默认是十六进制;如果数字是0开始的,默认转换为八进制,如果字符串0开头的则不转,如parseInt('0123'),转换为123」;如果写0和不写是一样的规则
 *                              如果不在取值范围内,返回NaN
 *                  把value看作是radix进制「从左侧找到所有符合这个进制的字符,遇到不符合的结束查找」,把找到的字符串转换成数字「10进制」
 *              + 扫盲:
 *                  1.常用进制
 *                      2进制:0,1
 *                      8进制: 0~7
 *                      10进制: 0~9
 *                      15进制: 0~9 A~F
 *                  2.把其他进制转换为10进制
 *                      八进制数据‘12345.23’转换成十进制
 *                         3*8^-2 + 2*8^-1 + 5*8^0 + 4*8^1 + 3*8^2 + 2*8^3 + 1*8^4
 *      把其他类型转化为字符串
 *          规则:原始值类型转换会直接用引号包起来「bigInt除外」,对象转化为字符串是特殊的
 *              + toString() 「除掉Object.prototype.toString(检测数据类型)」
 *              + 字符串/模板字符串拼接
 *          实例:
 *              console.log(+'10') // 数字10   console.log(+'') // 数字0
 *                  面试题:i++、i+=1、i=i+1 三个是否一样
 *                      + i+=1、i=i+1是一样的
 *                      + i++返回的一定是数字,i+=1、i=i+1有可能是字符串拼接
 *              10+{} '10'+{}  // "10[object Object]"
 *              {}+10  {}+'10' // 数字10
 *          注意:不是所有对象都是字符串拼接的
 *               规则:  + 先去调用对象的Symbol.toPrimitive属性值,如果没有这个属性
 *                      + 再去调用对象的valueOf获取原始值,如果不是原始值
 *                      + 再去调用对象的toString转化为字符串「如果想要转化为number,再去调用Number」
 *              
 *      把其他类型转化为布尔
 *          规则:只有0、NaN、null、undefined、‘’会变为false,其余都是true
 *          + Boolean(value)
 *          + !!value
 *          + 条件判断
 * 
 * 
 */


 /**
  * JS中验证两个值是否相等
  *     + ==:相等「如果两边类型不一致,首先会隐式转换成相同的类型,然后再做比较」
  *         + 对象==字符串  对象->字符串
  *         + null==undefined // true「三个等于号是不相等的,但是null/undefined和其他数值都不会相等」
  *         + NaN==NaN 不相等
  *         + Symbol()==Symbol() 不相等
  *         + 剩下的情况「例如:对象==数字,字符串==布尔...」都是要转换为数字,再进行比较
  *     + ===:绝对相等「要求两边的类型和值都要相等,例如switch case」
  *     + Object.is()
  *         特殊的:Object.is(NaN,NaN) true
  */

  // 解题思路1: 掌握数据类型转换规则,如果a是一个对象,我们就可以利用'对象->数字'的规则去做一些处理
  const a = {
   
      i: 0 ,
      [Symbol.toPrimitive](){
   
          // this指向a
          return ++this.i
      }
  }

  // 或者
  const a = [1,2,3]
  a.toString = a.shift

  if(a==1&&a==2&&a==3){
   
      console.log(a)
  }

//   解题思路2:我们可以劫持对象的成员访问
  let i = 0
  Object.defineProperty(window, 'a', {
   
      get(){
   
        return ++i;
      }
  })

//   面试题
  /**
   * 解析:
   *    parseInt(27.6, 0)  // 27
   *    parseInt(0, 1) // NaN
   *    parseInt('0013', 2) // 把找到符合2进制的数据拿出来就是'001',转换成10进制就是1*2^0 = 1
   *    parseInt('14px', 3) // 14 -> 1 -> 1*3^0 = 1
   *    parseInt(123, 4) // 27
   */
  let arr = [27.6, 0, '0013', '14px', 123]
  arr = arr.map(parseInt)
  console.log(arr)

/**
 * 几种实用基础的数据结构详解
 *  数组(Array)、栈(Stack)、堆(Heap)、队列(Queue) -> JS相关的几种数据结构
 *  链表(Linked List)、图(Graph)、树(Tree)、散列表(Hash)
 * 
 * 1. 数组
 *  数组是特殊对象(对象特点:由0到多组键值对组成,对象的键不能是一个对象类型,如果是对象,会默认转换成字符串「[object Object]」)
 *  以数字作为索引{属性名},逐级递增,内置length属性,存储数组长度
 *  可以存储不同的数据类型值
 *  数组容量伴随存储的内容自动缩放(浏览器帮组我们完成的)
 *  优势;基于索引直接进行查找和获取,效率很高
 *  弊端:进行中间插入和删除操作时,性能非常低(数组塌陷和删除中间项的优化),主要原因是删除或者插入中间项时,所有位置都要调整
 * 
 * 2.栈
 *  先进后出
 *  只能在一端进行操作(顶端):包括增加(进栈)和删除(出栈)
 */

//  自己封装一个简单的栈
class Stack {
   
    container = []
    enter(element){
   
        // 进栈
        this.container.unshift(element)
    }
    leave(){
   
        // 出栈
        return this.container.shift()
    }
    size(){
   
        // 查看大小
        return this.container.length
    }
    value(){
   
        // 获取栈的内容
        return this.container.slice(0) // 克隆一个新的,以免数据被改动
    }
}

// 面试题:十进制转换为二进制  内置方法 num.toString(value) value代表进制
function decimal2binary(decimal){
   
    if(decimal === 0) return decimal
    let negative = decimal<0
    decimal = Math.abs(decimal)
    let merchant = Math.floor(decimal / 2), remianer = decimal % 2, sk = new Stack
    sk.enter(remianer)
    while(merchant>0){
   
        remianer = merchant % 2
        sk.enter(remianer)
        merchant = decimal / 2
    }
    return `${
     negative?'-':''}${
     sk.value().join("")}`
}

/**
 * 详细剖析函数的底层运行机制
 *   在当前上下文(全局上下文/私有上下文/块级上下文),JS代码自上而下执行之前,首先会把当前上下文中代‘var/function’关键字进行变量提升,
 *      带var的只是提前声明,并没有赋值「定义」,但是带function提前声明并且定义
 * 
 * 函数的底层运行机制:
 *  「创建函数:声明函数和赋值」
 *      1.在Heap堆内存中分配一块空间「16进制的内存空间」
 *      2.除了存储一些键值对「和对象一样」,还做了很多其他事情
 *          声明了函数的作用域「scope」,作用域的值是当前创建函数时候所在的上下文
 *          把函数体中的代码当作字符串存储到堆内存中
 *          和对象一样存储了一些自己的键值对「静态私有的属性和方法」
 *          把内存地址赋值给对应的变量「函数名」即可
 *   「执行函数」
 *      1.形成一个私有上下文供后期函数体中的代码执行「进栈和出栈」
 *          在私有上下文中,有一个后期存储当前上下文中声明的变量的地方「私有变量对象AO,AO是VO的分支,在函数中,变量对象是VO」
 *          进栈执行
 *      2.在代码执行之前,他还会处理很多的事情
 *          初始作用域链「scope-chain」:<自己形成的上下文,函数的作用域「创建函数声明的」>
 *              作用:但凡代码执行的时候,遇到一个变量,首先看是否是自己上下文中的私有变量(AO),如果是自己的,接下来对变量的操作和外界没有
 *                   任何关系;如果不是自己私有的,则继续按照作用域链查找是否为上级上下文,直到EC(G)结束即可
 *          初始化this指向
 *          初始化arguments「实参集合」
 *          形参赋值:形参变量也是当前上下文中的私有变量,是要存储到AO中的
 *          变量提升
 *     「代码正常执行」
 *     「一般情况下,在代码执行完,当前形成的私有上下文会出栈释放,以此来优化内存空间」
 */

//  实例
var x = [12, 23]
function fn(y){
   
    y[0] = 100
    y = [100]
    y[1]=200
    console.log(y) // [100, 200]
}
fn(x)
console.log(x) // [100, 23]

/**
 * 内存释放「优化」
 *  「堆内存」:看当前堆内存地址是否被其他事物(变量/事件绑定),如果被占用就不能释放「因为一但释放,以后别的东西基于这个地址就找不到堆了」
 *            如果没有被占用,则可以释放
 *            手动释放:变量=null
 *  「栈内存:执行上下文」
 *          全局上下文,只有在页面关闭的时候才会释放
 *          函数执行形成的私有上下文,一般情况下,在代码执行完毕后,私有上下文会自动出栈私有;但是有一些特殊情况是无法出栈释放,就是当前
 *          上下文的某些内容被上下文以外的事物占用了,则不能出栈释放
 */
// 解析题1
var i = 0
function A(){
   
    var i = 10
    function x(){
   
        console.log(i) // 10
    }
    return x;
}
var y = A()
y()
function B(){
   
    var i =20
    /**
     * 函数无论在哪执行,它的上级上下文一定是它的作用域,而作用域和函数在哪创建有关系,和在哪执行没有关系
     */
    y() // 10 
}
B()

// 解析题2
let x=5
function fn(x){
   
    return function(y){
   
        console.log(y+(++x))
    }
}
let f = fn(6)
f(7)
fn(8)(9)
f(10)
console.log(x)

// 解析题3
let a=0,b=0
function A(a){
   
    A = function(b){
   
        alert(a+b++)
    }
    alert(a++)
}
A(1)
A(2)

/**
 * 函数执行会产生一个私有上下文
 *  保护作用:保护私有上下文中的私有变量不受外界的干扰「防止全局变量污染」
 *  保存作用:一旦当前上下文不被释放,上下文中的私有变量及其值都会被保存起来,供其下级上下文调取使用
 * 我们把函数执行产生的这两大作用称之为‘闭包’机制「市面上一般都是人为只有产生不被释放的上下文才算闭包」
 */

 /**
  * 在ES6中,除了全局上下文和函数执行的私有上下文,还有一种上下文机制:块级私有上下文「块级作用域」
  *     在除函数/对象等之外的大括号中「例如:判断体,循环体」,如果出现let/const/function这些关键词,那么当前大括号所处的代码块,就会产生私有的块级上下文
  *     在此块级上下文当中,基于let/const/function声明的变量都是私有变量
  *     特殊性:
  *         基于var关键字声明的变量,既不会产生块级上下文,也不会收到块级上下文的影响
  *         function会在块级上下文和其上级上下文中都会被声明处理
  */
 {
   
     let a = 10
     const b = 20
     var c = 30
     function d(){
   }
 }
 console.log(a,b,c,d)


**

/**
 * this(函数执行的主体「谁执行的函数,this就是谁」)
 *  事件绑定
 *      DOM0: 元素.on[Event Name] = function(){}
 *      DOM2: 元素.addListener('Event Name', function(){}); 
 *            元素.attachEvent('on[Event Name]', function(){})
 *      给当前元素的某个事件行为绑定方法「此时是创建方法,方法还没执行」,当事件行为触发,浏览器会把绑定的函数执行,此时函数中的this是当前元素本身
 *      特殊:基于attachEvent实现事件绑定,this指向window
 *  函数执行「普通函数执行、成员访问、匿名函数、回调函数...」
 *  构造函数
 *  箭头函数「生成器函数generator」:箭头函数没有自己的this,一般this是指当前所在上下文的上级上下文
 *      箭头函数和普通函数的区别
 *          1. 语法区别
 *          2. this区别:箭头函数中没有自己的this
 *          3. 原型区别:箭头函数没有prototype属性,不能被new执行
 *          4. 箭头函数中没有arguments实参集合
 *  基于call/apply/bind强制修改this指向
 *      Function.prototype
 *          + call
 *          + apply
 *          + bind
 *      每一个函数都是Function的实例,都可以调用其原型的三个方法,用来实现函数内部this的改变
 */
console.log(this)
// 全局上下文中的this指的是window;块级上下文中没有自己的this,所有的this都是继承上级上下文的this「和箭头函数类似」
/**
 * 函数执行
 *      正常的普通函数执行:看函数执行前有没有‘.’,有'.'前面是谁this就是谁,没有this是window「严格模式下是undefined」
 *      匿名函数:
 *          + 函数表达式:等同于普通函数执行或者事件绑定机制
 *              const fn = function(){ console.log(this) }
 *          + 自执行函数: this一般指的是window或者undefined
 *          + 回调函数: this一般指的是window或者undefined,但是如果另外函数执行中,对回调函数的执行做了特殊处理「如call」,以自己处理的为主
 *      括号表达式:
 *          小括号中应该包含多项,这样也只取最后一项,但是this收到影响了(一般是window或者undefined)
 */
function fn(){
   
    console.log(this)
}
let obj = {
   
    name: 'wq',
    fn
}
fn() // this是Window
obj.fn() // this是obj
(obj.fn)() // this是obj,只加一项和没有小括号是一样的
(10, obj.fn)() // this是window
// 自执行函数
(function(x){
   
    console.log(this)
})(10)
// 回调函数:把一个函数A作为实参,传递给另外一个执行的函数B「在B函数执行中,可以把A执行」
function fn(callback){
   
    callback()
}
fn(function(){
   
    console.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值