Maison  >  Article  >  interface Web  >  Résumé de l'entretien JavaScript de 10 000 mots

Résumé de l'entretien JavaScript de 10 000 mots

WBOY
WBOYavant
2022-10-10 14:43:001353parcourir

Cet article vous apporte des connaissances pertinentes sur JavaScript. Il présente principalement un résumé de quelques questions d'entretien courantes. Examinons-les ensemble. J'espère qu'il sera utile à tout le monde.

Résumé de l'entretien JavaScript de 10 000 mots

[Recommandations associées : Tutoriel vidéo JavaScript, front-end Web]

1 Les bases de JS

1.1 Quels types de données existe-t-il dans Js

Il existe huit types de données dans Js. JavaScript

Type de données de base : non défini, nul, booléen, nombre, chaîne, symbole, BigInt.

Type de données complexe : Objet

Symbol et BigInt sont de nouveaux types de données dans ES6 :

  • Symbol représente un type de données unique et immuable après la création. Il s'agit principalement de résoudre d'éventuels conflits de variables globales.

  • BigInt est une donnée de type numérique qui peut représenter des entiers dans n'importe quel format de précision. L'utilisation de BigInt peut stocker et exploiter en toute sécurité de grands entiers, même si le nombre dépasse la plage d'entiers sûrs que Number peut représenter.

1.2 Parlez de votre compréhension de la zone de tas et de la zone de pile

Dans le système d'exploitation, la mémoire est divisée en zone de pile et en zone de tas

  • La mémoire dans la zone de pile est automatiquement alloué et libéré par le compilateur. Stocker les valeurs des paramètres de fonction, les valeurs des variables locales, etc. Il fonctionne comme une pile dans une structure de données.

  • La mémoire dans la zone du tas est généralement allouée et libérée par le développeur. Si le développeur ne la libère pas, elle peut être récupérée par le mécanisme de récupération de place à la fin du programme.

Dans la structure de données :

  • Dans la structure de données, la méthode d'accès aux données dans la pile est premier entré, dernier sorti.

  • Le tas est une file d'attente prioritaire, qui est triée par priorité. La priorité peut être spécifiée en fonction de la taille.

Méthode de stockage des données

  • Le type de données d'origine est directement stocké dans le segment de données simple de la pile, qui occupe un petit espace et a une taille fixe. Ce sont des données fréquemment utilisées, elles sont donc stockées dans le. stack;

  • Le type de données de référence est un objet stocké dans le tas, qui prend beaucoup de place et n'est pas de taille fixe. S'il est stocké sur la pile, cela affectera les performances du programme ; le type de données de référence stocke un pointeur sur la pile, qui pointe vers l'adresse de départ de l'entité dans le tas. Lorsque l'interpréteur recherche une valeur de référence, il récupère d'abord son adresse sur la pile puis obtient l'entité du tas.

1.3 Quelles sont les méthodes de détection du type de données

Ensuite, la méthode de jugement du type de données peut généralement passer par quatre méthodes courantes : typeof, instanceof, constructeur, toString

Résumé de lentretien JavaScript de 10 000 mots

1.4 Juger le tableau Quelles sont les méthodes ?

  • Porter un jugement via Object.prototype.toString.call()

  • Porter un jugement via la chaîne de prototypes

  • Porter un jugement via Array.isArray() d'ES6

  • Via l'instance de Make Judgement

  • via Array.prototype.isPrototypeOf

1.5 La différence entre null et undefined

Tout d'abord, Undefined et Null sont des types de données de base. Chacun de ces deux types de données de base. Les types de données n'ont qu'une seule valeur, qui est indéfinie et nulle.

undefined signifie indéfini et null signifie un objet vide. Généralement, undefined sera renvoyé lorsqu'une variable est déclarée mais non définie. Null est principalement utilisé pour attribuer des valeurs à certaines variables pouvant renvoyer des objets en guise d'initialisation.

undéfini n'est pas un mot réservé en JavaScript, ce qui signifie que vous pouvez utiliser undéfini comme nom de variable, mais cela est très dangereux et affectera le jugement des valeurs non définies. Nous pouvons obtenir des valeurs sûres et non définies grâce à certaines méthodes, telles que void 0.

Lors de l'utilisation de typeof pour juger ces deux types, la saisie nulle renverra "objet", ce qui est un problème historique. Renvoie vrai lors de l'utilisation d'un double signe égal pour comparer deux types de valeurs et renvoie faux lors de l'utilisation d'un triple signe égal.

1.6 Quel est le résultat de typeof null et pourquoi ? Le résultat de

typeof null est Object.

Dans la première version de JavaScript, toutes les valeurs étaient stockées dans des unités de 32 bits, et chaque unité contenait une petite balise de type (1 à 3 bits) et les données réelles de la valeur actuelle à stocker. Les étiquettes de type sont stockées dans les bits faibles de chaque cellule et il existe cinq types de données :

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

Si le bit le plus bas est 1, la longueur de l'indicateur d'étiquette de type n'est que d'un bit ; si le bit le plus bas est 0, la longueur de l'indicateur d'étiquette de type est de trois bits, fournissant deux bits supplémentaires pour stocker les quatre autres données. types.

