>  기사  >  웹 프론트엔드  >  JavaScript 10,000 단어 인터뷰 요약

JavaScript 10,000 단어 인터뷰 요약

WBOY
WBOY앞으로
2022-10-10 14:43:001355검색

이 글은 JavaScript에 대한 지식을 소개합니다. 주로 몇 가지 면접 질문을 요약하여 소개합니다. 모두에게 도움이 되기를 바랍니다.

JavaScript 10,000 단어 인터뷰 요약

[관련 권장사항: JavaScript 비디오 튜토리얼, 웹 프론트엔드]

1. JS 기본

1.1 Js에는 어떤 데이터 유형이 있나요

8가지 데이터 유형이 있습니다. JavaScript

기본 데이터 유형: 정의되지 않음, Null, 부울, 숫자, 문자열, 기호, BigInt.

복잡한 데이터 유형: Object

Symbol 및 BigInt는 ES6의 새로운 데이터 유형입니다.

  • Symbol은 생성 후 발생할 수 있는 전역 변수 충돌 문제를 해결하기 위해 주로 사용됩니다.

  • BigInt는 모든 정밀도 형식의 정수를 표현할 수 있는 숫자형 데이터입니다. BigInt를 사용하면 숫자가 나타낼 수 있는 안전한 정수 범위를 초과하더라도 큰 정수를 안전하게 저장하고 연산할 수 있습니다.

1.2 힙 영역과 스택 영역에 대한 이해에 대해 이야기해 주세요.

운영 체제에서 메모리는 스택 영역과 힙 영역으로 구분됩니다.

  • 스택 영역의 메모리는 컴파일러에 의해 자동으로 할당 및 해제됩니다. 함수 매개변수 값, 지역 변수 값 등을 저장합니다. 데이터 구조의 스택처럼 작동합니다.

  • 힙 영역의 메모리는 일반적으로 개발자가 할당하고 해제합니다. 개발자가 이를 해제하지 않으면 프로그램 종료 시 가비지 수집 메커니즘에 의해 회수될 수 있습니다.

데이터 구조에서:

  • 데이터 구조에서 스택의 데이터에 대한 액세스 방법은 먼저 들어오고 마지막으로 나옵니다.

  • 힙은 우선순위에 따라 정렬된 우선순위 큐입니다. 우선순위는 크기에 따라 지정될 수 있습니다.

데이터 저장 방법

  • 원본 데이터형은 단순 데이터 세그먼트로 스택에 직접 저장되는데, 공간을 적게 차지하고 크기가 고정되어 있어 자주 사용하는 데이터이다.

  • 참조 데이터 유형은 힙에 저장되는 객체로, 많은 공간을 차지하고 크기가 고정되어 있지 않습니다. 스택에 저장되면 프로그램 성능에 영향을 미칩니다. 참조 데이터 유형은 힙에 있는 엔터티의 시작 주소를 가리키는 포인터를 스택에 저장합니다. 인터프리터가 참조 값을 찾을 때 먼저 스택에서 해당 주소를 검색한 다음 힙에서 엔터티를 가져옵니다.

1.3 데이터 유형 감지 방법은 무엇입니까

그런 다음 데이터 유형을 판단하는 방법은 일반적으로 typeof, instanceof, constructor, toString

JavaScript 10,000 단어 인터뷰 요약

1.4의 네 가지 일반적인 방법을 통해 수행할 수 있습니다. 배열 판단 메소드는 무엇인가요?

  • Object.prototype.toString.call()을 통한 판단

  • 프로토타입 체인을 통한 판단

  • ES6의 Array.isArray()를 통한 판단

  • Instanceof를 통해 판단

  • 을 통해 Array.prototype.isPrototypeOf

1.5 null과 undefine의 차이점

우선 Undefine과 Null은 각각 기본 데이터 타입입니다. 정의되지 않고 null인 값이 하나만 있습니다.

undefound는 정의되지 않음을 의미하고 null은 빈 개체를 의미합니다. 일반적으로 변수가 선언되었지만 정의되지 않은 경우 undefound가 반환됩니다. Null은 주로 초기화로 개체를 반환할 수 있는 일부 변수에 값을 할당하는 데 사용됩니다.

undefine은 JavaScript의 예약어가 아닙니다. 즉 undefound를 변수 이름으로 사용할 수 있지만 이는 매우 위험하며 정의되지 않은 값의 판단에 영향을 미칩니다. void 0과 같은 몇 가지 방법을 통해 안전한 정의되지 않은 값을 얻을 수 있습니다.

이 두 가지 유형을 판단하기 위해 typeof를 사용할 때 Null 타이핑은 "객체"를 반환하게 되는데 이는 역사적인 문제입니다. 두 가지 유형의 값을 비교하기 위해 이중 등호를 사용하는 경우 true를 반환하고, 삼중 등호를 사용하는 경우 false를 반환합니다.

1.6 typeof null의 결과는 무엇이며 그 이유는 무엇입니까?

typeof null의 결과는 개체입니다.

JavaScript의 첫 번째 버전에서는 모든 값이 32비트 단위로 저장되었으며 각 단위에는 작은 유형의 태그(1~3비트)와 저장될 현재 값의 실제 데이터가 포함되어 있었습니다. 유형 레이블은 각 셀의 하위 비트에 저장되며 5가지 데이터 유형이 있습니다.

000: object   - 当前存储的数据指向一个对象。
  1: int      - 当前存储的数据是一个 31 位的有符号整数。
010: double   - 当前存储的数据指向一个双精度的浮点数。
100: string   - 当前存储的数据指向一个字符串。
110: boolean  - 当前存储的数据是布尔值。

최하위 비트가 1이면 유형 레이블 플래그의 길이는 1비트입니다. 최하위 비트가 0이면 유형 레이블 플래그의 길이는 3비트이므로 나머지 4개 데이터를 저장하기 위해 추가 2비트가 제공됩니다. 종류.

두 가지 특수 데이터 유형이 있습니다.

  • undefine의 값은 (-2)30(정수 범위를 벗어난 숫자)입니다.

  • null의 값은 기계 코드 NULL 포인터(널 포인터의 값)입니다. 모두 0)

즉 null의 타입 라벨도 000이라는 뜻인데, 이는 Object의 타입 라벨과 동일하므로 Object로 판단하게 됩니다.

1.7 왜 0.1+0.2 ! == 0.3, 같게 만드는 방법(정밀도가 손실됨)

컴퓨터는 데이터를 이진수로 저장하므로 컴퓨터가 0.1+0.2를 계산할 때 실제로 이진합을 계산합니다. 두 개의 숫자.

JS에는 숫자 유형 하나만 있습니다. 구현은 IEEE 754 표준을 따르며 이를 표현하기 위해 표준 배정밀도 부동 소수점 숫자인 64비트 고정 길이를 사용합니다. 이진 과학 표기법에서 배정밀도 부동 소수점 숫자의 소수 부분은 최대 52자리까지만 유지할 수 있습니다. 또한 이전 1에 더해 실제로는 53개의 유효 숫자를 유지하고 나머지 숫자는 버리고 "0"을 따라야 합니다. 1" 원칙. 원칙적으로.

이 원리에 따르면 0.1과 0.2의 이진수를 더하여 십진수로 변환하면 0.30000000000000004가 됩니다. 따라서 같지 않습니다

해결책은 종종 "기계 정밀도"라고 불리는 오차 한계를 설정하는 것입니다. JavaScript의 경우 이 값은 보통 2~52입니다. ES6에서는 Number.EPSILON 속성이 제공되며 해당 값은 2~52입니다. 0.1+0.2-0.3이 Number.EPSILON보다 작은지 판단하면 됩니다. 그러면 0.1+0.2 ===0.3

function numberepsilon(arg1,arg2){                   
  return Math.abs(arg1 - arg2) < Number.EPSILON;        
}        
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true

1.8로 판단할 수 있습니다.8 안전한 정의되지 않은 값을 얻는 방법은 무엇입니까?

