日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
JavaScript中10個需要掌握基礎的問題

JavaScript 是一種客戶端編程語言。全球超過**90%**的網(wǎng)站都在使用它,它是世界上最常用的編程語言之一。因此,今天我們業(yè)討論 10 個有關 JavaScript 的常見問題。

1.如何從數(shù)組中移除一個特定的項

思路:首先,使用indexOf查找要刪除的數(shù)組元素的索引(index),然后使用splice方法刪除該索引所對應的項。

splice()是一個非純函數(shù),通過刪除現(xiàn)有元素和/或添加新元素來更改數(shù)組的內容。

 
 
 
 
  1. const array = [2, 5, 9]
  2. const index = array.indexOf(5)
  3. if (index > -1) {
  4.   array.splice(index, 1)
  5. }
  6. console.log(array)
  7. // [ 2, 9 ]

splice的第二個參數(shù)是要刪除的元素數(shù)量。注意,splice會在適當?shù)奈恢眯薷臄?shù)組,并返回一個包含已刪除元素的新數(shù)組。

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

 
 
 
 
  1. // 僅刪除第一個匹配項
  2. function removeItemOnce (arr, value) {
  3.   let index = arr.indexOf(value)
  4.   if (index > -1) {
  5.     arr.splice(index, 1)
  6.   }
  7.   return arr
  8. }
  9. // 刪除所有匹配項
  10. function removeItemAll (arr, value) {
  11.   let i = 0
  12.   while(i < arr.length) {
  13.     if (arr[i] === value) {
  14.       arr.splice(i, 1)
  15.     } else {
  16.       ++i
  17.     }
  18.   }
  19. }

刪除數(shù)組中索引i處的元素:

刪除數(shù)組中索引i處的元素:

 
 
 
 
  1. array.splice(i, 1)

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

 
 
 
 
  1. for (let i = array.length - 1; i>=0; i--) {
  2.   if (array[i] === number) {
  3.     array.splice(i, 1)
  4.   }
  5. }
  6. 如果你只想使索

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

 
 
 
 
  1. delete array[i]

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

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

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

事例:

 
 
 
 
  1. //模擬HTTP重定向
  2. window.location.replace("http://stackoverflow.com")
  3. // 模擬單擊鏈接
  4. window.location.href = "http://stackoverflow.com"

你還可以這樣做:

 
 
 
 
  1. $(location).attr('href', 'http://stackoverflow.com')

3.JavaScript 閉包是如何工作的

閉包是一個函數(shù)和對該函數(shù)外部作用域的引用(詞法環(huán)境),詞法環(huán)境是每個執(zhí)行上下文(堆棧)的一部分,并且是標識符(即局部變量名稱)和值之間的映射。

JavaScript 中的每個函數(shù)都維護對其外部詞法環(huán)境的引用。此引用用于配置調用函數(shù)時創(chuàng)建的執(zhí)行上下文。不管何時調用函數(shù),該引用使函數(shù)內的代碼能夠查看在函數(shù)外聲明的變量。

在下面的代碼中,inner與調用foo時創(chuàng)建的執(zhí)行上下文的詞法環(huán)境一起形成一個閉包,并對外部隱藏了變量secret:

 
 
 
 
  1. function foo() {
  2.   const secret = Math.trunc(Math.random()*100)
  3.   return function inner() {
  4.     console.log(`The secret number is ${secret}.`)
  5.   }
  6. }
  7. const f = foo() // secret 不能從foo 外部直接訪問
  8. f() // 訪問 secret 的唯一辦法就是調用 f

換句話說,在JavaScript中,函數(shù)帶有對私有狀態(tài)的引用,只有它們(以及在相同的詞法環(huán)境中聲明的任何其他函數(shù))可以訪問該私有狀態(tài)。這個狀態(tài)對函數(shù)的調用者是不可見的,這為數(shù)據(jù)隱藏和封裝提供了一種優(yōu)秀的機制。

請記住,JavaScript中的函數(shù)可以像變量一樣傳遞,這意味著這些功能和狀態(tài)的對可以在程序中傳遞:類似于在c++中傳遞類的實例。

如果JavaScript沒有閉包,則必須在函數(shù)之間顯式傳遞更多狀態(tài),從而使參數(shù)列表更長,代碼更冗余。

