JavaScript

1.数据类型

1.1概念篇

7种原始数据类型

- boolean
- string
- number
- undefined
- null
- Symbol
- bigInt

引用类型

- 对象Object
    - 普通对象-Object
    - 数组对象-Array
    - 正则对象-RegExp
    - 日期对象-Date
    - 数学函数-Math
    - 函数对象-Function
    - 基本包装类型 Boolean String Number

null是对象吗?为什么?

  • 结论: null不是对象
  • 解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。JS在运行之前编译成二进制形式,在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。

1 .toString()或者(1).toString为什么可以调用?

  • 数字后面的第一个点会被解释为小数点,而不是点调用。只不过不推荐这种使用方法,而且这样做也没什么意义
  • 基本包装类型:为什么基本类型却可以直接调用引用类型的方法呢?其实是js引擎在解析上面的语句的时候,会把这三种基本类型解析为包装对象(就是下面的new String()),而包装对象是引用类型可以调用Object.prototype上的方法。大概过程如下:

0.1+0.2为什么不等于0.3?

  • 在JS中数字采用的IEEE 754的双精度标准进行存储,到这里我们都理解只要采取IEEE 754 FP的浮点数编码的语言均会出现上述问题,只是它们的标准类库已经为我们提供了解决方案而已
  • 而对于像0.1这样的数值用二进制表示你就会发现无法整除,最后算下来会是 0.000110011….由于存储空间有限(双精度是64位存储空间),最后根据IEEE 754的规则会舍弃后面的数值,所以我们最后就只能得到一个,此时就已经出现了精度的损失
  • 简单理解 0.1和02不能被二进制浮点数精确表示
  • 在0.1 + 0.2这个式子中,0.1和0.2都是近似表示的,在他们相加的时候,两个近似值进行了计算,导致最后得到的值是0.30000000000000004,此时对于JS来说,其不够近似于0.3),于是就出现了0.1 + 0.2 != 0.3 这个现象

既然十进制0.1不能被二进制浮点数精确存储,那么为什么console.log(0.1)打印出来的确确实实是0.1这个精确的值?

  • 实际IEEE 754标准就是采用一套规则去近视于0。1 虽然无法精确存储,但是可以用一个近视值去表示,比如:0.100000000000000002 ==
    0.100000000000000010 // true
  • 当64bit的存储空间无法存储完整的无限循环小数,而IEEE 754 Floating-point采用round to nearest, tie to even的舍入模式,因此0.1实际存储时的位模式是0-01111111011-1001100110011001100110011001100110011001100110011010;

解决浮点数运算精度

  • 换算成整数进行运算,整数运算就不存在精度缺失问题,  我们可以把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完成后再进行降级(除以10的n次幂),这是大部分编程语言处理精度问题常用的方法。例如:
  • 但是换算也是浮点数运算的操作,同样也会存在问题,所以解决的方法就是采用字符串形式进行换算 比如:3.14===> {times: 100, num: 314} ===>有点类似大数相加的原理
  • 利用第三方库:Math.js,decimal.js

解题思路:

  • 二进制换算后(不会出现循环被截断,这是前提条件,这样换算后值落在这个区间就保证了精度无误,所以我们想办法使运算的数字落在这个区间 这个就是解搭问题的关键)由于仅位于Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER间的整数才能被精准地表示,也就是只要保证运算过程的操作数和结果均落在这个阀值内,那么运算结果就是精准无误的

  • 问题的关键落在如何将小数和极大数转换或拆分为Number.MIN_SAFE_INTEGER至Number.MAX_SAFE_INTEGER阀值间的数了

  • 小数转换为整数,自然就是通过科学计数法表示,并通过右移小数点,减小幂的方式处理;(如0.000123 等价于 123 * 10-6)

  • Number.EPSILON实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。

可以这样判断:0.1+0.2-0.3

1.2 检测篇

typeof 是否能正确判断类型?

  • 对于原始类型来说,除了 null 都可以调用typeof显示正确的类型。
  • 但对于引用数据类型,除了函数之外,都会显示"object"。

instanceof能判断基本数据类型

Object.is和===的区别?

  • Object在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0(false),NaN和NaN(true)。

Object.prototype.toString

1.3 转换篇

JS中类型转换有哪几种?

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

