【前端学习|面试题】JavaScript合集(五)

函数柯理化

  函数柯里化(Currying)是一种函数式编程的技术,它将一个接受多个参数的函数转化为一系列接受单个参数的函数。柯里化的主要目的是将函数变得更灵活,使其更容易复用和组合。

柯里化的过程如下:

  1. 原始函数接受多个参数。
  2. 第一个参数传递给函数,并返回一个接受剩余参数的新函数。
  3. 重复步骤2,直到所有参数都被处理。

示例代码:

// 非柯里化的加法函数
function add(a, b) {
  return a + b;
}

// 使用柯里化实现的加法函数
function curryAdd(a) {
  return function(b) {
    return a + b;
  };
}

// 使用柯里化函数
const add5 = curryAdd(5);
console.log(add5(3)); // 输出 8

  这个示例中,curryAdd 函数将原始的加法函数 add 转化为一个接受单个参数的函数,并返回一个新的函数,这个新函数也接受单个参数。通过柯里化,我们可以先传递部分参数,然后在需要时传递剩余的参数,使函数更加灵活。

柯里化的优点包括:

  1. 参数复用:可以轻松创建多个接受相同参数的函数,而不需要重复传递相同的参数。
  2. 组合函数:柯里化可以更容易地将多个函数组合在一起,构建复杂的函数管道。
  3. 延迟执行:可以先传递一部分参数,延迟执行函数,直到所有参数都准备好。
  4. 更容易测试:柯里化可以更容易进行单元测试,因为可以分别测试每个部分的函数。

收集参数:

// 函数柯理化
function open(a, b, c, d) {
    window.location.href = a + b + c + d
}

// open('https://', 'www.4399.com', ':443', '/flash/')

function fn(f, ...arg) {
    // 收集参数
    let argAll = arg || []
    //获取功能函数的形参个数
    let len = f.length
    // 打印当前获取的参数
    console.log(argAll)
    return function (...args) {
        // 合并实参数组
        argAll = [...argAll, ...args]
        console.log(argAll)
        if (argAll.length < len) {
            // 参数不够
            return fn(f, ...argAll)
        } else {
            // 参数够了,传给功能函数并调用
            return f(...argAll)
        }
    }
}

let resx = fn(open,)
let resy = fn(open, '1')()()('2')()('3')()()
// let resz = fn(open,'https://')('www.4399.com')(':443')('/flash/')
// resx('https://')('www.4399.com')(':443')('/flash/')
// let resq = fn(open, 'https://', 'www.4399.com', ':443', '/flash/')()

  以上是通过柯理化封装一个网址拼接并跳转的open函数,fn函数传入需要分段传参的功能函数open和分段地址参数,当参数个数小于功能函数形参个数时,返回一个携带当前收集到的参数的函数。

link和@import的区别

<link>@import 都用于在网页中引入外部资源,但它们之间有一些重要的区别。

  1. <link> 元素
    • <link> 是HTML标签,用于引入外部资源,通常用于引入CSS文件,但也可以用于其他资源如网站图标。
    • <link> 可以在HTML文档的<head>部分中使用,或者在文档的<body>中引入资源。
    • <link> 具有多个属性,用于指定资源的类型、关系等。最常见的属性是rel(关系)和href(资源路径)。
    • <link> 支持异步加载资源,不会阻塞页面的渲染。
    • 适用于引入CSS文件、网站图标、字体等。

示例:

<link rel="stylesheet" type="text/css" href="styles.css">
<link rel="icon" href="favicon.ico" type="image/x-icon">
  1. @import CSS规则

    • @import 是CSS规则,用于在CSS文件内部引入其他CSS文件,通常用于模块化CSS的组织。
    • @import 只能用于CSS文件内部,不能在HTML中使用。
    • @import 通常用于将多个CSS文件组织成一个主CSS文件,以便模块化管理和加载。
    • @import 不支持异步加载,它会阻塞页面的渲染,因此应该谨慎使用。

示例:

@import url("styles.css");
@import url("variables.css");

  总之,<link> 用于在HTML中引入外部资源,而 @import 用于在CSS文件内部引入其他CSS文件。通常情况下,<link> 更适用于引入CSS文件,因为它可以并行加载多个资源,不会阻塞页面渲染,而 @import 更适用于CSS模块化的组织。