Il existe deux types de données spéciaux : la valeur de

  • undefined est (-2)30 (un nombre au-delà de la plage des nombres entiers) ; la valeur de

  • null est un pointeur NULL de code machine (la valeur d'un pointeur nul) ; est tout 0)

Cela signifie que l'étiquette de type de null est également 000, ce qui est la même que l'étiquette de type de l'objet, il sera donc jugé comme objet.

1.7 Pourquoi 0,1+0,2 == 0,3, comment le rendre égal (la précision est perdue)

Les ordinateurs stockent les données en binaire, donc lorsque l'ordinateur calcule 0,1+0,2, il calcule en fait la somme binaire de deux nombres.

Il n'y a qu'un seul type de nombre dans JS : Nombre. Son implémentation suit la norme IEEE 754 et utilise une longueur fixe de 64 bits pour le représenter, qui est un nombre à virgule flottante double double précision standard. En notation scientifique binaire, la partie décimale d'un nombre à virgule flottante double précision ne peut contenir que jusqu'à 52 chiffres, plus le 1 précédent, ce qui signifie en réalité conserver 53 chiffres significatifs. Les chiffres restants doivent être arrondis après le "0". principe d'arrondi à 1".

Selon ce principe, additionner les nombres binaires de 0,1 et 0,2 et les convertir en nombres décimaux donne : 0,30000000000000004. Donc pas égal

La solution est de fixer une marge d'erreur, souvent appelée "précision machine". Pour JavaScript, cette valeur est généralement comprise entre 2 et 52. Dans ES6, l'attribut Number.EPSILON est fourni et sa valeur est comprise entre 2 et 52. Jugez simplement si 0,1+0,2-0,3 est inférieur à Number.EPSILON. alors il peut être jugé comme 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 Comment obtenir une valeur sûre et indéfinie ?

Parce qu'undéfini est un identifiant, il peut être utilisé et attribué en tant que variable, mais cela affectera le jugement normal d'undéfini. L'expression void ___ n'a pas de valeur de retour, le résultat n'est donc pas défini. void ne change pas le résultat de l'expression, cela empêche simplement l'expression de renvoyer une valeur. Vous pouvez donc utiliser void 0 pour ne pas être défini.

1.9 Quel est le résultat du type de NaN ?

NaN signifie "pas un nombre", NaN est une "valeur sentinelle" (valeur régulière avec un objectif spécial), utilisée pour indiquer des conditions d'erreur dans des types numériques, c'est-à-dire "effectuer des mathématiques". L'opération n'a pas réussi, ceci est le résultat renvoyé après un échec."

typeof NaN; // "number"

NaN ​​​​est une valeur spéciale qui n'est pas égale à elle-même et est la seule valeur non réflexive. Ce qu'on appelle irréflexif signifie que NaN n'est égal à personne, y compris lui-même, mais à NaN ! = NaN renverra true

1.10 Quelle est la différence entre les fonctions isNaN et Number.isNaN ?

Une fois que la fonction isNaN a reçu un paramètre, elle essaiera de convertir le paramètre en une valeur numérique. Toute valeur qui ne peut pas être convertie en valeur numérique retournera vrai. Par conséquent, si une valeur non numérique est transmise, il retournera également vrai, ce qui affectera le jugement de NaN.

La fonction Number.isNaN déterminera d'abord si le paramètre entrant est un nombre, elle continuera à déterminer s'il s'agit de NaN. La conversion du type de données ne sera pas effectuée. .

1.11 Quelles sont les règles de conversion pour l'opérateur == ?

Pour ==, si les types des deux parties de comparaison sont différents, une conversion de type sera effectuée. Si x et y sont comparés pour voir s'ils sont identiques, le processus de jugement suivant sera effectué :

Tout d'abord, il sera jugé si les deux types sont identiques. S'ils sont identiques, les tailles des deux. sera comparé ;

Si les types ne sont pas les mêmes, la conversion de type sera effectuée

Il déterminera d'abord si null et indéfini sont comparés. Si c'est le cas, il retournera true

pour déterminer si les deux types sont. chaîne et nombre. Si c'est le cas, il convertira la chaîne en nombre

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

pour déterminer si l'un d'eux est booléen. Si c'est le cas, il convertira le booléen en nombre puis jugera

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

pour juger si l'une des parties est un. objet et l'autre est une chaîne, un nombre ou un symbole. Si tel est le cas, il convertira l'objet en type d'origine puis jugera

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

L'organigramme est le suivant :

Résumé de lentretien JavaScript de 10 000 mots

1.12 Quelles sont les règles de conversion pour convertir d'autres types de valeur en chaînes ?

Types Null et Indéfini, null est converti en "null", undéfini est converti en "indéfini",

Type booléen, vrai est converti en "vrai", faux est converti en "faux".

Les valeurs de type numérique sont converties directement, mais ces très petits et grands nombres utilisent une forme exponentielle.

Les valeurs de type symbole sont directement converties, mais seules les conversions explicites sont autorisées. L'utilisation de conversions implicites générera une erreur.

对普通对象来说,除非自行定义 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 语句、逻辑语句、数学运算逻辑、== 等情况下都可能出现隐式类型转换。

Résumé de lentretien JavaScript de 10 000 mots

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

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的区别

Résumé de lentretien JavaScript de 10 000 mots

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两种状态,计算机对二进制数据进行的运算加减乘除等都是叫位运算,即将符号位共同参与运算的运算。

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

Résumé de lentretien JavaScript de 10 000 mots

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++等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。

Langage faiblement typé : Le langage faiblement typé est également appelé langage de définition faiblement typé, ce qui est l'opposé de la définition fortement typée. Le langage JavaScript est un langage faiblement typé. En termes simples, il s'agit d'un langage dans lequel les types de variables peuvent être ignorés. Par exemple, JavaScript est faiblement typé. En JavaScript, vous pouvez concaténer la chaîne « 12 » et l'entier 3 pour obtenir la chaîne « 123 », et une conversion de type forcée sera effectuée lors de l'ajout.

Comparaison des deux : Les langages fortement typés peuvent être légèrement inférieurs aux langages faiblement typés en termes de vitesse, mais la rigueur apportée par les langages fortement typés peut efficacement aider à éviter de nombreuses erreurs.

1.33 La différence entre les langages interprétés et les langages compilés

(1) Les langages interprétés utilisent un interpréteur spécialisé pour interpréter le programme source ligne par ligne en code machine pour une plate-forme spécifique et l'exécuter immédiatement. Le code est traduit dynamiquement et exécuté ligne par ligne par l'interpréteur lors de son exécution, plutôt que d'être traduit avant l'exécution. Les langages interprétés n'ont pas besoin d'être compilés à l'avance. Ils interprètent directement le code source en code machine et l'exécutent immédiatement, donc tant qu'une certaine plate-forme fournit un interpréteur correspondant, le programme peut être exécuté. Ses caractéristiques sont résumées comme suit

Chaque fois qu'un langage interprété est exécuté, le code source doit être interprété en code machine et exécuté, ce qui est inefficace ;

Tant que la plateforme fournit l'interpréteur correspondant, le code source peut être exécuté, afin de faciliter la transplantation du programme source ;

JavaScript, Python, etc. sont des langages interprétés.

(2) Les langages compilés utilisent un compilateur spécialisé pour compiler le code source du langage de haut niveau pour une plate-forme spécifique en code machine pouvant être exécuté par le matériel de la plate-forme, et le conditionner dans un exécutable pouvant être reconnu par la plate-forme. .Format des procédures sexuelles. Avant qu'un programme écrit dans un langage compilé ne soit exécuté, un processus de compilation spécial est nécessaire pour compiler le code source dans un fichier en langage machine, tel qu'un fichier au format exe. Lors d'une exécution ultérieure, vous pourrez utiliser directement le résultat de la compilation, tel que. comme exécutant le document exe directement. Parce qu'ils ne doivent être compilés qu'une seule fois et n'ont pas besoin d'être compilés lors d'une exécution ultérieure, les langages compilés ont une efficacité d'exécution élevée. Ses caractéristiques sont résumées comme suit :

Compilé en une seule fois dans des fichiers de langage machine liés à la plate-forme, il est séparé de l'environnement de développement lors de son exécution et a une efficacité opérationnelle élevée

Il est lié à une plate-forme spécifique et ne peut généralement pas être ; transplanté sur d'autres plateformes ;

C, C++, etc. appartiennent au langage compilé.

La principale différence entre les deux est la suivante : le premier programme source peut être exécuté sur la plateforme après la compilation, tandis que le second est compilé pendant le fonctionnement. Par conséquent, le premier fonctionne rapidement et le second offre de bonnes performances multiplateformes.

1.34 La différence entre for...in et for...of

for...of est une nouvelle méthode de traversée dans ES6, qui permet de parcourir une structure de données (tableau, objet, etc.) contenant l'interface de l'itérateur et renvoyant La différence entre la valeur de chaque élément et for...in dans ES3 est la suivante

for...of traverses pour obtenir la valeur clé de l'objet, tandis que for...in obtient la clé nom de l'objet ;

for...in traversera l'objet entier. La chaîne de prototypes a de très mauvaises performances et il n'est pas recommandé de l'utiliser, tandis que for...of ne traverse que l'objet actuel et ne traverse pas la chaîne de prototypes. ;

Pour le parcours de tableau, for...in renverra toutes les propriétés énumérables du tableau (y compris celles de la chaîne prototype) Attributs énumérés), for...of renvoie uniquement la valeur d'attribut correspondant à l'indice du tableau ;

