let与var的区别
- let命令不存在变量提升,如果在let前使用,会导致报错(var存在变量提升)
- 如果块区中存在let和const命令,就会形成封闭作用域
- 不允许重复声明,因此,不能在函数内部重新声明参数
map与forEach的区别
- forEach方法,是最基本的方法,就是遍历与循环,默认有3个传参:分别是遍历的数组内容 item 、数组索引 index 、和当前遍历数组Array
- map方法,基本用法与forEach 一致,但是不同的,它会返回一个新的数组 ,所以在callback需要有return值,如果没有,会返回undefined
谈一谈你理解的函数式编程
- 简单说,"函数式编程"是一种"编程范式"( programming paradigm),也就是如何编写程序的方法论
- 它具有以下特性:闭包和高阶函数、惰性计算、递归、函数是"第一等公民"、只用"表达式"
函数式编程(Functional Programming, FP)是一种编程范式,它强调使用函数作为基本构建块来编写程序,并尽量避免使用可变状态(mutable state)和副作用(side effects)。函数式编程的核心思想是将计算视为对数据的数学函数映射,这种思想使得代码更加模块化、易于理解和测试,同时也促进了并行和并发计算的发展。
以下是对函数式编程几个关键特性的理解:
-
函数作为一等公民(First-class citizens):在函数式编程中,函数被视为与数据(如整数、字符串等)同等的实体。这意味着函数可以作为参数传递给其他函数,也可以作为返回值从函数中返回,甚至可以存储在变量或数据结构中。
-
纯函数(Pure functions):纯函数是函数式编程中的一个核心概念。一个函数被认为是纯的,如果它满足以下两个条件:一是给定相同的输入,总是返回相同的输出(即没有副作用);二是它不修改其外部状态(即不依赖于或修改程序中的可变状态)。纯函数使得代码更加可预测和可靠。
-
避免可变状态(Immutable state):函数式编程鼓励使用不可变数据结构,即一旦创建就不能被修改的数据结构。这样做可以减少程序中的错误来源,因为不可变数据结构保证了数据在程序执行过程中的一致性,同时也有助于并行和并发计算,因为多个线程可以同时安全地读取相同的数据。
-
高阶函数(Higher-order functions):高阶函数是至少满足下列一个条件的函数:接受一个或多个函数作为输入,或者输出一个函数。高阶函数允许函数式编程实现更高级别的抽象,如柯里化(Currying)、部分应用(Partial Application)和函数组合(Function Composition)等。
-
惰性求值(Lazy evaluation):在函数式编程中,惰性求值是一种求值策略,它推迟表达式的计算直到该表达式的值真正需要时为止。这有助于优化程序性能,特别是在处理大量数据或执行复杂计算时。
-
模式匹配(Pattern matching):函数式编程语言通常支持模式匹配,这是一种通过检查数据结构并与一系列预定义的模式进行匹配来执行不同操作的机制。模式匹配使得处理复杂数据结构(如列表、树等)变得更加简单和直观。
总之,函数式编程通过强调函数、纯函数、不可变状态和高阶函数等概念,提供了一种强大的编程范式,有助于提高代码的可读性、可维护性和可测试性。同时,函数式编程也促进了并行和并发计算的发展,使得处理大规模数据和复杂计算变得更加高效和可靠。
普通函数与箭头函数
1. 定义语法
-
普通函数 :使用
function
关键字定义,后面跟着函数名和一对圆括号(包含参数列表),然后是花括号内的函数体。 -
箭头函数 :使用
=>
符号定义,更简洁。如果函数体只有一条语句(且为返回语句),可以省略花括号和return
关键字。箭头函数没有自己的function
关键字和函数名(除非它被赋值给一个变量或常量,从而可以间接引用)。
2. this
的绑定
-
普通函数 :
this
的值在函数调用时确定,它依赖于函数是如何被调用的。例如,作为对象的方法调用时,this
指向该对象;在全局作用域中调用时,this
指向全局对象(在浏览器中是window
)。 -
箭头函数 :不绑定自己的
this
,而是继承自它所在的上下文(即父执行上下文)的this
值。这意味着,在箭头函数内部,this
的值不会随着调用方式的改变而改变,它始终指向定义时所在的作用域中的this
。
3. arguments
对象
-
普通函数 :每个普通函数都会接收到一个名为
arguments
的类数组对象,它包含了传递给函数的所有参数。 -
箭头函数 :不接收
arguments
对象。如果需要访问传递给函数的参数列表,可以使用剩余参数(...args
)语法。
4. new
操作符
-
普通函数 :可以使用
new
操作符来调用,这时函数会作为构造函数执行,并创建一个新的对象实例。 -
箭头函数 :不能被用作构造函数,使用
new
操作符调用箭头函数会抛出一个TypeError
。
5.prototype
属性
-
普通函数 :具有
prototype
属性,它是一个对象,可以被该函数的实例对象所继承。 -
箭头函数 :没有
prototype
属性,因此不能用作构造函数,也无法通过prototype
添加共享属性或方法。
6. 返回值
-
普通函数 :使用
return
语句显式地返回值。 -
箭头函数 :如果函数体只有一条语句(且该语句为返回语句),可以省略花括号和
return
关键字,函数会自动返回该语句的结果。
7. 性能和用途
-
性能:在大多数情况下,普通函数和箭头函数的性能差异可以忽略不计。然而,在需要优化性能的极端情况下,应仔细考虑它们的使用。
-
用途 :箭头函数因其简洁性和对
this
的特殊处理,常用于定义回调函数、事件处理器等场景。普通函数则更适用于需要作为构造函数、需要访问arguments
对象或需要prototype
属性的场景。
this指向
this 的基本指向规则
-
全局上下文 :在全局执行上下文中(在任何函数体外部),
this
指向全局对象。在浏览器中是window
对象,在 Node.js 中是global
对象。 -
函数上下文 :在普通函数中,
this
的值取决于函数是如何被调用的。- 如果函数作为某个对象的方法被调用,则
this
指向该对象。 - 如果函数作为普通函数被调用(即非方法调用),
this
指向全局对象(在严格模式下,this
是undefined
)。 - 使用
call()
、apply()
或bind()
方法可以改变this
的指向。
- 如果函数作为某个对象的方法被调用,则
-
箭头函数 :箭头函数不绑定自己的
this
,它会捕获其所在上下文(即父执行上下文)的this
值作为自己的this
值。
如何改变 this 指向
-
使用 call() 方法
call()
方法调用一个函数,其具有一个指定的this
值和分别提供的参数(参数的列表)。function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}const person = {
name: 'John'
};greet.call(person, 'Hello', '!'); // 输出: Hello, John!
2. 使用 apply() 方法
apply()
方法调用一个函数,其具有一个指定的 this
值,以及以一个数组(或类数组对象)的形式提供的参数。
greet.apply(person, ['Hi', '.']); // 输出: Hi, John.
使用 bind() 方法
bind()
方法创建一个新的函数,在 bind()
被调用时,这个新函数的 this
被指定为 bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
const boundGreet = greet.bind(person, 'Howdy');
boundGreet('!'); // 输出: Howdy, John!
注意,bind()
返回的是一个新的函数,而 call()
和 apply()
是直接调用函数。
-
在 ES6 箭头函数中
由于箭头函数不绑定自己的
this
,因此你不能通过call()
、apply()
或bind()
来改变箭头函数中的this
指向。箭头函数中的this
总是从外围(函数或者全局)作用域继承。
Javascript中bind、call、apply區別-CSDN博客
异步编程实现
- 回调函数
回调函数是最早也是最基本的异步编程方式。它将需要等待的函数作为参数传递给另一个函数,当等待的操作完成时,再调用这个回调函数。但这种方式可能会导致"回调地狱"(Callback Hell),即多层嵌套的回调函数使得代码难以阅读和维护。
function doSomething(callback) {
setTimeout(() => {
console.log('Doing something...');
callback();
}, 1000);
}
doSomething(() => {
console.log('Done!');
});
- 优点:简单、容易理解
- 缺点:不利于维护,代码耦合高
- 事件监听(采用时间驱动模式,取决于某个事件是否发生):
- 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
- 缺点:事件驱区动型,流程不够清晰
- 发布/订阅(观察者模式)
- 类似于事件监听,但是可以通过'消息中心',了解现在有多少发布者,多少订阅者
- Promise对象
Promises是ES6中引入的,用于处理异步操作的对象。它代表了一个最终可能完成或失败的操作及其结果值。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
// 或者在某些情况下使用 reject('Error!');
}, 1000);
});
}
doSomething().then(result => {
console.log(result);
}).catch(error => {
console.error(error);
});
- 优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数;
- 缺点:编写和理解,相对比较难
- Generator函数
Generators是ES6中引入的,提供了一种执行暂停和恢复代码的机制。虽然它们本身不直接处理异步操作,但可以通过与Promises结合使用(例如通过co库),来实现异步编程。然而,由于async/await
的引入,使得Generators在异步编程中的使用逐渐减少。
- 优点:函数体内外的数据交换、错误处理机制
- 缺点:流程管理不方便
- async函数
async/await
是ES2017(ES8)中引入的,建立在Promises之上,使得异步代码看起来和同步代码一样。async
函数会隐式返回一个Promise,而await
关键字则用于等待Promise的解决(resolve)或拒绝(reject),并可以从中检索解决的值。
async function doSomething() {
try {
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve('Done!');
}, 1000);
});
console.log(result);
} catch (error) {
console.error(error);
}
}
doSomething();
- 优点:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰。
- 缺点:错误处理机制
对原生Javascript了解程度
数据类型、运算、对象、Function、继承、闭包、作用域、原型链、事件、RegExp 、JSON 、Ajax . DOM 、 BOM、内存泄漏、跨域、异步装载、模板引擎、前端MC .路由、模块化、 Canvas ECMAScript
Js动画与CSS动画区别及相应实现
- CSS3的动画的
- 优点:在性能上会稍微好一些,浏览器会对CSS3的动画做一些优化、代码相对简单
- 缺点在动画控制上不够灵活。兼容性不好
JavaScript 的动画正好弥补了这两个缺点,控制能力很强,可以单帧的控制、变换,同时写得好完全可以兼容IE6,并且功能强大。对于一些复杂控制的动画,使用javascript会比较靠谱。而在实现一些小的交互动效的时候,就多考虑考虑CSS 吧
JS数组和对象的遍历方式,以及几种方式的比较
通常我们会用循环的方式来遍历数组。但是循环是导致js 性能问题的原因之一。一般我们会采用下几种方式来进行数组的遍历
- for in 循环
- for-in需要分析出array 的每个属性,这个操作性能开销很大。用在key已知的数组上是非常不划算的。所以尽量不要用for-in ,除非你不清楚要处理哪些属性,例如JSON对象这样的情况
- for 循环
- 循环每进行一次,就要检查一下数组长度。读取属性(数组长度)要比读局部变量慢,尤其是当array 里存放的都是DOM元素,因为每次读取都会扫描一遍页面上的选择器相关元素,速度会大大降低
- forEach
- forEach回调中两个参数分别为value ,index
- forEach无法遍历对象
- IE不支持该方法; Firefox和chrome支持
- forEach无法使用break , continue 跳出循环,且使用return是跳过本次循环