判断数组的方法以及优缺点

  1. Array.isArray() 方法

    • 优点
      • 简单,内置在JavaScript中。
      • 可以准确判断是否为数组
    • 缺点
      • 只能判断是否为数组,不能判断其他可迭代对象。

    示例:

    Array.isArray([]); // true
    Array.isArray({}); // false
    
  2. instanceof 操作符

    • 优点
      • 可以判断是否为特定类的实例,包括数组。
    • 缺点
      • 有时在多个窗口或框架之间会出现问题。
      • 不适用于跨窗口或跨框架的对象。

    示例:

    [] instanceof Array; // true
    {} instanceof Array; // false
    
  3. Object.prototype.toString.call() 方法

    • 优点
      • 非常通用,可以用于判断各种类型
    • 缺点
      • 写法稍微复杂。
      • 不适用于跨窗口或跨框架的对象。

    示例:

    Object.prototype.toString.call([]); // "[object Array]"
    Object.prototype.toString.call({}); // "[object Object]"
    

JavaScript垃圾回收

  JavaScript的垃圾回收是自动管理内存的过程,它有助于释放不再使用的内存,防止内存泄漏。JavaScript的垃圾回收通过以下方式工作:

  1. 标记清除法(Mark and Sweep):这是JavaScript最常用的垃圾回收算法之一。它的工作原理如下:

    • 标记(Mark):垃圾回收器会定期扫描内存中的所有对象,标记那些仍然可访问的对象。通常,从全局对象(如window)开始,然后递归遍历所有引用的对象,标记为可访问的。

    • 清除(Sweep):在标记阶段之后,垃圾回收器会扫描内存中的对象,清除未被标记的对象。这些未被标记的对象被认为是不可达的,因此可以安全地释放它们占用的内存。

  2. 引用计数法:引用计数是一种简单的垃圾回收机制,但不是JavaScript的主要机制。它通过跟踪每个对象的引用次数来工作。当引用次数为零时,对象被认为是垃圾,可以被回收。然而,引用计数无法解决循环引用的问题,因此不常用。

JavaScript垃圾回收的优点和缺点:

优点

  • 自动管理内存:开发人员不需要显式释放内存,因为垃圾回收会自动处理。
  • 避免内存泄漏:垃圾回收可以检测和回收不再使用的对象,减少内存泄漏的风险。

缺点

  • 不精确:垃圾回收可能会在不恰当的时候执行,导致一些性能问题。
  • 暂停应用程序:某些垃圾回收算法在执行时可能导致短时间的应用程序暂停,这在实时应用程序中可能会成为问题。
  • 难以处理循环引用:标记和清除算法通常无法处理循环引用的情况,需要额外的技巧来解决。

另外,开发者可以通过手动释放对象的引用,如将变量赋值为null,来加速垃圾回收的过程。

0.1 + 0.2 = 0.3 吗?为什么?

  在JavaScript中,0.1 + 0.2 的确不等于 0.3。实际上,它们的和是一个接近 0.3 的浮点数,但不精确等于 0.3。

  这是因为 JavaScript 使用 IEEE 754 浮点数标准来表示数字,这种表示方法在某些情况下会导致小数运算的结果不精确。0.1 和 0.2 在二进制浮点表示中是无限循环小数,因此它们的精确表示是不可能的。当这两个无限循环小数相加时,会产生一个近似值,而不是精确的 0.3。

可以使用 toFixed() 方法或其他方法来控制小数的精度,以获得更精确的结果,例如:

var result = (0.1 + 0.2).toFixed(1); // 这将返回字符串 "0.3"

事件委派是什么?

  事件委派(Event delegation)是一种常见的前端开发技术,用于管理事件处理,特别是在处理大量类似子元素事件时非常有用。

  事件委派的核心思想是将事件处理程序附加到它们的父元素上,而不是直接附加到子元素上。当事件冒泡到父元素时,可以根据事件的目标(event.target)确定子元素是哪个,然后执行相应的操作。

  通过事件委派,可以避免给每个子元素单独添加事件处理程序。这对于大量相似子元素存在的情况特别有用,例如列表、表格或动态生成的内容。通过将事件处理程序添加到它们的共同父元素上,可以减少内存占用和提高性能

