likes
comments
collection
share

21天筑基期--JavaScript系列

作者站长头像
站长
· 阅读数 12

JavaScript、TypeScript和ES6

有重合部分,我认为放一起比较合理

JS 的数据类型有哪些?

纯记忆题,答案有 8 个词,建议背诵 10 次。

字符串、数字、布尔、undefined、null、大整数、符号、对象

string、number、boolean、undefined、null、bigint、symbol、object

提了就零分的答案有:数组、函数、日期。这些是类 class,不是类型 type

 ES6常用的API有哪些?

var、let和const的区别

var 声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined

let和const:引入了块级作用域变量的声明方式。

const声明一个只读的常量。一旦声明,常量的值就不能改变

箭头函数:提供了更简洁的函数声明语法。

  • 简洁的语法形式:箭头函数使用了更简洁的语法形式,省略了传统函数声明中的function关键字和大括号。它通常可以在更少的代码行数中表达相同的逻辑。

  • 没有自己的this:箭头函数没有自己的this绑定,它会捕获所在上下文的this值。这意味着箭头函数中的this与其定义时所在的上下文中的this保持一致,而不是在函数被调用时动态绑定。这可以避免传统函数中常见的this指向问题,简化了对this的使用和理解。

  • 没有arguments对象:箭头函数也没有自己的arguments对象。如果需要访问函数的参数,可以使用剩余参数(Rest Parameters)或使用展开运算符(Spread Operator)将参数传递给其他函数。

  • 无法作为构造函数:箭头函数不能用作构造函数,不能使用new关键字调用。它们没有prototype属性,因此无法使用new关键字创建实例。

  • 隐式的返回值:如果箭头函数的函数体只有一条表达式,并且不需要额外的处理逻辑,那么可以省略大括号并且该表达式将隐式作为返回值返回。

  • 不能绑定自己的this、super、new.target:由于箭头函数没有自己的this绑定,也无法使用