Résumé : La boucle for...in est principalement conçue pour parcourir des objets et ne convient pas pour parcourir des tableaux ; la boucle for...of peut être utilisée pour parcourir des tableaux, des objets de type tableau, des chaînes, des ensembles, des cartes. et les objets Générateur.

1.35 La différence entre ajax, axios et fetch

(1) AJAX Ajax est "AsynchronousJavascriptAndXML" (JavaScript et XML asynchrones), qui fait référence à une technologie de développement Web permettant de créer des applications Web interactives. Il s'agit d'une technologie qui permet de mettre à jour des parties d'une page Web sans recharger la page Web entière. Ajax permet aux pages Web de se mettre à jour de manière asynchrone en échangeant une petite quantité de données avec le serveur en arrière-plan. Cela signifie que certaines parties d'une page Web peuvent être mises à jour sans recharger la page entière. Les pages Web traditionnelles (n'utilisant pas Ajax) doivent recharger la page Web entière si le contenu doit être mis à jour. Ses défauts sont les suivants :

  • lui-même est destiné à la programmation MVC et n'est pas conforme à la tendance du MVVM front-end

  • est développé sur la base de XHR natif. L'architecture de XHR elle-même n'est pas claire.

  • ne respecte pas le principe de séparation des préoccupations (Separation of Concerns).

  • Les méthodes de configuration et d'appel sont très déroutantes, et le modèle asynchrone basé sur les événements n'est pas convivial.

(2) Fetch fetch est connu pour remplacer AJAX. Il est apparu dans ES6 et utilise l'objet promise dans ES6. Fetch est conçu sur la base de promesses. La structure du code de Fetch est beaucoup plus simple que celle d'ajax. Fetch n'est pas une encapsulation supplémentaire d'ajax, mais du js natif, qui n'utilise pas l'objet XMLHttpRequest.

Avantages de la récupération :

  • Syntaxe simple, plus sémantique

  • Basé sur l'implémentation standard de Promise, prend en charge async/await

  • Il est de plus bas niveau et fournit des API riches (requête, réponse)

  • Detachment XHR est une nouvelle méthode d'implémentation dans la spécification ES

Inconvénients de la récupération :

  • fetch signale uniquement les erreurs pour les requêtes réseau. 400 et 500 sont traitées comme des requêtes réussies. Lorsque le serveur renvoie 400 et 500 codes d'erreur, il ne sera pas rejeté. Uniquement lorsque des erreurs réseau entraînent l'échec de la requête. sera rejeté.

  • fetch n'a pas de cookies par défaut, vous devez ajouter des éléments de configuration : fetch(url, {credentials: 'include'})

  • fetch ne prend pas en charge l'abandon, ne prend pas en charge le contrôle du délai d'attente, utilisez setTimeout et Implémentation de Promise.reject Le contrôle du délai d'attente n'empêche pas le processus de requête de continuer à s'exécuter en arrière-plan, ce qui entraîne un gaspillage de trafic

  • fetch n'a aucun moyen de surveiller nativement la progression de la requête, mais XHR le peut

(3) Axios Axios est un client HTTP d'encapsulation basé sur des promesses, ses caractéristiques sont les suivantes:

  • Le navigateur initie des demandes XMLHTTPRequests

  • Node initie les demandes HTTP

  • aux demandes et aux retours

  • Demandes et retours Convertir

  • Annuler la demande

  • Convertir automatiquement les données json

  • Le client prend en charge la résistance aux attaques XSRF

  • 1.36 Quels sont les méthodes de parcours des tableaux

Résumé de lentretien JavaScript de 10 000 mots1.37 forEach Quelle est la différence entre la méthode map et la méthode map ? Cette méthode est utilisée pour parcourir le tableau. La différence entre les deux est la suivante : La méthode forEach() exécutera le tableau. fonction fournie pour chaque élément, et l'opération sur les données modifiera le tableau d'origine. Cette méthode n'a pas de valeur de retour ; la méthode

map() ne modifie pas la valeur du tableau d'origine, mais renvoie un nouveau tableau Les valeurs. ​​dans le nouveau tableau se trouvent les valeurs du tableau d'origine après avoir appelé la fonction ; 1.38 Parlez-moi de vos sentiments à propos de Qian Compréhension de la copie et de la copie profonde

Copie superficielle

La copie superficielle fait référence à la création de nouvelles données. Ces données ont une copie exacte de la valeur de l'attribut de données d'origine

Si l'attribut est un type de base, ce qui est copié est la valeur du type de base. Si l'attribut est un type de référence, l'adresse mémoire est copiéeC'est-à-dire que la copie superficielle copie une couche et le type de référence profonde partage l'adresse mémoireCopie superficielle commune :

Object.assign

Object create

    slice
  • concat()
  • Opérateur d'expansion
  • deep copy
  • deep copy ouvre une nouvelle pile Les deux objets sont complétés de la même manière, mais correspondent à deux. différentes adresses. , la modification des propriétés d'un objet ne modifiera pas les propriétés d'un autre objet. Les méthodes de copie profonde courantes sont :

  • _.cloneDeep()

jQuery.extend()

JSON.stringify. ( )

  • Récursion de boucle manuscrite

  • 1.39 Inconvénients de la copie profonde de JSON.stringify
  • S'il y a un objet time dans obj, le résultat de JSON.stringify puis JSON.parse, time sera juste une chaîne La forme, pas la forme de l'objet
  • S'il y a RegExp dans obj, il sera imprimé comme un objet vide

  • S'il y a une fonction ou non définie dans l'objet, elle sera directement supprimée

S'il y a un objet dans json qui est généré par le constructeur, la construction de l'objet sera perdue1.40 Connaissez-vous Lodash ? De quelles API communes dispose-t-il ?

Lodash est une bibliothèque d'utilitaires JavaScript cohérente, modulaire et hautes performances.

_.cloneDeep deep copy

_.reject supprime un élément en fonction des conditions.

_.drop(array, [n=1] ) Fonction : supprime les n premiers éléments du tableau, puis renvoie les parties restantes.1.41 Requête LHS et RHS

LHS (côté gauche ) et RHS (côté droit) sont deux façons pour le moteur JS d'exploiter les variables pendant la phase d'exécution du code. La différence entre les deux est de savoir si le but de l'interrogation des variables est l'affectation de variables ou l'interrogation.

LHS peut être compris comme la variable se trouve sur le côté gauche de l'opérateur d'affectation (=), par exemple, a = 1. Le but de la recherche de la variable a par le moteur actuel est l'affectation de variable. Dans ce cas, le moteur ne se soucie pas de la valeur originale de la variable a, il attribue simplement la valeur 1 à la variable a.

RHS peut être compris comme la variable se trouve à droite de l'opérateur d'affectation (=), par exemple : console.log(a). Le but de la recherche de la variable a par le moteur est d'interroger. valeur réelle correspondant à la variable a. Vous pouvez ensuite l'imprimer.

1.42 En quoi l'inclusion est-elle meilleure que indexOf ?

includes peut détecter NaN, indexOf ne peut pas détecter NaN, inclut utilise Number.isNaN en interne pour correspondre à NaN

1.43 Quelle est la différence entre AMD et CMD ?

1.44 (a == 1 && a == 2 && a == 3) Est-il possible d'être vrai ?

方案一:重写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 中的数组和函数在内存中是如何存储的?

Le stockage de tableaux en JavaScript doit être grossièrement divisé en deux situations :

Les tableaux avec le même type de données allouent un espace mémoire continu

Les tableaux avec un type de données différent utilisent un mappage de hachage pour allouer de l'espace mémoire

Rappel chaleureux : Oui Imaginez que l'espace mémoire continu n'ait besoin que de calculer directement l'emplacement de stockage en fonction de l'index (pointeur). S'il s'agit d'une carte de hachage, vous devez d'abord calculer la valeur d'index, puis s'il y a un conflit dans la valeur d'index, vous devez effectuer une deuxième recherche (vous devez savoir comment le hachage est stocké).

2. Clôture et portée

2.1 Qu'est-ce que la clôture ?

Déclaration officielle : Une fermeture fait référence à une fonction qui a le droit d'accéder à des variables dans le cadre d'une autre fonction.

MDN dit : La fermeture est un type particulier d'objet. Il se compose de deux parties : la fonction et l'environnement dans lequel la fonction est créée. L'environnement se compose de toutes les variables locales qui étaient dans la portée lors de la création de la fermeture.

Réponse approfondie

Lorsque le navigateur chargera la page, il mettra le code dans la mémoire de la pile (ECStack) pour l'exécution. Pousser la fonction dans la pile générera un contexte privé (EC). Ce contexte peut protéger le. variables d'utilisation (AO) à l'intérieur des interférences externes, et si du contenu dans le contexte d'exécution actuel est occupé par du contenu en dehors du contexte, le contexte actuel ne sera pas libéré de la pile, de sorte que les variables et les valeurs des variables dans il peut être sauvegardé, donc je pense que la fermeture est une sorte de mécanisme de préservation et de protection des variables privées internes.

2.2 Le rôle des fermetures

Les fermetures ont deux utilisations courantes

La première utilisation des fermetures est de nous permettre d'accéder aux variables à l'intérieur de la fonction depuis l'extérieur de la fonction ; En utilisant des fermetures, vous pouvez accéder aux variables à l'intérieur de la fonction en externe en appelant la fonction de fermeture en externe. Vous pouvez utiliser cette méthode pour créer des variables privées.

Une autre utilisation de la fermeture consiste à permettre à l'objet variable dans le contexte de fonction dont l'exécution est terminée de continuer à rester en mémoire. Étant donné que la fonction de fermeture conserve une référence à l'objet variable, l'objet variable ne sera pas recyclé.

2.3 Les scénarios de référence des fermetures dans les projets et les problèmes qu'elles entraînent

Dans les projets réels, vous envelopperez le contenu des modules que vous écrivez en fonction des fermetures, afin de pouvoir protéger votre propre code. private pour éviter les conflits avec des variables globales ou un autre code. Ceci est réalisé en utilisant un mécanisme de protection.

Cependant, il n'est pas recommandé d'utiliser trop de fermetures, car l'utilisation d'un contexte qui n'est pas libéré prend de l'espace mémoire dans la pile, et une utilisation excessive peut entraîner des fuites de mémoire.

La façon de résoudre le problème de fuite de mémoire causée par les fermetures est de libérer manuellement la fonction de fermeture après l'avoir utilisée.

2.4 Scénarios d'utilisation des fermetures

retourner une fonction

fonction en tant que paramètre

IIFE (fonction auto-exécutable)

affectation de boucle

Utiliser une fonction de rappel, c'est utiliser une fermeture

prévention de l'étranglement

Currying de fonctions

2.5 Le processus d'exécution de fermeture

forme un contexte privé

poussant dans la pile pour exécuter

une série d'opérations

(1). Portée actuelle Domaine, portée supérieure>)

