JavaScript

一、JS的数据类型以及它们的区别

JS的数据类型分为:基础数据类型和引用数据类型。

  • 基础数据类型:Number、Boolean、String、Undefined、Null,以及ES6新增的Symbol、BigInt。
  • 引用数据类型:Object

其中,SymbolBigInt是ES6新增的数据类型:

  • Symbol:代表创建后独一无二且不可改变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
  • BigInt:一种数字类型的数据。它可以表示任何精度格式的整数,使用BigInt可以安全存储和操作大整数,即使这个数已经超出了Number能够表示的安全整数范围。使用BigInt可以避免精度丢失。

二、数据类型检测的方式有哪些

  1. typeof
js 复制代码
console.log(typeof 1) // number
console.log(typeof 'a') // string
console.log(typeof true) // boolean
console.log(typeof undefiend) // undefiend

console.log(typeof null) // object
console.log(typeof {}) // object
console.log(typeof []) // object

其中,null、对象、数组的数据类型判断为object,其他判断正确。

typeof null === 'object'原因:

  • JavaScript早期设计失误,null底层二进制存储为000,与object相似,导致typeof null === 'object'
  1. instanceof

instanceof可以正确判断对象数据类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。

js 复制代码
console.log(1 instanceof Number) // false
console.log('a' instanceof String) // false
console.log(true instanceof Boolean) // false

console.log({} instanceof Object) // true
console.log([] instanceof Array) // true
console.log(function() {} instanceof Function) // true

instanceof只能判断引用数据类型,不能判断基本数据类型。

  1. constructor
js 复制代码
console.log((1).constructor === Number) // true
console.log(('a').constructor === String) // true
console.log((true).constructor === Boolean) // true

console.log(({}).constructor === Object) // true
console.log(([]).constructor === Array) // true
console.log((function(){}).constructor === Function) // true

constructor有两个作用,一是判断数据的类型,二是对象实例通过constructor对象访问对象的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了

  1. Object.prototype.toString.call()
js 复制代码
const a = Object.prototype.toString

console.log(a.call(1))
console.log(a.call(true))
console.log(a.call('a'))
console.log(a.call([]))
console.log(a.call(function(){}))
console.log(a.call({}))
console.log(a.call(undefined))
console.log(a.call(null))

Object.prototype.toString.call()可以检测所有数据类型。

同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?

因为toString是Object原型上的方法,向Array、Function(){}等类型作为Object上的实例,都重写了toString方法。不同的对象调用toString方法,调用的都是重写的toString方法,不会去调用Object原型上的方法。

三、判断数组的方法有哪些

  1. 通过instanceof判断 [] instanceof Array
  2. Object.prototype.toString.call([])
  3. 通过ES6的Array.isArray([])判断
  4. 通过原型链判断 [].__proto__ === Array.prototype
  5. 通过Array.prototype.isPrototypeOf([])判断

四、请简述js中的this

this是JavaScript中的上下文对象,它指向最后一次调用这个方法的对象。

常见的this使用情况

  1. 在普通函数中,this指向全局对象(在浏览器中指向window,在nodejs中指向global)
  2. 在函数作为对象的方法使用时,this指向该对象
  3. 箭头函数中的this,箭头函数没有自己的this,它的this永远指向它的作用域父级,且this指向不可变
  4. 构造函数中的this,指向实例对象
  5. callapplybind 方法,可以改变this指向

五、箭头函数与普通函数的区别

  1. 箭头函数更加简洁
js 复制代码
const fn = () => {} //箭头函数
function fn() {} //普通函数
  1. 箭头函数没有自己的this,它的this继承于它的上级作用域
  2. 箭头函数继承来的this指向永远不会被改变
  3. call、apply、bind不会改变箭头函数的this指向
  4. 箭头函数不能作为构造函数使用
  5. 箭头函数没有自己的arguments
  6. 箭头函数没有prototype
  7. 箭头函数不能作为Generator生成器函数,不能使用yeild关键字

六、js的模块化

js的模块化概念 :JS 模块化是为了解决作用域污染依赖混乱可维护性差的问题。随着项目变大,把代码拆成模块能让我们更好地管理依赖关系减少全局变量冲突,也方便团队协作。
模块化也为构建工具提供了基础,比如 tree-shaking按需加载等优化手段。

js的模块化分为:AMD、CommonJS、ES Module

AMD概念 :AMD(Asynchronous Module Definition)是一种浏览器端的模块化规范,由 RequireJS 推广,强调异步加载模块。

