首頁 > web前端 > js教程 > 主體

JavaScript中必須掌握的10個基礎問題

coldplay.xixi
發布: 2020-10-13 13:47:27
轉載
2031 人瀏覽過

JavaScript中必須掌握的10個基礎問題

JavaScript 是一種客戶端程式語言。全球超過90%的網站都在使用它,它是世界上最常用的程式語言之一。因此,今天JavaScript欄位來討論 10 個有關 JavaScript 的常見問題。

1.如何從陣列中移除一個特定的項目

#想法:首先,使用indexOf尋找要刪除的陣列元素的索引(index) ,然後使用splice方法刪除該索引所對應的項目。

splice()是一個非純函數,透過刪除現有元素和/或新增元素來更改陣列的內容。

const array = [2, 5, 9]

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

console.log(array)
// [ 2, 9 ]复制代码
登入後複製

splice的第二個參數是要刪除的元素數量。請注意,splice會在適當的位置修改數組,並傳回一個包含已刪除元素的新數組。

接著,我們可以來完善一下。下面有兩個函數,第一個函數只刪除一個匹配項(即從[2,5,9,1,5,8,5]中刪除第一個匹配項5 ),而第二個函數則刪除所有符合項目:

// 仅删除第一个匹配项
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
    }
  }
}复制代码
登入後複製

刪除數組中索引i處的元素

刪除數組中索引i處的元素:

array.splice(i, 1)复制代码
登入後複製

如果你想從數組中刪除值為number的每個元素,可以這樣做:

for (let i = array.length - 1; i>=0; i--) {
  if (array[i] === number) {
    array.splice(i, 1)
  }
}复制代码
登入後複製

如果你只想讓索引i處的元素不再存在,但又不想更改其他元素的索引:

delete array[i]复制代码
登入後複製

2. 如何使用jQuery 或純JS 將用戶從一個頁面重定向到另一個頁面

jQuery 不是必要的,window.location.replace(…)最適合模擬HTTP 重定向。 window.location.replace(...)優於使用window.location.href,因為replace()不會將原始頁面保留在會話歷史記錄中,這意味著用戶將不會陷入永無止境回退按鈕。

如果要模擬點擊鏈接,可以使用location.href,如果要模擬HTTP重定向,請使用location.replace

範例:

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

// 模拟单击链接
window.location.href = "http://stackoverflow.com"复制代码
登入後複製

你還可以這樣做:

$(location).attr(&#39;href&#39;, &#39;http://stackoverflow.com&#39;)复制代码
登入後複製

3.JavaScript 閉包是如何運作的

閉包是函數和對該函數外部作用域的引用(詞法環境),詞法環境是每個執行上下文(堆疊)的一部分,並且是標識符(即局部變數名稱)和值之間的映射。

JavaScript 中的每個函數都維護對其外部詞法環境的參考。此引用用於配置呼叫函數時建立的執行上下文。不管何時呼叫函數,該引用使函數內的程式碼能夠查看在函數外聲明的變數。

在下面的程式碼中,inner與呼叫foo時所建立的執行上下文的詞法環境一起形成一個閉包,並對外部隱藏了變數secret

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复制代码
登入後複製

換句話說,在JavaScript中,函數帶有對私有狀態的引用,只有它們(以及在相同的詞法環境中聲明的任何其他函數)可以存取該私有狀態。這個狀態對函數的呼叫者是不可見的,這為資料隱藏和封裝提供了一個優秀的機制。

請記住,JavaScript中的函數可以像變數一樣傳遞,這意味著這些功能和狀態的物件可以在程式中傳遞:類似於在c 中傳遞類別的實例。

如果JavaScript沒有閉包,則必須在函數之間明確傳遞更多狀態,從而使參數列表更長,程式碼更冗餘。

所以,如果你想讓一個函數總是能夠存取私有狀態,你可以使用一個閉包,我們經常想把狀態和函數連結起來。例如,在Java或c 中,當你為類別新增私有實例變數和方法時,這是將狀態與功能關聯。

在 C 語言和大多數其他程式語言中,函數返回後,由於堆疊被銷毀,所有的局部變數都不再可存取。在JavaScript中,如果在另一個函數中宣告一個函數,那麼外部函數的本機變數在傳回後仍然可以存取。這樣,在上面的程式碼中,secret在從foo返回後仍然對函數物件內部可用。

閉包在需要與函數關聯的私有狀態時非常有用。這是一個非常常見的場景,JavaScript直到2015年才有類別語法,它仍然沒有私有欄位語法,閉包滿足了這個需求。

私有實例變數

在下面的例子中,函數 toString 隱藏了 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())复制代码
登入後複製

函數式程式設計

在下面的程式碼中,函數inner隱藏了fnargs

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复制代码
登入後複製

面向事件的程式設計

