溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript中必須掌握的基礎(chǔ)知識(shí)案例

發(fā)布時(shí)間:2020-10-14 15:06:40 來源:億速云 閱讀:165 作者:小新 欄目:web開發(fā)

這篇文章主要介紹JavaScript中必須掌握的基礎(chǔ)知識(shí)案例,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

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

思路:首先,使用indexOf查找要?jiǎng)h除的數(shù)組元素的索引(index),然后使用splice方法刪除該索引所對(duì)應(yīng)的項(xiàng)。

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

const array = [2, 5, 9]

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

console.log(array)
// [ 2, 9 ]復(fù)制代碼

splice的第二個(gè)參數(shù)是要?jiǎng)h除的元素?cái)?shù)量。注意,splice會(huì)在適當(dāng)?shù)奈恢眯薷臄?shù)組,并返回一個(gè)包含已刪除元素的新數(shù)組。

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

// 僅刪除第一個(gè)匹配項(xiàng)
function removeItemOnce (arr, value) {
  let index = arr.indexOf(value)
  if (index > -1) {
    arr.splice(index, 1)
  }
  return arr
}

// 刪除所有匹配項(xiàng)
function removeItemAll (arr, value) {
  let i = 0
  while(i < arr.length) {
    if (arr[i] === value) {
      arr.splice(i, 1)
    } else {
      ++i
    }
  }
}復(fù)制代碼

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

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

array.splice(i, 1)復(fù)制代碼

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

for (let i = array.length - 1; i>=0; i--) {
  if (array[i] === number) {
    array.splice(i, 1)
  }
}復(fù)制代碼

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

delete array[i]復(fù)制代碼

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

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

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

事例:

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

// 模擬單擊鏈接
window.location.href = "http://stackoverflow.com"復(fù)制代碼

你還可以這樣做:

$(location).attr('href', 'http://stackoverflow.com')復(fù)制代碼

3.JavaScript 閉包是如何工作的

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

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

在下面的代碼中,inner與調(diào)用foo時(shí)創(chuàng)建的執(zhí)行上下文的詞法環(huán)境一起形成一個(gè)閉包,并對(duì)外部隱藏了變量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 的唯一辦法就是調(diào)用 f復(fù)制代碼

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

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

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

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

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

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

私有實(shí)例變量

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

function Car(manufacturer, model, year, color) {
  return {
    toString() {
      return `${manufacturer} ${model} (${year}, ${color})`
    }
  }
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())復(fù)制代碼

函數(shù)式編程

在下面的代碼中,函數(shù)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復(fù)制代碼

面向事件的編程

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

const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'

function onClick() {
  $('body').style.background = BACKGROUND_COLOR
}

$('button').addEventListener('click', onClick)復(fù)制代碼
<button>Set background color</button>復(fù)制代碼

模塊化

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

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())復(fù)制代碼

事例 1:

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

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

let f = foo()
f()復(fù)制代碼

事例 2:

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

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復(fù)制代碼

事例 3:

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

在下面的代碼中,每次循環(huán)中,都會(huì)創(chuàng)建一個(gè)新的inner函數(shù),變量i被覆蓋,但是因var會(huì)讓 i 提升到函數(shù)的頂部,所以所有這些inner函數(shù)覆蓋的都是同一個(gè)變量,這意味著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復(fù)制代碼

最后一點(diǎn):

  • 每當(dāng)在JavaScript中聲明函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)閉包。

  • 從一個(gè)函數(shù)內(nèi)部返回另一個(gè)函數(shù)是閉包的經(jīng)典例子,因?yàn)橥獠亢瘮?shù)內(nèi)部的狀態(tài)對(duì)于返回的內(nèi)部函數(shù)是隱式可用的,即使外部函數(shù)已經(jīng)完成執(zhí)行。

  • 只要在函數(shù)內(nèi)使用eval(),就會(huì)使用一個(gè)閉包。eval的文本可以引用函數(shù)的局部變量,在非嚴(yán)格模式下,甚至可以通過使用eval('var foo = ')創(chuàng)建新的局部變量。

  • 當(dāng)在函數(shù)內(nèi)部使用new Function()(Function constructor)時(shí),它不會(huì)覆蓋其詞法環(huán)境,而是覆蓋全局上下文。新函數(shù)不能引用外部函數(shù)的局部變量。
  • 在JavaScript中,閉包類似于在函數(shù)聲明時(shí)保留對(duì)作用域的引用(而不是復(fù)制),后者又保留對(duì)其外部作用域的引用,以此類推,一直到作用域鏈頂端的全局對(duì)象。
  • 聲明函數(shù)時(shí)創(chuàng)建一個(gè)閉包。 當(dāng)調(diào)用函數(shù)時(shí),此閉包用于配置執(zhí)行上下文。

  • 每次調(diào)用函數(shù)時(shí)都會(huì)創(chuàng)建一組新的局部變量。

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