所以,如果你想讓一個函數(shù)總是能夠訪問私有狀態(tài),你可以使用一個閉包,我們經常想把狀態(tài)和函數(shù)聯(lián)系起來。例如,在Java或c++中,當你向類添加私有實例變量和方法時,這是將狀態(tài)與功能關聯(lián)起來。

在 C 語言和大多數(shù)其他編程語言中,函數(shù)返回后,由于堆棧被銷毀,所有的局部變量都不再可訪問。在JavaScript中,如果在另一個函數(shù)中聲明一個函數(shù),那么外部函數(shù)的本地變量在返回后仍然可以訪問。這樣,在上面的代碼中,secret在從foo返回后仍然對函數(shù)對象內部可用。

閉包在需要與函數(shù)關聯(lián)的私有狀態(tài)時非常有用。這是一個非常常見的場景,JavaScript直到2015年才有類語法,它仍然沒有私有字段語法,閉包滿足了這一需求。

私有實例變量

在下面的事例中,函數(shù) toString 隱藏了 Car 類的一些細節(jié)。

 
 
 
 
  1. function Car(manufacturer, model, year, color) {
  2.   return {
  3.     toString() {
  4.       return `${manufacturer} ${model} (${year}, ${color})`
  5.     }
  6.   }
  7. }
  8. const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
  9. console.log(car.toString())

函數(shù)式編程

在下面的代碼中,函數(shù)inner隱藏了fn和args。

 
 
 
 
  1. function curry(fn) {
  2.   const args = []
  3.   return function inner(arg) {
  4.     if(args.length === fn.length) return fn(...args)
  5.     args.push(arg)
  6.     return inner
  7.   }
  8. }
  9. function add(a, b) {
  10.   return a + b
  11. }
  12. const curriedAdd = curry(add)
  13. console.log(curriedAdd(2)(3)()) // 5

面向事件的編程

在以下代碼中,函數(shù)onClick隱藏了變量BACKGROUND_COLOR。

 
 
 
 
  1. const $ = document.querySelector.bind(document)
  2. const BACKGROUND_COLOR = 'rgba(200,200,242,1)'
  3. function onClick() {
  4.   $('body').style.background = BACKGROUND_COLOR
  5. }
  6. $('button').addEventListener('click', onClick)

模塊化

在下面的示例中,所有實現(xiàn)細節(jié)都隱藏在一個立即執(zhí)行的函數(shù)表達式中。函數(shù)tick和toString隱藏了私有狀態(tài)和函數(shù),它們需要完成自己的工作。閉包使我們能夠模塊化和封裝我們的代碼。

 
 
 
 
  1. let namespace = {};
  2. (function foo(n) {
  3.   let numbers = []
  4.   function format(n) {
  5.     return Math.trunc(n)
  6.   }
  7.   function tick() {
  8.     numbers.push(Math.random() * 100)
  9.   }
  10.   function toString() {
  11.     return numbers.map(format)
  12.   }
  13.   n.counter = {
  14.     tick,
  15.     toString
  16.   }
  17. }(namespace))
  18. const counter = namespace.counter
  19. counter.tick()
  20. counter.tick()
  21. console.log(counter.toString())

事例 1:

此示例演示局部變量未在閉包中復制。閉包保留對原始變量本身的引用。似乎即使外部函數(shù)退出后,堆棧仍在內存中保留。

 
 
 
 
  1. function foo () {
  2.   let x = 42
  3.   let inner = function () {
  4.     console.log(x)
  5.   }
  6.   x = x + 1
  7.   return inner
  8. }
  9. let f = foo()
  10. f()

事例 2:

在下面的代碼中,三種方法log ,increment 和update 都在同一詞法環(huán)境閉包中。

 
 
 
 
  1. function createObject() {
  2.   let x = 42;
  3.   return {
  4.     log() { console.log(x) },
  5.     increment() { x++ },
  6.     update(value) { x = value }
  7.   }
  8. }
  9. const o = createObject()
  10. o.increment()
  11. o.log() // 43
  12. o.update(5)
  13. o.log() // 5
  14. const p = createObject()
  15. p.log() // 42

事例 3:

如果使用的變量是使用var聲明的,需要注意的一點是,使用var聲明的變量被提升。由于引入了let和const,這在現(xiàn)代JavaScript 中幾乎沒有問題。

在下面的代碼中,每次循環(huán)中,都會創(chuàng)建一個新的inner函數(shù),變量i被覆蓋,但是因var會讓 i 提升到函數(shù)的頂部,所以所有這些inner函數(shù)覆蓋的都是同一個變量,這意味著i(3)的最終值被打印了三次。

 
 
 
 
  1. function foo () {
  2.   var result = []
  3.   for (var i = 0; i < 3; i++) {
  4.     result.push(function inner () {
  5.       console.log(i)
  6.     })
  7.   }
  8.   return result
  9. }
  10. const result = foo()
  11. for(var i = 0; i < 3; i++) {
  12.   result[i]()
  13. }
  14. // 3 3 3

最后一點:

  • 每當在JavaScript中聲明函數(shù)時,都會創(chuàng)建一個閉包。
  • 從一個函數(shù)內部返回另一個函數(shù)是閉包的經典例子,因為外部函數(shù)內部的狀態(tài)對于返回的內部函數(shù)是隱式可用的,即使外部函數(shù)已經完成執(zhí)行。
  • 只要在函數(shù)內使用eval(),就會使用一個閉包。eval的文本可以引用函數(shù)的局部變量,在非嚴格模式下,甚至可以通過使用eval('var foo = ')創(chuàng)建新的局部變量。
  • 當在函數(shù)內部使用new Function()(Function constructor)時,它不會覆蓋其詞法環(huán)境,而是覆蓋全局上下文。新函數(shù)不能引用外部函數(shù)的局部變量。
  • 在JavaScript中,閉包類似于在函數(shù)聲明時保留對作用域的引用(而不是復制),后者又保留對其外部作用域的引用,以此類推,一直到作用域鏈頂端的全局對象。
  • 聲明函數(shù)時創(chuàng)建一個閉包。當調用函數(shù)時,此閉包用于配置執(zhí)行上下文。
  • 每次調用函數(shù)時都會創(chuàng)建一組新的局部變量。

JavaScript 中的每個函數(shù)都維護與其外部詞法環(huán)境的鏈接。詞法環(huán)境是所有名稱的映射(例如,變量,參數(shù))及其范圍內的值。因此,只要看到function關鍵字,函數(shù)內部的代碼就可以訪問在函數(shù)外部聲明的變量。

 
 
 
 
  1. function foo(x) {
  2.   var tmp = 3;
  3.   function bar(y) {
  4.     console.log(x + y + (++tmp)); // 16
  5.   }
  6.   bar(10);
  7. }
  8. foo(2);

上面輸出結果是16,參數(shù)x和變量tmp都存在于外部函數(shù)foo的詞法環(huán)境中。函數(shù)bar及其與函數(shù)foo的詞法環(huán)境的鏈接是一個閉包。

函數(shù)不必返回即可創(chuàng)建閉包。僅僅憑借其聲明,每個函數(shù)都會在其封閉的詞法環(huán)境中關閉,從而形成一個閉包。

 
 
 
 
  1. function foo(x) {
  2.   var tmp = 3;
  3.   return function (y) {
  4.     console.log(x + y + (++tmp)); // 16
  5.   }
  6. }
  7. var bar = foo(2);
  8. bar(10); // 16
  9. bar(10); // 17

上面還是打印16,因為bar內的代碼仍然可以引用參數(shù)x和變量tmp,即使它們不再直接的作用域內。

但是,由于tmp仍然在bar的閉包內部徘徊,因此可以對其進行遞增。每次調用bar時,它將增加1。

閉包最簡單的例子是這樣的:

 
 
 
 
  1. var a = 10;
  2. function test() {
  3.   console.log(a); // will output 10
  4.   console.log(b); // will output 6
  5. }
  6. var b = 6;
  7. test();

當調用一個JavaScript函數(shù)時,將創(chuàng)建一個新的執(zhí)行上下文ec。連同函數(shù)參數(shù)和目標對象,這個執(zhí)行上下文還接收到調用執(zhí)行上下文的詞法環(huán)境的鏈接,這意味著在外部詞法環(huán)境中聲明的變量(在上面的例子中,a和b)都可以從ec獲得。

每個函數(shù)都會創(chuàng)建一個閉包,因為每個函數(shù)都有與其外部詞法環(huán)境的鏈接。

注意,變量本身在閉包中是可見的,而不是副本。

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

引用一些有趣的部分:

嚴格模式是ECMAScript 5中的一個新特性,它允許我們將程序或函數(shù)放置在嚴格的操作上下文中。這種嚴格的上下文會防止某些操作被執(zhí)行,并引發(fā)更多異常。

