英文原文:Stack Overflow 編譯:西城一隅
這是stackoverflow上的一個老問題,卻有個乾貨答案,但是擴充套件的資訊量很大,我只在此拋個磚。
Not jQuery. Not YUI. Not 等等…
js的框架的確很有用,但是它們卻常常把一些js的醜陋細節和DOM原理給你隱藏了。如果你的目標是做一個精通javascript的工程師,那花大把的時間放在框架上可能恰恰背道而馳了。
下面就有javascript這門語言的一些特性,你應該知道並且深諳此道,但是很多人可能還並不清楚。
一、物件屬性,object.prop和object[‘prop’]是一回事(所以你能停止使用eval了嗎?!3KU);物件的屬性多是String型別(有些也是陣列Array)
for…in是什麼情況下使用,什麼情況慎用?
方括號可以通過變數來訪問屬性
1 2 3 4 |
person.name; person['name']; var propertyName = 'name'; person[propertyName]; // name |
當屬性是帶空格的string時就只能用方括號了:person[‘first name’];
for…in 迴圈輸出的屬性名順序不可預測,使用之前先檢測物件是否為null 或者 undefined
二、屬性檢測;undefined和null;為什麼鮮為人知的in運算子非常有用,以及它和typeof、undefined的區別;hasOwnProperty;delete作用
undefined好理解一般用來表示未定義,而且不能用delete來刪除它。
null 表示一個空物件指標 所以 typeof null返回 object
undefined派生自null alert(null == undefined) 返回true; 但alert(null === undefined)就返回false了
關於hasOwnProperty和Object:
hasOwnProperty是js中唯一一個處理屬性但是不查詢原型鏈的函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Object.prototype.prop = 'propsss'; var obj = {und:undefined}; obj.prop; // propsss 'und' in obj; // true obj.hasOwnProperty('prop'); // false obj.hasOwnProperty('und'); // true //只有hasOwnProperty可以給出正確和期望的結果,尤其在遍歷一個物件時 //除了hasOwnProperty外,沒有其他方法可以排除原型鏈上的屬性(不是定義在物件自身上的屬性) //如果hasOwnProperty被佔用呢?來看: var obj = { hasOwnProperty: function(){ return false; }, prop: 'this is bad...' }; obj.hasOwnProperty('prop'); // 總是返回false //這樣解決: {}.hasOwnProperty.call(obj,'prop'); // 返回true |
var o =new Object();
Object的每個例項都具有下列屬性方法:
1.Constructor:儲存著用於建立當前物件的函式 上面例子 建構函式就是 Object()
2.hasOwnProperty(prop):檢查給定的屬性是否在當前物件例項中(而不是在例項的原型中)。作為引數的屬性必須以string形式指定
3.isPrototypeOf(object):用於檢查傳入的物件是否是另一個物件的原型。
4.propertyIsEnumerable(propertyName):用於檢查給定的屬性是否能夠使用for in語句
5.toLocaleString():返回物件的字串表示,與環境的地區對應
6.toString():同上
7.valueOf(): 返回物件的字串、number、Boolean表示。通常與toString()相同
三、Number型別就是浮點型別(64位浮點數);使用浮點數會遇到語言無關性的問題;避免使用parseInt時的八進位制陷阱
ECMAScript5不具有解析八進位制的能力,可在IE7和chrome上測試 parseInt(069);
ES3和ES5之間存在分歧
javascript中的乘法問題:
一般可以用 10000 作為基數
31.12 * 10000 * 9.7 / 10000
四、巢狀函式作用域;避免全域性變數導致的意外而使用var的必要性;閉包的作用域如何結合使用;在迴圈與閉包的問題
作用域和var關鍵字的面試題
1 2 3 4 5 |
function(){ var a=b=10; } console.log(a); console.log(b); |
迴圈中使用閉包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function createFunctions(){ var result = new Array(); for(var i=0;i<10;i++){ result[i] = function(){ return i; } } return result; } //每個函式的作用域鏈中都儲存著createFunctions()函式的活動物件,所以他們引用的都是同一個變數i。 //當createFunctions()返回後 變數i的值是10 //所以可以這樣寫 for(var i=0;i<10;i++){ result[i] = function(num){ return function(){ return num; }; }(i); } |
之前寫過的閉包的理解關於閉包
五、全域性變數和window物件的屬性產生衝突怎麼辦(它們其實是一回事);全域性變數和DOM元素在IE中的衝突;在全域性作用域中使用var來避免這些問題
六、 function語句在解析時會被提升(不管function被放置在哪裡,它都會被移動到定義時所在作用域的頂層) 函式宣告和函式表示式;為什麼命名函式表示式不應該使用
關於函式宣告提升:
解析器會執行一個函式宣告提升(function decalaration hoisting)的過程,讀取並將函式宣告新增到執行環境中。
對程式碼求值時js引擎在第一遍會宣告函式並將它們放到原始碼樹的頂部。
1 2 3 4 5 6 7 8 9 |
alert(sum(10,10)) function sum(n1,n2){ return n1+n2; } //單獨使用下面程式碼時,函式表示式會出錯: alert(sum(10,10)); var sum = function (n1,n2){ return n1+n2; } |
關於命名函式表示式:
1、命名函式表示式即被認為是函式宣告也被認為是函式表示式
1 2 3 4 |
typeof g; // "function" var f = function g(){}; //上面這個例子論證了 jScript 是如何把一個命名函式表示式處理成一個函式宣告的 //在函式宣告發生之前就把 g 給解析了 【在IE中檢測】 |
2、命名函式表示式還能建立兩個不同的函式物件—-這是js的bug
1 2 3 4 5 |
var f = function g(){}; f === g; //false f.prop = 'a prop'; g.prop; // undefined 【在IE中檢測】 |
竟然建立了兩個物件,他們之間還不是引用的關係,是不是很有趣。。。我只能說:呵呵 interesting……
3、在條件語句中命名函式表達的宣告式仍然會被解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var f = function g(){ return 1; }; if(false){ f= function g(){ return 100; }; } g(); //猜猜返回什麼 【在IE中檢測】 //還有arguments也中槍了 var f = function g(){ return [ arguments.callee == f, arguments.callee == g ]; }; console.log(f()); // [true, false] console.log(g()); // [false, true] 【在IE中檢測】 |
注:上面的3條準確的說應該是算是jScript的bug
七、建構函式;prototype屬性;new運算子的執行機制;利用這些方法實現一個類-子類-例項的系統;在何時應該考慮使基於閉包的物件來替代原型設計
八、this是在函式呼叫時才被確定的而不是定義的時候;把函式當做引數傳入時不像其他語言那樣執行;如何使用閉包或者Function.prototype.bind來解決這些問題呢
關於this的呼叫,直接上程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var Dog = { toString: function() { return 'dog';}, fn: function() { alert(this);}, }; var Cat = { toString: function() { return 'cat';} }; Dog.fn(); // dog Dog['fn']() // dog Cat.fn = Dog.fn; Cat.fn(); // cat var func = Dog.fn; func(); // window |
上面程式碼很簡單 請自行補腦……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Obj.method = function() { var self = this; function test() { //this 被設定為全域性物件(window) //self 指向 Obj 物件 } } //想到了arguments做個低耦合的遞迴求階乘 function factorial(num) { if(num<=1) return 1; else return num*arguments.callee(num-1); } //callee指向擁有這個arguments物件的函式 |
關於Function.prototype.bind(thisArg [, arg1 [, arg2, …]]):
這是ECMAScript 5中的方法看看Opera的對它的介紹吧
簡單翻譯就是:
Function.prototype.bind 返回一個新的函式物件,該物件的 this 繫結到了thisArg引數上。本質就是:這允許你在其他物件鏈中執行一個函式
但是很多瀏覽器不支援,通過一個js的hack看看原理吧:
1 2 3 4 5 6 7 8 9 10 11 12 |
if(!Object.bind){ Function.prototype.bind = function(owner){ var self = this; var args = Array.prototype.slice.call(arguments,1); return function() { return self.allpy( args.length===0 ? arguments : arguments.length===0? args: args.contact(Array.prototype.slice.call(arguments,0)) ); }; }; } |
九、其他的ES5新特性如indexOf 、 forEach 以及Array使用函數語言程式設計;舊瀏覽器如何相容這些新的方法;使用匿名函式呼叫這些方法來使程式碼更加緊緻具有可讀性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
Array.prototype.indexOf(searchString ,position) var data = [1,3,5,7,9]; console.log(data.indexOf(5)); //2 console.log(data.indexOf(5,4)); //-1 從4號位開始搜尋 console.log(data.indexOf('7')); //-1 7!== '7' Array.prototype.lastIndexOf //從末尾開始查詢 Array.prototype.some //某些條件符合 var arr = [1,2,3,4,11]; function larger(item){ return item> 10 } console.log(arr.some(larger)?'ok':'no...'); //注:只要有一條符合 即返回true 不再執行 Array.prototype.every // all符合 和some相比就是返回的 true 和 false 調換一下 Array.prototype.forEach //類似jQ裡的each [1, 2 ,3, 4].forEach(alert); [1, 2 ,3, 4].forEach(console.log);//FF下執行 不知為什麼chrome不行。。。。。 Array.prototype.map //對映 類似forEach 把原陣列對映成新陣列 var arr = [1,3,5,7]; var result = arr.map(function(item){ return item * item; }); console.log(result); //求各項平方 Array.prototype.filter //篩選 var filter = [0,2,3,0,undefined].filter(function(item){ return item; }); console.log(filter); //filter的callback函式需要返回值為 true 或 false。若為false則過濾掉 Array.prototype.reduce //不是減少 是一種迭代 var sum = [1,2,3,4,5].reduce(function(pre,current,index,array) { return pre + current; }); console.log(sum); //15 Array.prototype.reduceRight //right 自然是從陣列末未開始 |
這些都是 ES5 中 Array 物件的擴充套件方法
PS:還是點此自行補腦,我也在研究中…..後續會再補充
十、瀏覽器和js程式碼之間控制流程的原理;同步和非同步執行;事件在執行時觸發和事件在控制返回時觸發的區別;呼叫同步執行的方法如alert而引起控制流重新進入的潛在問題(翻譯不通,請自行補腦)。
十一、跨window指令碼對instanceof的影響 在不同的DOM中跨window指令碼對控制流的影響;postMessage怎麼解決這個問題
postMessage就是HTML5解決跨域問題引入的API,使得多個iframe/window實現跨域通訊。
寫了個postMessage跨域通訊的demo: 點此檢視
最重要的是,你需要批判的去看待javascript,承認因為種種歷史原因而導致各種不完美(甚至比其他語言還要糟糕),並要避免各種陷阱。Crockford在這方面的研究很值得學習(雖然我不完全認同他的《javascript語言精粹》)