Maison > interface Web > js tutoriel > le corps du texte

10 questions de base que vous devez maîtriser en JavaScript

coldplay.xixi
Libérer: 2020-10-13 13:47:27
avant
2008 Les gens l'ont consulté

10 questions de base que vous devez maîtriser en JavaScript

JavaScript est un langage de programmation côté client. Utilisé par plus de 90% des sites Web dans le monde, c'est l'un des langages de programmation les plus couramment utilisés au monde. Par conséquent, la chronique JavaScript d'aujourd'hui abordera 10 questions courantes sur JavaScript.

1. Comment supprimer un élément spécifique d'un tableau

Idée : utilisez d'abord indexOf pour trouver le 索引(index) de l'élément du tableau que vous souhaitez supprimer, puis utilisez le splice méthode pour supprimer L'élément correspondant à cet index.

splice() est une fonction impure qui modifie le contenu d'un tableau en supprimant des éléments existants et/ou en ajoutant de nouveaux éléments. Le deuxième paramètre de

const array = [2, 5, 9]

const index = array.indexOf(5)
if (index > -1) {
  array.splice(index, 1)
}

console.log(array)
// [ 2, 9 ]复制代码
Copier après la connexion

splice est le nombre d'éléments à supprimer. Notez que splice modifie le tableau en place et renvoie un nouveau tableau contenant les éléments supprimés.

Ensuite, nous pouvons l'améliorer. Vous trouverez ci-dessous deux fonctions, la première fonction supprime une seule correspondance (c'est-à-dire supprime la première correspondance [2,5,9,1,5,8,5] de 5), tandis que la deuxième fonction supprime toutes les correspondances :

// 仅删除第一个匹配项
function removeItemOnce (arr, value) {
  let index = arr.indexOf(value)
  if (index > -1) {
    arr.splice(index, 1)
  }
  return arr
}

// 删除所有匹配项
function removeItemAll (arr, value) {
  let i = 0
  while(i < arr.length) {
    if (arr[i] === value) {
      arr.splice(i, 1)
    } else {
      ++i
    }
  }
}复制代码
Copier après la connexion

Supprimer l'élément i à l'index dans le tableau :

Supprimer l'élément à l'index i dans le tableau :

array.splice(i, 1)复制代码
Copier après la connexion

Si vous souhaitez supprimer la valeur du tableau Pour chaque élément de number, vous pouvez le faire ceci :

for (let i = array.length - 1; i>=0; i--) {
  if (array[i] === number) {
    array.splice(i, 1)
  }
}复制代码
Copier après la connexion

Si vous souhaitez uniquement que l'élément à l'index i n'existe plus, mais que vous ne souhaitez pas modifier l'index des autres éléments :

delete array[i]复制代码
Copier après la connexion

2 Comment rediriger. les utilisateurs d'une page à l'autre en utilisant jQuery ou JS pur

jQuery n'est pas requis ,window.location.replace(…) Idéal pour simuler des redirections HTTP. window.location.replace(...) est préférable à l'utilisation de window.location.href car replace() ne conserve pas la page d'origine dans l'historique de la session, ce qui signifie que l'utilisateur ne sera pas coincé avec un bouton de retour interminable.

Si vous souhaitez simuler un clic sur un lien, vous pouvez utiliser location.href, si vous souhaitez simuler une redirection HTTP, utilisez location.replace.

Exemple :

//模拟HTTP重定向
window.location.replace("http://stackoverflow.com")

// 模拟单击链接
window.location.href = "http://stackoverflow.com"复制代码
Copier après la connexion

Vous pouvez également faire ceci :

$(location).attr(&#39;href&#39;, &#39;http://stackoverflow.com&#39;)复制代码
Copier après la connexion

3. Comment fonctionnent les fermetures JavaScript

Fermer un package est une fonction et une référence à la portée en dehors de cette fonction (l'environnement lexical), qui fait partie de chaque contexte d'exécution (la pile) et constitue le mappage entre les identifiants (c'est-à-dire les noms de variables locales) et les valeurs.

Chaque fonction en JavaScript maintient une référence à son environnement lexical externe. Cette référence permet de configurer le contexte d'exécution créé lors de l'appel de la fonction. Chaque fois qu'une fonction est appelée, cette référence permet au code dans la fonction d'afficher les variables déclarées en dehors de la fonction.

Dans le code ci-dessous, inner forme une fermeture avec l'environnement lexical du contexte d'exécution créé lorsque foo est appelé, et cache la variable secret de l'extérieur :

function foo() {
  const secret = Math.trunc(Math.random()*100)
  return function inner() {
    console.log(`The secret number is ${secret}.`)
  }
}
const f = foo() // secret 不能从foo 外部直接访问
f() // 访问 secret 的唯一办法就是调用 f复制代码
Copier après la connexion

En d'autres termes, en JavaScript, les fonctions portent une référence à un état privé auquel elles seules (et toute autre fonction déclarée dans le même environnement lexical) peuvent accéder. Cet état est invisible pour les appelants de fonctions, ce qui constitue un excellent mécanisme de masquage et d'encapsulation des données.

N'oubliez pas que les fonctions en JavaScript peuvent être transmises comme des variables, ce qui signifie que ces paires de fonctions et d'états peuvent être transmises dans le programme : de la même manière que le passage d'instances de classes en C++.

Si JavaScript n'avait pas de fermetures, davantage d'états devraient être transmis explicitement entre les fonctions, ce qui rendrait les listes de paramètres plus longues et le code plus redondant.

Donc, si vous souhaitez qu'une fonction ait toujours accès à un état privé, vous pouvez utiliser une fermeture. Nous souhaitons souvent associer un état à une fonction. Par exemple, en Java ou C++, lorsque vous ajoutez des variables et des méthodes d'instance privées à une classe, vous associez un état à une fonctionnalité.

En C et dans la plupart des autres langages de programmation, après le retour d'une fonction, toutes les variables locales ne sont plus accessibles car la pile est détruite. En JavaScript, si vous déclarez une fonction au sein d'une autre fonction, les variables locales de la fonction externe restent accessibles après retour. De cette façon, dans le code ci-dessus, secret est toujours disponible en interne pour l'objet fonction après le retour de foo.

Les fermetures sont utiles lorsque vous avez besoin d'un état privé associé à une fonction. Il s'agit d'un scénario très courant, JavaScript n'avait pas de syntaxe de classe jusqu'en 2015, il n'avait toujours pas de syntaxe de champ privé, les fermetures répondaient à ce besoin.

Variables d'instance privée

Dans l'exemple suivant, la fonction toString masque certains détails de la classe Car.

function Car(manufacturer, model, year, color) {
  return {
    toString() {
      return `${manufacturer} ${model} (${year}, ${color})`
    }
  }
}
const car = new Car(&#39;Aston Martin&#39;,&#39;V8 Vantage&#39;,&#39;2012&#39;,&#39;Quantum Silver&#39;)
console.log(car.toString())复制代码
Copier après la connexion

Programmation Fonctionnelle

Dans le code ci-dessous, la fonction inner cache fn et args.

function curry(fn) {
  const args = []
  return function inner(arg) {
    if(args.length === fn.length) return fn(...args)
    args.push(arg)
    return inner
  }
}

function add(a, b) {
  return a + b
}

const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5复制代码
Copier après la connexion

Programmation orientée événement

Dans le code suivant, la fonction onClick masque la variable BACKGROUND_COLOR.

const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = &#39;rgba(200,200,242,1)&#39;

function onClick() {
  $(&#39;body&#39;).style.background = BACKGROUND_COLOR
}

$(&#39;button&#39;).addEventListener(&#39;click&#39;, onClick)复制代码
Copier après la connexion
rrree

Modulaire

在下面的示例中,所有实现细节都隐藏在一个立即执行的函数表达式中。函数ticktoString隐藏了私有状态和函数,它们需要完成自己的工作。闭包使我们能够模块化和封装我们的代码。

let namespace = {};

(function foo(n) {
  let numbers = []
  function format(n) {
    return Math.trunc(n)
  }
  function tick() {
    numbers.push(Math.random() * 100)
  }
  function toString() {
    return numbers.map(format)
  }
  n.counter = {
    tick,
    toString
  }
}(namespace))

const counter = namespace.counter
counter.tick()
counter.tick()
console.log(counter.toString())复制代码
Copier après la connexion

事例 1:

此示例演示局部变量未在闭包中复制。 闭包保留对原始变量本身的引用。 似乎即使外部函数退出后,堆栈仍在内存中保留。

function foo () {
  let x = 42
  let inner = function () {
    console.log(x)
  }
  x = x + 1
  return inner
}

let f = foo()
f()复制代码
Copier après la connexion

事例 2:

在下面的代码中,三种方法logincrementupdate 都在同一词法环境闭包中。

function createObject() {
  let x = 42;
  return {
    log() { console.log(x) },
    increment() { x++ },
    update(value) { x = value }
  }
}

const o = createObject()
o.increment()
o.log() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42复制代码
Copier après la connexion

事例 3:

如果使用的变量是使用var声明的,需要注意的一点是,使用var声明的变量被提升。 由于引入了letconst,这在现代JavaScript 中几乎没有问题。

在下面的代码中,每次循环中,都会创建一个新的inner函数,变量i被覆盖,但是因var会让 i 提升到函数的顶部,所以所有这些inner函数覆盖的都是同一个变量,这意味着i(3)的最终值被打印了三次。

function foo () {
  var result = []
  for (var i = 0; i < 3; i++) {
    result.push(function inner () {
      console.log(i)
    })
  }
  return result
}
const result = foo()

for(var i = 0; i < 3; i++) {
  result[i]()
}
// 3 3 3复制代码
Copier après la connexion

最后一点:

  • 每当在JavaScript中声明函数时,都会创建一个闭包。

  • 从一个函数内部返回另一个函数是闭包的经典例子,因为外部函数内部的状态对于返回的内部函数是隐式可用的,即使外部函数已经完成执行。

  • 只要在函数内使用eval(),就会使用一个闭包。eval的文本可以引用函数的局部变量,在非严格模式下,甚至可以通过使用eval(&#39;var foo = &#39;)创建新的局部变量。

  • 当在函数内部使用new Function()(Function constructor)时,它不会覆盖其词法环境,而是覆盖全局上下文。新函数不能引用外部函数的局部变量。
  • 在JavaScript中,闭包类似于在函数声明时保留对作用域的引用(而不是复制),后者又保留对其外部作用域的引用,以此类推,一直到作用域链顶端的全局对象。
  • 声明函数时创建一个闭包。 当调用函数时,此闭包用于配置执行上下文。

  • 每次调用函数时都会创建一组新的局部变量。

JavaScript 中的每个函数都维护与其外部词法环境的链接。 词法环境是所有名称的映射(例如,变量,参数)及其范围内的值。因此,只要看到function关键字,函数内部的代码就可以访问在函数外部声明的变量。

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // 16
  }

  bar(10);
}

foo(2);复制代码
Copier après la connexion

上面输出结果是16,参数x和变量tmp都存在于外部函数foo的词法环境中。函数bar及其与函数foo的词法环境的链接是一个闭包。

函数不必返回即可创建闭包。 仅仅凭借其声明,每个函数都会在其封闭的词法环境中关闭,从而形成一个闭包。

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // 16
  }
}

var bar = foo(2);
bar(10); // 16
bar(10); // 17复制代码
Copier après la connexion

上面还是打印16,因为bar内的代码仍然可以引用参数x和变量tmp,即使它们不再直接的作用域内。

但是,由于tmp仍然在bar的闭包内部徘徊,因此可以对其进行递增。 每次调用bar时,它将增加1

闭包最简单的例子是这样的:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();复制代码
Copier après la connexion

当调用一个JavaScript函数时,将创建一个新的执行上下文ec。连同函数参数和目标对象,这个执行上下文还接收到调用执行上下文的词法环境的链接,这意味着在外部词法环境中声明的变量(在上面的例子中,ab)都可以从ec获得。

每个函数都会创建一个闭包,因为每个函数都有与其外部词法环境的链接。

注意,变量本身在闭包中是可见的,而不是副本。

4. use strict 在 JavaScript 中做了什么,背后的原因是什么

引用一些有趣的部分:

严格模式是ECMAScript 5中的一个新特性,它允许我们将程序或函数放置在严格的操作上下文中。这种严格的上下文会防止某些操作被执行,并引发更多异常。

严格模式在很多方面都有帮助:

  • 它捕获了一些常见的编码漏洞,并抛出异常。
  • 当采取相对不安全的操作(例如访问全局对象)时,它可以防止错误或抛出错误。
  • 它禁用令人困惑或考虑不周到的特性。

另外,请注意,我信可以将“strict mode”应用于整个文件,也可以仅将其用于特定函数。

// Non-strict code...

(function(){
  "use strict";

  // Define your library strictly...
})();

// Non-strict code... 
复制代码
Copier après la connexion

如果是在混合使用旧代码和新代码的情况,这可能会有所帮助。它有点像在Perl中使用的“use strict”。通过检测更多可能导致损坏的东西,帮助我们减少更多的错误。

现在所有主流浏览器都支持严格模式。

在原生ECMAScript模块(带有importexport语句)和ES6类中,严格模式始终是启用的,不能禁用。

5.如何检查字符串是否包含子字符串?

ECMAScript 6 引入了string .prototype.include

const string = "foo";
const substring = "oo";

console.log(string.includes(substring));复制代码
Copier après la connexion

不过,IE 不支持 includes。在 CMAScript 5或更早的环境中,使用String.prototype.indexOf。如果找不到子字符串,则返回-1:

var string = "foo";
var substring = "oo";

console.log(string.indexOf(substring) !== -1);复制代码
Copier après la connexion

为了使其在旧的浏览器中运行,可以使用这种polyfill

if (!String.prototype.includes) {
  String.prototype.includes = function(search, start) {
    &#39;use strict&#39;;
    if (typeof start !== &#39;number&#39;) {
      start = 0;
    }

    if (start + search.length > this.length) {
      return false;
    } else {
      return this.indexOf(search, start) !== -1;
    }
  };
}复制代码
Copier après la connexion

6. var functionName = function() {} 与 function functionName() {}

不同之处在于functionOne是一个函数表达式,因此只在到达这一行时才会定义,而functionTwo是一个函数声明,在它周围的函数或脚本被执行(由于提升)时就定义。

如,函数表达式

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};复制代码
Copier après la connexion

函数声明:

// "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}复制代码
Copier après la connexion

过去,在不同的浏览器之间,在块中定义的函数声明的处理是不一致的。严格模式(在ES5中引入)解决了这个问题,它将函数声明的范围限定在其封闭的块上。

&#39;use strict&#39;;    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError复制代码
Copier après la connexion

function abc(){}也具有作用域-名称abc在遇到该定义的作用域中定义。 例:

function xyz(){
  function abc(){};
  // abc 在这里定义...
}
// ...不是在这里复制代码
Copier après la connexion

如果想在所有浏览器上给函数起别名,可以这么做:

function abc(){};
var xyz = abc;复制代码
Copier après la connexion

在本例中,xyz和abc都是同一个对象的别名

console.log(xyz === abc) // true复制代码
Copier après la connexion

它的名称是自动分配的。但是当你定义它的时候

var abc = function(){};
console.log(abc.name); //  ""复制代码
Copier après la connexion

它的name称为空,我们创建了一个匿名函数并将其分配给某个变量。使用组合样式的另一个很好的理由是使用简短的内部名称来引用自身,同时为外部用户提供一个长而不会冲突的名称:

// 假设 really.long.external.scoped 为 {}
really.long.external.scoped.name = function shortcut(n){
  // 它递归地调用自己:
  shortcut(n - 1);
  // ...
  // 让它自己作为回调传递::
  someFunction(shortcut);
  // ...
}复制代码
Copier après la connexion

在上面的例子中,我们可以对外部名称进行同样的操作,但是这样做太笨拙了(而且速度更慢)。另一种引用自身的方法是arguments.callee,这种写法也相对较长,并且在严格模式中不受支持。

实际上,JavaScript对待这两个语句是不同的。下面是一个函数声明:

function abc(){}复制代码
Copier après la connexion

这里的abc可以定义在当前作用域的任何地方:

// 我们可以在这里调用
abc(); 

// 在这里定义
function abc(){}

// 也可以在这里调用 
abc(); 
复制代码
Copier après la connexion

此外,尽管有 return 语句,也可以提升:

// 我们可以在这里调用
abc(); 
return;
function abc(){}复制代码
Copier après la connexion

下面是一个函数表达式:

var xyz = function(){};复制代码
Copier après la connexion

这里的xyz是从赋值点开始定义的:

// 我们不可以在这里调用
xyz(); 

// 在这里定义 xyz
xyz = function(){}

// 我们可以在这里调用
xyz(); 
复制代码
Copier après la connexion

函数声明与函数表达式之间存在差异的真正原因。

var xyz = function abc(){};
console.log(xyz.name); // "abc"复制代码
Copier après la connexion

就个人而言,我们更喜欢使用函数表达式声明,因为这样可以控制可见性。当我们像这样定义函数时:

var abc = function(){};复制代码
Copier après la connexion

我们知道,如果我们没有在作用域链的任何地方定义abc,那么我们是在全局作用域内定义的。即使在eval()内部使用,这种类型的定义也具有弹性。而定义:

function abc(){};复制代码
Copier après la connexion

取决于上下文,并且可能让你猜测它的实际定义位置,特别是在eval()的情况下,—取决于浏览器。

7.如何从 JavaScript 对象中删除属性?

我们可以这样删除对象的属性:

delete myObject.regex;
// 或者
delete myObject[&#39;regex&#39;];
//  或者
var prop = "regex";
delete myObject[prop];复制代码
Copier après la connexion

事例:

var myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI",
    "regex": "^http://.*"
};
delete myObject.regex;

console.log(myObject);复制代码
Copier après la connexion

JavaScript 中的对象可以看作键和值之间的映射。delete操作符用于一次删除一个键(通常称为对象属性)。

var obj = {
  myProperty: 1    
}
console.log(obj.hasOwnProperty(&#39;myProperty&#39;)) // true
delete obj.myProperty
console.log(obj.hasOwnProperty(&#39;myProperty&#39;)) // false复制代码
Copier après la connexion

delete 操作符不是直接释放内存,它不同于简单地将nullundefined值赋给属性,而是将属性本身从对象中删除。

注意,如果已删除属性的值是引用类型(对象),而程序的另一部分仍然持有对该对象的引用,那么该对象当然不会被垃圾收集,直到对它的所有引用都消失。

delete只对其描述符标记为configurable的属性有效。

8. JS 的比较中应使用哪个等于运算符(== vs ===)?

严格相等运算符(===)的行为与抽象相等运算符(==)相同,除非不进行类型转换,而且类型必须相同才能被认为是相等的。

==运算符会进行类型转换后比较相等性。 ===运算符不会进行转换,因此如果两个值的类型不同,则===只会返回false。

JavaScript有两组相等运算符:===!==,以及它们的孪生兄弟==!=。如果这两个操作数具有相同的类型和相同的值,那么===的结果就是 true,而!==的结果就是 false

下面是一些事例:

&#39;&#39; == &#39;0&#39;           // false
0 == &#39;&#39;             // true
0 == &#39;0&#39;            // true

false == &#39;false&#39;    // false
false == &#39;0&#39;        // true

false == undefined  // false
false == null       // false
null == undefined   // true

&#39; \t\r\n &#39; == 0     // true复制代码
Copier après la connexion

上面有些看起来会挺困惑的,所以尽量还是使用严格比较运算符(===)。对于引用类型,=====操作一致(特殊情况除外)。

var a = [1,2,3];
var b = [1,2,3];

var c = { x: 1, y: 2 };
var d = { x: 1, y: 2 };

var e = "text";
var f = "te" + "xt";

a == b            // false
a === b           // false

c == d            // false
c === d           // false

e == f            // true
e === f           // true复制代码
Copier après la connexion

特殊情况是,当你将一个字符串字面量与一个字符串对象进行比较时,由于该对象的toStringvalueOf方法,该对象的值与相字面量的值一样。

考虑将字符串字面量与由String构造函数创建的字符串对象进行比较:

"abc" == new String("abc")    // true
"abc" === new String("abc")   // false复制代码
Copier après la connexion

在这里,==操作符检查两个对象的值并返回true,但是===看到它们不是同一类型并返回false。哪一个是正确的?这取决于你想要比较的是什么。

我们的建议是完全绕开该问题,只是不要使用String构造函数来创建字符串对象。

使用==运算符(等于)

true == 1; //true, 因为 true 被转换为1,然后进行比较
"2" == 2;  //true, 因为 “2” 被转换成 2,然后进行比较复制代码
Copier après la connexion

使用===操作符

true === 1; //false
"2" === 2;  //false复制代码
Copier après la connexion

9.在 JavaScript 中深拷贝一个对象的最有效方法是什么?

快速克隆,数据丢失– JSON.parse/stringify

如果您没有在对象中使用Date、函数、undefinedInfinityRegExpMapSet、blob、、稀疏数组、类型化数组或其他复杂类型,那么可以使用一行简单代码来深拷贝一个对象:

JSON.parse(JSON.stringify(object))复制代码
Copier après la connexion
const a = {
  string: &#39;string&#39;,
  number: 123,
  bool: false,
  nul: null,
  date: new Date(), 
  undef: undefined,  // 丢失
  inf: Infinity,  // 被设置为 null
  re: /.*/,  // 丢失
}
console.log(a);
console.log(typeof a.date);  // object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
/*
object
{
  string: &#39;string&#39;,
  number: 123,
  bool: false,
  nul: null,
  date: &#39;2020-09-04T00:45:41.823Z&#39;,
  inf: null,
  re: {}
}

*/
console.log(typeof clone.date);  // string复制代码
Copier après la connexion

使用库进行可靠的克隆

由于克隆对象不是一件简单的事情(复杂类型、循环引用、函数等等),大多数主要的库都提供了拷贝对象的函数。如果你已经在使用一个库,请检查它是否具有对象克隆功能。例如

  • lodashcloneDeep; 可以通过lodash.clonedeep模块单独导入,如果你尚未使用提供深拷贝功能的库,那么它可能是你的最佳选择

  • AngularJS – angular.copy

  • jQuery – jQuery.extend(true, { }, oldObject); .clone()仅克隆DOM元素

ES6

ES6 提供了两种浅拷贝机制:Object.assign()spread语法。它将所有可枚举的自有属性的值从一个对象复制到另一个对象。例如

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax复制代码
Copier après la connexion

在以前的测试中,速度是最主要的问题

JSON.parse(JSON.stringify(obj))复制代码
Copier après la connexion

这是深拷贝对象的最慢方法,它比jQuery.extend慢 10-20%。

deep标志设置为false(浅克隆)时,jQuery.extend非常快。 这是一个不错的选择,因为它包括一些用于类型验证的额外逻辑,并且不会复制未定义的属性等,但这也会使你的速度变慢。

如果想拷贝的一个对象且你知道对象结构。那么,你可以写一个简单的for (var i in obj)循环来克隆你的对象,同时检查hasOwnProperty,这将比jQuery快得多。

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}复制代码
Copier après la connexion

注意在 Date 对象JSON上使用JSON.parse(JSON.stringify(obj))方法。JSON.stringify(new Date())以ISO格式返回日期的字符串表示,JSON.parse()不会将其转换回Date对象。

10.如何在另一个JavaScript文件中包含一个JavaScript文件?

旧版本的JavaScript没有importincluderequire,因此针对这个问题开发了许多不同的方法。

但是从2015年(ES6)开始,JavaScript已经有了ES6模块标准,可以在Node中导入模块。为了与旧版浏览器兼容,可以使用WebpackRollup之类的构建工具和/或Babel这样的编译工具。

ES6 Module

从v8.5开始,Node.js就支持ECMAScript (ES6)模块,带有--experimental-modules 标志,而且至少Node.js v13.8.0没有这个标志。要启用ESM(相对于Node.js之前的commonjs风格的模块系统[CJS]),你可以在 package.json中使用“type”:“module”。或者为文件提供扩展名.mjs。(类似地,如果默认为ESM,则用 Node.js 以前的CJS模块编写的模块可以命名为.cjs。)

使用package.json

{
    "type": "module"
}复制代码
Copier après la connexion

module.js:

export function hello() {
  return "Hello";
}复制代码
Copier après la connexion
Copier après la connexion

main.js:

import { hello } from &#39;./module.js&#39;;
let val = hello();  // val is "Hello";复制代码
Copier après la connexion

使用.mjs,会有对应的module.mjs

export function hello() {
  return "Hello";
}复制代码
Copier après la connexion
Copier après la connexion

main.mjs

import { hello } from &#39;./module.mjs&#39;;
let val = hello();  // val is "Hello";复制代码
Copier après la connexion

自Safari 10.1,Chrome 61,Firefox 60 和 Edge 16 开始,浏览器就已经支持直接加载ECMAScript模块(不需要像Webpack这样的工具)。无需使用Node.js的.mjs扩展名; 浏览器完全忽略模块/脚本上的文件扩展名。

<script type="module">
  import { hello } from &#39;./hello.mjs&#39;; // Or it could be simply `hello.js`
  hello(&#39;world&#39;);
</script>复制代码
Copier après la connexion
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const p = document.createElement(&#39;p&#39;);
  p.textContent = `Hello ${text}`;
  document.body.appendChild(p);
}复制代码
Copier après la connexion

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】。

浏览器中的动态导入

动态导入允许脚本根据需要加载其他脚本

<script type="module">
  import(&#39;hello.mjs&#39;).then(module => {
      module.hello(&#39;world&#39;);
    });
</script>复制代码
Copier après la connexion

Node.js require

在 Node.js 中用的较多还是 module.exports/require

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}复制代码
Copier après la connexion

// server.js const myModule = require(&#39;./mymodule&#39;); let val = myModule.hello(); // val is "Hello"

动态加载文件

我们可以通过动态创建 script 来动态引入文件:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script"); 

    document.head.appendChild(script); 
}复制代码
Copier après la connexion

检测脚本何时执行

现在,有一个个大问题。上面这种动态加载都是异步执行的,这样可以提高网页的性能。 这意味着不能在动态加载下马上使用该资源,因为它可能还在加载。

例如:my_lovely_script.js包含MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined复制代码
Copier après la connexion

然后,按F5重新加载页面,可能就有效了。那么该怎么办呢?

我们可以使用回调函数来解决些问题。

function loadScript(url, callback)
{
    var head = document.head;
    var script = document.createElement(&#39;script&#39;);
    script.type = &#39;text/javascript&#39;;
    script.src = url;

    script.onload = callback;

    head.appendChild(script);
}复制代码
Copier après la connexion

然后编写在lambda函数中加载脚本后要使用的代码

var myPrettyCode = function() {
   // Here, do whatever you want
};复制代码
Copier après la connexion

然后,运行代码:

loadScript("my_lovely_script.js", myPrettyCode);复制代码
Copier après la connexion

请注意,脚本可能在加载DOM之后或之前执行,具体取决于浏览器以及是否包括行script.async = false;

相关免费学习推荐:javascript(视频)

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!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!