嚴格模式在很多方面都有幫助:

  • 它捕獲了一些常見的編碼漏洞,并拋出異常。
  • 當采取相對不安全的操作(例如訪問全局對象)時,它可以防止錯誤或拋出錯誤。
  • 它禁用令人困惑或考慮不周到的特性。

另外,請注意,我信可以將“strict mode”應用于整個文件,也可以僅將其用于特定函數(shù)。

 
 
 
 
  1. // Non-strict code...
  2. (function(){
  3.   "use strict";
  4.   // Define your library strictly...
  5. })();
  6. // Non-strict code... 

如果是在混合使用舊代碼和新代碼的情況,這可能會有所幫助。它有點像在Perl中使用的“use strict”。通過檢測更多可能導致?lián)p壞的東西,幫助我們減少更多的錯誤。

現(xiàn)在所有主流瀏覽器都支持嚴格模式。

在原生ECMAScript模塊(帶有import和export語句)和ES6類中,嚴格模式始終是啟用的,不能禁用。

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

ECMAScript 6 引入了string .prototype.include

 
 
 
 
  1. const string = "foo";
  2. const substring = "oo";
  3. console.log(string.includes(substring));

不過,IE 不支持 includes。在 CMAScript 5或更早的環(huán)境中,使用String.prototype.indexOf。如果找不到子字符串,則返回-1:

 
 
 
 
  1. var string = "foo";
  2. var substring = "oo";
  3. console.log(string.indexOf(substring) !== -1);

為了使其在舊的瀏覽器中運行,可以使用這種polyfill:

 
 
 
 
  1. if (!String.prototype.includes) {
  2.   String.prototype.includes = function(search, start) {
  3.     'use strict';
  4.     if (typeof start !== 'number') {
  5.       start = 0;
  6.     }
  7.     if (start + search.length > this.length) {
  8.       return false;
  9.     } else {
  10.       return this.indexOf(search, start) !== -1;
  11.     }
  12.   };
  13. }

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

不同之處在于functionOne是一個函數(shù)表達式,因此只在到達這一行時才會定義,而functionTwo是一個函數(shù)聲明,在它周圍的函數(shù)或腳本被執(zhí)行(由于提升)時就定義。

如,函數(shù)表達式

 
 
 
 
  1. // TypeError: functionOne is not a function
  2. functionOne();
  3. var functionOne = function() {
  4.   console.log("Hello!");
  5. };

函數(shù)聲明:

 
 
 
 
  1. // "Hello!"
  2. functionTwo();
  3. function functionTwo() {
  4.   console.log("Hello!");
  5. }

過去,在不同的瀏覽器之間,在塊中定義的函數(shù)聲明的處理是不一致的。嚴格模式(在ES5中引入)解決了這個問題,它將函數(shù)聲明的范圍限定在其封閉的塊上。

 
 
 
 
  1. 'use strict';    
  2. { // note this block!
  3.   function functionThree() {
  4.     console.log("Hello!");
  5.   }
  6. }
  7. functionThree(); // ReferenceError

function abc(){}也具有作用域-名稱abc在遇到該定義的作用域中定義。例:

 
 
 
 
  1. function xyz(){
  2.   function abc(){};
  3.   // abc 在這里定義...
  4. }
  5. // ...不是在這里

如果想在所有瀏覽器上給函數(shù)起別名,可以這么做:

 
 
 
 
  1. function abc(){};
  2. var xyz = abc;

在本例中,xyz和abc都是同一個對象的別名

 
 
 
 
  1. console.log(xyz === abc) // true

它的名稱是自動分配的。但是當你定義它的時候

 
 
 
 
  1. var abc = function(){};
  2. console.log(abc.name); //  ""

它的name稱為空,我們創(chuàng)建了一個匿名函數(shù)并將其分配給某個變量。使用組合樣式的另一個很好的理由是使用簡短的內部名稱來引用自身,同時為外部用戶提供一個長而不會沖突的名稱:

 
 
 
 
  1. // 假設 really.long.external.scoped 為 {}
  2. really.long.external.scoped.name = function shortcut(n){
  3.   // 它遞歸地調用自己:
  4.   shortcut(n - 1);
  5.   // ...
  6.   // 讓它自己作為回調傳遞::
  7.   someFunction(shortcut);
  8.   // ...
  9. }

在上面的例子中,我們可以對外部名稱進行同樣的操作,但是這樣做太笨拙了(而且速度更慢)。另一種引用自身的方法是arguments.callee,這種寫法也相對較長,并且在嚴格模式中不受支持。