(2). Initialisez ceci

(3). Initialisez les arguments

(4). Attribuez des paramètres formels

(5).

Lorsque vous rencontrez une variable, vérifiez d'abord si elle vous est privée. Si elle ne l'est pas, recherchez-la en fonction de la chaîne de portée. Si ce n'est pas la chaîne supérieure, continuez la recherche en ligne jusqu'à EC(G). la variable est en fait une chaîne de portée. Le processus d'épissage, la chaîne de requêtes d'épissage est la chaîne de portée.

Dans des circonstances normales, une fois l'exécution du code terminée, le contexte privé est retiré de la pile et recyclé. Cependant, dans des circonstances particulières, si quelque chose dans le contexte privé actuel est occupé par autre chose que le contexte d'exécution une fois l'exécution du contexte privé actuel terminée, le contexte privé actuel ne sera pas libéré de la pile, c'est-à-dire un contexte et la fermeture ne sera pas détruite.

2.6 Types de contexte d'exécution

(1) Contexte d'exécution global

Tout ce qui n'est pas à l'intérieur d'une fonction est un contexte d'exécution global. Il crée d'abord un objet fenêtre global et définit la valeur de ceci égale à ceci. Objet global, il n'y a qu'un seul contexte d'exécution global dans un programme.

(2) Contexte d'exécution de fonction