undefine은 식별자이기 때문에 변수로 사용하고 할당할 수 있지만, 이는 undefed의 일반적인 판단에 영향을 미칩니다. void ___ 표현식에는 반환 값이 없으므로 결과가 정의되지 않습니다. void는 표현식의 결과를 변경하지 않고 표현식이 값을 반환하지 않게 할 뿐입니다. 따라서 void 0을 사용하여 정의되지 않은 상태를 얻을 수 있습니다.

1.9 NaN 유형의 결과는 무엇입니까?

NaN은 "숫자가 아님"을 의미하고, NaN은 "센티넬 값"(특수 목적을 가진 일반 값)으로 숫자 유형의 오류 조건, 즉 "수학적 수행"을 지적하는 데 사용됩니다. 연산이 성공하지 못했습니다. 실패 후 반환된 결과입니다."

typeof NaN; // "number"

NaN은 자신과 같지 않은 특별한 값이며 유일한 비반사 값입니다. 소위 무반사적이라는 것은 NaN이 자신을 포함하여 누구와도 동일하지 않지만 NaN에서는 동일하다는 것을 의미합니다! = NaN은 true를 반환합니다

1.10 isNaN과 Number.isNaN 함수의 차이점은 무엇인가요?

매개변수를 받은 후 isNaN 함수는 매개변수를 숫자 값으로 변환하려고 시도합니다. 따라서 숫자 값으로 변환할 수 없는 값은 true를 반환합니다. 또한 true를 반환하며 이는 NaN의 판단에 영향을 미칩니다.

Number.isNaN 함수는 먼저 들어오는 매개변수가 숫자인지 여부를 확인합니다. 숫자인 경우 NaN 판단에는 이 방법이 더 정확합니다. .

1.11 == 연산자의 캐스팅 규칙은 무엇입니까?

==의 경우 두 비교 대상의 유형이 다른 경우 유형 변환이 수행됩니다. x와 y가 동일한지 비교하면 다음과 같은 판단 과정이 진행됩니다.

먼저 두 유형이 동일한지 여부를 판단합니다.

유형이 동일하지 않으면 유형 변환이 수행됩니다.

먼저 null과 정의되지 않은 항목이 비교되는지 확인하고, 그렇다면 true를 반환하여 두 유형이 일치하는지 확인합니다. 그렇다면 문자열을 숫자

1 == &#39;1&#39;
      ↓
1 ==  1

로 변환하여 둘 중 하나가 부울인지 확인합니다. 그렇다면 부울을 숫자로 변환한 다음

&#39;1&#39; == true
        ↓
&#39;1&#39; ==  1
        ↓
 1  ==  1

를 판단하여 당사자 중 하나가 아닌지 판단합니다. 객체이고 다른 하나는 문자열, 숫자 또는 기호인 경우 객체를 원래 유형으로 변환한 다음 판단합니다.

&#39;1&#39; == { name: &#39;js&#39; }        ↓&#39;1&#39; == &#39;[object Object]&#39;

흐름도는 다음과 같습니다.

JavaScript 10,000 단어 인터뷰 요약

1.12 변환 규칙은 무엇입니까? 다른 값 유형을 문자열로 변환합니까? Null 및 정의되지 않은 유형, null은 "null"로 변환, undefound는 "undefine"으로 변환,

Boolean 유형, true는 "true"로 변환, false는 "false"로 변환됩니다.

숫자형 값은 바로 변환되는데, 아주 작은 숫자나 큰 숫자는 지수형을 사용합니다.

심볼 유형 값은 직접 변환되지만 암시적 캐스트를 사용하면 오류가 발생합니다.

对普通对象来说,除非自行定义 toString() 方法,否则会调用 toString()(Object.prototype.toString())来返回内部属性 [[Class]] 的值,如"[object Object]"。如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。

1.13. 其他值类型转成数字的转换规则?

Undefined 类型的值转换为 NaN。

Null 类型的值转换为 0。

Boolean 类型的值,true 转换为 1,false 转换为 0。

String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。

Symbol 类型的值不能转换为数字,会报错。

对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。

为了将值转换为相应的基本类型值, 隐式转换会首先检查该值是否有valueOf()方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。

如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。

1.14 其他值类型转成布尔类型的转换规则?

以下这些是假值: undefined 、 null 、 false 、 +0、-0 和 NaN 、 ""

假值的布尔强制类型转换结果为 false。从逻辑上说,假值列表以外的都应该是真值。

1.15. || 和 && 操作符的返回值?

|| 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。

  • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。

  • && 则相反,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。

|| 和 && 返回它们其中一个操作数的值,而非条件判断的结果

1.16. Object.is() 与比较操作符 “===”、“==” 的区别?

  • 使用双等号(==)进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。

  • 使用三等号(===)进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。

  • 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 是相等的。

1.17. 什么是 JavaScript 中的包装类型?

在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象。如:

const a = "abc";
a.length; // 3

在访问'abc'.length时,JavaScript 将'abc'在后台转换成String('abc'),然后再访问其length属性。

1.18 Js中隐式转换规则

在 if 语句、逻辑语句、数学运算逻辑、== 等情况下都可能出现隐式类型转换。

JavaScript 10,000 단어 인터뷰 요약

坑: 判断时, 尽量不要用 = = , 要用 = = = ( 两个等号判断, 如果类型不同, 默认会进行隐式类型转换再比较)

1.19 说说你对this的理解

this是一个在运行时才进行绑定的引用,在不同的情况下它可能会被绑定不同的对象。

1.20 如何判断 this 的指向

第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。

第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。

第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。

第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。

this绑定的优先级

new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级

1.21 Map和Object的区别

JavaScript 10,000 단어 인터뷰 요약

1.22 说说你对JSON的理解

JSON 是一种基于文本的轻量级的数据交换格式。它可以被任何的编程语言读取和作为数据格式来传递。

在项目开发中,使用 JSON 作为前后端数据交换的方式。在前端通过将一个符合 JSON 格式的数据结构序列化为 JSON 字符串,然后将它传递到后端,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。

因为 JSON 的语法是基于 js 的,因此很容易将 JSON 和 js 中的对象弄混,但是应该注意的是 JSON 和 js 中的对象不是一回事,JSON 中对象格式更加严格,比如说在 JSON 中属性值不能为函数,不能出现 NaN 这样的属性值等,因此大多数的 js 对象是不符合 JSON 对象的格式的。

在 js 中提供了两个函数来实现 js 数据结构和 JSON 格式的转换处理,

JSON.stringify 函数,通过传入一个符合 JSON 格式的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不符合 JSON 格式,那么在序列化的时候会对这些值进行对应的特殊处理,使其符合规范。在前端向后端发送数据时,可以调用这个函数将数据对象转化为 JSON 格式的字符串。

JSON.parse() 函数,这个函数用来将 JSON 格式的字符串转换为一个 js 数据结构,如果传入的字符串不是标准的 JSON 格式的字符串的话,将会抛出错误。当从后端接收到 JSON 格式的字符串时,可以通过这个方法来将其解析为一个 js 数据结构,以此来进行数据的访问。

1.222 String和JSON.stringify的区别

console.log(String("abc")); // abc
console.log(JSON.stringify("abc")); // "abc"
console.log(String({ key: "value" })); // [object Object]
console.log(JSON.stringify({ key: "value" })); // {"key":"value"}
console.log(String([1, 2, 3])); // 1,2,3
console.log(JSON.stringify([1, 2, 3])); // [1,2,3]
const obj = {
    title: "devpoint",
    toString() {
        return "obj";
    },
};
console.log(String(obj)); // obj
console.log(JSON.stringify(obj)); // {"title":"devpoint"}

当需要将一个数组和一个普通对象转换为字符串时,经常使用JSON.stringify。

如果需要对象的toString方法被重写,则需要使用String()。

在其他情况下,使用String()将变量转换为字符串。

1.23 什么是伪数组(类数组)

一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用数组的方法。

常见的类数组对象有 arguments 和 DOM 方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有 length 属性值,代表可接收的参数个数。