== 和 ===有什么区别?

  • ===叫做严格相等,是指:左右两边不仅值要相等,类型也要相等,例如'1'===1的结果是false,因为一边是string,另一边是number。

  • ==不像===那样严格,对于一般情况,只要值相等,就返回true,但==还涉及一些类型转换,它的转换规则如下:(比较最终都是转化为数字的)

      - 两边的类型是否相同,相同的话就比较值的大小,例如1==2,返回false
      - 判断的是否是null和undefined,是的话就返回true
      - 判断的类型是否是String和Number,是的话,把String类型转换成Number,再进行比较
      - 判断其中一方是否是Boolean,是的话就把Boolean转换成Number,再进行比较
      - 如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较
    

对象转原始类型是根据什么流程运行的

  • 如果存在Symbol.toPrimitive()方法,优先调用再返回
  • 调用valueOf(),如果转换为原始类型,则返回
  • 调用toString(),如果转换为原始类型,则返回
  • 如果都没有返回原始类型,会报错

如何让if(a == 1 && a == 2)条件成立?

  • 其实就是上一个问题的应用。
var a = {
  value: 0,
  valueOf: function() {
    this.value++;
    return this.value;
  }
};
console.log(a == 1 && a == 2);// true

2.拷贝

2.1 浅拷贝:shallowClone

  • 一个新的对象直接拷贝已存在的对象的对象属性的引用,即浅拷贝。(对象的属性)
  • Object.assign
  • ...展开运算符
  • concat浅拷贝数组
  • slice浅拷贝

2.2 深拷贝:deepClone

  • 深拷贝会另外拷贝一份一个一模一样的对象,从堆内存中开辟一个新的区域存放新对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

JSON.parse(JSON.stringify());

  • 无法解决循环引用的问题。举个例子:
  • 无法拷贝一写特殊的对象,诸如 RegExp, Date, Set, Map等
  • 无法拷贝函数(划重点)

动手实现一个深拷贝

  1. 普通类型

    • 直接返回就行
  2. 引用类型

    • 循环引用
  • 解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。

    • 可遍历类型

        - Set
        - Map
        - Array
        - Object
      
    • 不可遍历类型(考虑基本包装类型(引用类型))

        - Boolean
        - Number
        - String
      
      • Date
        • Unix时间戳:value
          一个 Unix 时间戳(Unix Time Stamp),它是一个整数值,表示自1970年1月1日00:00:00 UTC(the Unix epoch)以来的毫秒数,忽略了闰秒。请注意大多数 Unix 时间戳功能仅精确到最接近的秒。
          - 重新生成一个Date实例,参数传入一个Unix
    • Error

    • Symbol

      • Object(Symbol('foo'))
      • es6过后就不提倡用new 直接类似Symbol(xxx)这样执行就行
  • WeakMap、WeakSet、ArrayBuffer对象、TypedArray视图和DataView视图、Float32Array、Float64Array、Int8Array

  • Blob、File、FileList、ImageData

拷贝函数

    - lodash对函数的处理 因为拷贝函数没有啥意义
    - 函数(prototype来区分下箭头函数和普通函数,箭头函数是没有prototype)

        - 箭头函数

            - 我们可以直接使用eval和函数字符串来重新生成一个箭头函数,注意这种方法是不适用于普通函数的。

        - 非箭头函数

            - 分别使用正则取出函数体和函数参数,然后使用new Function ([arg1[, arg2[, ...argN]],] functionBody)构造函数重新构造一个新的函数

赋值

  • 基本数据类型:赋值,赋值之后两个变量互不影响

  • 引用数据类型:赋,两个变量具有相同的引用,指向同一个对象,相互之间有影响

  • 为什么需要浅拷贝和深拷贝?

    • 对引用类型进行赋操作,两个变量指向同一个对象,改变变量 a 之后会影响变量 b,哪怕改变的只是对象 a 中的基本类型数据
    • 通常在开发中并不希望改变变量 a 之后会影响到变量 b,这时就需要用到浅拷贝和深拷贝。
    • 这就是为什么需要浅拷贝和深拷贝的缘由,因为我们在赋值操作时候,操作引用类型的时候不想b影响a,所以需要浅拷贝或者深拷贝,这也从中可以看出赋值与拷贝深拷贝的区别
    • 通常深浅拷贝是解决引用类型之间互相影响的,要明白这点
  • ❗️当我们进行赋值,考虑到引用类型赋值完做修改会相互影响,就引出了对应的深浅拷贝方案去解决

