天天看點

JavaScript必知必會+了解總結

這是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對象的函數

之前寫過的關于this的了解

關于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語言精粹》)

源引:http://blog.jobbole.com/39571/

繼續閱讀