知识总结[JS+ES6+TS篇]

865 阅读10分钟

JavaScript

1. 数据类型有哪些?ES6新增哪些?

基本数据类型:Number,String,Boolean,Null,Undefined,Symbol(es6)

引用类型:Object,Array,Function

2. 判断类型的方式

  1. typeof
类型输出注释
numbernumber
stringstring
booleanboolean
undefinedundefined
nullobjectnull会被当成空对象
objectobject
arrayobjecttypeof 无法判断 null,object, array
functionfunction
  1. instanceof

判断右侧构造函数的prototype是否在左侧实例的原型链上

  1. Object.prototype.toString.call()

    • 终极版,可判断任何类型;输出 '[object Number/Array...]'

3. 浅拷贝/深拷贝的方法都有哪些?

  1. 扩展运算符
    <!-- 仅对包含基本数据类型的数组有效 -->
    let newArr = [...oldArr]
    
  2. Array.prototype.concat
    let newArr = oldArr.concat()
    当concat不传参时,浅拷贝数组
    
  3. 序列化
    let newObj = JSON.parse(JSON.stringify(oldObj))
    // 无法拷贝Function,Symbol
    
  4. Object.assign()
    let obj = Object.assign({}, oldObj)
    // 可以拷贝Function,如果是引用类型则拷贝的是地址
    
  5. 递归版实现
    function deepClone (obj) {
        let newObj = obj instanceof Array ? [] : {}
        for(var i in obj){
            newObj[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]
        }
        return newObj
    }
    
  6. 非递归版实现
    // todo...
    

4. 什么是原型,原型链?

  • 原型对象也是普通的对象,是对象一个自带隐式的__proto__属性,原型对象也有可能有自己的原型,如果一个原型对象的原型不为null的话,我们就称之为原型链。
  • 原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。
  1. __proto__是内部原型,prototype是构造器(函数)的原型
  2. prototype只有构造器(函数)才有,__proto__除null,undefined都存在
  3. 所有构造器(函数)的__proto__都指向Function.prototype -> ƒ () { [native code] }
  4. 所有的构造器(函数)都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。
// 例子
function Fun(){}
let f = new Fun()
f.__proto__ === Fun.prototype // true   实例对象f的__proto__指向其构造函数的prototype
f.__proto__.__proto__ === Object.prototype // true  // f的原型也是原型对象,其也有__proto__并指向构造函数Object.prototype
// 原型对象上包含__proto__和constructor两个属性,constructor指回构造函数本身
Fun.__proto__ === Function.prototype // true 

👉 详情参考

5. 创建对象的方式

  1. 字面形式
    const obj = {
        name: "哈哈"
    }
  1. new操作符
    const oldObj = {age: 20}
    const obj = new Object(oldObj);
    obj.name = "哈哈"
    obj.age // 20
    obj.hasOwnProperty('age') // true
    // 无任何继承
  1. Object.create()
    const oldObj = {age: 20}
    const obj = Object.create(oldObj)
    obj.name = "哈哈" 
    obj.age // 20
    obj.hasOwnProperty('age') // false
    obj.__proto__ === oldObj // true
    // obj继承自oldObj
  • Object.create() 创建一个新对象,第一个参数是对象的原型,不能为空
  • 如果传入null,则创建一个没有原型的对象,也不继承Object的属性和方法( ! 不推荐 )

  • 如果想创建一个空对象,则传入{}或Object.prototype

  1. 工厂模式
    function createObj(name) {
        const obj = new Object();
        obj.name = name;
        return obj;
    }
    const obj = ctreateObj("哈哈");
  • 工厂模式解决了重复实例化多个对象的问题
  1. 构造函数模式
    function CreateObj(name) {
        this.name = name;
    }
    const obj = new CreateObj("哈哈");
  1. 原型模式
    function CreateObj() {}
    CreateObj.prototype.name="哈哈";
    CreateObj.prototype.action= function() {
        return "吃饭"
    };
    const obj = new CreateObj();
  • 所有实例共享构造函数的属性及方法,原型链上的属性和方法改变,则所有继承实例都会改变
  1. 构造函数+原型模式

6. 遍历对象的属性

方法解释
Object.keys()遍历可枚举的属性,不包含继承属性
for...in遍历可枚举的属性,包含继承属性
Object.getOwnPropertyNames()获取自身所有属性,包含不可枚举属性,返回数组

7. 判断对象的属性

方法解释
Object.prototype.hasOwnProperty()对象自身属性中是否具有指定的属性,返回布尔值
prototypeObj.isPrototypeOf(object)检测左侧对象是否在右侧对象的原型链上
Object.getPropertyOf()返回指定对象的原型