基本步骤包括:

  1. 找到共同的父元素:确定将事件处理程序附加的父元素。这通常是包含所有子元素的容器元素。

  2. 利用事件冒泡:将事件处理程序附加到父元素,并利用事件冒泡,通过event.target确定触发事件的特定子元素。

  3. 根据目标元素进行操作:在事件冒泡到父元素时,检查事件的目标(event.target),根据需要执行相应的操作。

事件委派能够简化事件处理程序的管理减少内存占用,并提高性能,特别是在处理大量子元素的情况下。

git常用命令

  Git 是一个强大的分布式版本控制系统,用于跟踪和管理代码变更。以下是一些常用的 Git 命令:

  1. git init:初始化一个新的 Git 仓库。

  2. git clone <repository-url>:克隆远程仓库到本地。

  3. git add <file>:将文件添加到暂存区,准备提交。

  4. git commit -m "Commit message":提交暂存区的提交到历史区,附带提交消息描述更改内容。

  5. git status:查看工作区、暂存区和版本库(历史区)的状态。

  6. git log:查看提交历史记录。

  7. git pull:从远程仓库拉取最新更改并合并到当前分支。

  8. git push:将本地更改推送到远程仓库。

  9. git branch:列出本地分支。

  10. git checkout 切换到指定分支。

  11. git checkout -b <new-branch-name>创建并切换到新的分支。

  12. git merge :将指定分支的更改合并到当前分支。

  13. git rebase :将当前分支的更改在指定分支上进行变基操作。

  14. git remote -v:显示配置的远程仓库的 URL。

  15. git fetch:从远程仓库获取最新的更改,但不进行合并。

  16. git reset :从暂存区中移除文件,但保留在工作区。

  17. git reset --hard HEAD:重置工作区和暂存区到最近的提交状态。

  18. git diff:查看工作区和暂存区之间的差异。

  19. git tag:列出标签(用于版本标记)。

  20. git stash:将当前的工作进度暂存,以便在切换分支时不丢失更改。

浏览器本地存储

  浏览器本地存储是一种用于在客户端浏览器上存储数据的机制,以便将数据持久化保存在用户的计算机上。这有助于在不同的网页访问之间保持数据,并且不需要与服务器来回通信。有三种主要的本地存储技术可供选择:

  1. Cookies(Cookie):

    • Cookies最早的本地存储方法之一,它们是小的文本文件,由浏览器存储在用户计算机上。
    • Cookies 通常用于存储少量的文本数据,如会话标识符、用户首选项等。
    • Cookies 有一些限制,如大小限制、安全性问题和只能存储文本数据。
  2. Web Storage

    • Web Storage 包括两种技术:localStoragesessionStorage
    • localStorage:允许存储大量数据,数据存储在用户的浏览器中,且不会过期,直到用户清除浏览器缓存。
    • sessionStorage:仅在会话期间存储数据,当用户关闭浏览器标签或窗口时会被清除。
  3. IndexedDB

    • IndexedDB 是一个功能强大的本地数据库,允许在浏览器中存储结构化数据,如对象和索引。
    • IndexedDB 提供了更高级的查询和事务支持,适用于处理大量和复杂的数据。

以下是一个示例,演示如何使用localStorage 存储和检索数据:

// 存储数据
localStorage.setItem('username', 'JohnDoe');

// 检索数据
const username = localStorage.getItem('username');
console.log('Username:', username);

cookie 、sessionStorage、 localStorage 三者的区别 面试重点

  • 存储时效不同

    • cookie 如果不设置 session 会话级 关闭页面或者浏览器就没有

    • sessionStorage 有效期是仅保持在当前页面 关闭当前会话页面或者浏览器后消失

    • localStorage 在不手动删除的情况下 一直存在

  • 存储的大小不同

    • cookie 一般存储 4kb左右 一般存50条
    • localStorage、sessionStorage 一般在5M
  • 与服务端通信

    • cookie可以和服务端进行通信 一般请求头 存储关键信息
    • localStorage、sessionStorage 只能前端存储
  • 读写的便捷度

    • localStorage、sessionStorage 操作更简单
  • 支持的浏览器

    • cookie 出现比较早 支持所有的浏览器
    • localStorage、sessionStorage 不支持低版本的ie IE8版本之下的都不支持