this指向

其实JS中的this是一个非常简单的东西,只需要理解它的❗️执行规则就行

显示绑定

  • call
  • apply
  • bind

隐式绑定

  • 全局上下文

    • 全局上下文默认this指向window, 严格模式下指向undefined。
  • 直接调用函数

    • this相当于全局上下文的情况
  • 对象.方法的形式调用

    • 谁调用这个方法,它就指向谁
  • DOM事件绑定(特殊)

    • onclick和addEventerListener中 this 默认指向绑定事件的元素。

IE比较奇异,使用attachEvent,里面的this默认指向window。

  • new构造函数绑定

    • 此时构造函数中的this指向实例对象
  • 箭头函数

    • 箭头函数没有this, 因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到就是window(严格模式是undefined)

JS数组

函数的arguments为什么不是数组?如何转化成数组?

  • 常见的类数组

    • 用getElementsByTagName/ClassName()获得的HTMLCollection
    • 用querySelector获得的nodeList
  • 转换成数组

    • Array.prototype.slice.call()
    • Array.from()
    • ES6展开运算符
    • 利用concat+apply

forEach中return有效果吗?如何中断forEach循环?

  • 在forEach中用return不会返回,函数会继续执行。

  • 中断方法:

    • 使用try监视代码块,在需要中断的地方抛出异常
    • 官方推荐方法(替换方法):用every和some替代forEach函数。every在碰到return false的时候,中止循环。some在碰到return true的时候,中止循环 (面试问到团队规范✅)

JS判断数组中是否包含某个值

  • array.indexOf
  • array.includes(searcElement[,fromIndex]) 推荐✅
  • array.find(callback[,thisArg])
  • array.findeIndex(callback[,thisArg])

JS中flat---数组扁平化

  • 递归
  • reduce+递归
  • 原型链上的flat方法(数组实例上的方法) [1,2,3].flat(2)

JS数组的高阶函数

  • 什么是高阶函数:一个函数就可以接收另一个函数作为参数或者返回值为一个函数,这种函数就称之为高阶函数。

  • 数组中的高阶函数

    • map
    • reduce
    • filter
    • sort

JS如何实现继承

什么是继承

  • 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率