8. 继承有哪几种实现方式

  1. 原型链继承
	function parent(){
    	this.name = 'parent'
    }
    parent.prototype.age = '20'
    function child(){}
    child.prototype = new parent()
    let c = new child()
    c.name -> 'parent'
    c.age -> 20
  • 基于原型链,是父类和子类的实例,可继承父类的属性和原型链上的属性与方法,无法实现多继承
  1. 构造函数继承
	function parent(){
    	this.name = 'parent'
    }
    parent.prototype.age = '20'
    function child(){
    	parent.call(this)
    }
    c.name -> 'parent'
    c.age -> undefined
  • 复制父类的实例给子类,只继承父类实例的属性与方法,不继承原型上的,可实现多继承
  1. 组合继承(原型链继承+构造函数继承)
	function parent(){
    	this.name = 'parent'
    }
    parent.prototype.age = '20'
    function child(){
    	parent.call(this)
    }
  1. 原型式继承
  2. 寄生式继承
  3. 寄生组合继承
  4. Object.create()继承

9. ==和===的区别

  • == 比较时会先进行类型转换,然后比较转换值; 比如:2 == '2' // true
  • === 不会进行类转换,先比较类型,然后比较值,若类型不相同则直接false;比如: 2 === '2' // false
  1. 如果一个操作为布尔值,则将其转换为数值类型再比较;false转换为0,true转换为1
  2. 如果一个是字符串,另一个是数值,则将字符串转换为数值类型
  3. 如果一个是对象,另一个不是,则通过valueOf()获取对象的基本数据类型的值,然后按照上述规则进行转换
  4. 如果双方都是对象,则对比是否来自同一个引用
  5. null等于undefined,NaN不等于自身
  6. 比较时,nulll和undefined不能被转换成其他类型值

10. 闭包

👉 详细解读

11. Event Loop 微任务/宏任务

👉 详细解读

12 数组相关

👉 详细解读

13. 实现快速排序

快速排序采用分治法的思想,将一个复杂问题分为两个或多个子问题,直到子问题简单到可以直接求解,那么子问题的解的组合便是原问题的解

  function quickSort (arr) {
    if (arr.length <= 1) return arr;
    //取中间位置的数据作为基准值,并从原数组中删除该基准值
    let jzIndex = Math.floor(arr.length/2) // 获取基准值下标
    let jzNum = arr.splice(jzIndex, 1) // 删除并获取基准值

    let leftArr = [], rightArr = [] // 分别保存小于和大于基准值的数据
    arr.forEach(item => {
      if (item < jzNum[0]) {
        leftArr.push(item)
      }
      if (item >= jzNum[0]) {
        rightArr.push(item)
      }
    })
    //concat()连接两个数组
    return quickSort(leftArr).concat(jzNum, quickSort(rightArr))
  }
  console.log(quickSort([10,5,15,2,4])) // [2,4,5,10,15]

14. 实现防抖与节流

防抖: 任务频繁触发情况下,只有两次任务间隔超过指定时间,才会执行。若还未超过却又一次触发,则时间重新开始计算

 // 防抖函数
 function debounce (fn, time) {
    // 新建一个变量保存定时器
    let timeout = null;
    return function () {
      // 每次用户点击、输入时,清空上一个定时器
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        fn(...arguments)
      }, time)
    }
  }
  // 处理函数
  function handler () {
    console.log('防抖成功!')
  }
  // 触发
  debounce(handler, 1000)

节流: 频繁触发任务,任务按照一定时间间隔进行执行(稀释触发频率)

 // 节流函数
 function throttle (fn, time) {
    // 利用闭包保存是否可执行的变量
    let canRun = true
    return function () {
      // 如果为false,则终止函数执行
      if (!canRun) return;
      // 执行前将变量设为false
      canRun = false
      setTimeout(() => {
        fn(...arguments)
        canRun = true
      }, time)
    }
  }
  // 处理函数
  function handler () {
    console.log('节流成功!')
  }
  // 触发
  throttle(throttle, 1000)

15. bind(),call(),apply()用法及手写实现

👉 详细解读

16. ++i 与 i++

以下代码输出什么?

let i = 3;
while(i--) {
    setTimeout(() => {
        console.log(i)
    })
}
// result: -1 -1 -1

解析: 每次遍历,setTimeout都会产生一个宏任务,并push到宏任务队列中; i--是先赋值,后运算;每次遍历校验时,先校验当前未进行运算的i值;因此三次遍历校验的i值分别为:3,2,1,0;当i为0时,遍历结束,但i--计算后i得最终值为-1 结束后执行任务队列中的三个宏任务,得出-1,-1,-1

把i--换成--i,输入结果又是什么呢?

i--为先运算,后赋值;因而循环校验的是运算后的i值;故输出结果为: 00
  • i-- 先赋值,后运算
  • --i 先运算,后赋值