Lorsqu'une fonction est appelée, un nouveau contexte d'exécution sera créé pour la fonction. Il peut y avoir n'importe quel nombre de contextes de fonction.

(3) Contexte d'exécution de la fonction eval

Le code exécuté dans la fonction eval aura son propre contexte d'exécution, mais la fonction eval n'est pas couramment utilisée et ne sera pas présentée.

2.7 Qu'est-ce que la pile de contexte d'exécution

Le moteur JavaScript utilise la pile de contexte d'exécution pour gérer le contexte d'exécution

Lorsque JavaScript exécute du code, il rencontre d'abord le code global, crée un contexte d'exécution global et le place sur la pile d'exécution. Chaque fois qu'il rencontre un appel de fonction, il crée un nouveau contexte d'exécution pour la fonction et le place en haut de la pile. , le moteur exécutera la fonction en haut de la pile de contexte d'exécution. Lorsque l'exécution de la fonction est terminée, le contexte d'exécution sera extrait de la pile et continuera à exécuter le contexte suivant. Lorsque tout le code a été exécuté, le contexte d'exécution global est extrait de la pile.

2.8 Trois phases de contexte d'exécution

Phase de création → Phase d'exécution → Phase de recyclage

Phase de création

(1) cette liaison

Dans le contexte d'exécution global, cela pointe vers l'objet global (objet fenêtre )

