寫出下題的輸出
1、函數的實參與形參length
var length = 10; function fn() { console.log(this.length); } var obj = { length: 5, method: function(fn) { fn(); arguments[0](); } }; console.log(obj.method(fn, 1)); // 0 2
我們都知道,[1, 2, 3].length可以得到3,"123".length可以得到3,那么函數的length得到什么呢?
function test(a,b,c) {} test.length // 3
function test(a,b,c,d) {} test.length // 4
可以看到,函數的length似乎返回了參數的個數,那么對於形參和實參有沒有區別呢?答案是有。
function test() { console.log( arguments.length );} test(1,2,3); // 輸出 3
test(1,2,3,4); // 輸出 4
可以看到,在函數中,用arguments.length取到的是函數的實際參數的個數。
另外,我們要知道var length = 10 這樣寫是不行的,因為length是JavaScript內置的屬性,不能用作變量名或函數名。戳這里http://www.runoob.com/js/js-reserved.html查看JavaScript有哪些保留關鍵字、內置屬性等。
所以,當執行fn()時,this.length打印的是fn這個函數的形參的個數,為0;而執行arguments[0]()時,實際上是obj.method()這個方法的arguments調用了fn函數,this.length的this指向的是arguments,他的實際參數個數為2。
2、函數的解析與預解析過程(變量提升)
function fn(a) { console.log(a); // function a() {alert(1)} var a = 2; function a() {alert(1)} console.log(a); //2 } fn(1);
這道題還是挺吊炸天的。。我也想了半天。。下面我來講一下,涉及到函數的解析和預解析過程。
首先遇到function fn這樣的函數聲明,會進行函數預解析這么個過程,什么是函數預解析?通俗的說就是,從函數體里找變量和函數聲明的過程,找到的變量(遇到var就找到了變量)不會去讀具體的值,只會賦為undefined;找到的函數聲明會賦值為整個函數體,這里有個知識點就是,如果找到的變量和聲明同名,那么聲明會覆蓋變量(我的理解是,畢竟函數體比undefined的強嘛)。
比如此例中,預解析時找到了變量a,並且賦值為undefined,找到了聲明function a(){alert(1)},為整個函數體;兩者同名,所以聲明覆蓋了變量a的值,a不再是undefined的,而是函數體。
預解析完成后調用了方法,開始一步一步走方法。首先console.log(a),這時打印出的是函數體;接着var a = 2,a的值從函數體被改成了 2 ;接着是個function a(){}函數聲明,注意,聲明不能改變變量的值,所以走完這一句,a的值還是2,接着打印出了2。
有人肯定有這樣的疑惑,為什么a=1傳進去沒起作用呢?這里有一個原則,就是局部變量優先,基於這個原則,我們再來分析一下a的變化過程。預解析中,a=undefined,a=function(){alert(1)},此時參數有值等於1,本應該將a賦值為1,但卻沒有,原因是此時的a已經等於局部函數聲明function(){alert(1)},所以外部傳進來的參數1並沒有取代a的值;假如本例沒有function(){alert(1)}這一句,打印出的將是1, 2。
局部變量優先原則,原理同下:
var a = 5; function fn(){ var a = 10; console.log(a) // 10,局部變量優先,在局部找到a后,不會再向外查找 }
3、變量提升、window的變量
if('a' in window) { var a = 10; } console.log(a); // 10
首先,if(){}的花括號並不像function(){}的花括號一樣,具有自己的塊級作用域,if的花括號還是全局的環境。根據JavaScript的變量提升機制,var a會被js引擎解釋到第一行,如下:
var a; if ('a' in window) { a = 10; }
接着有個知識點,全局變量是window對象的屬性,所以'a' in window會返回true,答案就很直白了。
這道題我在做的時候踩了個坑,我在代碼編輯器里寫了如下代碼:
window.onload = function(){ if('a' in window){ var a = 10; } console.log(a) // undefined }
這時候,a這個變量是定義在匿名函數function(){}里的,屬於該函數的局部變量,所以a不再是window的對象。大家一定要注意細節。
4、基本類型無屬性
var a = 10; a.pro = 10; console.log(a.pro + a); // NaN var s = 'hello'; s.pro = 'world'; console.log(s.pro + s) // undefinedhello
變量a與s都是基本類型,無法給他們添加屬性,所以a.pro和s.pro都是undefined。
undefined + 10 得到NaN(not a number)。
undefined + 'hello' 得到undefinedhello,其中undefined被轉化為字符串類型。
如果實在想給字符串添加屬性,我們需要將字符串定義為對象類型的字符串,如下:
var a= new String('objectString') a.pro = "aaaaaaa" console.log(a.pro) // aaaaaaa
5、async與await的執行
async function sayHello() { console.log('Hello') await sleep(1000) console.log('world!') } function sleep(ms) { return new Promise(resolve => { console.log("666666"); setTimeout(resolve, ms); console.log("888888")}) } sayHello() // hello 666666 888888 world!
async 表示這是一個async函數,await只能用在這個函數里面。
await 表示在這里等待promise返回結果了,再繼續執行。
首先打出hello,到了await,會等待promise的返回,所以“world”不會立刻打出,接着進入sleep函數,打出666,接着開了一個1秒的定時器,雖然js是單線程的,但setTimeout是異步的,在瀏覽器中,異步操作都是被加入到一個稱為“events loop”隊列的地方,瀏覽器只會在所有同步代碼執行完成之后采取循環讀取的方式執行這里面的代碼,所以resolve被加入任務隊列,先打印了888,一秒后執行了resolve,表示promise成功返回,打出了world。
以上每道題都是本渣自己的想法和理解,如有不正確的地方煩請讀者指正,大佬輕噴~