function foo(x) {
  var tmp = 3;

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

  bar(10);
}

foo(2);復(fù)制代碼

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

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

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復(fù)制代碼

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

但是,由于tmp仍然在bar的閉包內(nèi)部徘徊,因此可以對(duì)其進(jìn)行遞增。 每次調(diào)用bar時(shí),它將增加1

閉包最簡(jiǎn)單的例子是這樣的:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();復(fù)制代碼

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

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

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

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

引用一些有趣的部分:

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

嚴(yán)格模式在很多方面都有幫助:

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

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

// Non-strict code...

(function(){
  "use strict";

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

// Non-strict code... 
復(fù)制代碼

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

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

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

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

ECMAScript 6 引入了string .prototype.include

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

console.log(string.includes(substring));復(fù)制代碼

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

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

console.log(string.indexOf(substring) !== -1);復(fù)制代碼

為了使其在舊的瀏覽器中運(yùn)行,可以使用這種polyfill

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

    if (start + search.length > this.length) {
      return false;
    } else {
      return this.indexOf(search, start) !== -1;
    }
  };
}復(fù)制代碼

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

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

如,函數(shù)表達(dá)式

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

var functionOne = function() {
  console.log("Hello!");
};復(fù)制代碼

函數(shù)聲明:

// "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}復(fù)制代碼

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

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError復(fù)制代碼

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

function xyz(){
  function abc(){};
  // abc 在這里定義...
}
// ...不是在這里復(fù)制代碼

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

function abc(){};
var xyz = abc;復(fù)制代碼

在本例中,xyz和abc都是同一個(gè)對(duì)象的別名

console.log(xyz === abc) // true復(fù)制代碼

它的名稱是自動(dòng)分配的。但是當(dāng)你定義它的時(shí)候

var abc = function(){};
console.log(abc.name); //  ""復(fù)制代碼

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

// 假設(shè) really.long.external.scoped 為 {}
really.long.external.scoped.name = function shortcut(n){
  // 它遞歸地調(diào)用自己:
  shortcut(n - 1);
  // ...
  // 讓它自己作為回調(diào)傳遞::
  someFunction(shortcut);
  // ...
}復(fù)制代碼

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

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

function abc(){}復(fù)制代碼

這里的abc可以定義在當(dāng)前作用域的任何地方:

// 我們可以在這里調(diào)用
abc(); 

// 在這里定義
function abc(){}

// 也可以在這里調(diào)用 
abc(); 
復(fù)制代碼

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

// 我們可以在這里調(diào)用
abc(); 
return;
function abc(){}復(fù)制代碼

下面是一個(gè)函數(shù)表達(dá)式:

var xyz = function(){};復(fù)制代碼

這里的xyz是從賦值點(diǎn)開始定義的:

// 我們不可以在這里調(diào)用
xyz(); 

// 在這里定義 xyz
xyz = function(){}

// 我們可以在這里調(diào)用
xyz(); 
復(fù)制代碼

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

var xyz = function abc(){};
console.log(xyz.name); // "abc"復(fù)制代碼

就個(gè)人而言,我們更喜歡使用函數(shù)表達(dá)式聲明,因?yàn)檫@樣可以控制可見性。當(dāng)我們像這樣定義函數(shù)時(shí):

var abc = function(){};復(fù)制代碼

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

function abc(){};復(fù)制代碼

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

7.如何從 JavaScript 對(duì)象中刪除屬性?

我們可以這樣刪除對(duì)象的屬性:

delete myObject.regex;
// 或者
delete myObject['regex'];
//  或者
var prop = "regex";
delete myObject[prop];復(fù)制代碼

事例:

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

console.log(myObject);復(fù)制代碼

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

var obj = {
  myProperty: 1    
}
console.log(obj.hasOwnProperty('myProperty')) // true
delete obj.myProperty
console.log(obj.hasOwnProperty('myProperty')) // false復(fù)制代碼

delete 操作符不是直接釋放內(nèi)存,它不同于簡(jiǎn)單地將nullundefined值賦給屬性,而是將屬性本身從對(duì)象中刪除。

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

delete只對(duì)其描述符標(biāo)記為configurable的屬性有效。

8.  JS 的比較中應(yīng)使用哪個(gè)等于運(yùn)算符(== vs ===)?