第一种: 借助call(构造函数式继承

第二种: 借助原型链(原型链继承)

第三种:将前两种组合(组合继承)

第四种:原型式继承(如果我要继承的父类是一个普通对象而不是构造函数(因为JavaScript 语言中,生成实例对象的传统方法是通过构造函数),那么如何实现)

  • Object.create方法

第五种:寄生继承

  • 核心:在原型式继承的基础上,增强对象,返回构造函数(类似工厂函数进行包装)

第六种:寄生组合继承

原型链

原型对象和构造函数有何关系?

  • 在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象。
  • 当函数经过new调用时,这个函数就成为了构造函数,返回一个全新的实例对象,这个实例对象有一个proto属性,指向构造函数的原型对象。

能不能描述一下原型链

  • 首先要明白实例的proto属性与构造函数的protype属性都是指向原型对象,原型对象的constructor属性又是指向构造函数
  • JavaScript对象通过proto 指向父类的原型对象,直到指向Object的原型对象为止,这样就形成了一个原型指向的链条, 即原型链。
  • 对象的 hasOwnProperty() 来检查对象自身中是否含有该属性
  • 使用 in 检查对象中是否含有某个属性时,如果对象中没有但是原型链中有,也会返回 true

DOM事件

绑定事件的⽅法

    1. HTML的内联属性
    1. 元素的onXXX属性添加事件
    1. addEventListener
    • 标准方法

      • el.addEventListener(eventNam
        e, handle, useCapture |
        options)

        • {String} eventName 事件名称
        • {Function} handle 事件函数
        • {Boolean} useCapture 是否在事
          件捕获阶段触发事件,true 代表
          捕获阶段触发,false 代表在冒
          泡阶段触发
        • {Object} options 选项对象
      • el.removeEventListener(eventN
        ame, handle)

    • IE⽅法

      • el.attachEvent(eventName,
        handle)
      • el.detachEvent(eventName,
        handle)
    • 对⽐:

      • 由于IE8不⽀持 事件捕获 ,所以
        通过 attachEvent/detachEvent
        绑定的时间也只能在 冒泡阶段
        触发
      • 通过 attachEvent/detachEvent
        绑定的事件函数会在全局作⽤域
        中运⾏,即: this === window
      • 通过 attachEvent/detachEvent
        绑定的事件函数以绑定时的先后
        顺序 “倒序” 被执⾏
      • attachEvent/detachEvent 的第
        ⼀个参数要在事件名称前⾯加
        'on'
  • 评价

      1. 违反最佳实践
      1. 由于只能赋值⼀个handler,
        因此会存在覆盖的问题
      1. 调⽤addEventListener时,要
        注意销毁组件时回收handler,
        removeEventListener。但是这
        样的话,handler⼜必须⽤⼀个变
        量保持引⽤

事件对象

  • 标准

    • 属性

      • currentTarget:currentTarget
        的值始终等于 this,即指向事件
        所绑定到的元素

      • target:真正触发事件的元素

      • bubbles:表示事件是否冒泡

      • cancelable:是否可以取消默认
        ⾏为

      • defaultPrevented:为真则被调
        ⽤了preventDefault()

      • detail:描述事件的细节

      • eventPhase:描述事件处理函数
        的阶段

        • 1:捕获
        • 2:处于⽬标
        • 3:冒泡
      • trusted:为真则是浏览器原⽣事
        件,为假则是⼿动添加的事件

      • type:事件类型

    • 方法

      • event.preventDefault():阻⽌默
        认事件

      • event.stopIPropagation():阻⽌
        冒泡 也可以阻止捕获(根据dom事件流 捕获阶段被阻止了 处于目标阶段和事件冒泡也不会被触发了)

      • stopImmediatePropagation 既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stopPropagation 只能实现前者的效果

        • react使用合成事件,如果出现点击空白区域弹框消失,可以利用stopImmediatePropagation阻止事件的其它函数执行 (只执行我这个事件的回调函数,其它不执行)因为我都冒泡到document上了,阻止冒泡没什么用了,另外一种解决方法关闭操作注册到window上
        • e.nativeEvent.stopImmediatePropagation 这个解决问题的不是阻止冒泡 而是不允许其他的事件回调触发 因为我们这时候事件已经冒泡到document上了 为document再绑定了一个click事件 此时我们想触发按钮这个click事件(实际绑定到document上)触发以后,不允许再触发其他document上click的事件回调函数
  • IE

    • 属性

      • srcElement:与target的值相同
      • returnValue:默认为真,若设置
        为false,可以阻⽌默认事件
      • cancelBubble:默认为假,设置
        为true可以阻⽌冒泡
    • 方法

      • el.onclick = function () {
        window.event
        }
      • el.attachEvent:回调中的event
        可以为传⼊的event参数也可为
        window.event

DOM事件流(统一这两种事件流)

  • 事件流:描述的是从页面中接收事件的顺序。但有意思的是

  • 执行的三个阶段

    • 事件捕获

      • 当事件发生时,首先发生的是事件捕获,为父元素截获事件提供了机会
    • 处于⽬标

      • 事件到了具体元素时,在具体元素上发生,并且被看成冒泡阶段的一部分。
    • 事件冒泡

      • 冒泡阶段发生,事件开始冒泡
  • 注意点

    • DOM事件流确实会按照这三个阶段执行,我们可以通过addEventListener注册事件时候指定useCapture的值来规定事件在捕获阶段还是冒泡阶段中执行(如果该对象是目标对象,则会在目标阶段执行)
    • 你会注意到按照DOM事件流这种执行顺序,事件不会被触发两次吧,造成重复触发,并不是的,我们可以有选择是在冒泡阶段触发还是捕获阶段,默认是冒泡阶段
    • // 这段代码表示该click事件会在事件捕获阶段执行(❗️注意得判断是不是目标对象,如果是目标对象就是表示它在处于目标这个阶段执行)
      // 如何判断是否是目标对象:最具体的元素(文档中嵌套层次最深的那个节点)
      document.querySelector("#button").addEventListener(
      "click",
      function () {
      console.log("处于目标button click");
      },
      true
      );

多种事件

  • UI 事件

    • load

      • window上触发:⻚⾯完全加载
        完,包括所有图像、js⽂件、css
        ⽂件、内嵌对象等等
      • window上触发:⻚⾯完全加载
        完,包括所有图像、js⽂件、css
        ⽂件、内嵌对象等等
      • 你可能感兴趣的:(JavaScript)