模板字符串:允许使用反引号(`)创建多行字符串和插入变量。

解构赋值:可以从数组或对象中快速提取值并赋给变量。

JavaScript 解构赋值实用指南 - 掘金 (juejin.cn)

默认参数:在函数声明时可以设置参数的默认值。

扩展运算符:用于将数组或对象展开为独立的元素。

类(Class) :引入了类和面向对象编程的概念。

模块化(Modules) :通过import和export语句实现模块的导入和导出。

Promise:用于处理异步操作,提供了更优雅的方式来处理回调函数。

  • Promise.all():中的Promise序列会全部执行通过才认为是成功,否则认为是失败;

  • Promise.race():中的Promise序列中第一个执行完毕的是通过,则认为成功,如果第一个执行完毕的Promise是拒绝,则认为失败;

  • Promise.any():中的Promise序列只要有一个执行通过,则认为成功,如果全部拒绝,则认为失败;

  • Promise.allSettled():是一个用于处理多个 Promise 对象的方法,并且会返回一个包含每个 Promise 对象的解决状态的数组;

  • Promise.resolve():会返回一个新的 Promise 实例,该实例的状态为fulfilled

  • Promise.reject():也会返回一个新的 Promise 实例,该实例的状态为rejected

Set和Map:提供了集合和字典数据结构,分别对应Set和Map对象。

Symbol:引入了一种新的原始数据类型,用于创建唯一的标识符。

Proxy:允许创建一个代理对象,用于拦截和自定义对象的操作。

数组方法

想学Js数据结构与算法,学好数组必不可少! - 掘金 (juejin.cn)

Map 和 forEach 的区别

相同点

  1. 都能遍历数组
  2. 中途不能被break打断
  3. 函数中都有三个参数,当前遍历的元素,当前元素的索引,原数组。

不同

  1. forEach没有返回值,也就是返回undefined,map会开辟新的一个内存空间,返回新的数组,这点也方便链式调用其他数组方法。
  2. map的效率比forEach高

对象方法

JavaScript 对象解析 - 掘金 (juejin.cn)

ES6引入了一些新的对象方法,如Object.assign、Object.keys、Object.values等。

== 和 ===区别

== 等于操作符 === 全等操作符

区别 相等操作符(==)会做类型转换,再进行值的比较,全等运算符不会做类型转换

除了在比较对象属性为null或者undefined的情况下,我们可以使用相等操作符(==),其他情况建议一律使用全等操作符(===)

闭包

闭包是指那些能够访问自由变量的函数,当所有函数被保存到外部时

闭包 = 函数 + 自由变量

任何闭包的使用场景都离不开这两点:

  • 创建私有变量
  • 延长变量的生命周期

原型链是什么?

  • 原型:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,其实就是 prototype 对象。 假设我们有一个数组对象 a=[] ,这个 a 也会有一个隐藏属性,叫做_proto_这个属性会指向 Array.prototype
var a = [];
a.__proto__ ===  Array.prototype;
// 用 b表示 Array.prototype
b._proto_ === Object.prototype

于是就通过隐藏属性 __proto__ 形成了一个链条:

a ===> Array.prototype ===> Object.prototype 

这就是原型链。 怎么做:

看起来只要改写 b 的隐藏属性 __proto__ 就可以改变 b的原型(链)

const x = Object.create(原型)
// 或
const x = new 构造函数() // 会导致 x.__?????__ === 构造函数.prototype

这样一来,a 就既拥有 Array.prototype 里的属性,又拥有 Object.prototype 里的属性。 解决了什么问题:

在没有 Class 的情况下实现「继承」。以 a ===> Array.prototype ===> Object.prototype 为例,我们说:

  1. a 是 Array 的实例,a 拥有 Array.prototype 里的属性
  1. Array 继承了 Object(注意专业术语的使用)
  1. a 是 Object 的间接实例,a 拥有 Object.prototype 里的属性

优点:

简单、优雅。

缺点:

跟 class 相比,不支持私有属性。

怎么解决缺点:

使用 class 呗。但 class 是 ES6 引入的,不被旧 IE 浏览器支持。

建议熟读这篇文章:

JS 中 proto 和 prototype 存在的意义是什么?

JS继承方式

  • 原型链继承
  • 构造函数继承(借助 call)
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

bind、call、apply 区别

Function.prototype.apply() - JavaScript | MDN (mozilla.org)

call apply bind 作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向

  • 三者都可以改变函数的this对象指向
  • 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefinednull,则默认指向全局window
  • 三者都可以传参,但是apply是数组,而call是参数列表,且applycall是一次性传入参数,而bind可以分为多次传入
  • bind 是返回绑定this之后的函数,apply call 则是立即执行

TS 和 JS 的区别是什么?有什么优势?

  1. 语法层面:TypeScript = JavaScript + Type(TS 是 JS 的超集)
  1. 执行环境层面:浏览器、Node.js 可以直接执行 JS,但不能执行 TS(Deno 可以执行 TS)
  1. 编译层面:TS 有编译阶段,JS 没有编译阶段(只有转译阶段和 lint 阶段)
  1. 编写层面:TS 更难写一点,但是类型更安全
  1. 文档层面:TS 的代码写出来就是文档,IDE 可以完美提示。JS 的提示主要靠 TS

21天筑基期--JavaScript系列

21天筑基期--JavaScript系列

TS特性

  • 类型批注和编译时类型检查 :在编译时批注变量类型
  • 类型推断:ts中没有批注变量类型会自动推断变量的类型
  • 类型擦除:在编译过程中批注的内容和接口会在运行时利用工具擦除
  • 接口:ts中用接口来定义对象类型
  • 枚举:用于取值被限定在一定范围内的场景
  • Mixin:可以接受任意类型的值
  • 泛型编程:写代码时使用一些以后才指定的类型
  • 名字空间:名字只在该区域内有效,其他区域可重复使用该名字而不冲突
  • 元组:元组合并了不同类型的对象,相当于一个可以装不同类型数据的数组

typescript 的数据类型有哪些

typescript 的数据类型主要有如下:

  • boolean(布尔类型)
  • number(数字类型)
  • string(字符串类型)
  • array(数组类型)
  • tuple(元组类型)
  • enum(枚举类型)
  • any(任意类型)
  • null 和 undefined 类型
  • void 类型
  • never 类型
  • object 对象类型

type 和 interface 的区别是什么?

typescript 中的 interface 和 type 到底有什么区别?

  1. 组合方式:interface 使用 extends 来实现继承,type 使用 & 来实现联合类型。
  1. 扩展方式:interface 可以重复声明用来扩展,type 一个类型只能声明一次
  1. 范围不同:type 适用于基本类型,interface 一般不行。
  1. 命名方式:interface 会创建新的类型名,type 只是创建类型别名,并没有新创建类型。

其他……建议搜一下博客。

前端路由

这个大佬文章清晰 深入浅出前端路由

手写new操作符

那么我们就动手来实现一下new

function mynew(Func, ...args) {
    // 1.创建一个新对象
    const obj = {}
    // 2.新对象原型指向构造函数原型对象
    obj.__proto__ = Func.prototype
    // 3.将构建函数的this指向新对象
    let result = Func.apply(obj, args)
    // 4.根据返回值判断
    return result instanceof Object ? result : obj
}

手写 AJAX

AJAX(Asynchronous JavaScript and XML),指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。

1.创建XMLHttpRequest对象,创建一个异步调用对象. 2.创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息. 3.设置响应HTTP请求状态变化的函数. 4.发送HTTP请求

记忆题,写博客吧

const ajax = (method, url, data, success, fail) => {
  var request = new XMLHttpRequest()
  request.open(method, url);
  request.onreadystatechange = function () {
    if(request.readyState === 4) {
      if(request.status >= 200 && request.status < 300 || request.status === 304) {
        success(request)
      }else{
        fail(request)
      }
    }
  };
  request.send();
}
ajax({
  method: 'GET',
  url: 'https://example.com/api/data',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your_token'
  },
  data: null, // Optional data to send in the request body
  success: function(response) {
    console.log('Request successful:', response);
    // Handle the successful response
  },
  error: function(status) {
    console.log('Request failed with status:', status);
    // Handle the error response
  }
});

深拷贝和浅拷贝

浅拷贝

基本类型数据保存在在栈内存中

引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中 下面简单实现一个浅拷贝

    function shallowClone(obj) {
        const newObj = {};
        for(let prop in obj) {
            if(obj.hasOwnProperty(prop)){
                newObj[prop] = obj[prop];
            }
        }
        return newObj;
    }

JavaScript中,存在浅拷贝的现象有:

  • Object.assign
  • Array.prototype.slice()Array.prototype.concat()
  • 使用拓展运算符实现的复制

Object.assign

    var obj = {
        age: 18,
        nature: ['smart', 'good'],
        names: {
            name1: 'fx',
            name2: 'xka'
        },
        love: function () {
            console.log('fx is a great girl')
        }
    }
    var newObj = Object.assign({}, fxObj);

slice()

    const fxArr = ["One", "Two", "Three"]
    const fxArrs = fxArr.slice(0)
    fxArrs[1] = "love";
    console.log(fxArr) // ["One", "Two", "Three"]
    console.log(fxArrs) // ["One", "love", "Three"]

concat()

    const fxArr = ["One", "Two", "Three"]
    const fxArrs = fxArr.concat()
    fxArrs[1] = "love";
    console.log(fxArr) // ["One", "Two", "Three"]
    console.log(fxArrs) // ["One", "love", "Three"]

拓展运算符

    const fxArr = ["One", "Two", "Three"]
    const fxArrs = [...fxArr]
    fxArrs[1] = "love";
    console.log(fxArr) // ["One", "Two", "Three"]
    console.log(fxArrs) // ["One", "love", "Three"]

深拷贝

深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

常见的深拷贝方式有:

  • _.cloneDeep()
  • jQuery.extend()
  • JSON.stringify()
  • 手写循环递归

_.cloneDeep()

    const _ = require('lodash');
    const obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    const obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);// false

jQuery.extend()

    const $ = require('jquery');
    const obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    const obj2 = $.extend(true, {}, obj1);
    console.log(obj1.b.f === obj2.b.f); // false

JSON.stringify()

    const obj2=JSON.parse(JSON.stringify(obj1));

但是这种方式存在弊端,会忽略undefinedsymbol函数

    const obj = {
        name: 'A',
        name1: undefined,
        name3: function() {},
        name4:  Symbol('A')
    }
    const obj2 = JSON.parse(JSON.stringify(obj));
    console.log(obj2); // {name: "A"}

循环递归

    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
      if (typeof obj !== "object") return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    

防抖和节流

  • 节流:只执行第一次点击,在第一次点击完成前,后面的点击都会无效,技能CD
  • 防抖:防抖是在多次点击中,只执行最后一次,前面的点击都会被取消,回城
function throttled(fn, delay = 500) {
    let timer = null
    return function (...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args)
                timer = null
            }, delay);
        }
    }
}
function debounce(fun,time){
    let timer
    return function(...args){
        clearTimeout(timer) // 打断回城
         // 重新回城
        timer=setTimeout(()=>{
          fun.apply(this,args) // 回城后调用 fn
        },time)
    }
}

大文件上传如何做断点续传

上传大文件时,以下几个变量会影响我们的用户体验

  • 服务器处理数据的能力
  • 请求超时
  • 网络波动

面试官:大文件上传如何做断点续传?

web常见的攻击方式

21天筑基期--JavaScript系列

axios封装

axios拦截器分为响应和请求拦截器,请求拦截器 在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装; 响应拦截器 同理,响应拦截器也是如此功能,只是在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等

带你梳理我的Axios封装(TS) - 掘金 (juejin.cn)