嚴(yán)格相等運(yùn)算符(===)的行為與抽象相等運(yùn)算符(==)相同,除非不進(jìn)行類型轉(zhuǎn)換,而且類型必須相同才能被認(rèn)為是相等的。

==運(yùn)算符會(huì)進(jìn)行類型轉(zhuǎn)換后比較相等性。 ===運(yùn)算符不會(huì)進(jìn)行轉(zhuǎn)換,因此如果兩個(gè)值的類型不同,則===只會(huì)返回false。

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

下面是一些事例:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

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

' \t\r\n ' == 0     // true復(fù)制代碼

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

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復(fù)制代碼

特殊情況是,當(dāng)你將一個(gè)字符串字面量與一個(gè)字符串對(duì)象進(jìn)行比較時(shí),由于該對(duì)象的toStringvalueOf方法,該對(duì)象的值與相字面量的值一樣。

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

"abc" == new String("abc")    // true
"abc" === new String("abc")   // false復(fù)制代碼

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

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

使用==運(yùn)算符(等于)

true == 1; //true, 因?yàn)?true 被轉(zhuǎn)換為1,然后進(jìn)行比較
"2" == 2;  //true, 因?yàn)?“2” 被轉(zhuǎn)換成 2,然后進(jìn)行比較復(fù)制代碼

使用===操作符

true === 1; //false
"2" === 2;  //false復(fù)制代碼

9.在 JavaScript 中深拷貝一個(gè)對(duì)象的最有效方法是什么?

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

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

JSON.parse(JSON.stringify(object))復(fù)制代碼
const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(), 
  undef: undefined,  // 丟失
  inf: Infinity,  // 被設(shè)置為 null
  re: /.*/,  // 丟失
}
console.log(a);
console.log(typeof a.date);  // object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
/*
object
{
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: '2020-09-04T00:45:41.823Z',
  inf: null,
  re: {}
}

*/
console.log(typeof clone.date);  // string復(fù)制代碼

使用庫進(jìn)行可靠的克隆

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

  • lodashcloneDeep; 可以通過lodash.clonedeep模塊單獨(dú)導(dǎo)入,如果你尚未使用提供深拷貝功能的庫,那么它可能是你的最佳選擇

  • AngularJS – angular.copy

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

ES6

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

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax復(fù)制代碼

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

JSON.parse(JSON.stringify(obj))復(fù)制代碼

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

當(dāng)deep標(biāo)志設(shè)置為false(淺克隆)時(shí),jQuery.extend非常快。 這是一個(gè)不錯(cuò)的選擇,因?yàn)樗ㄒ恍┯糜陬愋万?yàn)證的額外邏輯,并且不會(huì)復(fù)制未定義的屬性等,但這也會(huì)使你的速度變慢。

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

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}復(fù)制代碼

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

10.如何在另一個(gè)JavaScript文件中包含一個(gè)JavaScript文件?

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

但是從2015年(ES6)開始,JavaScript已經(jīng)有了ES6模塊標(biāo)準(zhǔn),可以在Node中導(dǎo)入模塊。為了與舊版瀏覽器兼容,可以使用WebpackRollup之類的構(gòu)建工具和/或Babel這樣的編譯工具。

ES6 Module

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

使用package.json

{
    "type": "module"
}復(fù)制代碼

module.js:

export function hello() {
  return "Hello";
}復(fù)制代碼

main.js:

import { hello } from './module.js';
let val = hello();  // val is "Hello";復(fù)制代碼

使用.mjs,會(huì)有對(duì)應(yīng)的module.mjs

export function hello() {
  return "Hello";
}復(fù)制代碼

main.mjs

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";復(fù)制代碼

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

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');
</script>復(fù)制代碼
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const p = document.createElement('p');
  p.textContent = `Hello ${text}`;
  document.body.appendChild(p);
}復(fù)制代碼

大家都說簡(jiǎn)歷沒項(xiàng)目寫,我就幫大家找了一個(gè)項(xiàng)目,還附贈(zèng)【搭建教程】。

瀏覽器中的動(dòng)態(tài)導(dǎo)入

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

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>復(fù)制代碼

Node.js require

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

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}復(fù)制代碼

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

動(dòng)態(tài)加載文件

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

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

    document.head.appendChild(script); 
}復(fù)制代碼

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

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

例如: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復(fù)制代碼

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

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

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

    script.onload = callback;

    head.appendChild(script);
}復(fù)制代碼

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

var myPrettyCode = function() {
   // Here, do whatever you want
};復(fù)制代碼

然后,運(yùn)行代碼:

loadScript("my_lovely_script.js", myPrettyCode);復(fù)制代碼

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

以上是JavaScript中必須掌握的基礎(chǔ)知識(shí)案例的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI