JavaScript 是一種有趣的語言,我們都喜歡它,因為它的性質。瀏覽器是JavaScript的主要運作的地方,兩者在我們的服務中協同工作。 JS有一些概念,人們往往會對它掉以輕心,有時可能會忽略不計。原型、閉包和事件循環等概念仍然是大多數JS開發人員繞道而行的晦澀領域之一。正如我們所知,無知是一件危險的事情,它可能會導致錯誤。
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
接下來,來看看幾個問題,你也可以試試看,然後作答。
問題1:瀏覽器控制台上會列印什麼?
var a = 10; function foo() { console.log(a); // ?? var a = 20; } foo();
問題2:如果我們使用let 或const 來代替var,輸出是否相同
var a = 10; function foo() { console.log(a); // ?? let a = 20; } foo();
問題3:「newArray」中有哪些元素?
var array = []; for(var i = 0; i i); } var newArray = array.map(el => el()); console.log(newArray); // ??
問題4:如果我們在瀏覽器控制台中執行'foo'函數,是否會導致堆疊溢位錯誤?
function foo() { setTimeout(foo, 0); // 是否存在堆栈溢出错误? };
問題5: 如果在控制台中執行以下函數,頁面(選項卡)的UI 是否仍回應
function foo() { return Promise.resolve().then(foo); };
問題6:我們能否以某種方式為下面的語句使用展開運算而不導致類型錯誤
var obj = { x: 1, y: 2, z: 3 }; [...obj]; // TypeError
#問題7:運行以下程式碼片段時,控制台上會列印什麼?
var obj = { a: 1, b: 2 }; Object.setPrototypeOf(obj, {c: 3}); Object.defineProperty(obj, 'd', { value: 4, enumerable: false }); // what properties will be printed when we run the for-in loop? for(let prop in obj) { console.log(prop); }
問題8:xGetter() 會印出什麼值?
var x = 10; var foo = { x: 90, getX: function() { return this.x; } }; foo.getX(); // prints 90 var xGetter = foo.getX; xGetter(); // prints ??
答案
現在,讓我們從頭到尾回答每個問題。我將給您一個簡短的解釋,同時試圖揭開這些行為的神秘面紗,並提供一些參考資料。
問題1:undefined
解析:
#使用var
關鍵字宣告的變數在JavaScript中會被提升,並在記憶體中分配值undefined
。但初始化恰發生在你給變數賦值的地方。另外,var
宣告的變數是函數作用域的,而let
和const
是塊作用域的。所以,這就是這個過程的樣子:
var a = 10; // 全局使用域 function foo() { // var a 的声明将被提升到到函数的顶部。 // 比如:var a console.log(a); // 打印 undefined // 实际初始化值20只发生在这里 var a = 20; // local scope }
問題 2:ReferenceError:a undefined
。
解析:
let
和const
宣告可以讓變數在其作用域上受限於它所使用的區塊、語句或表達式。與var
不同的是,這些變數沒有被提升,並且有一個所謂的暫時死區(TDZ)。試圖存取TDZ中的這些變數將引發ReferenceError
,因為只有在執行到達宣告時才能存取它們。
var a = 10; // 全局使用域 function foo() { // TDZ 开始 // 创建了未初始化的'a' console.log(a); // ReferenceError // TDZ结束,'a'仅在此处初始化,值为20 let a = 20; }
問題3: [3, 3, 3]
#解析:
在for
循環的頭部宣告帶有var
關鍵字的變數會為該變數建立單一綁定(儲存空間)。閱讀更多關於閉包的資訊。讓我們再看一次for迴圈。
// 误解作用域:认为存在块级作用域 var array = []; for (var i = 0; i i); } var newArray = array.map(el => el()); console.log(newArray); // [3, 3, 3]
如果使用 let
宣告一個具有區塊級作用域的變量,則為每個循環迭代建立一個新的綁定。
// 使用ES6块级作用域 var array = []; for (let i = 0; i i); } var newArray = array.map(el => el()); console.log(newArray); // [0, 1, 2]
解決這個問題的另一種方法是使用閉包。
let array = []; for (var i = 0; i el()); console.log(newArray); // [0, 1, 2]
問題4 : 不會溢位
解析:
JavaScript並發模型是基於「事件循環」。當我們說「瀏覽器是 JS 的家」時我真正的意思是瀏覽器提供運行時環境來執行我們的JS程式碼。
瀏覽器的主要元件包括呼叫堆疊,事件循環,任務佇列和Web API。像是setTimeout
,setInterval
和Promise
這樣的全域函數不是JavaScript的一部分,而是 Web API 的一部分。
JS呼叫堆疊是後進先出(LIFO)的。引擎每次從堆疊中取出一個函數,然後從上到下依序運行程式碼。每當它遇到一些非同步程式碼,例如setTimeout
,它就把它交給Web API
#(箭頭1)。因此,每當事件被觸發時,callback
都會被傳送到任務佇列(箭頭2)。
事件循環(Event loop)不斷監視任務佇列(Task Queue),並依照它們排隊的順序一次處理一個回呼。每當呼叫堆疊(call stack)為空時,Event loop取得回呼並將其放入堆疊(stack )(箭頭3)中進行處理。請記住,如果呼叫堆疊不是空的,則事件循環不會將任何回調推入堆疊。
現在,有了這些知識,讓我們來回答前面提到的問題:
步骤
- 调用
foo()
会将foo
函数放入调用堆栈(call stack)。 - 在处理内部代码时,JS引擎遇到
setTimeout
。 - 然后将
foo
回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空 - 计时器被设置为0,因此
foo
将被发送到任务队列(箭头2)。 - 由于调用堆栈是空的,事件循环将选择
foo
回调并将其推入调用堆栈进行处理。 - 进程再次重复,堆栈不会溢出。
问题5 : 不会响应
解析:
大多数时候,开发人员假设在事件循环
在底层来看,JavaScript中有宏任务和微任务。setTimeout
回调是宏任务,而Promise
回调是微任务。
主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行后返回到事件循环之前清空。因此,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面、
现在,当你在控制台中运行以下代码段
function foo() { return Promise.resolve().then(foo); };
每次调用'foo
'都会继续在微任务队列上添加另一个'foo
'回调,因此事件循环无法继续处理其他事件(滚动,单击等),直到该队列完全清空为止。 因此,它会阻止渲染。
问题6 : 会导致TypeError错误
解析:
展开语法 和 for-of 语句遍历iterable
对象定义要遍历的数据。Array
或Map
是具有默认迭代行为的内置迭代器。对象不是可迭代的,但是可以通过使用iterable和iterator协议使它们可迭代。
在Mozilla文档中,如果一个对象实现了@@iterator
方法,那么它就是可迭代的,这意味着这个对象(或者它原型链上的一个对象)必须有一个带有@@iterator
键的属性,这个键可以通过常量Symbol.iterator
获得。
上述语句可能看起来有点冗长,但是下面的示例将更有意义:
var obj = { x: 1, y: 2, z: 3 }; obj[Symbol.iterator] = function() { // iterator 是一个具有 next 方法的对象, // 它的返回至少有一个对象 // 两个属性:value&done。 // 返回一个 iterator 对象 return { next: function() { if (this._countDown === 3) { const lastValue = this._countDown; return { value: this._countDown, done: true }; } this._countDown = this._countDown + 1; return { value: this._countDown, done: false }; }, _countDown: 0 }; }; [...obj]; // 打印 [1, 2, 3]
还可以使用 generator 函数来定制对象的迭代行为:
var obj = {x:1, y:2, z: 3} obj[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; } [...obj]; // 打印 [1, 2, 3]
问题7 : a, b, c
解析:
for-in
循环遍历对象本身的可枚举属性以及对象从其原型继承的属性。 可枚举属性是可以在for-in
循环期间包含和访问的属性。
var obj = { a: 1, b: 2 }; var descriptor = Object.getOwnPropertyDescriptor(obj, "a"); console.log(descriptor.enumerable); // true console.log(descriptor); // { value: 1, writable: true, enumerable: true, configurable: true }
现在你已经掌握了这些知识,应该很容易理解为什么我们的代码要打印这些特定的属性
var obj = { a: 1, b: 2 }; //a,b 都是 enumerables 属性 // 将{c:3}设置为'obj'的原型,并且我们知道 // for-in 循环也迭代 obj 继承的属性 // 从它的原型,'c'也可以被访问。 Object.setPrototypeOf(obj, { c: 3 }); // 我们在'obj'中定义了另外一个属性'd',但是 // 将'enumerable'设置为false。 这意味着'd'将被忽略。 Object.defineProperty(obj, "d", { value: 4, enumerable: false }); for (let prop in obj) { console.log(prop); } // 打印 // a // b // c
问题8 : 10
解析:
在全局范围内初始化x
时,它成为window对象的属性(不是严格的模式)。看看下面的代码:
var x = 10; // global scope var foo = { x: 90, getX: function() { return this.x; } }; foo.getX(); // prints 90 let xGetter = foo.getX; xGetter(); // prints 10
咱们可以断言:
window.x === 10; // true
this
始终指向调用方法的对象。因此,在foo.getx()
的例子中,它指向foo
对象,返回90
的值。而在xGetter()
的情况下,this
指向 window对象, 返回 window 中的x
的值,即10
。
要获取 foo.x
的值,可以通过使用Function.prototype.bind
将this
的值绑定到foo
对象来创建新函数。
let getFooX = foo.getX.bind(foo); getFooX(); // 90
就这样! 如果你的所有答案都正确,那么干漂亮。 咱们都是通过犯错来学习的。 这一切都是为了了解背后的“原因”。
推荐教程:《JS教程》
以上是8個問題測試你的JavaScript基礎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

記事本++7.3.1
好用且免費的程式碼編輯器

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器