函数柯理化
函数柯里化
(Currying)是一种函数式编程的技术,它将一个接受多个参数的函数转化为一系列接受单个参数的函数。柯里化的主要目的是将函数变得更灵活,使其更容易复用和组合。
柯里化的过程如下:
- 原始函数接受多个参数。
- 第一个参数传递给函数,并返回一个接受剩余参数的新函数。
- 重复步骤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
转化为一个接受单个参数的函数,并返回一个新的函数,这个新函数也接受单个参数。通过柯里化,我们可以先传递部分参数,然后在需要时传递剩余的参数,使函数更加灵活。
柯里化的优点包括:
- 参数复用:可以轻松创建多个接受相同参数的函数,而不需要重复传递相同的参数。
- 组合函数:柯里化可以更容易地将多个函数组合在一起,构建复杂的函数管道。
- 延迟执行:可以先传递一部分参数,延迟执行函数,直到所有参数都准备好。
- 更容易测试:柯里化可以更容易进行单元测试,因为可以分别测试每个部分的函数。
收集参数:
// 函数柯理化
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
都用于在网页中引入外部资源,但它们之间有一些重要的区别。
<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">
-
@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模块化的组织。
判断数组的方法以及优缺点
-
Array.isArray() 方法:
- 优点:
- 简单,内置在JavaScript中。
- 可以准确判断是否为
数组
。
- 缺点:
- 只能判断是否为数组,不能判断其他可迭代对象。
示例:
Array.isArray([]); // true Array.isArray({}); // false
- 优点:
-
instanceof 操作符:
- 优点:
- 可以判断是否为
特定类的实例
,包括数组。
- 可以判断是否为
- 缺点:
- 有时在多个窗口或框架之间会出现问题。
- 不适用于跨窗口或跨框架的对象。
示例:
[] instanceof Array; // true {} instanceof Array; // false
- 优点:
-
Object.prototype.toString.call() 方法:
- 优点:
- 非常通用,可以用于判断
各种类型
。
- 非常通用,可以用于判断
- 缺点:
- 写法稍微复杂。
- 不适用于跨窗口或跨框架的对象。
示例:
Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call({}); // "[object Object]"
- 优点:
JavaScript垃圾回收
JavaScript的垃圾回收是自动管理内存
的过程,它有助于释放不再使用的内存,防止内存泄漏
。JavaScript的垃圾回收通过以下方式工作:
-
标记清除法(Mark and Sweep):这是JavaScript最常用的垃圾回收算法之一。它的工作原理如下:
-
标记
(Mark):垃圾回收器会定期扫描内存中的所有对象,标记那些仍然可访问的对象。通常,从全局对象(如window
)开始,然后递归遍历所有引用的对象,标记为可访问的。 -
清除
(Sweep):在标记阶段之后,垃圾回收器会扫描内存中的对象,清除未被标记的对象。这些未被标记的对象被认为是不可达的,因此可以安全地释放它们占用的内存。
-
-
引用计数法:引用计数是一种简单的垃圾回收机制,但不是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
)确定子元素是哪个,然后执行相应的操作。
通过事件委派,可以避免给每个子元素单独添加事件处理程序。这对于大量相似子元素存在的情况特别有用,例如列表、表格或动态生成的内容。通过将事件处理程序添加到它们的共同父元素上,可以减少内存占用和提高性能。
基本步骤包括:
-
找到共同的父元素:确定将事件处理程序附加的父元素。这通常是包含所有子元素的容器元素。
-
利用事件冒泡:将事件处理程序附加到父元素,并利用事件冒泡,通过
event.target
确定触发事件的特定子元素。 -
根据目标元素进行操作:在事件冒泡到父元素时,检查事件的目标(
event.target
),根据需要执行相应的操作。
事件委派能够简化事件处理程序的管理
,减少内存占用
,并提高性能
,特别是在处理大量子元素的情况下。
git常用命令
Git 是一个强大的分布式版本控制系统,用于跟踪和管理代码变更。以下是一些常用的 Git 命令:
-
git init
:初始化一个新的 Git 仓库。 -
git clone <repository-url>
:克隆远程仓库到本地。 -
git add <file>
:将文件添加到暂存区
,准备提交。 -
git commit -m "Commit message"
:提交暂存区的提交到历史区
,附带提交消息描述
更改内容。 -
git status:查看工作区、暂存区和版本库(历史区)的状态。
-
git log:查看提交历史记录。
-
git pull
:从远程仓库拉取
最新更改并合并到当前分支。 -
git push
:将本地更改推送
到远程仓库。 -
git branch:列出本地分支。
-
git checkout :
切换
到指定分支。 -
git checkout -b <new-branch-name>
:创建并切换
到新的分支。 -
git merge :将指定分支的更改
合并
到当前分支。 -
git rebase :将当前分支的更改在指定分支上进行变基操作。
-
git remote -v:显示配置的远程仓库的 URL。
-
git fetch:从远程仓库获取最新的更改,但不进行合并。
-
git reset :从暂存区中移除文件,但保留在工作区。
-
git reset --hard HEAD:重置工作区和暂存区到最近的提交状态。
-
git diff:查看工作区和暂存区之间的差异。
-
git tag:列出标签(用于版本标记)。
-
git stash:将当前的工作进度暂存,以便在切换分支时不丢失更改。
浏览器本地存储
浏览器本地存储是一种用于在客户端浏览器上存储数据的机制,以便将数据持久化保存在用户的计算机上。这有助于在不同的网页访问之间保持数据,并且不需要与服务器来回通信。有三种主要的本地存储技术可供选择:
-
Cookies(Cookie):
Cookies
是最早
的本地存储方法之一,它们是小的文本文件,由浏览器存储在用户计算机上。- Cookies 通常用于存储
少量
的文本数据,如会话标识符、用户首选项等。 - Cookies 有一些限制,如大小限制、安全性问题和只能存储文本数据。
-
Web Storage:
- Web Storage 包括两种技术:
localStorage
和sessionStorage
。 - localStorage:允许存储大量数据,数据存储在用户的浏览器中,且不会过期,直到用户清除浏览器缓存。
- sessionStorage:仅在
会话
期间存储数据,当用户关闭浏览器标签或窗口时会被清除。
- Web Storage 包括两种技术:
-
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
的指向:
- 全局上下文:在全局作用域中,
this
指向全局对象(在浏览器中为window
对象,在Node.js中为global
对象)。
console.log(this); // 输出:Window(浏览器环境下)
-
函数调用:在函数中,
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
- 函数使用
call
、apply
或bind
方法调用:当使用call
、apply
或bind
方法来调用函数时,可以显式地指定this
的值。
function foo() { console.log(this); } var obj = { name: 'Alice' }; foo.call(obj); // 输出:{ name: 'Alice' }
-
箭头函数:箭头函数没有自己的
this
绑定,它会捕获上层作用域的this
值。
var obj = {
name: 'Alice',
sayHello: function() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
obj.sayHello(); // 输出:Alice
需要注意的是,this
的值是在运行时确定的,而不是在定义时确定的。在使用this
时,需要注意上下文环境以及函数是如何被调用的,以确定this
的指向。
移动端适配方案有哪些?
移动端适配方案有以下几种:
- 媒体查询(Media Queries):使用CSS3的媒体查询功能,根据不同的屏幕尺寸和设备特性,为不同的屏幕宽度设置不同的样式。
@media screen and (max-width: 768px) {
/* 在宽度小于等于768px时应用的样式 */
}
@media screen and (min-width: 769px) and (max-width: 1024px) {
/* 在宽度在769px到1024px之间时应用的样式 */
}
- 弹性布局(Flexbox):使用CSS3的弹性布局,通过设置容器的
display: flex
,可以自动调整子元素的宽度和高度,实现自适应布局。
.container {
display: flex;
flex-direction: row; /* 水平排列子元素 */
justify-content: space-between; /* 子元素之间平均分布 */
}
- REM + Viewport缩放:使用rem单位和Viewport缩放结合的方式,使用JavaScript根据屏幕宽度动态计算根元素的字体大小,从而实现自适应。
html {
font-size: 16px; /* 设置根元素的字体大小 */
}
.container {
width: 10rem; /* 宽度等于根元素字体大小的10倍 */
height: 5rem; /* 高度等于根元素字体大小的5倍 */
}
-
CSS框架:一些流行的CSS框架(如
Bootstrap
、Foundation等)提供了移动端适配的解决方案,通过使用栅格系统和响应式布局,使网页在不同的屏幕上呈现出良好的效果。 -
JavaScript框架:一些JavaScript框架(如
React
、Vue
等)提供了移动端适配的解决方案,通过使用组件和响应式设计,使应用在不同的设备上具有良好的用户体验。
JavaScript最后一期☀️☀️,下期更新Vue❕ ❕ ❕
个人收集整理、创作不易, 若有帮助🉑, 请帮忙点赞👍➕收藏❤️, 谢谢!✨✨🚀🚀