10段代碼打通js學習的任督二脈

www345 9年前發布 | 10K 次閱讀 JavaScript開發 JavaScript

前言

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

寫個對象a繼承Obj的方法(不使用new)。

原文作者:亞里士朱德

博客網址: http://yalishizhude.github.io

</div> 原文 http://www.ccwebsite.com/10段代碼打通js學習的任督二脈/

 本文由用戶 www345 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!