在以下程式碼中,函數onClick隱藏了變數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)复制代码
登入後複製
<button>Set background color</button>复制代码
登入後複製

模組化

#

在下面的示例中,所有实现细节都隐藏在一个立即执行的函数表达式中。函数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())复制代码
登入後複製

事例 1:

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

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

let f = foo()
f()复制代码
登入後複製

事例 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复制代码
登入後複製

事例 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复制代码
登入後複製

最后一点:

  • 每当在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);复制代码
登入後複製

上面输出结果是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复制代码
登入後複製

上面还是打印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();复制代码
登入後複製

当调用一个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... 
复制代码
登入後複製

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

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

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

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

ECMAScript 6 引入了string .prototype.include

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

console.log(string.includes(substring));复制代码
登入後複製

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

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

console.log(string.indexOf(substring) !== -1);复制代码
登入後複製

为了使其在旧的浏览器中运行,可以使用这种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;
    }
  };
}复制代码
登入後複製

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

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

如,函数表达式

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

var functionOne = function() {
  console.log("Hello!");
};复制代码
登入後複製

函数声明:

// "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}复制代码
登入後複製

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

&#39;use strict&#39;;    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError复制代码
登入後複製

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

function xyz(){
  function abc(){};
  // abc 在这里定义...
}
// ...不是在这里复制代码
登入後複製

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

function abc(){};
var xyz = abc;复制代码
登入後複製

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

console.log(xyz === abc) // true复制代码
登入後複製

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

var abc = function(){};
console.log(abc.name); //  ""复制代码
登入後複製

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

// 假设 really.long.external.scoped 为 {}
really.long.external.scoped.name = function shortcut(n){
  // 它递归地调用自己:
  shortcut(n - 1);
  // ...
  // 让它自己作为回调传递::
  someFunction(shortcut);
  // ...
}复制代码
登入後複製

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

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

function abc(){}复制代码
登入後複製

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

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

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

// 也可以在这里调用 
abc(); 
复制代码
登入後複製

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

// 我们可以在这里调用
abc(); 
return;
function abc(){}复制代码
登入後複製

下面是一个函数表达式:

var xyz = function(){};复制代码
登入後複製

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

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

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

// 我们可以在这里调用
xyz(); 
复制代码
登入後複製

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

var xyz = function abc(){};
console.log(xyz.name); // "abc"复制代码
登入後複製

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

var abc = function(){};复制代码
登入後複製

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

function abc(){};复制代码
登入後複製

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

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

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

delete myObject.regex;
// 或者
delete myObject[&#39;regex&#39;];
//  或者
var prop = "regex";
delete myObject[prop];复制代码
登入後複製

事例:

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

console.log(myObject);复制代码
登入後複製

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复制代码
登入後複製

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复制代码
登入後複製

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

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复制代码
登入後複製

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

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

"abc" == new String("abc")    // true
"abc" === new String("abc")   // false复制代码
登入後複製

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

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

使用==运算符(等于)

true == 1; //true, 因为 true 被转换为1,然后进行比较
"2" == 2;  //true, 因为 “2” 被转换成 2,然后进行比较复制代码
登入後複製

使用===操作符

true === 1; //false
"2" === 2;  //false复制代码
登入後複製

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

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

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

JSON.parse(JSON.stringify(object))复制代码
登入後複製
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复制代码
登入後複製

使用库进行可靠的克隆

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

  • 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复制代码
登入後複製

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

JSON.parse(JSON.stringify(obj))复制代码
登入後複製

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

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

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

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}复制代码
登入後複製

注意在 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"
}复制代码
登入後複製

module.js:

export function hello() {
  return "Hello";
}复制代码
登入後複製
登入後複製

main.js:

import { hello } from &#39;./module.js&#39;;
let val = hello();  // val is "Hello";复制代码
登入後複製

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

export function hello() {
  return "Hello";
}复制代码
登入後複製
登入後複製

main.mjs

import { hello } from &#39;./module.mjs&#39;;
let val = hello();  // val is "Hello";复制代码
登入後複製

自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>复制代码
登入後複製
// 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);
}复制代码
登入後複製

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

浏览器中的动态导入

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

<script type="module">
  import(&#39;hello.mjs&#39;).then(module => {
      module.hello(&#39;world&#39;);
    });
</script>复制代码
登入後複製

Node.js require

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

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}复制代码
登入後複製

// 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); 
}复制代码
登入後複製

检测脚本何时执行

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

例如: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复制代码
登入後複製

然后,按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);
}复制代码
登入後複製

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

var myPrettyCode = function() {
   // Here, do whatever you want
};复制代码
登入後複製

然后,运行代码:

loadScript("my_lovely_script.js", myPrettyCode);复制代码
登入後複製

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

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

以上是JavaScript中必須掌握的10個基礎問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.im
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板