1.24 类数组转换成数组的方法有哪些

常见的类数组转换为数组的方法有这样几种:

通过 call 调用数组的 slice 方法来实现转换

Array.prototype.slice.call(arrayLike);

通过 call 调用数组的 splice 方法来实现转换

Array.prototype.splice.call(arrayLike, 0);

通过 apply 调用数组的 concat 方法来实现转换

Array.prototype.concat.apply([], arrayLike);

通过 Array.from 方法来实现转换

Array.from(arrayLike);

1.25 Unicode、UTF-8、UTF-16、UTF-32的区别?

Unicode 是编码字符集(字符集),而UTF-8、UTF-16、UTF-32是字符集编码(编码规则);

UTF-16 使用变长码元序列的编码方式,相较于定长码元序列的UTF-32算法更复杂,甚至比同样是变长码元序列的UTF-8也更为复杂,因为其引入了独特的代理对这样的代理机制;

UTF-8需要判断每个字节中的开头标志信息,所以如果某个字节在传送过程中出错了,就会导致后面的字节也会解析出错;而UTF-16不会判断开头标志,即使错也只会错一个字符,所以容错能力教强;

如果字符内容全部英文或英文与其他文字混合,但英文占绝大部分,那么用UTF-8就比UTF-16节省了很多空间;而如果字符内容全部是中文这样类似的字符或者混合字符中中文占绝大多数,那么UTF-16就占优势了,可以节省很多空间;

1.26 常见的位运算符有哪些?其计算规则是什么?

现代计算机中数据都是以二进制的形式存储的,即0、1两种状态,计算机对二进制数据进行的运算加减乘除等都是叫位运算,即将符号位共同参与运算的运算。

常见的位运算有以下几种:

JavaScript 10,000 단어 인터뷰 요약

1.27 为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组?

arguments是一个对象,它的属性是从 0 开始依次递增的数字,还有callee和length等属性,与数组相似;但是它却没有数组常见的方法属性,如forEach, reduce等,所以叫它们类数组。

要遍历类数组,有三个方法:

(1)将数组的方法应用到类数组上,这时候就可以使用call和apply方法,如:

function foo(){ 
  Array.prototype.forEach.call(arguments, a => console.log(a))
}

(2)使用Array.from方法将类数组转化成数组:‌

function foo(){ 
  const arrArgs = Array.from(arguments) 
  arrArgs.forEach(a => console.log(a))
}

(3)使用展开运算符将类数组转化成数组

function foo(){ 
    const arrArgs = [...arguments] 
    arrArgs.forEach(a => console.log(a)) 
}

1.28 escape、encodeURI、encodeURIComponent 的区别

encodeURI 是对整个 URI 进行转义,将 URI 中的非法字符转换为合法字符,所以对于一些在 URI 中有特殊意义的字符不会进行转义。

encodeURIComponent 是对 URI 的组成部分进行转义,所以一些特殊字符也会得到转义。

escape 和 encodeURI 的作用相同,不过它们对于 unicode 编码为 0xff 之外字符的时候会有区别,escape 是直接在字符的 unicode 编码前加上 %u,而 encodeURI 首先会将字符转换为 UTF-8 的格式,再在每个字节前加上 %。

1.29 什么是尾调用,使用尾调用有什么好处?

尾调用指的是函数的最后一步调用另一个函数。代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这时可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。

但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

1.30 use strict是什么? 它有什么用?

use strict 是一种 ECMAscript5 添加的(严格模式)运行模式,这种模式使得 Javascript 在更严格的条件下运行。设立严格模式的目的如下:

消除 Javascript 语法的不合理、不严谨之处,减少怪异行为;

消除代码运行的不安全之处,保证代码运行的安全;

提高编译器效率,增加运行速度;

为未来新版本的 Javascript 做好铺垫。

区别:

禁止使用 with 语句。

禁止 this 关键字指向全局对象。

对象不能有重名的属性。

1.31 如何判断一个对象是否属于某个类?

第一种方式,使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

第二种方式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。

第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用 Object.prototype.toString() 方法来打印对象的[[Class]] 属性来进行判断。

1.32 强类型语言和弱类型语言的区别

强类型语言:强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。Java和C++等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。

약한 유형의 언어: 약한 유형의 언어는 강력한 유형의 정의와 반대되는 약한 유형의 정의 언어라고도 합니다. JavaScript 언어는 약한 유형의 언어입니다. 쉽게 이해하면 변수형을 무시할 수 있는 언어이다. 예를 들어 JavaScript에서는 약한 형식입니다. JavaScript에서는 문자열 '12'와 정수 3을 연결하여 문자열 '123'을 얻을 수 있으며 추가 시 강제 형식 변환이 수행됩니다.

둘의 비교: 강력한 유형의 언어는 약한 유형의 언어에 비해 속도가 약간 떨어질 수 있지만 강력한 유형의 언어가 가져오는 엄격함은 많은 오류를 피하는 데 효과적으로 도움이 될 수 있습니다.

1.33 해석된 언어와 컴파일된 언어의 차이점 ​

(1) 해석된 언어는 전문적인 인터프리터를 사용하여 소스 프로그램을 한 줄씩 특정 플랫폼에 맞는 기계어 코드로 해석하고 실행합니다. 즉시. 코드는 실행 전에 번역되는 것이 아니라 실행 시 인터프리터에 의해 한 줄씩 동적으로 번역되어 실행됩니다. 통역언어는 미리 컴파일할 필요가 없으며, 소스코드를 기계어로 직접 해석해 즉시 실행하기 때문에 특정 플랫폼에서 해당 통역사를 제공하면 프로그램을 실행할 수 있다. 그 특징은 다음과 같이 요약됩니다

통역된 언어가 실행될 때마다 소스 코드를 기계어 코드로 해석하여 실행해야 하므로 비효율적입니다.

플랫폼에서 해당 통역사를 제공하는 한 소스 코드는 실행되므로 소스 프로그램 이식이 용이합니다.

JavaScript, Python 등은 해석 언어입니다.

(2) 컴파일된 언어는 특수한 컴파일러를 사용하여 특정 플랫폼에 대한 고급 언어 소스 코드를 플랫폼의 하드웨어에서 실행할 수 있는 기계어 코드로 컴파일하고 플랫폼에서 인식할 수 있는 실행 파일로 패키징합니다. . 성행위의 형식. 컴파일된 언어로 작성된 프로그램을 실행하기 전에 소스코드를 exe 형식의 파일 등의 기계어 파일로 컴파일하는 특별한 컴파일 과정이 필요하며, 이후 실행 시에는 컴파일 결과를 바로 사용할 수 있습니다. exe 문서를 직접 실행하는 것과 같습니다. 한 번만 컴파일하면 되고 나중에 실행할 때 컴파일할 필요가 없기 때문에 컴파일된 언어는 실행 효율성이 높습니다. 그 특징을 요약하면 다음과 같습니다.

플랫폼 관련 기계어 파일로 한번에 컴파일되어 실행 시 개발 환경과 분리되어 운영 효율성이 높습니다.

특정 플랫폼과 관련되어 일반적으로 불가능합니다.

C, C++ 등은 컴파일된 언어에 속합니다.

둘 사이의 주요 차이점은 전자의 소스 프로그램은 컴파일 후 플랫폼에서 실행할 수 있는 반면 후자는 작업 중에 컴파일된다는 것입니다. 따라서 전자는 빠르게 실행되고 후자는 크로스 플랫폼 성능이 뛰어납니다.

1.34 for...in과 for...of

for...of의 차이점은 ES6의 새로운 순회 방법으로, 데이터 구조(배열, 개체 등)를 순회할 수 있습니다. iterator 인터페이스를 포함하고 ES3에서 각 항목의 값과 for...in의 차이점은 다음과 같습니다.

for...of는 객체의 키 값을 얻기 위해 트래버스하고 for...in은 키를 얻습니다.

for...in은 전체 객체를 순회합니다. 프로토타입 체인은 성능이 매우 낮으므로 사용하지 않는 것이 좋습니다. 반면 for...of는 현재 객체만 순회하고 프로토타입 체인은 순회하지 않습니다.

배열 탐색의 경우 for...in은 배열의 모든 열거 가능한 속성(프로토타입 체인의 속성 포함)을 반환하고, for...of는 배열의 첨자에 해당하는 속성 값만 반환합니다.

요약: for...in 루프는 주로 객체를 탐색하도록 설계되었으며 배열을 탐색하는 데 적합하지 않습니다. for...of 루프는 배열, 배열과 유사한 객체, 문자열, 세트, ​​맵을 탐색하는 데 사용할 수 있습니다. 및 생성기 개체.

1.35 ajax, axios, fetch의 차이점

(1) AJAX Ajax는 "AsynchronousJavascriptAndXML"(비동기 JavaScript 및 XML)로, 대화형 웹 애플리케이션을 만들기 위한 웹 개발 기술을 말합니다. 전체 웹페이지를 다시 로드하지 않고도 웹페이지의 일부를 업데이트할 수 있는 기술입니다. Ajax를 사용하면 백그라운드에서 서버와 소량의 데이터를 교환하여 웹 페이지를 비동기적으로 업데이트할 수 있습니다. 이는 전체 페이지를 다시 로드하지 않고도 웹페이지의 일부를 업데이트할 수 있음을 의미합니다. Ajax가 없는 기존 웹 페이지는 콘텐츠를 업데이트해야 하는 경우 전체 웹 페이지를 다시 로드해야 합니다. 단점은 다음과 같습니다.

  • 자체는 MVC 프로그래밍을 목표로 하며 프런트엔드 MVVM의 추세를 따르지 않습니다.

  • XHR 자체의 아키텍처는 명확하지 않습니다.

  • 우려 사항 분리(Separation of Concerns) 원칙을 준수하지 않습니다.

  • 구성 및 호출 방법이 매우 혼란스럽고 이벤트 기반 비동기 모델이 비우호적입니다.

(2) Fetch fetch는 AJAX를 대체하는 것으로 알려져 있으며 ES6에서 Promise 객체를 사용합니다. Fetch는 약속을 기반으로 설계되었습니다. Fetch의 코드 구조는 ajax보다 훨씬 간단합니다. Fetch는 ajax를 추가로 캡슐화한 것이 아니라 XMLHttpRequest 객체를 사용하지 않는 기본 js입니다.

가져오기의 장점:

  • 간단한 구문, 더 많은 의미

  • 표준 Promise 구현을 기반으로 async/await 지원

  • 더 낮은 수준이며 풍부한 API(요청, 응답) 제공

  • Detachment XHR은 ES 사양의 새로운 구현 방법입니다

가져오기의 단점:

  • fetch는 네트워크 요청에 대한 오류만 보고합니다. 서버가 400 및 500 오류 코드를 반환하면 요청이 완료되지 못한 경우에만 거부되지 않습니다. 거부됩니다.

  • fetch에는 기본적으로 쿠키가 없으므로 구성 항목을 추가해야 합니다: fetch(url, {credentials: 'include'})

  • fetch는 중단을 지원하지 않으며 시간 초과 제어를 지원하지 않으며 setTimeout을 사용하고 Promise.reject 구현 시간 초과 제어는 요청 프로세스가 백그라운드에서 계속 실행되는 것을 막지 않으므로 트래픽 낭비가 발생합니다.

  • fetch는 기본적으로 요청 진행 상황을 모니터링할 수 없지만 XHR은

(3) Axios Axios는 Promise 기반 캡슐화 HTTP 클라이언트이며 그 특징은 다음과 같습니다.

  • 브라우저가 XMLHttpRequests 요청을 시작합니다.

  • 노드가 http 요청을 시작합니다

  • Promise API 지원

  • Listen 요청 및 반환

  • 요청 및 반환 변환

  • 요청 취소

  • json 데이터 자동 변환

  • 클라이언트는 XSRF 공격 저항을 지원합니다

1.36 배열의 순회 방법

JavaScript 10,000 단어 인터뷰 요약

1.37 forEach map 메소드와 map 메소드의 차이점은 무엇입니까? 이 메소드는 배열을 순회하는 데 사용됩니다. 두 메소드의 차이점은 다음과 같습니다. 각 요소에 대해 함수가 제공되며 데이터에 대한 작업은 원래 배열을 변경합니다. 이 메서드는 반환 값이 없습니다. map() 메서드는 원래 배열의 값을 변경하지 않지만 새 배열의 값을 반환합니다. ​​새 배열에는 함수를 호출한 후 원래 배열의 값이 있습니다.

1.38 Qian에 대한 감상을 말씀해 주세요. 복사 및 전체 복사에 대한 이해

얕은 복사얕은 복사는 다음을 의미합니다. 새 데이터 생성 이 데이터에는 원래 데이터 속성 값의 정확한 복사본이 있습니다속성이 기본 유형인 경우 복사된 것이 기본 유형 값입니다. 속성이 참조 유형이면 메모리 주소가 복사됩니다

즉, 얕은 복사본은 하나의 레이어를 복사하고 깊은 참조 유형은 메모리 주소를 공유합니다

일반적인 얕은 복사본:

Object.sign

    Object.create
  • slice
  • concat()
  • Expand 연산자
  • deep copy
  • deep copy는 새 스택을 엽니다. , 한 개체의 속성을 수정해도 다른 개체의 속성은 변경되지 않습니다. 일반적인 전체 복사 방법은 다음과 같습니다. ( )

손으로 쓴 루프 재귀

    1.39 JSON.stringify의 전체 복사의 단점
  • obj에 시간 객체가 있으면 JSON.stringify에 이어 JSON.parse를 수행한 결과, time 그냥 문자열이 됩니다 객체의 형태가 아닌 형태
  • obj에 RegExp가 있으면 빈 객체로 출력됩니다

  • 객체에 함수나 정의되지 않은 것이 있으면 바로 폐기됩니다
  • 생성자에 의해 생성된 객체가 json에 있으면 객체의 생성자가 손실됩니다.

  • 1.40 lodash를 아시나요? 어떤 공통 API가 있나요?

Lodash는 일관된 모듈식 고성능 JavaScript 유틸리티 라이브러리입니다. _.cloneDeep 깊은 복사 _.reject는 조건에 따라 요소를 제거합니다.

_.drop(array, [n=1] ) 함수: 배열의 처음 n개 요소를 제거한 다음 나머지 부분을 반환합니다.

1.41 LHS 및 RHS 쿼리

LHS(왼쪽 측면 ) 및 RHS(오른쪽)는 JS 엔진이 코드 실행 단계에서 변수를 작동하는 두 가지 방법입니다. 둘 사이의 차이점은 변수를 쿼리하는 목적이 변수 할당 또는 쿼리라는 것입니다. LHS는 변수가 할당 연산자(=)의 왼쪽에 있는 것으로 이해될 수 있습니다(예: a = 1). 현재 엔진이 변수 a를 검색하는 목적은 변수 할당입니다. 이 경우 엔진은 변수 a의 원래 값이 무엇인지 신경 쓰지 않고 값 1을 변수 a에 할당합니다. RHS는 할당 연산자(=) 오른쪽에 있는 변수로 이해될 수 있습니다(예: console.log(a)). 엔진이 변수 a를 검색하는 목적은 실제 쿼리를 찾는 것입니다. 변수 a에 해당하는 값을 출력할 수 있습니다.

1.42 include가 indexOf보다 나은 점은 무엇인가요?

include는 NaN을 감지할 수 있고, indexOf는 NaN을 감지할 수 없으며, include는 NaN과 일치시키기 위해 내부적으로 Number.isNaN을 사용합니다.

1.43 AMD와 CMD의 차이점은 무엇인가요?

1.44 (a == 1 && a == 2 && a == 3) 사실이 가능할까요?

方案一:重写toString()或valueOf()

let a = {  
    i: 1,  
    toString: function () {    
        return a.i++;  
    }
}
console.log(a == 1 && a == 2 && a == 3); // true

方案二:数组

数组的toString接口默认调用数组的join方法,重写join方法。定义a为数字,每次比较时就会调用 toString()方法,我们把数组的shift方法覆盖toString即可:

let a = [1,2,3];
a.toString = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true

当然把toString改为valueOf也是一样效果:

let a = [1,2,3];
a. valueOf  = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true

方案三:使用Object.defineProperty()

Object.defineProperty()用于定义对象中的属性,接收三个参数:object对象、对象中的属性,属性描述符。属性描述符中get:访问该属性时自动调用。

var  _a = 1;
Object.defineProperty(this,&#39;a&#39;,{  
    get:function(){    
        return _a++  
    }
})
console.log(a===1 && a===2 && a===3)//true

1.45 JS中的 MUL 函数

MUL表示数的简单乘法。在这种技术中,将一个值作为参数传递给一个函数,而该函数将返回另一个函数,将第二个值传递给该函数,然后重复继续。例如:xyz可以表示为

const mul = x => y => z => x * y * z
console.log(mul(1)(2)(3)) // 6

1.46 深度遍历广度遍历的区别?

对于算法来说 无非就是时间换空间 空间换时间

1、深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大

2、深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点

3、深度优先采用的是堆栈的形式, 即先进后出

4、广度优先则采用的是队列的形式, 即先进先出

1.47 JS中的设计模式有哪些?

  • 单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

  • 策略模式

定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。

  • 代理模式

为一个对象提供一个代用品或占位符,以便控制对它的访问。

  • 中介者模式

通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。

  • 装饰者模式

在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。

1.48 forEach如何跳出循环?

forEach是不能通过break或者return来实现跳出循环的,为什么呢?实现过forEach的同学应该都知道,forEach的的回调函数形成了一个作用域,在里面使用return并不会跳出,只会被当做continue

可以利用try catch

  function getItemById(arr, id) {
        var item = null;
        try {
            arr.forEach(function (curItem, i) {
                if (curItem.id == id) {
                    item = curItem;
                    throw Error();
                }
            })
        } catch (e) {
        }
        return item;
    }

1.49 JS中如何将页面重定向到另一个页面?

1、使用 location.href:window.location.href ="url"

2、使用 location.replace: window.location.replace("url");

1.50 移动端如何实现上拉加载,下拉刷新?

上拉加载

上拉加载的本质是页面触底,或者快要触底时的动作

判断页面触底我们需要先了解一下下面几个属性

scrollTop:滚动视窗的高度距离window顶部的距离,它会随着往上滚动而不断增加,初始值是0,它是一个变化的值

clientHeight:它是一个定值,表示屏幕可视区域的高度;

scrollHeight:页面不能滚动时也是存在的,此时scrollHeight等于clientHeight。scrollHeight表示body所有元素的总长度(包括body元素自身的padding)

综上我们得出一个触底公式:

scrollTop + clientHeight >= scrollHeight

下拉刷新

下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作

关于下拉刷新的原生实现,主要分成三步:

监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY;

监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0表示向下拉动,并借助CSS3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值;

监听原生touchend事件,若此时元素滑动达到最大值,则触发callback,同时将translateY重设为0,元素回到初始位置

1.51 JS 中的数组和函数在内存中是如何存储的?

JavaScript의 배열 저장은 대략 두 가지 상황으로 나누어야 합니다.

동일한 유형의 데이터가 있는 배열은 연속 메모리 공간을 할당합니다.

동일하지 않은 유형의 데이터가 있는 배열은 메모리 공간을 할당하기 위해 해시 매핑을 사용합니다.

알림: 예 연속 메모리 공간은 인덱스(포인터)를 기준으로 저장 위치만 직접 계산하면 된다고 상상해 보세요. 해시맵이라면 먼저 인덱스 값을 계산해야 하고, 인덱스 값에 충돌이 있으면 2차 검색을 해야 한다(해시가 어떻게 저장되는지 알아야 한다).

2. 클로저와 범위

2.1 클로저란 무엇인가요?

공식 성명: 클로저는 다른 함수의 범위에 있는 변수에 액세스할 수 있는 권한이 있는 함수를 나타냅니다.

MDN의 말: 클로저는 특별한 종류의 객체입니다. 이는 함수와 함수가 생성되는 환경이라는 두 부분으로 구성됩니다. 환경은 클로저가 생성될 때 범위에 있었던 모든 지역 변수로 구성됩니다.

심층 답변

브라우저가 페이지를 로드하면 실행을 위해 코드를 스택 메모리(ECStack)에 넣습니다. 함수를 스택에 푸시하면 개인 컨텍스트(EC)가 생성됩니다. 외부 간섭으로 인해 내부의 사용 변수(AO)가 제거되고, 현재 실행 컨텍스트의 일부 콘텐츠가 컨텍스트 외부의 콘텐츠에 의해 점유되는 경우 현재 컨텍스트는 스택에서 해제되지 않으므로 해당 변수 및 변수 값은 저장할 수 있기 때문에 클로저란 일종의 내부 프라이빗 변수에 대한 보존 및 보호 메커니즘이라고 생각합니다.

2.2 클로저의 역할

클로저에는 두 가지 일반적인 용도가 있습니다.

클로저의 첫 번째 용도는 함수 외부에서 함수 내부의 변수에 액세스할 수 있도록 하는 것입니다. 클로저를 사용하면 외부에서 클로저 함수를 호출하여 외부에서 함수 내부의 변수에 액세스할 수 있습니다. 이 메서드를 사용하여 비공개 변수를 만들 수 있습니다.

클로저의 또 다른 용도는 종료된 함수 컨텍스트의 변수 개체가 메모리에 남아 있도록 허용하는 것입니다. 클로저 함수는 변수 개체에 대한 참조를 유지하므로 변수 개체는 재활용되지 않습니다.

2.3 프로젝트의 클로저에 대한 참조 시나리오와 이로 인해 발생하는 문제

실제 프로젝트에서는 자신이 작성한 모듈의 내용을 클로저를 기반으로 래핑하여 자신의 코드를 보호할 수 있습니다. 전역 변수나 다른 코드와의 충돌을 방지하기 위해 보호 메커니즘을 사용합니다.

그러나 클로저를 너무 많이 사용하는 것은 권장되지 않습니다. 해제되지 않은 컨텍스트를 사용하면 스택 메모리 공간을 차지하고 과도하게 사용하면 메모리 누수가 발생할 수 있기 때문입니다.

클로저로 인한 메모리 누수 문제를 해결하는 방법은 클로저 기능을 사용한 후 수동으로 해제하는 것입니다.

2.4 클로저 사용 시나리오

함수 반환

함수를 매개변수로

IIFE(자체 실행 함수)

루프 할당

콜백 함수를 사용하는 것은 클로저를 사용하는 것입니다

제한 방지

함수 커링

2.5 클로저 실행 프로세스

개인 컨텍스트를 형성하고

스택에 푸시하여 일련의 작업

을 실행합니다

(1). 범위 도메인, 상위 범위>)

(2). 인수 초기화

(4). 변수 승격

(6). 변수를 발견하면 먼저 자신에게만 해당되는 변수인지 확인하고, 상위 변수가 아닌 경우에는 EC(G)까지 온라인으로 검색합니다. 실제로는 스플라이싱 프로세스이고 스플라이싱 쿼리 체인이 스코프 체인입니다.

일반적인 상황에서는 코드 실행이 완료된 후 개인 컨텍스트가 스택에서 제거되어 재활용됩니다. 그러나 특수한 상황에서 현재 Private 컨텍스트의 실행이 완료된 후 현재 Private 컨텍스트의 어떤 것이 실행 컨텍스트가 아닌 다른 것에 의해 점유되면 현재 Private 컨텍스트는 스택에서 해제되지 않습니다. 폐쇄는 파괴되지 않습니다.

2.6 실행 컨텍스트의 종류

(1) 전역 실행 컨텍스트함수 내부에 있지 않은 모든 것은 전역 실행 컨텍스트입니다. 먼저 전역 창 개체를 만들고 this의 값을 다음과 동일하게 설정합니다. 전역 개체에는 프로그램에 전역 실행 컨텍스트가 하나만 있습니다. (2) 함수 실행 컨텍스트

함수가 호출되면 함수에 대한 새로운 실행 컨텍스트가 생성됩니다. 함수 컨텍스트는 얼마든지 있을 수 있습니다.

(3) eval 함수 실행 컨텍스트

eval 함수에서 실행되는 코드에는 고유한 실행 컨텍스트가 있지만 eval 함수는 일반적으로 사용되지 않으므로 소개하지 않습니다.

2.7 실행 컨텍스트 스택이란 무엇입니까

JavaScript 엔진은 실행 컨텍스트 스택을 사용하여 실행 컨텍스트를 관리합니다

JavaScript가 코드를 실행할 때 먼저 전역 코드를 만나 전역 실행 컨텍스트를 생성하고 이를 실행 스택에 푸시합니다. 함수 호출을 만날 때마다 함수에 대한 새로운 실행 컨텍스트를 생성하여 스택 맨 위에 푸시합니다. . 이면 엔진은 실행 컨텍스트 스택의 맨 위에 있는 함수를 실행합니다. 함수 실행이 완료되면 실행 컨텍스트가 스택에서 꺼내지고 다음 컨텍스트를 계속 실행합니다. 모든 코드가 실행되면 전역 실행 컨텍스트가 스택에서 팝됩니다.

2.8 실행 컨텍스트의 세 단계

Creation 단계 → 실행 단계 → 재활용 단계

Creation 단계

(1) this 바인딩

전역 실행 컨텍스트에서 this는 전역 객체(window 객체)를 가리킵니다. )

함수 실행 컨텍스트에서 이는 함수가 호출되는 방식에 따라 달라집니다. 참조 객체에 의해 호출되면 this는 해당 객체로 설정되고, 그렇지 않으면 this의 값은 전역 객체 또는 정의되지 않은 값으로 설정됩니다

(2) 어휘 환경 구성 요소 만들기

어휘 환경은 식별자입니다. - 변수 식별자가 변수/함수 이름을 참조하고 변수가 실제 객체 또는 원시 데이터에 대한 참조인 매핑된 데이터 구조입니다.

어휘 환경 내부에는 두 가지 구성 요소가 있습니다. 굵은 스타일: 환경 레코더: 변수 및 함수 선언의 실제 위치를 저장하는 데 사용됩니다. 외부 환경 참조: 상위 범위에 액세스할 수 있습니다.

(3) 변수 환경 구성 요소 만들기

변수 환경은 어휘 환경이기도 하며 해당 환경 레코더는 실행 컨텍스트에서 변수 선언문에 의해 생성된 바인딩 관계를 보유합니다.

실행 단계

이 단계에서는 변수 할당 및 코드 실행이 수행됩니다.

자바스크립트 엔진이 소스 코드에 선언된 실제 위치에서 변수 값을 찾지 못하면 정의되지 않은 값이 할당됩니다

재활용 단계

실행 컨텍스트는 스택에서 튀어나오고 가상 머신이 실행 컨텍스트를 재활용할 때까지 기다립니다.

2.9 범위에 대한 이해에 대해 이야기해 주세요

범위는 일련의 규칙으로 간주될 수 있습니다. 규칙은 엔진이 현재 범위에서 작동하는 방식을 관리하고 식별자 이름을 기반으로 변수 조회의 하위 범위를 중첩하는 데 사용됩니다.

간단히 말하면 범위는 변수의 유효 범위입니다. 변수 데이터는 특정 공간에서 읽고 쓸 수 있으며, 이 공간이 변수의 범위입니다.

(1) 전역 범위

script 태그에 직접 작성된 JS 코드는 전역 범위에 속합니다. 전역 범위에 선언된 변수를 전역 변수(블록 수준 외부에서 정의된 변수)라고 합니다.

전역 변수는 전 세계 어디에서나 사용할 수 있습니다. 로컬 범위의 변수는 전역 범위에서 액세스할 수 없습니다.

전역 범위는 페이지가 열릴 때 생성되고 페이지가 닫힐 때 소멸됩니다.

창 개체의 모든 속성에는 전역 범위가 있습니다.

var 및 function 명령으로 선언된 전역 변수와 함수는 창 개체의 속성과 메서드입니다.

let 명령, const 명령, class 명령으로 선언된 전역 변수는 그렇지 않습니다. 윈도우 객체 속성에 속함

(2) 함수 범위(로컬 범위)

함수가 호출되면 함수 범위가 생성되고 함수가 실행된 후 범위가 소멸됩니다. 함수가 호출될 때마다 새로운 함수 범위가 생성되며 서로 독립적입니다.

전역 변수는 함수 범위 내에서 접근할 수 있지만 함수 내부 변수는 함수 외부에서 접근할 수 없습니다.

함수 범위에서 변수를 조작할 때는 먼저 자체 범위에서 검색하고, 없으면 전역 범위를 찾을 때까지 이전 범위에서 검색합니다. 여전히 전역 범위에 있습니다. 찾을 수 없으면 오류가 보고됩니다.

(3) 블록 수준 범위

ES6 이전에는 JavaScript에서 함수 범위 + 어휘 범위를 사용했으며 ES6에서는 블록 수준 범위를 도입했습니다.

중괄호 {}로 묶인 모든 명령문 세트는 블록에 속합니다. 블록에서 let 및 const를 사용하여 선언된 변수는 외부에서 액세스할 수 없습니다.

var를 통해 선언된 변수 또는 엄격하지 않은 모드에서 생성된 함수 선언에는 블록 수준 범위가 없습니다.

(4) 어휘 범위

어휘 범위는 함수가 호출되는 위치나 호출 방법에 관계없이 선언된 함수에 의해서만 결정됩니다.

컴파일의 어휘 분석 단계에서는 기본적으로 모든 식별자가 선언된 위치와 방법을 알 수 있으므로 실행 중에 식별자를 찾는 방법을 예측할 수 있습니다.

즉, 어휘 범위는 코드를 작성할 때 변수의 범위를 결정했다는 의미입니다.

2.10 범위 체인이란 무엇입니까

js에서 변수를 사용할 때 js 엔진은 먼저 현재 범위에서 변수를 찾으려고 시도합니다. 변수가 없으면 상위 범위에서 검색합니다. , 변수가 발견되거나 전역 범위에 도달할 때까지 이러한 변수 범위 액세스의 체인 구조를 범위 체인이라고 합니다

심층 답변

범위 체인은 기본적으로 변수 개체에 대한 포인터 목록입니다. 변수 객체는 실행 환경의 모든 변수와 함수를 포함하는 객체입니다. 범위 체인의 프런트 엔드는 항상 현재 실행 컨텍스트의 변수 개체입니다. 전역 실행 컨텍스트의 변수 개체(즉, 전역 개체)는 항상 범위 체인의 마지막 개체입니다.

2.11 범위 체인의 역할

범위 체인의 역할은 실행 환경이 액세스하는 모든 변수와 함수에 대한 질서 있는 액세스를 보장하는 것입니다. 범위 체인을 통해 외부 환경의 변수는 액세스하고 기능할 수 있습니다.

2.12 범위의 일반적인 적용 시나리오

범위의 일반적인 적용 시나리오 중 하나는 모듈화입니다.

JavaScript는 기본적으로 모듈화를 지원하지 않기 때문에 전역 범위 오염 및 변수 이름 충돌, 비대해진 코드 구조 및 낮은 재사용성과 같은 많은 문제를 야기했습니다. 정식 모듈화 솔루션이 도입되기 전에는 이러한 문제를 해결하기 위해 개발자들이 함수 범위를 사용하여 모듈을 생성하는 방법을 생각했습니다.

2.13 Js의 사전 파싱에 대해 이야기하시나요?

JS 엔진이 코드를 실행하면 다음 단계에 따라 작동합니다.

1. 변수 선언을 현재 범위 앞으로 승격합니다. 할당은 승격되지 않습니다.

2. 함수 선언을 현재 범위 앞으로 승격합니다. 선언만 승격되고 호출은 승격되지 않습니다.

3. 함수를 먼저 승격한 다음 var

2.14 차이점은 무엇인가요? 변수 승격과 기능 승격?

변수 승격

간단히 말하면 엔진은 JavaScript 코드가 실행되기 전에 사전 컴파일됩니다. 사전 컴파일 중에 변수 선언과 함수 선언은 해당 범위의 맨 위로 승격됩니다. 함수 내부에 정의된 변수가 외부와 동일할 경우 함수 본문의 변수가 맨 위로 올라갑니다.

함수 승격

함수 승격은 함수 선언적 작성만 개선합니다.

함수 승격의 우선순위가 변수 승격보다 높습니다. 즉, 함수 승격이 변수 승격보다 높습니다.

2.14 스코프 체인을 확장하는 방법은 무엇입니까?

스코프 체인을 확장할 수 있습니다.

확장된 범위 체인: 실행 환경에는 전역 및 로컬(함수)의 두 가지 유형만 있습니다. 그러나 일부 명령문에서는 범위 체인의 프런트 엔드에 임시로 변수 개체를 추가할 수 있으며, 코드가 실행된 후 변수 개체가 제거됩니다.

구체적으로, 이 두 명령문이 실행되면 범위 체인이 강화됩니다

catch 문의 try-catch 블록: 발생한 오류 객체의 선언을 포함하는 새로운 변수 객체가 생성됩니다.

with 문: with 문은 지정된 개체를 범위 체인에 추가합니다.

2.15 브라우저 가비지 수집 메커니즘

(1) 메모리 수명주기

JS 환경에서 할당된 메모리는 일반적으로 다음과 같은 수명주기를 갖습니다.

메모리 할당: 변수 및 함수, 객체를 선언할 때, 시스템이 자동으로 메모리를 할당합니다.

메모리 사용: 즉, 메모리를 읽고 쓰는 것, 즉 변수, 함수 등을 사용하는 것입니다.

메모리 재활용: 사용 후 더 이상 사용되지 않는 메모리는 자동으로 재활용됩니다. 가비지 컬렉션에 의한

전역 변수는 일반적으로 재활용되지 않습니다. 일반적으로 로컬 변수의 값은 더 이상 사용되지 않으면 자동으로 재활용됩니다.

(2) 가비지 컬렉션의 개념

가비지 컬렉션: JavaScript를 사용할 때 코드가 실행 중이면 변수와 값을 저장하기 위해 메모리 공간을 할당해야 합니다. 변수가 더 이상 작업에 참여하지 않으면 시스템은 점유된 메모리 공간을 회수해야 합니다. 이것이 가비지 수집입니다.

재활용 메커니즘:

Javascript에는 더 이상 사용되지 않는 변수와 객체가 차지하는 메모리를 정기적으로 해제하는 자동 가비지 수집 메커니즘이 있습니다. 원칙은 더 이상 사용되지 않는 변수를 찾아 메모리를 해제하는 것입니다. 그들에 의해 점령되었습니다.

JavaScript에는 지역 변수와 전역 변수라는 두 가지 유형의 변수가 있습니다. 전역 변수의 수명 주기는 페이지가 언로드될 때까지 계속됩니다. 반면 지역 변수는 함수에서 선언되며 해당 수명 주기는 함수 실행부터 함수 실행이 끝날 때까지 시작됩니다. 힙이나 스택에 있는 해당 값. 함수 실행이 끝나면 이러한 로컬 변수는 더 이상 사용되지 않으며 해당 변수가 차지하는 공간이 해제됩니다.

그러나 외부 함수에서 지역 변수를 사용할 때 상황 중 하나는 함수 실행이 끝난 후에도 함수 외부의 변수가 여전히 함수 내부의 지역 변수를 가리킵니다. 사용 중이므로 재활용되지 않습니다.

(3) 가비지 수집 방법

  • 1. 참조 카운팅 방법

이것은 IE에서 사용하는 참조 계산 알고리즘으로 비교적 거의 사용되지 않습니다. 참조 횟수는 각 값이 참조되는 횟수를 추적합니다. 변수가 선언되고 변수에 참조 유형이 할당되면 해당 값에 대한 참조 수는 1개입니다. 반대로, 이 값에 대한 참조를 포함하는 변수가 다른 값을 얻으면 이 값에 대한 참조 개수가 1개 줄어듭니다. 참조 수가 0이 되면 해당 변수에 값이 없다는 의미입니다. 따라서 이 변수가 차지하는 메모리 공간은 머신 재활용 기간 동안 다음에 실행될 때 해제됩니다.

이 방법은 순환 참조 문제를 발생시킵니다. 예를 들어 obj1과 obj2는 속성을 통해 서로를 참조하며 두 개체의 참조 수는 2입니다. 루프 계산을 사용하는 경우 함수가 실행된 후 두 개체가 모두 범위를 벗어나 함수 실행이 종료되므로 obj1 및 obj2는 계속 존재하므로 참조 수가 0이 되지 않아 순환 참조가 발생합니다.

  • 2. 마크 지우기 방법

최신 브라우저는 더 이상 참조 계산 알고리즘을 사용하지 않습니다.

대부분의 최신 브라우저는 마크 앤 클리어 알고리즘을 기반으로 일부 개선된 알고리즘을 기반으로 하며 전체적인 아이디어는 동일합니다.

마크 지우기는 브라우저에서 흔히 사용되는 가비지 수집 방법입니다. 변수가 실행 환경에 들어가면 "환경에 들어갑니다"라고 표시됩니다. "환경에 들어갑니다"라고 표시된 변수는 사용 중이므로 재활용할 수 없습니다. 변수가 환경을 떠나면 "환경 떠나기"로 표시되고, "환경 떠나기"로 표시된 변수는 메모리에서 해제됩니다.

가비지 수집기는 실행 시 메모리에 저장된 모든 변수를 표시합니다. 그런 다음 환경의 변수와 환경의 변수가 참조하는 태그를 제거합니다. 이후에 표시된 변수는 환경 내의 변수가 더 이상 해당 변수에 접근할 수 없기 때문에 삭제 대상 변수로 간주됩니다. 마침내. 가비지 수집기는 메모리 정리 작업을 완료하고, 표시된 값을 삭제하고, 해당 값이 차지하는 메모리 공간을 회수합니다.

(4) 가비지 수집을 줄이는 방법

브라우저가 자동으로 가비지를 수집할 수 있지만, 코드가 복잡해지면 가비지 수집 비용이 상대적으로 높기 때문에 가비지 수집을 최대한 줄여야 합니다.

배열 최적화: 배열을 지울 때 가장 간단한 방법은 [ ] 값을 할당하는 것이지만 동시에 새로운 빈 객체가 생성됩니다. 이를 위해 배열의 길이를 0으로 설정할 수 있습니다. 배열을 지우는 목적입니다.

객체 최적화: 객체를 최대한 재사용하세요. 더 이상 사용하지 않는 객체의 경우 null로 설정하고 최대한 빨리 재활용하세요.

함수 최적화: 루프의 함수 표현식을 재사용할 수 있는 경우 함수 외부에 배치해 보세요.

(5) 메모리 누수란?

프로그램이 부주의나 오류로 인해 더 이상 사용하지 않는 메모리를 해제하지 못하는 것을 말합니다.

(6) 메모리 누수가 발생하는 상황은 무엇인가요?

다음 네 가지 상황에서는 메모리 누수가 발생합니다.

예기치 않은 전역 변수: 선언되지 않은 변수의 사용으로 인해 전역 변수가 실수로 생성되어 이 변수가 메모리에 남아 재활용할 수 없습니다.

잊혀진 타이머 또는 콜백 함수: setInterval 타이머를 설정하고 취소하는 것을 잊었습니다. 루프 함수에 외부 변수에 대한 참조가 있는 경우 해당 변수는 메모리에 남아 있으며 재활용할 수 없습니다.

DOM에서 참조: DOM 요소에 대한 참조를 가져온 다음 해당 요소는 삭제됩니다. 이 요소에 대한 참조는 항상 유지되므로 재활용할 수 없습니다.

클로저: 클로저를 잘못 사용하면 일부 변수가 메모리에 남게 됩니다.

3. 함수 및 함수형 프로그래밍

3.1 함수형 프로그래밍이란

함수형 프로그래밍은 프로그램 작성을 위한 방법론인 "프로그래밍 패러다임"입니다.

메인 프로그래밍 패러다임에는 세 가지가 있습니다. 명령형 프로그래밍, 선언형 프로그래밍, 함수형 프로그래밍

명령형 프로그래밍에 비해 함수형 프로그래밍은 실행 프로세스보다는 프로그램 실행 결과에 더 중점을 두고, 계산 결과를 점진적으로 연속시키기 위해 여러 가지 간단한 실행 단위를 사용하는 것을 옹호합니다. , 복잡한 실행 프로세스를 설계하는 대신 복잡한 작업을 계층별로 파생시키세요

3.2 함수형 프로그래밍의 장점과 단점

장점

더 나은 상태 관리: 목적이 무국적이거나 더 적은 수의 상태를 유지하는 것이기 때문입니다. , 우리는 이러한 알려지지 않은 사항을 최소화하고, 코드를 최적화하고, 오류를 줄일 수 있습니다

더 간단한 재사용: 고정 입력->고정 출력, 다른 외부 변수의 영향이 없고 부작용이 없습니다. 이런 식으로 코드를 재사용할 때 내부 구현과 외부 영향을 고려할 필요가 없습니다. 보다 우아한 조합: 광범위하게 말하면 웹 페이지는 다양한 구성 요소로 구성됩니다. 더 작은 의미에서 함수는 여러 개의 작은 함수로 구성될 수도 있습니다. 재사용성이 강화되면 더 강력한 조합

숨겨진 이점이 제공됩니다. 코드 양을 줄이고 유지 관리성을 향상

단점

性能:函数式编程相对于指令式编程,性能绝对是一个短板,因为它往往会对一个方法进行过度包装,从而产生上下文切换的性能开销

资源占用:在 JS 中为了实现对象状态的不可变,往往会创建新的对象,因此,它对垃圾回收所产生的压力远远超过其他编程方式

递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作

3.3 什么是纯函数,它有什么优点

纯函数是对给定的输入返还相同输出的函数,并且要求你所有的数据都是不可变的,即纯函数=无状态+数据不可变

特性:

函数内部传入指定的值,就会返回确定唯一的值

不会造成超出作用域的变化,例如修改全局变量或引用传递的参数

优势:

使用纯函数,我们可以产生可测试的代码

不依赖外部环境计算,不会产生副作用,提高函数的复用性

可读性更强 ,函数不管是否是纯函数 都会有一个语义化的名称,更便于阅读

可以组装成复杂任务的可能性。符合模块化概念及单一职责原则

3.4 什么是组合函数 (compose)

在函数式编程中,有一个很重要的概念就是函数组合,实际上就是把处理的函数数据像管道一样连接起来,然后让数据穿过管道连接起来,得到最终的结果。

组合函数,其实大致思想就是将 多个函数组合成一个函数,c(b(a(a(1)))) 这种写法简写为 compose(c, b, a, a)(x) 。但是注意这里如果一个函数都没有传入,那就是传入的是什么就返回什么,并且函数的执行顺序是和传入的顺序相反的。

var compose = (...funcs) => {
  // funcs(数组):记录的是所有的函数
  // 这里其实也是利用了柯里化的思想,函数执行,生成一个闭包,预先把一些信息存储,供下级上下文使用
  return (x) => {
    var len = funcs.length;
    // 如果没有函数执行,直接返回结果
    if (len === 0) return x;
    if (len === 1) funcs[0](x);
    return funcs.reduceRight((res, func) => {
      return func(res);
    }, x);
  };
};
var resFn = compose(c, b, a, a);
resFn(1);

组合函数的思想,在很多框架中也被使用,例如:redux,实现效果来说是其实和上面的代码等价。

3.5 什么是惰性函数

惰性载入表示函数执行的分支只会在函数第一次掉用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了

惰性函数相当于有记忆的功能一样,当它已经判断了一遍的话,第二遍就不会再判断了。

比如现在要求写一个test函数,这个函数返回首次调用时的new Date().getTime(),注意是首次,而且不允许有全局变量的污染

//一般会这样实现
var test = (function () {
    var t = null;
    return function () {
        if (t) {
            return t;
        }
        t = new Date().getTime();
        return t;
    }
})();
// 用惰性函数实现
var test = function () {
    var t = new Date().getTime();
    test = function () {
        return t;
    }
    return test();
}
console.log(test());
console.log(test());
console.log(test());

3.6 什么是高阶函数

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。

3.7 说说你对函数柯里化的理解

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

函数柯里化的好处:

(1)参数复用:需要输入多个参数,最终只需输入一个,其余通过 arguments 来获取

(2)提前确认:避免重复去判断某一条件是否符合,不符合则 return 不再继续执行下面的操作

(3)延迟运行:避免重复的去执行程序,等真正需要结果的时候再执行

3.8 什么是箭头函数,有什么特征

使用 "箭头" ( => ) 来定义函数. 箭头函数相当于匿名函数, 并且简化了函数定义

箭头函数的特征:

箭头函数没有this, this指向定义箭头函数所处的外部环境

箭头函数的this永远不会变,call、apply、bind也无法改变

箭头函数只能声明成匿名函数,但可以通过表达式的方式让箭头函数具名

箭头函数没有原型prototype

箭头函数不能当做一个构造函数 因为 this 的指向问题

箭头函数没有 arguments 在箭头函数内部访问这个变量访问的是外部环境的arguments, 可以使用 ...代替

3.9 说说你对递归函数的理解

如果一个函数在内部调用自身本身,这个函数就是递归函数

其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

一般来说,递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回

优点:结构清晰、可读性强

缺点:效率低、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。

3.10 什么是尾递归

尾递归,即在函数尾位置调用自身(或是一个尾调用本身的其他函数等等)。

在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出

这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误

3.11 函数传参,传递复杂数据类型和简单数据类型有什么区别

传递复杂数据类型传递的是引用的地址,修改会改变

简单数据类型传递的是具体的值,不会相互影响

/* let a = 8
    function fn(a) {
      a = 9
    }
    fn(a)
    console.log(a) // 8 */
    let a = { age: 8 }
    function fn(a) {
      a.age = 9
    }
    fn(a)
    console.log(a.age) // 9

3.12 函数声明与函数表达式的区别

函数声明: funtion开头,有函数提升

函数表达式: 不是funtion开头,没有函数提升

3.13 什么是函数缓存,如何实现?

概念

函数缓存,就是将函数运算过的结果进行缓存

本质上就是用空间(缓存存储)换时间(计算过程)

常用于缓存数据计算结果和缓存对象

如何实现

实现函数缓存主要依靠闭包、柯里化、高阶函数

应用场景

对于昂贵的函数调用,执行复杂计算的函数

对于具有有限且高度重复输入范围的函数

对于具有重复输入值的递归函数

对于纯函数,即每次使用特定输入调用时返回相同输出的函数

3.14 call、apply、bind三者的异同

  • 共同点 :

都可以改变this指向;

三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window

  • 不同点:

call 和 apply 会调用函数, 并且改变函数内部this指向.

call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递,且apply和call是一次性传入参数,而bind可以分为多次传入

bind是返回绑定this之后的函数

  • 应用场景

call 经常做继承.

apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值

bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指

【相关推荐:JavaScript视频教程web前端

위 내용은 JavaScript 10,000 단어 인터뷰 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.im에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제