CommonJS概念 :CommonJS 是一种模块化规范,最初是为服务器端(Node.js)设计的,强调同步加载模块。

AMD和CommonJS的区别

  1. AMD是异步加载,适用于浏览器端
  2. CommonJS是同步加载,适用于nodejs

七、let、const、var区别

  1. 变量提升:var具有变量提升,let、const不存在变量提升
  2. 暂时性死区:let、const具有暂时性死区,var不存在暂时性死区
  3. 块级作用域 :块级作用域是由{}包裹,let、const具有块级作用域,var没有块级作用域。
  4. 给全局添加属性 :浏览器的全局对象是window,node的全局对象是global。var声明的变量为全局变量,并会将该变量添加到全局对象的属性上。但是let、const不会。
  5. 重复声明:var声明变量时,可以重复声明变量,后面声明的同名变量会覆盖前面的。let、const不允许重复声明。
  6. 初始值设置:const声明必须赋值,let、var可以不用设置初始值。
  7. 指针指向:var、let创建的变量可以改变指针指向(重新赋值),const声明的变量不可以改变指针指向。

八、块级作用域解决了ES5中的什么问题

  1. 解决了变量提升的问题:内层变量可以覆盖外层变量

    js 复制代码
    var x = 10
    function fn() {
        console.log(x) // undefined
        var x = 20
        console.log(x) // 20
    }
    fn()
    console.log(x) // 10 
    // fn()函数经过变量提升之后
    function fn() {
        var x
        console.log(x)
        x = 20
        console.log(x)
    }
  2. 解决了变量覆盖问题

    js 复制代码
    var x = 10
    if (true) {
    var x = 20 // 重新赋值
    }
    console.log(x) // 20
  3. for循环变量共享的问题

    • var没有块级作用域,i 变成了全局变量。
    js 复制代码
    for(var i = 0; i < 3; i++){
    setTimeOut(function() {
    console.log(i)
    }, 0)
    }
    // 预期结果: 0,1,2
    // 实际结果: 3,3,3
    • let具有块级作用域,每次循环都会创建一个新的i,setTimeout中的i互不影响。
    js 复制代码
    for(let i = 0; i < 3; i++){
    setTimeOut(function() {
    console.log(i)
    }, 0)
    }
    // 实际结果: 0,1,2
  4. 变量重复声明的问题:var创建的变量可以重复声明,容易引发问题。let、const禁止重复声明变量。

    js 复制代码
    var x = 10
    var x = 20 // 重复声明不会报错
    console.log(x) // 20

九、new操作符的实现原理

  1. 新建一个空对象
  2. 将空对象的原型挂载到构造函数的原型上
  3. 将空对象的this指向构造函数
  4. 返回这个对象
js 复制代码
// 实现
function myNew(constructor, ...args) {
    if (typeof constructor !== 'function') return
    let obj = {}
    obj.__proto__ = constructor.prototype
    const result = constructor.apply(obj, args)
    return result instanceof Object ? result : obj
}

// 使用实例 - 构造函数没有显式返回值
function Person(name, age) {
    this.name = name;
    this.age = age;
}
const person1 = new Person('lili', 24)
console.log(person1) // 打印结果 {name: 'lili', age: 24}
const person2 = myNew(Person, 'qiqi', 21)
console.log(person2) // 打印结果 {name: 'qiqi', age: 21}
console.log(person1 instanceof Person); // 打印结果 true
console.log(person2 instanceof Person); // 打印结果 true

// 使用实例 - 构造函数有显式返回值
function Fn(name, age, sex) {
    this.name = name
    this.age = age
    return { sex }
}
const fn1 = new Fn('lili', 24, '女')
console.log(fn1.name) // 打印结果 undefined
console.log(fn1) // 打印结果 {sex: '女'}
console.log(fn1.sex) // 打印结果 '女'

const fn2 = myNew(Fn, 'qiqi', 21, '男')
console.log(fn2) // 打印结果 {sex: '男'}
console.log(fn2 instanceof Fn) // 打印结果 false

十、for in和for of的区别

  • for in 循环的是键值
  • for of 循环的是键名

十一、原型和原型链

当访问对象上的一个属性时,如果在该对象身上没有找到该属性,通常会去该对象的原型上找,该原型对象又会有自己的原型,这样一层一层的向上寻找,形成了原型链。原型链的尽头一般是Object.prototype

十二、事件循环(EventLoop)