GET和POSE的区别

  • GET

    • 获取数据:GET 方法用于从服务器获取数据。它是一种幂等请求,意味着多次发送相同的 GET 请求应该不会对服务器和资源状态产生影响。
    • 数据传递:数据通常附在 URL 的查询字符串中,可以通过 URL 参数传递。由于数据附在 URL 上,因此在浏览器历史、书签和服务器日志中都可见。
    • 限制:由于数据在 URL 中,有长度限制(通常几千个字符),因此适合用于发送少量数据。
  • POST

    • 提交数据:POST 方法用于向服务器提交数据,通常用于在服务器上创建、更新或删除资源。它不是幂等的,同样的 POST 请求可能会对服务器状态产生不同的影响。
    • 数据传递:数据传递通常通过请求正文(Request Body)传递,而不是暴露在 URL 上。因此,POST 请求通常用于传递大量数据,如表单提交、文件上传等。
    • 不可缓存:POST 请求通常不会被浏览器缓存,以确保数据的安全性和完整性。

总结

  • GET 用于获取数据,数据附在 URL 上,不安全,适合传递少量数据,是幂等的,可被缓存
  • POST 用于提交数据,数据传递通过请求正文,相对安全,适合传递大量数据,不是幂等的,不可被缓存。

实现继承的方法有哪些?

在JavaScript中,实现继承的方法有以下几种:

  • 原型链继承:通过将子类的原型对象指向父类的实例对象来实现继承。
function Parent() {
  // 父类构造函数
}

function Child() {
  // 子类构造函数
}

Child.prototype = new Parent(); // 子类的原型对象指向父类的实例对象

var child = new Child(); // 创建子类的实例
  • 借用构造函数继承:通过在子类构造函数中调用父类构造函数来实现继承,即在子类构造函数体内使用call方法将父类构造函数的this’指向子类实例化对象(该方法不能继承分类原型链上的属性或方法)。
function Parent() {
  // 父类构造函数
}

function Child() {
  Parent.call(this); // 在子类构造函数中调用父类构造函数
}

var child = new Child(); // 创建子类的实例
  • 组合继承:结合原型链继承和构造函数继承的方式。
function Parent() {
  // 父类构造函数
}

function Child() {
  Parent.call(this); // 在子类构造函数中调用父类构造函数
}

Child.prototype = new Parent(); // 子类的原型对象指向父类的实例对象
Child.prototype.constructor = Child; // 修复子类原型对象的constructor属性

var child = new Child(); // 创建子类的实例
  • ES6 class继承:在ES6中,可以使用class关键字和extends关键字来实现继承。
