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);
}
}