事件循环是js执行异步代码的机制,它保证了js是单线程的,也能执行异步任务不阻塞主任务。

  1. 首先会先执行同步任务,再执行异步任务,进入到事件循环过程中。
  2. 执行异步任务的时候又会区分微任务宏任务。先执行微任务
  3. 后执行宏任务,如果执行宏任务的过程中产生微任务的话,会把微任务放到微任务队列中。
  4. 执行完当前宏任务,立刻执行微任务,再执行下一轮宏任务
  • 同步任务:console.log()、变量声明、函数调用、for循环
  • 异步任务setTimeoutfetch、事件绑定(click)、Promise、DOM 渲染
  • 微任务
    1. Promise.then/catch/finallyAsync/Await
    2. MutationObserver监听 DOM 变动(浏览器原生)
    3. queueMicrotask(fn)手动添加微任务(浏览器和 Node 支持)
    4. process.nextTickNode.js 中的微任务,比 Promise 更早
  • 宏任务
    1. setTimeout(fn, 0)定时器
    2. setInterval(fn, 1000)定时重复任务
    3. setImmediate(fn)Node.js 特有,立即执行
    4. axios 网络请求

十三、闭包

  • 闭包:指在函数内部可以访问到函数外部的变量。
  • 在哪里使用过闭包:防抖、节流函数。

十四、Promise

Promise的概念:Promise是ES6新引入的异步编程解决方案。

Promise的状态

  1. pending 进行中
  2. fulfilled 已成功
  3. rejected 已失败

状态只能从 pending 变为 fulfilled或 rejected,一旦变更不可逆。

Promise的方法

  1. Promise.all: 可以放多个promise对象,会等所有promise对象都执行完成后一起返回。如果其中有一个promise对象执行失败,promise.all返回失败结果。
  2. Promise.race: race的意思是"赛跑"。它里面可以放多个promise对象,哪个promise对象先执行完成,先返回哪个promise对象。

十五、异步编程的实现方式

  1. Promise
  2. Async/Await
  3. 回调函数
  4. generator(生成器)函数

十六、内存泄露

  1. 意外的全局变量
  2. 被遗忘的计时器和回调函数
  3. 不正确的使用闭包
  4. 脱离DOM的引用

十七、ES6的新特性

1.箭头函数 2. 新的变量声明方式let、const 3. 解构赋值 const [a, b] = [1, 2] 4. promise 5. 模版字符串: 在代码层面可换行

js 复制代码
const a = 'str'
console.log(`<p>${a}</p>
<div>${a}</div>`)
  1. 扩展运算符
js 复制代码
const a = [1, 2, 3]
const b = [5, 6, 7]
const c = [...a, ...b] // [1, 2, 3, 5, 6, 7]
  1. Symbol数据类型,代表独一无二的值
  2. 模块化
  3. for...of 遍历对象的键值
  4. Map 和 Set复杂数据类型:
  5. Proxy代理
  6. 类(Class):面向对象编程的概念
  7. 默认参数(Default Parameter),在定义函数时可以给参数设置默认值

十八、数组的原生方法

七个可以改变原数组的数组方法 unshift()、shift()、push()、pop()、sort()、reverse()、splice()

数组和字符串的转换

  1. toString() 默认按逗号分隔 ["hello", "world"].toString()"hello,world"
  2. join() 将数组元素用指定分隔符连接成字符串 ["a", "b", "c"].join("-")"a-b-c"
相关推荐
梦幻通灵24 分钟前
Vue3 Element日期控件置灰明天之后日期
前端·javascript·vue.js
晓131344 分钟前
【Cocos Creator 3.x】篇——第一章 简介
前端·javascript·游戏引擎
ZC跨境爬虫1 小时前
跟着 MDN 学JavaScript day_10:数组——数据的有序集合
android·java·开发语言·前端·javascript
晓13131 小时前
【Cocos Creator 2.x】篇——第五章 游戏常用关键技术
前端·javascript·vue.js·游戏引擎
qq4356947012 小时前
Vue03
javascript·vue.js
樱花的浪漫2 小时前
Typescript、Zod基础
前端·javascript·人工智能·语言模型·自然语言处理·typescript
竹林8182 小时前
监听智能合约事件,我用 wagmi v2 踩了三天坑,终于找到了稳定方案
前端·javascript
用户852495071842 小时前
Bun 到底是什么?一个比 Node.js "更快更香"的 JS 运行时
javascript·程序员
riuphan3 小时前
JavaScript 事件循环:单线程异步编程的核心机制
前端·javascript