1、在局部作用域中,使用var操作符定義的變量將成為定義該變量的作用域中的局部變量,省略var的會創建全局變量;在全局作用域中,不管是否使用var操作符定義的變量都會創建一個全局變量。但是,在全局作用域中使用var創建的全局變量是不能被delete刪除的,而未使用var創建的變量和局部作用域中未使用var操作符創建的全局變量是可以刪除的。(與其說省略var會創建全局變量,倒不如說省略var會直接給全局對象添加一個新的屬性,因為ES中的變量只能通過var關鍵字才能創建);
var a = 1; // 全局作用域使用var創建全局變量 b = 2; // 全局作用域未使用var創建全局變量 function fn() { c = 3; // 局部作用域未使用var,創建全局變量 } fn(); console.log(a); //1 console.log(b); //2 console.log(c); //3 delete a; //false delete b; //true delete c; //true console.log(typeof a); //number console.log(typeof b); //undefined console.log(typeof c); //undefined
2、瀏覽器中,Window對象作為全局對象,全局作用域中聲明的變量、函數都會變成window對象的屬性和方法。然而,定義全局變量和在window對象上直接定義屬性是有差別的: 全局變量不能通過delete操作符刪除,而直接定義在window對象上的屬性可以。原因是通過var語句添加的window屬性的[[Configurable]]特性被設置為false。 例如:
var a = 10; window.b = 20; //IE < 9 時拋出錯誤,其他瀏覽器中返回false delete window.a; //IE < 9 時拋出錯誤,其他瀏覽器中返回true delete window.b; alert(window.a); //10 alert(window.b); //undefined
3、undefined不是關鍵字,也不是保留字。在某些低版本的瀏覽器(例如IE8、IE7)中值是可以被修改的(在ECMAScript3中,undefined是可讀/寫的變量,可以給它賦任意值,這個錯誤在ECMAScript5中做了修正),在其他瀏覽器中是可以被賦值的,但是值不會改變。另外eval、arguments之類的也不是關鍵字或者保留字,並且可以更改它們的值。
undefined = 1; console.log(undefined); //undefined //IE8中 undefined = 1; console.log(undefined); //1 eval = 1; console.log(eval); //1
4、如果在文檔開始沒發現文檔聲明,則瀏覽器會默認開啟混雜模式。
5、typeof null 的值為"object",含義:null值表示空對象指針。然而:
typeof null //"object" null instanceof Object //false
6、Array類型的原型是數組
Array.isArray(Array.prototype ); //true Array.isArray(String.prototype ) //false
7、浮點數中必須包含一個小數點,並且小數點后面必須至少有一位數字,如果小數點后面沒有任何數字,那么這個數字會作為整數值保存;同樣,如果這個數小數點后面只有0(本身就是一個整數),那么它也會作為整數值保存。這么做的原因:保存浮點數值需要的內存空間是保存整數值的兩倍,為了節省內存。
1..toString().length //1 (1.+"").length //1 1.0.toString().length //1 (1.0+"").length //1 (1.0000000000+"").length //1 1.0000000000.toString().length //1 (1.00000000001+"").length //13 1.00000000001.toString().length //13
8、如果整數后面跟着一個句點(.),那么JavaScript引擎會將它理解為前面整數的小數點
1.toString() //SyntaxError: identifier starts immediately after numeric literal JavaScript引擎希望看到的數字而不是toString() (1).toString() // "1" 1..toString() // "1" 1...toString() //SyntaxError: missing name after . operator
9、JavaScript中浮點數值的最高精度為17位小數,浮點數值計算會產生舍入誤差。所以盡量避免測試某個特定的浮點數的值。
0.1 + 0.2 == 0.3 //false 0.1 + 0.2 == 0.30000000000000004 //true 0.8 - 0.2 //0.6000000000000001 0.8 - 0.6 //0.20000000000000007
10、NaN表示"不是數字類型",但是typeof NaN的值為"number"。NaN不等於任何值,包括它自己。
typeof NaN //"number" NaN == NaN //false
11、對未初始化和未聲明的變量執行typeof操作符都會返回undefined值。但是未初始化的變量我們可以進行加減乘除等操作,而未聲明的操作只能進行typeof操作,除此之外都會報錯。
b + "" // Uncaught ReferenceError: b is not defined typeof b //"undefined" var x; typeof x //"undefined" x + "abc" //"undefinedabc"
12、ES3中引入undefined值,為了區分空對象指針和未初始化的變量。null == undefined的結果為true。
13、typeof正則表達式結果為"object",但是在Chrome7及之前版本和Safari 5及之前版本的結果為“function”。正則表達式的valueOf方法返回正則表達式本身。
typeof /123/ // "object" /123/.valueOf() // /123/
14、數組的長度可以修改,並且會影響數組元素的值。例如:
var arr = [1,2,3,4,5]; arr.length = 1; alert(arr[2]); //undefined
15、函數的length屬性表示函數希望接受的命名參數的個數。
(function fn(a,b,c){}).length //3
16、每當讀取基本類型(boolean、string、number)值的時候,后台會創建一個對應的基本包裝類型的對象,從而讓我們能夠調用一些方法來操作這些數據。
var s1 = "abc"; s1.color = "red"; alert(s1.color); //undefined
為什么基本數據類型會有屬性?其實在第二句的時候會創建臨時的對應基本包裝類型對象並調用指定的方法,之后會被立即銷毀。
17、命名函數表達式在創建的時候,會在當前作用域最前段添加一個新的對象{func_name:refer_function_expression},然后,將作用域鏈添加到函數表達式的[[scope]]中,接着再刪除該對象。命名函數表達式的名字只在當前函數中起作用。例如:
var x=1; if( function f(){} ){ x+=typeof f; } console.log(x); //'1undefined'
18、函數作用域鏈 Scope = 被調用函數函數的活動對象 + [[scope]]; [[scope]]屬性在函數創建時被存儲,永遠不變,直到函數銷毀。函數可以不被調用,但這個屬性一直存在。與作用域鏈相比,作用域鏈是執行環境的一個屬性,而[[scope]]是函數的屬性。
19、[[scope]]屬性存儲着所有父環境的活動對象和變量對象。然而,用Function構造函數定義的函數例外,它的[[scope]]僅僅包含全局對象。
var name = "global"; function outer() { var name = "inner"; var getName = new Function('alert(name)'); getName(); } outer(); //global
20、 setTimeout(function(){},1000)並不表示在1s之后調用對應函數,它表示1s之后把對應的任務添加到任務隊列中。如果隊列是空的,那么添加的任務會立即執行;否則就要等待前面的代碼先執行。因為JavaScript是單線程語言。同樣的原理也存在於setInterval方法上,這可能會導致這樣一種現象發生:setInterval添加的第一個任務在任務隊列中等待前面代碼的執行,第二個任務也被添加到了任務隊列中。這樣就可能會導致不必要的錯誤發生。因此,我們應該盡量不要使用setInterval間歇調用,而是用setTimeout超時調用來模擬間歇調用。
function test() { //... } setInterval(test,1000); //改為 function test() { setTimeout(test,1000); } test();
21、call 方法可將一個函數的對象上下文從初始的上下文改變為指定的新對象。 然而如果call方法的第一個參數為null或者undefined,那么call方法將把全局對象(瀏覽器中為window)作為新的對象上下文。
var x = "window_x"; var obj = { x: "obj_x", getX: function() { console.log(this.x); } }; obj.getX(); //obj_x obj.getX.call(null); //window_x obj.getX.call(undefined); //window_x obj.getX.call(window); //window_x obj.getX.call(document); //undefined
22、arguments對象中除了包含傳遞到函數中的參數外,還包含了length(參數個數)、callee(當前函數的引用,嚴格模式下不能訪問)等屬性。其中,arguments中保存的參數的值與對應的命名參數的值是保持同步的,但這並不表明它們共享同一個內存地址。它們的內存空間是獨立的,其值通過JavaScript引擎來保持同步。注意,上面提到的同步有一個前提:對應索引值要小於傳入到函數中的參數個數。即如果你傳入函數2個參數,那么arguments[2]與命名參數是互不影響的。另外,arguments的length屬性是由傳入函數的參數決定的,就算手動為arguments添加元素,其length屬性的值是不變的,除非你手動修改length的值。例如:
function fn(a,b,x) { arguments[1] = 10; console.log(b); } fn(1,2); //10 function fn(a,b,x) { arguments[2] = 10; console.log(x); } fn(1,2); //undefined function fn(a,b,x) { x = 10; console.log(arguments[2]); } fn(1,2); //undefined function fn(a,b,x) { arguments[2] = 10; arguments[3] = 20; console.log(arguments.length); } fn(1,2); //2 function fn(a,b,x) { arguments.length = 10; console.log(arguments.length); } fn(1,2); //10
23、arguments雖然有length屬性,並可以通過[index]訪問對應的值,但它並不是數組,然而我們可以通過Array.prototype.slice方法將它轉換成數組。
function fn (a,b) { console.log(Array.isArray(arguments)); //false arguments = Array.prototype.slice.call(arguments,0); console.log(Array.isArray(arguments)); //true } fn(1,2);
24、document.write()、document.writeln()可以在頁面呈現的過程中直接向頁面中輸出內容,但如果頁面加載結束后再調用document.write()、document.writeln(),那么輸出的內容將會重寫整個頁面。
<body> <div> Hello <script> document.write(" world!"); </script> </div> <script> window.onload = function() { setTimeout(function() { document.write("Be Matched for Marriage!!!"); //輸出的內容將會重寫頁面 },1000); } </script> </body>
25、在Html中每個擁有id屬性的元素,在JavaScript中有一個與之對應的全局變量,變量名為id的值。
<div id="myDiv"> Hello World! </div> <script> window.onload = function() { console.log(myDiv.innerHTML); // Hello World! }; </script>
26、 JavaScript語句優先原則:當{}既可以被理解為復合語句塊也可以被理解為對象直接量或函數聲明的時候,JavaScript將會將其理解成為復合語句塊。
{a:10} //返回10,而不是對象 : 表示標簽 var x = { a:10 } // {a:10}作為右值出現,不能是語句塊,只能理解為對象直接量
27、JavaScript中的加法操作其實很簡單,只能把數字和數字相加或者字符串和字符串相加,其它的所有類型都被轉換成這兩種類型相加形式。v1 + v2,轉換過程如下:
1) 如果v1或者v2是對象,則轉換為基本數據類型;
2) 如果v1或v2為字符串,或者原本為對象轉換成基本數據類型后為字符串,則把另一個也轉換成字符串,然后執行字符串鏈接操作得到結果;
3) 否則,轉換為數字類型,返回它們的和
28、JavaScript中的加法操作中,如果操作數為對象,JavaScript引擎通過內部的抽象操作ToPrimitive()將其轉換為基本數據類型:
ToPrimitive(input [, PreferredType]) PreferredType可以是Number或者String,如果為Number,則轉換過程如下:
1) 調用valueOf()方法,如果valueOf()返回的結果是基本數據類型,則返回結果為轉換的結果
2) 否則,調用toString()方法,如果返回結果為基本數據類型,則返回結果為轉換結果
3) 否則,拋出TypeError異常
如果PreferredType為String,則轉換操作的第一步和第二步的順序會調換。
如果省略PreferredType這個參數,則PreferredType的值會按照這樣的規則來自動設置:Date類型的對象會被設置為String,其它類型的值會被設置為Number。
var obj = { valueOf: function () { console.log("valueOf"); return {}; // 沒有返回基本數據類型 }, toString: function () { console.log("toString"); return {}; // 沒有返回基本數據類型 } } obj + "" // valueOf toString Uncaught TypeError: Cannot convert object to primitive value(…)
[] + [] // "" "" + "" {} + [] // 0 {}; +[] => {}; +"" 語句優先 [] + {} //"[object Object]" "" + "[object Object]" {} + {} // NaN {}; +{} => NaN 語句優先 ({}+{}) //"[object Object][object Object]" "[object Object]" + "[object Object]" ({}+[]) //"[object Object]" "[object Object]" + ""
29、+、-具有二義性,既可以表示一元加減操作符,又可以表示加法(減法)操作符,其中一元加減操作符為從右向左結合,加法(減法)操作符為從左向右結合。一元加減操作符的優先級高於加法(減法)操作符。++、--后置遞增(遞減)優先級高於一元加減操作符,++、--前置遞增(遞減)優先級等於一元加減操作符。
var a = 1, b = 2, c; c = a++ + - + - - - + + ++b; console.log(c); //4 console.log(a); //2 console.log(b); //3
30、JavaScript中的hoisting(懸置/置頂解析/預解析):你可以在函數的任何位置聲明多個var語句,並且它們就好像是在函數頂部聲明一樣發揮作用。
var a = 1; function fn() { console.log(a); //undefined var a = 10; console.log(a); //10 } fn(); //相當於 function fn() { var a; console.log(a); //undefined a = 10; console.log(a); //10 } fn();
31、與hoisting類似,函數聲明也有一個重要的特征——函數聲明提升:執行代碼之前會先讀取函數聲明。
fn(); function fn() { console.log(a); //undefined var a = 10; console.log(a); //10 }
因此下面這種寫法就不能達到想要的效果了:
if(condition) { function fn() { alert("1"); } } else { function fn() { alert("2"); } }
大多數瀏覽器會返回第二個聲明,忽略condition(這個問題在新版本的瀏覽器中做了改變);Firefox會在condition為true時返回第一個聲明。但是我們可以使用函數表達式來達到我們的目的:
var fn; if(condition) { fn = function() { alert("1"); } } else { fn = function() { alert("2"); } }
32、 對於左大括號的位置每個人有着不同的偏好:同一行或者另起一行。其實放在哪里無可非議,但是由於JavaScript中分號插入機制(semicolon insertion mechanism)的存在,某些情況下,大括號的位置會產生不必要的麻煩。例如:
function fn() { return // 下面代碼不會執行 { name : "Marco" } } var out = fn(); console.log(out); //相當於 function fn() { return ; { name: "Marco" } }
33、JavaScript中的ASI(自動分號插入機制)不是說在JavaScript引擎解析過程中將分號添加到代碼中,而是指解析器除了分號還會以換行為基礎按一定的規則作為短劇的依據,來保證解析的正確性。
產生ASI的規則:
1) 新行並入當前行,構成非法語句
2) ++、--后綴表達式作為新行開始
3) continue,break,return,throw之后
4) 代碼塊的最后一個語句
var name = "Marco" alert(name) //相當於 var name = "Marco"; alert(name) var a = 1,b = 2 a ++ b console.log(a); //1 console.log(b); //3 //相當於 a; ++b function fn() { return // 下面代碼不會執行 { name : "Marco" } } var out = fn(); console.log(out); //undefined //相當於 function fn() { return ; { name: "Marco" } } function fn() { var x = 1, y = 2 } //相當於 function fn() { var x = 1, y = 2; }
34、不會產生ASI的規則 (承接上一點)
1)新行以 [ 開始
2)新行以 ( 開始
3)新行以 / 開始
4)新行以 . 或者 , 開始
5)新行以 + 、- 、* 、% 開始
var a = ['a1', 'a2'] var b = a [0,1].slice(1) console.log(b) //2 //相當於 var a = ['a1', 'a2'] var b = a[0,1].slice(1) //[0,1]不能解析成數組字面量,所以0,1被解析成逗號表達式,其值為1,所以相當於a[1].slice(1) console.log(b) var x = 1 var y = x (x+y).toString() // 相當於 var x = 1 var y = x(x+y).toString() //相當於將x+y的值作為參數調用函數並調用返回值的toString函數,因為x不是函數,所以會報錯 Uncaught TypeError: x is not a function var m = 100 var n = m /10/.test(n) // 相當於 var m = 100 var n = m/10/.test(n) // m/10會被理解為除法運算,而第二個 / 后面希望跟的是數字繼續進行除法運算 所以會報錯 Uncaught SyntaxError: Unexpected token . var a = 1 var b = a .toString() // 相當於 var a = 1 var b = a.toString() //因此不會報錯 var a = 1 var b = a + 1 //相當於 var a = 1 var b = a + 1 //b的結果為2
35、使用元素的contenteditable屬性可以使用戶可以編輯該元素。同時我們可以通過JavaScript來開啟或者關閉元素的可編輯模式。
<div id="content" contentEditable></div> <button id="on">開啟編輯</button> <button id="off">關閉編輯</button> <script> window.onload = function() { on.onclick = function() { content.contentEditable = "true"; } off.onclick = function() { content.contentEditable = "false"; } } </script>
36、 執行上下文的代碼被分成兩個階段來處理:
1) 進入執行上下文 2) 執行代碼
在進入上下文時,變量對象中將會包含以下屬性:
函數所有形參(如果是在函數上下文中);所有函數聲明;所有變量聲明
function fn(a, b) { var c = 1; function d() {} var e = function _e() {}; (function f() {}); } fn(10); // call
進入上下文,代碼執行之前,活動對象表現如下:
AO(test) = { a: 10, b: undefined, c: undefined, d: <reference to FunctionDeclaration "d"> e: undefined };
37、在執行上下文中變量、函數、形參的重名問題
1) 形參重名:形參的初始值為最后一個同名形參所對應的實參的值,如果不存在,則為undefined
function fn(a,a,a) { console.log(a); } fn(1,2,3); //3 fn(1,2); //undefined fn(1); //undefined
2) 變量重名:由於hoisting,聲明被提升到開始,所以沒有影響
function fn() { console.log(a); //undefined var a = 10; console.log(a); //10 var a; console.log(a); //10 } fn();
3) 函數重名:函數聲明被提升,初始值為最后一次聲明的函數
function fn() { console.log(1); } function fn() { console.log(2); } fn(); //2
4) 函數與形參重名:初始值為函數
function fn(a) { console.log(a); function a() { alert("Marco"); } } fn(2); //function a() { alert("Marco");}
5)變量與函數重名:初始值為函數
function fn() { console.log(a); var a = 10; function a() { alert("Marco"); } } fn(); //function a() { alert("Marco");}
6) 變量與形參重名:初始值為形參
function fn(a) { console.log(typeof a); var a = 10; } fn("Marco"); //string
7) 一個形參為arguments:arguments的初始值為對應的實參
function fn(arguments) { console.log(arguments); } fn(1,2,3); //1
8) 一個變量聲明為arguments: arguments的初始值為原始值,而不是新定義的變量
function fn() { console.log(typeof arguments); var arguments = 1; } fn(1,2,3); //object
9)多個變量重名問題
在進入上下文的時候,函數聲明和變量聲明被提升到了作用域的開始,當他們出現重名的時候其初始值優先級為:函數聲明 > 形參 > arguments > 變量聲明
38、函數的形參是不可刪除的
function fn(a) { console.log(a); //1 delete a; console.log(a); //1 } fn(1);
39、JavaScript中的this不是變量對象的屬性,而是執行上下文環境的屬性。當在代碼中使用this,this的值直接從執行上下文中獲取,而不會從作用域鏈中搜尋。
40、如何確定this的值: this的值在進入上下文時確定,並且在上下文運行期間不會改變,this的值由函數的調用方式決定。
1) 直接調用函數,this的值為undefined(嚴格模式)或者window(非嚴格模式)
2) 作為對象方法調用,this的值為該對象
3) 作為構造函數調用(new),this的值為新創建的對象
4) call、apply顯式指定this的值
var name = "baby"; var fn; var obj = { name: 'Marco', getname: function() { console.log(this.name); } } obj.getname(); //Marco (fn = obj.getname)(); //baby 賦值表達式的結果為所賦的值
var name = "baby"; var fn; var obj = { name: 'Marco', getname: function() { return function() { console.log(this.name); } } } obj.getname()(); //baby
41、在定時器(setTimeout和setInterval)和requestAnimationFrame的回調函數中,無論是否使用嚴格模式,this的值都是全局對象。
var name = "baby"; var obj = { name: 'Marco', getname: function() { setTimeout(function(){ console.log(this.name); },1000); } } obj.getname(); //baby
42、當使用new 操作函數時,該函數就成為構造函數。new操作的過程如下:
1) 創建一個空對象obj{},this指向obj;
2) 將空對象的__proto__指向構造函數的prototype
3) 在obj的執行環境調用構造函數,如果構造函數返回值為對象,那么返回該對象;如果構造函數沒有顯式返回值或者返回值為基本數據類型,那么忽略原來的返回值,並將obj作為新的對象返回。
舉個栗子:
function People(name) { this.name = name; } People.prototype.getName = function() { console.log("I am "+this.name); } var p = new People('Marco'); p.getName(); //I am Marco console.log(p.name); //Marco
new的模擬操作過程如下 :
var p = new People('Marco'); new People('Marco') = { var obj = {}; obj.__proto__ = People.prototype; var result = People.call(obj,"Marco"); return typeof result === 'obj' ? result : obj; }
最終的原型鏈為: p.__proto__->People.prototype->Object.prototype->null
再來個例子:
function f(){ return f; } new f() instanceof f;//false function f(){ return this; } new f() instanceof f;//true function f(){ return 123; } new f() instanceof f;//true
所以,我們在使用構造函數的時候盡量避免返回值,因為如果返回值為對象的話,就得不到我們想要的對象實例了。
43、全等運算符比較准則:
1) 如果數據類型不同,返回false
2) 如果兩個被比較的值類型相同,值也相同,並且都不是 number 類型,返回true
3) 如果兩個值都是 number 類型,當兩個都不是 NaN,並且數值相同,或是兩個值分別為 +0 和 -0 時,兩個值被認為是全等
4) NaN === NaN 返回false
NaN === NaN //false +0 === -0 //true
44、相等運算符比較准則:
1) null == undefined //true
2) 如果數據類型相同,則按全等運算符的比較准則進行比較
3) 否則,如果一方為對象類型,則將其轉換為原始值(ToPrimitive())
4) 將被比較的值轉換為數字,再按全等運算符的比較准則進行比較
[] == [] //false '2' == true //false '1' == true //true var a = /123/, b = /123/; a == b //fasle a === b //false
45、in操作符的用法
1) 對於一般的對象判斷某個屬性是否屬於對象
2) 對於數組可以指定數字形式的索引值來判斷該位置是否存在數組元素
3) 使用delete刪除屬性之后,使用in檢查該屬性返回false
4) 將屬性值設置為undefined,使用in檢查返回true
var obj = { name: 'Marco', age: 24 } console.log('name' in obj); //true delete obj.name; console.log('name' in obj); //false obj.age = undefined; console.log('age' in obj); //true var arr = [1,2,3]; console.log(0 in arr); //true console.log(3 in arr); //false
46、如何創建稀疏數組
1) 使用構造函數Array()
2) 簡單地指定數組索引值大於當前數組的長度
3) 使用delete操作符
4) 省略數組直接量中的值
5) 指定length屬性
var arr1 = new Array(3); var arr2 = [1,2,3]; arr2[100] = 4; var arr3 = [1,2,3]; delete arr3[0]; var arr4 = [,,,,]; var arr5 = [1,,2,]; var arr6 = new Array(); arr6.length = 3;
47、 判斷稀疏數組
1) index in array
2)forEach
var arr = [1,,2,]; console.log(0 in arr); //true console.log(1 in arr); //false console.log(3 in arr); //false console.log(arr.length); //3 arr.forEach(function(x,i) { console.log(i + " => "+x ); //0 => 1 2 => 2 });
注意:數組直接量的語法允許有可選的結尾的逗號,所以arr只有3個元素
48、連續大於號或者小於號的比較問題,從左向右依次進行,並且需要注意隱式轉換
3 < 2 < 1 // true 3 > 2 > 1 //false 4 > 3 < 2 > 1 == 0 //true
49、 比較運算符
1) 如果操作數為對象,則轉換為原始值(ToPrimitive())
2) 轉換為原始值后如果兩個操作數都為字符串,則按字典順序比較
3) 如果至少一個不是字符串,則兩個操作數都轉換為數字進行數值比較,Infinity比除自己之外的其它值都大,-Infinity比除自己之外的其它值都小,-0和+0相等。如果其中一個為NaN,那么返回false
var a = [1, 2, 3], b = [1, 2, 4]; console.log( a < b); //true Array.prototype.toString = function(){ return 1; } console.log( a < b ); //false
50、函數有一個非標准的屬性——name,通過它我們可以訪問該函數的名字,該屬性是只讀的。(IE不支持)
function foo() { } var oldName = foo.name; foo.name = "bar"; console.log([oldName, foo.name] ); //["foo", "foo"] IE下返回 [undefined, "bar"]
原創文章:轉載請注明文章出處 http://www.cnblogs.com/MarcoHan
很多問題沒有展開講(展開講的話,估計只能講一點了),可能講的不是很詳細,大家可以先看代碼如果了解可以直接跳過解釋。如果有問題或者錯誤,歡迎指正。
以上