實際上,JavaScript對待這兩個語句是不同的。下面是一個函數(shù)聲明:

 
 
 
 
  1. function abc(){}

這里的abc可以定義在當前作用域的任何地方:

 
 
 
 
  1. // 我們可以在這里調用
  2. abc(); 
  3. // 在這里定義
  4. function abc(){}
  5. // 也可以在這里調用 
  6. abc(); 

此外,盡管有 return 語句,也可以提升:

 
 
 
 
  1. // 我們可以在這里調用
  2. abc(); 
  3. return;
  4. function abc(){}

下面是一個函數(shù)表達式:

 
 
 
 
  1. var xyz = function(){};

這里的xyz是從賦值點開始定義的:

 
 
 
 
  1. // 我們不可以在這里調用
  2. xyz(); 
  3. // 在這里定義 xyz
  4. xyz = function(){}
  5. // 我們可以在這里調用
  6. xyz(); 

函數(shù)聲明與函數(shù)表達式之間存在差異的真正原因。

 
 
 
 
  1. var xyz = function abc(){};
  2. console.log(xyz.name); // "abc"

就個人而言,我們更喜歡使用函數(shù)表達式聲明,因為這樣可以控制可見性。當我們像這樣定義函數(shù)時:

 
 
 
 
  1. var abc = function(){};

我們知道,如果我們沒有在作用域鏈的任何地方定義abc,那么我們是在全局作用域內定義的。即使在eval()內部使用,這種類型的定義也具有彈性。而定義:

 
 
 
 
  1. function abc(){};

取決于上下文,并且可能讓你猜測它的實際定義位置,特別是在eval()的情況下,—取決于瀏覽器。

7.如何從 JavaScript 對象中刪除屬性?

我們可以這樣刪除對象的屬性:

 
 
 
 
  1. delete myObject.regex;
  2. // 或者
  3. delete myObject['regex'];
  4. //  或者
  5. var prop = "regex";
  6. delete myObject[prop];

事例:

 
 
 
 
  1. var myObject = {
  2.     "ircEvent": "PRIVMSG",
  3.     "method": "newURI",
  4.     "regex": "^http://.*"
  5. };
  6. delete myObject.regex;
  7. console.log(myObject);

JavaScript 中的對象可以看作鍵和值之間的映射。delete操作符用于一次刪除一個鍵(通常稱為對象屬性)。

 
 
 
 
  1. var obj = {
  2.   myProperty: 1    
  3. }
  4. console.log(obj.hasOwnProperty('myProperty')) // true
  5. delete obj.myProperty
  6. console.log(obj.hasOwnProperty('myProperty')) // false

delete 操作符不是直接釋放內存,它不同于簡單地將null或undefined值賦給屬性,而是將屬性本身從對象中刪除。

注意,如果已刪除屬性的值是引用類型(對象),而程序的另一部分仍然持有對該對象的引用,那么該對象當然不會被垃圾收集,直到對它的所有引用都消失。

delete只對其描述符標記為configurable的屬性有效。

8. JS 的比較中應使用哪個等于運算符(== vs ===)?

嚴格相等運算符(===)的行為與抽象相等運算符(==)相同,除非不進行類型轉換,而且類型必須相同才能被認為是相等的。

==運算符會進行類型轉換后比較相等性。===運算符不會進行轉換,因此如果兩個值的類型不同,則===只會返回false。

JavaScript有兩組相等運算符:===和!==,以及它們的孿生兄弟==和!=。如果這兩個操作數(shù)具有相同的類型和相同的值,那么===的結果就是 true,而!==的結果就是 false。

下面是一些事例:

 
 
 
 
  1. '' == '0'           // false
  2. 0 == ''             // true
  3. 0 == '0'            // true
  4. false == 'false'    // false
  5. false == '0'        // true
  6. false == undefined  // false
  7. false == null       // false
  8. null == undefined   // true
  9. ' \t\r\n ' == 0     // true

上面有些看起來會挺困惑的,所以盡量還是使用嚴格比較運算符(===)。對于引用類型,==和===操作一致(特殊情況除外)。

 
 
 
 
  1. var a = [1,2,3];
  2. var b = [1,2,3];
  3. var c = { x: 1, y: 2 };
  4. var d = { x: 1, y: 2 };
  5. var e = "text";
  6. var f = "te" + "xt";
  7. a == b            // false
  8. a === b           // false
  9. c == d            // false
  10. c === d           // false
  11. e == f            // true
  12. e === f           // true