Dans le contexte d'exécution de la fonction, cela dépend de la manière dont la fonction est appelée. S'il est appelé par un objet de référence, alors celui-ci sera défini sur cet objet, sinon la valeur de ceci sera définie sur l'objet global ou non défini

(2) Créer un composant d'environnement lexical

L'environnement lexical est un identifiant - une variable Une structure de données mappée, où l'identifiant fait référence au nom de la variable/fonction et la variable est une référence à l'objet réel ou aux données brutes.

Il y a deux composants à l'intérieur de l'environnement lexical : style gras : enregistreur d'environnement : utilisé pour stocker l'emplacement réel des variables et des déclarations de fonction références d'environnement externe : peut accéder à la portée parent

(3) Créer des composants d'environnement variables

La variable L'environnement est également un environnement lexical, et son enregistreur d'environnement contient la relation de liaison créée par l'instruction de déclaration de variable dans le contexte d'exécution.

Phase d'exécution

Dans cette phase, l'affectation des variables et l'exécution du code sont effectuées

Si le moteur Javascript ne trouve pas la valeur de la variable à l'emplacement réel déclaré dans le code source, alors une valeur indéfinie lui sera attribuée

Phase de recyclage

Exécution Le contexte sort de la pile et attend que la machine virtuelle recycle le contexte d'exécution

2.9 Parlez de votre compréhension de la portée

La portée peut être considérée comme un ensemble de règles Cet ensemble de règles. Les règles sont utilisées pour gérer le fonctionnement du moteur dans la portée actuelle et l'imbrication. La sous-portée de la recherche de variable basée sur le nom de l'identifiant.

En termes simples, la portée est la plage effective d'une variable. Les données variables peuvent être lues et écrites dans un certain espace, et cet espace est la portée de la variable.

(1) Portée globale

Le code JS écrit directement dans la balise script est dans la portée globale. Les variables déclarées dans la portée globale sont appelées variables globales (variables définies en dehors du niveau bloc).

Les variables globales peuvent être utilisées n'importe où dans le monde ; les variables de la portée locale ne sont pas accessibles dans la portée globale.

La portée globale est créée à l'ouverture de la page et détruite à la fermeture de la page.

Toutes les propriétés de l'objet window ont une portée globale

Les variables et fonctions globales déclarées par les commandes var et function sont des propriétés et méthodes de l'objet window

Les variables globales déclarées par la commande let, la commande const et la commande class ne le sont pas. appartiennent à l'objet fenêtre Attributs

(2) Portée de la fonction (portée locale)

Lorsqu'une fonction est appelée, une portée de fonction est créée Une fois la fonction exécutée, la portée est détruite. Chaque fois qu'une fonction est appelée, une nouvelle portée de fonction est créée et elles sont indépendantes les unes des autres.

Les variables globales sont accessibles dans la portée de la fonction, mais les variables à l'intérieur de la fonction ne sont pas accessibles en dehors de la fonction.

Lors de l'utilisation d'une variable dans la portée de la fonction, elle recherchera d'abord dans sa propre portée, s'il y en a une, elle sera utilisée directement. Sinon, elle recherchera dans la portée précédente jusqu'à ce qu'elle trouve la portée globale. est toujours dans la portée globale. S'il n'est pas trouvé, une erreur sera signalée.

(3) Portée au niveau du bloc

Avant ES6, JavaScript utilisait la portée de la fonction + la portée lexicale, et ES6 introduisait la portée au niveau du bloc.

Tout ensemble d'instructions entourées d'accolades {} appartient à un bloc. Les variables déclarées à l'aide de let et const dans un bloc ne sont pas accessibles de l'extérieur. Ce type de règle de portée est appelé portée au niveau du bloc.

Les variables déclarées via des déclarations var ou de fonction créées en mode non strict n'ont pas de portée au niveau du bloc.

(4) Portée lexicale

La portée lexicale est une portée statique, peu importe où la fonction est appelée ou comment elle est appelée, sa portée lexicale n'est déterminée par la fonction que lorsqu'elle est déclarée.

La phase d'analyse lexicale de la compilation peut essentiellement savoir où et comment tous les identifiants sont déclarés, afin de pouvoir prédire comment les retrouver lors de l'exécution.

En d'autres termes, la portée lexicale signifie que vous avez décidé de la portée de la variable lorsque vous écrivez le code.

2.10 Qu'est-ce qu'une chaîne de portée

Lors de l'utilisation d'une variable dans js, le moteur js essaiera d'abord de trouver la variable dans la portée actuelle. Si elle n'est pas trouvée, il recherchera dans sa portée supérieure, et. ainsi de suite jusqu'à ce que la variable soit trouvée ou ait atteint la portée globale. Une telle structure de chaîne d'accès à la portée variable est appelée une chaîne de portée

Réponse approfondie

.