17. arguments是什么

  1. arguments实际是接收实参的一种对象,本身是一个类数组,可通过下标访问,不可使用数组的方法进行遍历
  2. 存在于函数中,不可被显示创建,只有在函数运行中才可用

arguments转换成数组的方法

1. 常规方法遍历
function transformsArrs() {
    var arr = [];
    for (var i = 0; i < arguments.length; i++) {
        arr.push(arguments[i]);
    }
    return arr;
}

2. 扩展运算符(arguments是一个对象)
function transformsArrs() {
    console.log(arguments instanceof Array); // false 
    var argsArray = [...arguments ];
    console.log(argsArray instanceof Array); // true 
}

3. Array.prototype.slice.call();
function transformsArrs() {
    console.log(arguments instanceof Array); // false 
    var argsArray = Array.prototype.slice.call(arguments);
    console.log(argsArray instanceof Array); // true 
}

4. Array.from() ES6
function transformsArrs() {
    console.log(arguments instanceof Array); // false 
    var argsArray = Array.from(arguments);
    console.log(argsArray instanceof Array); // true 
}

18. new原理

// 1.创建一个空对象
var obj = {}
// 2.将空对象的原型赋值为构造函数的原型
obj.__proto__ = Perons.prototype
// 3.变更构造函数内部this,将其指向刚刚创建出来的对象
Perons.call(obj)
// 4.返回对象
return obj

19. 什么是AST语法树

ES6

1. Promise解析及代码实现

什么是Promise?

  • promise是异步编程的解决方案,比起传统的异步解决方案“回调函数”、“事件”更合理强大,已被ES6纳入规范
  • 具有三种状态:pending 过渡态fulfilled 完成态rejected 失败态,状态一旦确定不可修改

Promise优缺点?

优点缺点
解决回调地域无法停止,一旦创建Promise立即执行
可读性高,便于维护必须指定回调,否则内部抛出异常
--当处于pending状态时,无法确定此时进行到哪一阶段(刚开始还是即将完成)

Promise的方法有哪些?作用都是什么?

  • Promise.prototype.then()

    Promise 实例添加状态改变时的回调函数,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数

    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

  • Promise.prototype.catch()

    .then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

  • Promise.prototype.finally()

    无论Promise最后状态如何,都会执行finally内的函数,ES2018引入

  • Promise.all()

    多个Promise同时执行,若全部成功,则以数组形式返回执行结果;若有一个是rejected,则只返回rejected的结果

  • Promise.race()

    多个Promise同时执行,返回最先结束Promise执行任务的结果,无论成功或失败

  • Promise.resolve()

    返回一个新的 Promise 实例,该实例的状态为resolve

    1. 若参数为Promise实例,则返回该实例

    2. 参数是一个thenable对象,(thenable为具有then方法的对象),将此对象转换为Promise并立即执行then方法

    3. 参数不是具有then方法的对象,或根本就不是对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

    4. 若不带任何参数,直接返回一个resolved状态的 Promise 对象

  • Promise.reject()

    返回一个新的 Promise 实例,该实例的状态为rejected

promise如何使用?

  • Promise是构造函数,需要new一个Promise的实例
  • Promise构造函数的参数为一个函数,函数内的参数为resolved和rejected
  • 实例后用then方法分别指定resolved状态和rejected状态的回调函数
  • then方法接受两个参数,rejected非必须;then返回Promise对象,则可链式调用

手写一个promise

// 定义Promise构造函数
  function Promise (exector) {
    let self = this;
    this.value = undefined;
    this.reason = undefined;
    // 定义Promise的状态
    this.status = 'pending'
    // 存储then中成功的回调函数
    this.onResolvedCallbacks = [];
    // 存储then中失败的回调函数
    this.onRejectedCallbacks = [];

    function resolve (value) {
      if (self.status === 'pending') {
        self.value = value
        self.status = 'resolved'
        self.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    function reject (reason) {
      if (self.status === 'pending') {
        self.reason = reason
        self.status = 'rejected'
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    // 异常处理
    try{
      exector(resolve, reject)
    }catch (e) {
      reject(e)
    }
  }

  // 在Promise原型上定义then方法,参数为成功和失败的回调
  Promise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    if (this.status === 'resolved') {
      onFulfilled(self.value)
    }
    if (this.status === 'rejected') {
      onRejected(self.reason);
    }
    if (this.status === 'pending') {
      this.onResolvedCallbacks.push(() => {
        onFulfilled(self.value)
      })

      this.onRejectedCallbacks.push(() => {
        onRejected(self.reason)
      })
    }
  }

  let promise = new Promise((resolve, reject)=> {
    setTimeout(() => {
      resolve('success')
    }, 1000)
  })

  promise.then(data => {
    console.log(data)
  }, err => {
    console.log(err)
  })

TS

1. 如何手动声明第三方库

2. interface和type的区别

3. 描述一个类

4. 使用ts的优点