特殊情況是,當你將一個字符串字面量與一個字符串對象進行比較時,由于該對象的toString或valueOf方法,該對象的值與相字面量的值一樣。

考慮將字符串字面量與由String構造函數(shù)創(chuàng)建的字符串對象進行比較:

 
 
 
 
  1. "abc" == new String("abc")    // true
  2. "abc" === new String("abc")   // false

在這里,==操作符檢查兩個對象的值并返回true,但是===看到它們不是同一類型并返回false。哪一個是正確的?這取決于你想要比較的是什么。

我們的建議是完全繞開該問題,只是不要使用String構造函數(shù)來創(chuàng)建字符串對象。

使用==運算符(等于)

 
 
 
 
  1. true == 1; //true, 因為 true 被轉換為1,然后進行比較
  2. "2" == 2;  //true, 因為 “2” 被轉換成 2,然后進行比較

使用===操作符

 
 
 
 
  1. true === 1; //false
  2. "2" === 2;  //false

9.在 JavaScript 中深拷貝一個對象的最有效方法是什么?

快速克隆,數(shù)據(jù)丟失– JSON.parse/stringify

如果您沒有在對象中使用Date、函數(shù)、undefined、Infinity、RegExp、Map、Set、blob、、稀疏數(shù)組、類型化數(shù)組或其他復雜類型,那么可以使用一行簡單代碼來深拷貝一個對象:

 
 
 
 
  1. JSON.parse(JSON.stringify(object))
  2. const a = {
  3.   string: 'string',
  4.   number: 123,
  5.   bool: false,
  6.   nul: null,
  7.   date: new Date(), 
  8.   undef: undefined,  // 丟失
  9.   inf: Infinity,  // 被設置為 null
  10.   re: /.*/,  // 丟失
  11. }
  12. console.log(a);
  13. console.log(typeof a.date);  // object
  14. const clone = JSON.parse(JSON.stringify(a));
  15. console.log(clone);
  16. /*
  17. object
  18. {
  19.   string: 'string',
  20.   number: 123,
  21.   bool: false,
  22.   nul: null,
  23.   date: '2020-09-04T00:45:41.823Z',
  24.   inf: null,
  25.   re: {}
  26. }
  27. */
  28. console.log(typeof clone.date);  // string

使用庫進行可靠的克隆

由于克隆對象不是一件簡單的事情(復雜類型、循環(huán)引用、函數(shù)等等),大多數(shù)主要的庫都提供了拷貝對象的函數(shù)。如果你已經在使用一個庫,請檢查它是否具有對象克隆功能。例如

  • lodash – cloneDeep; 可以通過lodash.clonedeep模塊單獨導入,如果你尚未使用提供深拷貝功能的庫,那么它可能是你的最佳選擇
  • AngularJS – angular.copy
  • jQuery – jQuery.extend(true, { }, oldObject); .clone()僅克隆DOM元素

ES6

ES6 提供了兩種淺拷貝機制:Object.assign()和spread語法。它將所有可枚舉的自有屬性的值從一個對象復制到另一個對象。例如

 
 
 
 
  1. var A1 = {a: "2"};
  2. var A2 = Object.assign({}, A1);
  3. var A3 = {...A1};  // Spread Syntax

在以前的測試中,速度是最主要的問題

 
 
 
 
  1. JSON.parse(JSON.stringify(obj))

這是深拷貝對象的最慢方法,它比jQuery.extend慢 10-20%。

當deep標志設置為false(淺克隆)時,jQuery.extend非???。這是一個不錯的選擇,因為它包括一些用于類型驗證的額外邏輯,并且不會復制未定義的屬性等,但這也會使你的速度變慢。

如果想拷貝的一個對象且你知道對象結構。那么,你可以寫一個簡單的for (var i in obj)循環(huán)來克隆你的對象,同時檢查hasOwnProperty,這將比jQuery快得多。

 
 
 
 
  1. var clonedObject = {
  2.   knownProp: obj.knownProp,
  3.   ..
  4. }

注意在 Date 對象JSON上使用JSON.parse(JSON.stringify(obj))方法。JSON.stringify(new Date())以ISO格式返回日期的字符串表示,JSON.parse()不會將其轉換回Date對象。

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