Une chaîne de portées est essentiellement une liste de pointeurs vers des objets variables. Un objet variable est un objet qui contient toutes les variables et fonctions de l'environnement d'exécution. L’extrémité avant de la chaîne de portées est toujours l’objet variable du contexte d’exécution actuel. L'objet variable du contexte d'exécution global (c'est-à-dire l'objet global) est toujours le dernier objet de la chaîne de portée.

2.11 Le rôle de la chaîne de portée

Le rôle de la chaîne de portée est d'assurer un accès ordonné à toutes les variables et fonctions auxquelles l'environnement d'exécution a accès Grâce à la chaîne de portée, les variables de l'environnement externe peuvent. être accessible et fonctions.

2.12 Scénarios d'application courants de portée

L'un des scénarios d'application courants de portée est la modularisation.

Étant donné que JavaScript ne prend pas en charge nativement la modularisation, cela a conduit à de nombreux problèmes alléchants, tels qu'une pollution à l'échelle mondiale et des conflits de noms de variables, une structure de code gonflée et une faible réutilisabilité. Avant l'introduction de la solution de modularisation formelle, afin de résoudre ce type de problème, les développeurs pensaient utiliser la portée des fonctions pour créer des modules.

2.13 Vous parlez de pré-analyse en Js ?

Lorsque le moteur JS exécute un code, il fonctionnera selon les étapes suivantes :

1 Promouvoir la déclaration de la variable au début de la portée actuelle. Seule la déclaration sera promue, pas l'affectation

.

2. Promouvoir la déclaration de fonction au premier plan de la portée actuelle. Seule la déclaration sera promue, mais l'appel ne sera pas promu

3. Promouvoir d'abord la fonction, puis promouvoir var

2.14 Quelle est la différence entre. promotion variable et promotion de fonction ?

Promotion des variables

En termes simples, le moteur sera précompilé avant que le code JavaScript ne soit exécuté. Lors de la précompilation, la déclaration de variable et la déclaration de fonction seront promues en haut de leurs portées correspondantes. uniquement Monter au sommet de la portée de la fonction Lorsqu'une variable définie à l'intérieur de la fonction est la même qu'à l'extérieur, la variable dans le corps de la fonction sera élevée au sommet.

Promotion de fonction

La promotion de fonction n'améliorera que l'écriture déclarative de fonction, l'écriture d'expression de fonction n'existe pas de promotion de fonction

La priorité de la promotion de fonction est supérieure à la priorité de la promotion de variable, c'est-à-dire que la promotion de fonction est supérieure à la promotion de variable

2.14 Comment étendre la chaîne de périmètre ?

La chaîne de portée peut être étendue.

Chaîne à portée étendue : Il n'existe que deux types d'environnements d'exécution, global et local (fonction). Cependant, certaines instructions peuvent ajouter temporairement un objet variable à l'avant de la chaîne de portée, qui sera supprimé une fois le code exécuté.

Plus précisément, lorsque ces deux instructions sont exécutées, la chaîne de portée sera renforcée

bloc try-catch de l'instruction catch : un nouvel objet variable sera créé, contenant la déclaration de l'objet d'erreur généré.

with instruction : L'instruction with ajoutera l'objet spécifié à la chaîne de portée.

2.15 Mécanisme de récupération de place du navigateur

(1) Cycle de vie de la mémoire

La mémoire allouée dans l'environnement JS a généralement le cycle de vie suivant :

Allocation de mémoire : lorsque nous déclarons des variables et des fonctions, des objets, le système leur allouera automatiquement de la mémoire

Utilisation de la mémoire : c'est-à-dire lire et écrire de la mémoire, c'est-à-dire utiliser des variables, des fonctions, etc.

Recyclage de la mémoire : après utilisation, la mémoire qui n'est plus utilisée sera automatiquement recyclée par garbage collection

Les variables globales ne sont généralement pas recyclées. Généralement, les valeurs des variables locales, si elles ne sont plus utilisées, seront automatiquement recyclées

(2) Le concept de garbage collection

Garbage collection : Lorsque JavaScript le code est en cours d'exécution, l'espace mémoire doit être alloué pour stocker les variables et la valeur. Lorsque les variables ne participent plus à l'opération, le système doit récupérer l'espace mémoire occupé. Il s'agit d'un garbage collection.

Mécanisme de recyclage :

Javascript dispose d'un mécanisme de garbage collection automatique, qui va régulièrement libérer la mémoire occupée par les variables et les objets qui ne sont plus utilisés, le principe est de retrouver les variables qui ne sont plus utilisées, puis de libérer la mémoire. occupé par eux.

Il existe deux types de variables en JavaScript : les variables locales et les variables globales. Le cycle de vie des variables globales continuera jusqu'à ce que la page soit déchargée ; tandis que les variables locales sont déclarées dans une fonction, et leur cycle de vie commence depuis l'exécution de la fonction jusqu'à la fin de l'exécution de la fonction. Au cours de ce processus, les variables locales seront stockées. leurs valeurs dans le tas ou la pile , lorsque l'exécution de la fonction se termine, ces variables locales ne sont plus utilisées et l'espace qu'elles occupent sera libéré.

Cependant, lorsque des variables locales sont utilisées par des fonctions externes, l'une des situations est la fermeture. Une fois l'exécution de la fonction terminée, les variables extérieures à la fonction pointent toujours vers les variables locales à l'intérieur de la fonction. utilisé, il n’est donc pas recyclé.

(3) Méthodes de collecte des déchets

  • 1. Méthode de comptage de référence

    .