class Parent {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, ${this.name}`);
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 调用父类的构造函数
    this.age = age;
  }

  sayAge() {
    console.log(`I am ${this.age} years old`);
  }
}

const child = new Child("Alice", 10);
child.sayHello(); // 输出:Hello, Alice
child.sayAge(); // 输出:I am 10 years old

  在上面的示例中,Parent类是父类,Child类通过extends关键字继承了Parent类。子类的构造函数中使用super关键字来调用父类的构造函数,并传递相应的参数。子类可以访问父类的属性和方法,也可以定义自己的属性和方法。

  需要注意的是,ES6中的继承是基于类的继承,实际上使用的是原型链继承的方式。子类的原型对象是父类的实例对象,子类通过原型链继承了父类的属性和方法。

  • 原型式继承:通过创建一个临时的构造函数来实现继承。
function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

var parent = {
  // 父对象
};

var child = createObj(parent); // 创建子对象
  • 寄生式继承:在原型式继承的基础上,增强对象。
function createObj(o) {
  var clone = Object.create(o); // 使用Object.create()方法创建一个新对象,并将其原型对象指向o
  clone.sayHello = function() {
    console.log('Hello');
  };
  return clone;
}

var parent = {
  // 父对象
};

var child = createObj(parent); // 创建子对象
  • 寄生组合式继承:在组合继承的基础上,通过Object.create()方法来实现父类原型对象的复制。
function Parent() {
  // 父类构造函数
}

function Child() {
  Parent.call(this); // 在子类构造函数中调用父类构造函数
}

Child.prototype = Object.create(Parent.prototype); // 使用Object.create()方法复制父类原型对象
Child.prototype.constructor = Child; // 修复子类原型对象的constructor属性

var child = new Child(); // 创建子类的实例

谈一下你对this的理解

  在JavaScript中,this关键字表示当前执行代码的上下文对象。它的值是在函数被调用时确定的,根据不同的情况,this可能指向不同的对象。

以下是一些常见情况下this的指向:

  1. 全局上下文:在全局作用域中,this指向全局对象(在浏览器中为window对象,在Node.js中为global对象)。
console.log(this); // 输出:Window(浏览器环境下)
  1. 函数调用:在函数中,this的值取决于函数是如何被调用的。

    • 函数作为函数调用:当函数作为普通函数调用时,this指向全局对象。
    function foo() {
      console.log(this);
    }
    
    foo(); // 输出:Window(浏览器环境下)
    
    • 函数作为方法调用:当函数作为对象的方法调用时,this指向调用该方法的对象
    var obj = {
      name: 'Alice',
      sayHello: function() {
        console.log(this.name);
      }
    };
    
    obj.sayHello(); // 输出:Alice
    
    • 函数作为构造函数调用:当函数用new关键字作为构造函数调用时,this指向新创建的对象。
    function Person(name) {
      this.name = name;
    }
    
    var person = new Person('Alice');
    console.log(person.name); // 输出:Alice
    
    • 函数使用callapplybind方法调用:当使用callapplybind方法来调用函数时,可以显式地指定this的值。
    function foo() {
      console.log(this);
    }
    
    var obj = {
      name: 'Alice'
    };
    
    foo.call(obj); // 输出:{ name: 'Alice' }
    
  2. 箭头函数:箭头函数没有自己的this绑定,它会捕获上层作用域的this值。

var obj = {
  name: 'Alice',
  sayHello: function() {
    setTimeout(() => {
      console.log(this.name);
    }, 1000);
  }
};

obj.sayHello(); // 输出:Alice

  需要注意的是,this的值是在运行时确定的,而不是在定义时确定的。在使用this时,需要注意上下文环境以及函数是如何被调用的,以确定this的指向。

移动端适配方案有哪些?

移动端适配方案有以下几种:

  1. 媒体查询(Media Queries):使用CSS3的媒体查询功能,根据不同的屏幕尺寸和设备特性,为不同的屏幕宽度设置不同的样式。
@media screen and (max-width: 768px) {
  /* 在宽度小于等于768px时应用的样式 */
}

@media screen and (min-width: 769px) and (max-width: 1024px) {
  /* 在宽度在769px到1024px之间时应用的样式 */
}
  1. 弹性布局(Flexbox):使用CSS3的弹性布局,通过设置容器的display: flex,可以自动调整子元素的宽度和高度,实现自适应布局。
.container {
  display: flex;
  flex-direction: row; /* 水平排列子元素 */
  justify-content: space-between; /* 子元素之间平均分布 */
}
  1. REM + Viewport缩放:使用rem单位和Viewport缩放结合的方式,使用JavaScript根据屏幕宽度动态计算根元素的字体大小,从而实现自适应。
html {
  font-size: 16px; /* 设置根元素的字体大小 */
}

.container {
  width: 10rem; /* 宽度等于根元素字体大小的10倍 */
  height: 5rem; /* 高度等于根元素字体大小的5倍 */
}
  1. CSS框架:一些流行的CSS框架(如Bootstrap、Foundation等)提供了移动端适配的解决方案,通过使用栅格系统和响应式布局,使网页在不同的屏幕上呈现出良好的效果。

  2. JavaScript框架:一些JavaScript框架(如ReactVue等)提供了移动端适配的解决方案,通过使用组件和响应式设计,使应用在不同的设备上具有良好的用户体验。


JavaScript最后一期☀️☀️,下期更新Vue❕ ❕ ❕
个人收集整理、创作不易, 若有帮助🉑, 请帮忙点赞👍➕收藏❤️, 谢谢!✨✨🚀🚀

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLechazo~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值