舊版本的JavaScript沒有import、include或require,因此針對這個問題開發(fā)了許多不同的方法。

但是從2015年(ES6)開始,JavaScript已經有了ES6模塊標準,可以在Node中導入模塊。為了與舊版瀏覽器兼容,可以使用Webpack和Rollup之類的構建工具和/或Babel這樣的編譯工具。

ES6 Module

從v8.5開始,Node.js就支持ECMAScript (ES6)模塊,帶有--experimental-modules標志,而且至少Node.js v13.8.0沒有這個標志。要啟用ESM(相對于Node.js之前的commonjs風格的模塊系統(tǒng)[CJS]),你可以在 package.json中使用“type”:“module”?;蛘邽槲募峁U展名.mjs。(類似地,如果默認為ESM,則用 Node.js 以前的CJS模塊編寫的模塊可以命名為.cjs。)

使用package.json:

 
 
 
 
  1. {
  2.     "type": "module"
  3. }

在 module.js: 中

 
 
 
 
  1. export function hello() {
  2.   return "Hello";
  3. }

main.js:

 
 
 
 
  1. import { hello } from './module.js';
  2. let val = hello();  // val is "Hello";

使用.mjs,會有對應的module.mjs:

 
 
 
 
  1. export function hello() {
  2.   return "Hello";
  3. }

在main.mjs 中

 
 
 
 
  1. import { hello } from './module.mjs';
  2. let val = hello();  // val is "Hello";

自Safari 10.1,Chrome 61,F(xiàn)irefox 60 和 Edge 16 開始,瀏覽器就已經支持直接加載ECMAScript模塊(不需要像Webpack這樣的工具)。無需使用Node.js的.mjs擴展名;瀏覽器完全忽略模塊/腳本上的文件擴展名。

 
 
 
 
  1. // hello.mjs -- or it could be simply `hello.js`
  2. export function hello(text) {
  3.   const div = document.createElement('div');
  4.   div.textContent = `Hello ${text}`;
  5.   document.body.appendChild(div);
  6. }

瀏覽器中的動態(tài)導入

動態(tài)導入允許腳本根據(jù)需要加載其他腳本

 
 
 
 

Node.js require

在 Node.js 中用的較多還是 module.exports/require

 
 
 
 
  1. // mymodule.js
  2. module.exports = {
  3.    hello: function() {
  4.       return "Hello";
  5.    }
  6. }

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

動態(tài)加載文件

我們可以通過動態(tài)創(chuàng)建 script 來動態(tài)引入文件:

 
 
 
 
  1. function dynamicallyLoadScript(url) {
  2.     var script = document.createElement("script"); 
  3.     document.head.appendChild(script); 
  4. }

檢測腳本何時執(zhí)行

現(xiàn)在,有一個個大問題。上面這種動態(tài)加載都是異步執(zhí)行的,這樣可以提高網(wǎng)頁的性能。這意味著不能在動態(tài)加載下馬上使用該資源,因為它可能還在加載。

例如:my_lovely_script.js包含MySuperObject:

 
 
 
 
  1. var js = document.createElement("script");
  2. js.type = "text/javascript";
  3. js.src = jsFilePath;
  4. document.body.appendChild(js);
  5. var s = new MySuperObject();
  6. Error : MySuperObject is undefined

然后,按F5重新加載頁面,可能就有效了。那么該怎么辦呢?

我們可以使用回調函數(shù)來解決些問題。

 
 
 
 
  1. function loadScript(url, callback)
  2. {
  3.     var head = document.head;
  4.     var script = document.createElement('script');
  5.     script.type = 'text/javascript';
  6.     script.src = url;
  7.     script.onload = callback;
  8.     head.appendChild(script);
  9. }

然后編寫在lambda函數(shù)中加載腳本后要使用的代碼

 
 
 
 
  1. var myPrettyCode = function() {
  2.    // Here, do whatever you want
  3. };

然后,運行代碼:

 
 
 
 
  1. loadScript("my_lovely_script.js", myPrettyCode);

請注意,腳本可能在加載DOM之后或之前執(zhí)行,具體取決于瀏覽器以及是否包括行script.async = false;。

本文轉載自微信公眾號「大遷世界」,可以通過以下二維碼關注。轉載本文請聯(lián)系大遷世界公眾號。


分享名稱:JavaScript中10個需要掌握基礎的問題
網(wǎng)頁網(wǎng)址:http://www.5511xx.com/article/cohecep.html