C'est relativement rarement utilisé, l'algorithme de comptage de références utilisé par IE. Le comptage de références garde une trace du nombre de fois où chaque valeur est référencée. Lorsqu'une variable est déclarée et qu'un type référence est affecté à la variable, le nombre de références à la valeur est 1. A l'inverse, si la variable contenant une référence à cette valeur obtient une autre valeur, le nombre de références à cette valeur est décrémenté de un. Lorsque le nombre de références devient 0, cela signifie que la variable n'a aucune valeur. Par conséquent, l'espace mémoire occupé par cette variable sera libéré lors de sa prochaine exécution pendant la période de recyclage machine.

Cette méthode posera le problème de la référence circulaire : par exemple : obj1 et obj2 se réfèrent l'un à l'autre via des attributs, et le nombre de références des deux objets est de 2. Lors de l'utilisation du comptage de boucles, puisqu'après l'exécution de la fonction, les deux objets quittent la portée et que l'exécution de la fonction se termine, obj1 et obj2 continueront d'exister, donc leur nombre de références ne sera jamais 0, ce qui provoquera une référence circulaire.

  • 2. Méthode d'effacement des marques

Les navigateurs modernes n'utilisent plus d'algorithmes de comptage de références.

La plupart des navigateurs modernes sont basés sur des algorithmes améliorés basés sur l'algorithme mark et clear, et l'idée générale est la même.

Le nettoyage des marques est une méthode de récupération de place courante dans les navigateurs. Lorsqu'une variable entre dans l'environnement d'exécution, elle est marquée comme « entrant dans l'environnement ». Les variables marquées comme « entrant dans l'environnement » ne peuvent pas être recyclées car elles sont en cours d'utilisation. Lorsqu'une variable quitte l'environnement, elle sera marquée comme "quittant l'environnement", et la variable marquée comme "quittant l'environnement" sera libérée de la mémoire.

Le garbage collector marquera toutes les variables stockées en mémoire lors de son exécution. Ensuite, il supprime les variables de l'environnement et les balises référencées par les variables de l'environnement. Les variables marquées après cela seront considérées comme des variables à supprimer car les variables de l'environnement ne peuvent plus accéder à ces variables. enfin. Le garbage collector termine le travail de nettoyage de la mémoire, détruit les valeurs marquées et récupère l'espace mémoire qu'elles occupent.

(4) Comment réduire la collecte des ordures

Bien que le navigateur puisse collecter automatiquement les ordures, lorsque le code est plus complexe, le coût de la collecte des ordures est relativement élevé, la collecte des ordures doit donc être réduite autant que possible.

Optimiser les tableaux : lors de l'effacement d'un tableau, le moyen le plus simple est de lui attribuer une valeur de [ ], mais en même temps, un nouvel objet vide sera créé. Vous pouvez définir la longueur du tableau sur 0 pour y parvenir. Objectif de vider le tableau.

Optimiser les objets : Réutilisez autant que possible les objets qui ne sont plus utilisés, définissez-les sur null et recyclez-les dès que possible.

Optimiser les fonctions : Si l'expression de fonction dans la boucle peut être réutilisée, essayez de la placer en dehors de la fonction.

(5) Qu'est-ce qu'une fuite de mémoire ?

fait référence à l'échec du programme à libérer de la mémoire qui n'est plus utilisée en raison d'une négligence ou d'une erreur

(6) Quelles situations provoqueront des fuites de mémoire

Les quatre situations suivantes provoqueront des fuites de mémoire :

Variable globale inattendue : En raison de l'utilisation de variables non déclarées, une variable globale a été accidentellement créée, laissant cette variable en mémoire et incapable d'être recyclée.

Minuterie ou fonction de rappel oubliée : définissez la minuterie setInterval et oubliez de l'annuler. Si la fonction de boucle a une référence à une variable externe, alors la variable restera en mémoire et ne pourra pas être recyclée.

Référence depuis le DOM : Obtenez une référence à un élément DOM, puis l'élément est supprimé La référence à cet élément étant toujours conservée, elle ne peut pas être recyclée.

Fermetures : une mauvaise utilisation des fermetures entraîne le maintien de certaines variables en mémoire.

3. Fonctions et programmation fonctionnelle

3.1 Qu'est-ce que la programmation fonctionnelle

La programmation fonctionnelle est un « paradigme de programmation » (paradigme de programmation), une méthodologie d'écriture de programmes

Principal Il existe trois paradigmes de programmation : programmation impérative, programmation déclarative et programmation fonctionnelle

Par rapport à la programmation impérative, la programmation fonctionnelle met davantage l'accent sur les résultats de l'exécution du programme plutôt que sur le processus d'exécution et préconise l'utilisation d'un certain nombre d'unités d'exécution simples pour rendre progressivement les résultats de calcul continus. , dérivez des opérations complexes couche par couche au lieu de concevoir un processus d'exécution complexe

3.2 Avantages et inconvénients de la programmation fonctionnelle

Avantages

Meilleure gestion de l'état : parce que son but est d'être apatride, ou en parlant de moins d'états , nous pouvons minimiser ces inconnues, optimiser le code et réduire les erreurs

Réutilisation plus simple : entrée fixe-> sortie fixe, aucune influence d'autres variables externes et aucun effet secondaire. De cette façon, lorsque le code est réutilisé, il n'est pas nécessaire de prendre en compte sa mise en œuvre interne et ses influences externes. Une combinaison plus élégante : d'une manière générale, une page Web est composée de différents composants. En termes plus simples, une fonction peut également être composée de plusieurs petites fonctions. Une réutilisabilité plus forte apporte des combinaisons plus puissantes

avantages cachés. Réduisez la quantité de code et améliorez la maintenabilité

Inconvénients

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

资源占用:在 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前端

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer