10段代碼打通js學習的任督二脈
前言
為了node.js做準備,js的基本功還是很重要的。所以正值1024程序員節的時候所以找了些題目,整理了一下知識點。這篇文章感覺代碼太多,難免枯燥,所以文章最后留了個 彩蛋給讀者。
簡單回調
代碼
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } function doFoo2(o){ o.foo(); } var obj = { a: 2, foo: foo }; var a = "I'm an a"; doFoo(obj.foo); doFoo2(obj);
分析在Javascript中,this指向函數 執行時的當前對象,而非聲明環境有。
執行doFoo的時候執行環境就是doFoo函數,執行環境為全局。
執行doFoo2時是在對象內部調用函數,this指針指向該對象。
結果
I'm an a 2
用apply改變函數作用域
function foo(somthing){ console.log(this.a, somthing); } function bind(fn, obj){ return function(){ return fn.apply(obj, arguments); } } var obj = { a:2 } var bar = bind(foo, obj); var b = bar(3); console.log(b);
分析
apply、call、bind都有個作用就是改變作用域,這里用apply將foo函數的作用域指向obj對象,同時傳入參數。
再簡單分析一下bind函數內部的嵌套,執行bind函數的時候返回的是一個匿名函數,所以執行bar(3)的時候實際上是執行的bind內部的匿名函數,返回的是之前傳入的foo函數的執行結果。
函數沒有返回值的情況下默認返回undefined。
結果
2 3 undefined
new關鍵字
function foo(a,b){ this.val = a+b; } var bar = foo.bind(null, 'p1'); var baz = new bar('p2'); console.log(baz.val);
分析
bind函數的第一個參數為null代表作用域不變,后面的不定參數將會和函數本身的參數按次序進行綁定,綁定之后執行函數只能從未綁定的參數開始傳值。
結果
p1p2
自執行函數
function foo(){ console.log(this.a); } var a = 2; var o = {a:3,foo:foo}; var p = {a:4}; (p.foo=o.foo)();
分析
經常可以看到這樣的代碼
(function(){ //... })()
這種代碼通常是創建一個立即執行的函數同時避免污染全局變量。
很少有人去關注賦值語句執行之后會返回什么結果,其實就是返回當前值。也就是說當括號內執行完賦值之后,返回的是o對象中的foo函數。函數的執行環境中有一個a對象,嗯,就是它了~
答案
變量屬性
var a = []; a[0] = 1; a['foobar'] = 2; console.log(a.length); console.log(a.foobar);
分析
當一個變量被聲明后,擴充其屬性并不會改變原數據類型。
結果
1 2
精度問題
var a = 'foo'; a[1] = 'O'; console.log(0.1+0.2==0.3||a);
分析
當操作小數時請小心,js的小數計算并不精確,所以上面的判斷是false。字符串變量是常量。
結果
foo
命名提升
foo(); var foo = 0; function foo(){ console.log(1); } foo = function(){ console.log(2); };
分析
聲明的變量和命名函數都會被提升到代碼的最前面,只不過聲明的變量的賦值語句在代碼中的位置不變。所以上面這段代碼應該被理解為:
var foo; function foo(){ console.log(1); } foo(); foo = 0; foo = function(){ console.log(2); };
結果
思考
foo(); var foo = 0; function foo(){ console.log(1); } foo(); foo = function(){ console.log(2); }; foo();
上面代碼的結果:
1 報錯
作用域
foo(); var a = true; if(a){ function foo(){ console.log('a'); } } else { function foo(){ console.log('b'); } }
分析
javascript并不是以代碼段為作用域,而是以函數。
再根據命名提升的原則,所以這段代碼應該是這樣的:
function foo(){ console.log('a'); } function foo(){ console.log('b'); } foo(); var a = true; if(a){ } else { }
結果
b
閉包陷阱
for(var i=1;i<=5;i++){ setTimeout(function(){ console.log(i); }, i*1000); }
分析
閉包有個重要的作用就是,在內層函數引用外層函數定義的變量時,外層函數的變量不會被會被持久化。
這里有個隱藏陷阱就是for循環結束之后i仍然自增了1。
結果
6 6 6 6 6
偽閉包
function foo(){ console.log(a); } function bar () { var a = 3; foo(); } var a = 2; bar();
分析
閉包是函數的嵌套定義,而不是函數的嵌套調用。
結果
思考
如何輸出3?
function bar () { function foo(){ console.log(a); } var a = 3; foo(); } var a = 2; bar();
彩蛋
光說不練假把式~
一周月內將下題正確答案發送至我郵箱內(郵箱地址請參考博客),將獲得本年度我閱讀過最優秀的關于AngularJS的電子書一本。
var Obj = { name: 'zdl', do: function(){ console.log(this.name); } }