JS重要知識點(diǎn)小結(jié)
更新時(shí)間:2011年11月06日 18:20:22 作者:
這里列出了一些JS重要知識點(diǎn)(不全面,但自己感覺很重要)。徹底理解并掌握這些知識點(diǎn),對于每個(gè)想要深入學(xué)習(xí)JS的朋友應(yīng)該都是必須的
講解還是以示例代碼搭配注釋的形式,這里做個(gè)小目錄:
JS代碼預(yù)解析原理(包括三個(gè)段落);
函數(shù)相關(guān)(包括 函數(shù)傳參,帶參數(shù)函數(shù)的調(diào)用方式,閉包);
面向?qū)ο螅ò?對象創(chuàng)建、原型鏈,數(shù)據(jù)類型的檢測,繼承)。
JS代碼預(yù)解析原理
/****************** JS代碼預(yù)解析原理 ******************/
/*
JS代碼預(yù)解析、變量作用域、作用域鏈等 應(yīng)該能作為學(xué)習(xí)JS語言的入門必備知識。
下邊給出些簡要解釋和一些典型的代碼段,若要了解更多,能從網(wǎng)上搜索到更多相關(guān)示例。
引用網(wǎng)上的一段有關(guān) “JS的執(zhí)行順序” 的解釋:
如果一個(gè)文檔流中包含多個(gè)script代碼段(用script標(biāo)簽分隔的js代碼或引入的js文件),它們的運(yùn)行順序是:
步驟1. 讀入第一個(gè)代碼段(js執(zhí)行引擎并非一行一行地執(zhí)行程序,而是一段一段地分析執(zhí)行的)
步驟2. 做語法分析,有錯(cuò)則報(bào)語法錯(cuò)誤(比如括號不匹配等),并跳轉(zhuǎn)到步驟5
步驟3. 對var變量和function定義做“預(yù)解析”(永遠(yuǎn)不會報(bào)錯(cuò)的,因?yàn)橹唤馕稣_的聲明)
步驟4. 執(zhí)行代碼段,有錯(cuò)則報(bào)錯(cuò)(比如變量未定義)
步驟5. 如果還有下一個(gè)代碼段,則讀入下一個(gè)代碼段,重復(fù)步驟2
步驟6. 結(jié)束
*/
// 下邊給出 三段覺得比較典型的代碼示例:
/********** 一:基本的幾條語句 **********/
alert(num); // undefined
var num = 0;
alert(str); // 錯(cuò)誤:str未定義
str = "string";
alert(func); // undefined
var func = function (){ alert('exec func'); }
test(); // exec test
alert(test()); // 先exec test 后undefined
function test(){ alert('exec test'); }
/********** 二:函數(shù)名與變量名相同 **********/
//var mark = 1;
function mark(x) {
return x * 2;
}
var mark;
alert(mark); // function mark(x) { return x * 2; }
// 去掉前邊的var mark = 1;則會返回1
/********** 三:把第二段包括在語句塊中 **********/
// 當(dāng)有條件時(shí)候(代碼包含在條件語句塊里)
if (false) {
var mark1 = 1;
function mark1() {
alert("exec mark1");
}
//var mark1;
alert(mark1);
}
alert(mark1);
mark1();
// 由于解析瀏覽器解析不同,這段代碼在不同瀏覽器里執(zhí)行的結(jié)果不一致,具體原因可從網(wǎng)上查找答案
函數(shù)相關(guān)(包括 函數(shù)傳參,帶參數(shù)函數(shù)的調(diào)用方式,閉包)
/****************** 函數(shù)相關(guān) ******************/
/********** 一:函數(shù)傳參 **********/
/*
編程語言大概都有 值類型與引用類型 的區(qū)別,JS也不例外。
原始類型:undefined null number boolean 均為值類型。
string比較特殊,因?yàn)樗遣豢筛淖兊?,String類定義的方法都不能改變字符串的內(nèi)容。
function object array 這三種為引用類型。
*/
/* JavaScript 函數(shù)傳遞參數(shù)時(shí),是值傳遞。
ECMAScript 中,所有函數(shù)的參數(shù)都是按值來傳遞的。
基本類型值的傳遞和基本類型變量復(fù)制一致(采用在棧內(nèi)新建值),
引用類型值的傳遞和引用類型變量的復(fù)制一致(棧內(nèi)存放的是指針,指向堆中同一對象)。
具體參考:http://www.xiaoxiaozi.com/2010/03/05/1719/
*/
function setName(obj){
//obj拷貝了person的值(person是一個(gè)對象的引用地址),所以obj也指向了person所指向的對象。
obj.name = "xiaoxiaozi";
obj = {}; // 讓obj 指向了另一個(gè)對象
obj.name = "admin";
}
var person = {};
setName(person);
alert(person.name); // xiaoxiaozi
/********** 二:帶參數(shù)函數(shù)的調(diào)用方式 **********/
/* 在DOM不同版本中,函數(shù)調(diào)用方式不太一樣。標(biāo)準(zhǔn)推薦的是addEventListener和attachEvent
這兩種方式有很多資料可查。但是有些已經(jīng)不被推薦的函數(shù)調(diào)用仍舊有實(shí)際應(yīng)用,相關(guān)資料發(fā)現(xiàn)的不多。
這里主要討論這些函數(shù)調(diào)用方式
*/
var g = "全局變量";
function show(str) {
alert("my site: " + str);
}
setTimeout("show(g);",100); // g是全局變量,函數(shù)正確執(zhí)行
function t() {
var url = "www.xujiwei.cn";
var num = 2;
//setTimeout("alert("+url+")", 3000); // 解析錯(cuò)誤,www未定義
//setTimeout("alert("+num+")", 3000); // 解析正確,注意與上句對比
//setTimeout("show('url');", 2000); // url
//setTimeout("show("+ url +");", 2000); // 解析錯(cuò)誤,www未定義
//setTimeout("show(url);", 2000); // 解析錯(cuò)誤,url未定義
//setTimeout('"show("+ url +");"', 2000); // 解析錯(cuò)誤,url未定義
//setTimeout("show('"+ url +"');", 2000); // 正確
//setTimeout(function(){show(url);},1000); // 正確
}
t();
/* 結(jié)論:
諸如onclick="xx();"等函數(shù)調(diào)用方式,在雙引號內(nèi)的內(nèi)容直接解析為js語句執(zhí)行。
若調(diào)用的函數(shù)帶有參數(shù),注意對比以上各種寫法,保證傳遞進(jìn)去的參數(shù)為正確的。
*/
/********** 三:閉包 **********/
/*
閉包,幾乎是每個(gè)學(xué)習(xí)JS的朋友都要討論的問題,因此各種相關(guān)資料應(yīng)有盡有。
它的作用很大,但也有弊端,例如如果使用不當(dāng),容易引起內(nèi)存泄漏等問題,因此有不少人
提倡少用閉包。
這里列出閉包的一種經(jīng)典應(yīng)用,一個(gè)有爭議的應(yīng)用。
*/
function test1() { //通過閉包,每次能傳入不同的j值。
for (var j = 0; j < 3; j++) {
(function (j) {
setTimeout(function () { alert(j) }, 3000);
})(j);
}
}
test1();
/* 這個(gè)是閉包的典型應(yīng)用 */
(function tt() {
for (var i = 1; i < 4; i++) {
document.getElementById("b" + i).attachEvent("onclick",
new Function('alert("This is button' + i + '");')); // 在IE中測試
}
})() // 立即執(zhí)行函數(shù),一個(gè)文件是否只能有一個(gè)?把上邊函數(shù)寫成立即執(zhí)行出問題,怎么回事?
/* 這個(gè)問題出現(xiàn)在論壇里,有很多爭議
有說是new Function動態(tài)生成個(gè)閉包結(jié)構(gòu)的函數(shù),所以能保存外部變量。
有說是跟閉包無關(guān),new Function,就是新定義了一個(gè)function,
i的值也作為這個(gè)新的function的參數(shù)固化在其內(nèi)部了。
*/
面向?qū)ο螅ò?對象創(chuàng)建、原型鏈,數(shù)據(jù)類型的檢測,繼承)
/****************** 面向?qū)ο?******************/
/********** 一:對象創(chuàng)建、原型鏈 **********/
/* 討論 構(gòu)造函數(shù)(類方式)創(chuàng)建對象 ,深入理解這些內(nèi)容,是很重要的
*/
function MyFunc() { }; //定義一個(gè)空函數(shù)
var anObj = new MyFunc(); //使用new操作符,借助MyFun函數(shù),就創(chuàng)建了一個(gè)對象
// 等價(jià)于:
function MyFunc() { };
var anObj = {}; //創(chuàng)建一個(gè)對象
anObj.__proto__ = MyFunc.prototype;
MyFunc.call(anObj); //將anObj對象作為this指針調(diào)用MyFunc函數(shù)
/*
用 var anObject = new aFunction() 形式創(chuàng)建對象的過程實(shí)際上可以分為三步:
第一步:建立一個(gè)新對象(anObject);
第二步:將該對象內(nèi)置的原型對象(__proto__)設(shè)置為構(gòu)造函數(shù)prototype引用的那個(gè)原型對象;
第三步:將該對象作為this參數(shù)調(diào)用構(gòu)造函數(shù),完成成員設(shè)置等初始化工作。
對象建立之后,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關(guān),
與構(gòu)造函數(shù)再扯不上關(guān)系了。
換句話說,構(gòu)造函數(shù)只是在創(chuàng)建對象時(shí)起到介紹原型對象和初始化對象兩個(gè)作用。
原型鏈:(參考:http://hi.baidu.com/fegro/blog/item/41ec7ca70cdb98e59152eed0.html)
每個(gè)對象(此處對象應(yīng)該僅指大括號括起來的object,不包括function、array。待驗(yàn)證?)
都會在其內(nèi)部初始化一個(gè)屬性,就是__proto__,當(dāng)我們訪問一個(gè)對象的屬性時(shí),
如果這個(gè)對象內(nèi)部不存在這個(gè)屬性,那么他就會去__proto__里找這個(gè)屬性,
這個(gè)__proto__又會有自己的__proto__,于是就這樣 一直找下去,也就是我們平時(shí)所說的原型鏈的概念。
*/
/* 理解了對象創(chuàng)建的原理,可試著分析下邊兩個(gè)示例的結(jié)果 */
var yx01 = new function() {return "圓心"};
alert(yx01); // [object Object]
var yx02 = new function() {return new String("圓心")};
alert(yx02); // “圓心”
/* 解釋:
"圓心"是基本的字符串類型,new String("圓心")則創(chuàng)建了一個(gè)string對象。
只要new表達(dá)式之后的構(gòu)造函數(shù)返回一個(gè)引用對象(數(shù)組,對象,函數(shù)等),都將覆蓋new創(chuàng)建的對象,
如果返回一個(gè)原始類型(無 return 時(shí)其實(shí)為 return 原始類型 undefined),
那么就返回 new 創(chuàng)建的對象。
參考:http://www.planabc.net/2008/02/20/javascript_new_function/
*/
/********** 二:數(shù)據(jù)類型的檢測 **********/
/* 判斷數(shù)據(jù)類型可能想到的方法:
constructor、typeof、instanceof、Object.prototype.toString.call()
*/
/***** 1、通過constructor屬性 *****/
var myvar= new Array("a","b","c","d");
function A(){}
myvar.constructor = A;
var c = myvar.constructor;
alert(c); // function A(){}
//可見,通過constructor屬性獲取類型的方法很容易被修改,不應(yīng)該用來判斷類型。
/***** 2、通過typeof *****/
/*
typeof是一個(gè)操作符,而不是個(gè)函數(shù)。
typeof的實(shí)際應(yīng)用是用來檢測一個(gè)對象是否已經(jīng)定義或者是否已經(jīng)賦值。
如if(typeof a!="undefined"){},而不要去使用if(a)因?yàn)槿绻鸻不存在(未聲明)則會出錯(cuò)。
typeof檢測對象類型時(shí)一般只能返回如下幾個(gè)結(jié)果:
number,boolean,string,function,object,undefined。
對于Array,Null,自定義對象 等使用typeof一律返回object,
這正是typeof的局限性。
*/
var num = new Number(1);
var arr = [1,2,3];
alert(typeof num); //object 而不是number
alert(typeof arr); //object 而不是Array
alert(typeof null); // object
/***** 3、通過 instanceof *****/
/* 用instanceof操作符來判斷對象是否是某個(gè)類的實(shí)例。
如果obj instanceof Class返回true,那么Class的原型與obj原型鏈上的某個(gè)原型是同一個(gè)對象,
即obj要么由Class創(chuàng)建,要么由Class的子類創(chuàng)建。
*/
function t(){};
t.prototype = Array.prototype;
//t.prototype = [];
var x = new t();
alert(x instanceof t);//彈出true
alert(x instanceof Array);//彈出true
alert(x instanceof Object);//彈出true
/*
由此可知,通過 instanceof 判斷數(shù)據(jù)類型也不可靠。
因?yàn)橐粋€(gè)對象(此處x)的原型鏈可以很長,每個(gè)原型的類型可以不同。
另外在iframe內(nèi)也會容易出錯(cuò):
即有個(gè)頁面定義了一個(gè)數(shù)組a,頁面又嵌套了一個(gè)IFrame,在Iframe里面通過 top.a instanceof Array, 是返回false的。
這個(gè)說明 父頁面和內(nèi)嵌iframe里的對象是不同的,不能混合在一起使用。
改成top.a instanceof top.Array 就會返回true
*/
/***** 4、通過 Object.prototype.toString.call() *****/
/*
Object.prototype.toString.call() 作用是:
1、獲取對象的類名(對象類型)。
2、然后將[object、獲取的類名]組合并返回。
可應(yīng)用于判斷Array,Date,Function等類型的對象
*/
var num = new Number(1);
var arr = [1,2,3];
alert(Object.prototype.toString.call(num)); // [object Number]
alert(Object.prototype.toString.call(arr)); // [object Array]
// 擴(kuò)展示例:(apply等價(jià)于call)
window.utils = {
toString: Object.prototype.toString,
isObject: function (obj) {
return this.toString.apply(obj) === '[object Object]';
},
isFunction: function (obj) {
return this.toString.apply(obj) === '[object Function]';
},
isArray: function (obj) {
return this.toString.apply(obj) === '[object Array]';
}
}
function A() { }
window.utils.isFunction(A); //true
window.utils.isObject(new A()); //true
window.utils.isArray([]); //true
/*
jQuery等框架 就是用這個(gè)方法判斷對象的類型的,因此可以把這種方法作為權(quán)威的判斷方法。
但是,如果重寫了Object.prototype.toString方法,這時(shí)候再用來判斷數(shù)據(jù)類型可能就會出錯(cuò),
所以,一般不要去重寫Object.prototype.toString方法。
*/
/********** 三:繼承 **********/
/*
JS繼承和閉包一樣,幾乎是每個(gè)想深入學(xué)習(xí)JS的朋友都要討論的問題,因此各種相關(guān)資料應(yīng)有盡有。
JS繼承代碼的版本非常多,但原理都是一樣的,核心都是利用了prototype對象。
為了和其他面向?qū)ο笳Z言的風(fēng)格相似,大多數(shù)都采用“類式”風(fēng)格模擬。
繼承的詳細(xì)原理不再贅述,網(wǎng)上有許多資料介紹。
這里給出一個(gè)示例:Jquery作者John Resig寫的繼承。
(其中的詳細(xì)注釋是來自某個(gè)博客,不知道是誰原創(chuàng),這里私自轉(zhuǎn)帖出來)
*/
(function () {
// initializing變量用來標(biāo)示當(dāng)前是否處于類的創(chuàng)建階段,
// - 在類的創(chuàng)建階段是不能調(diào)用原型方法init的
// - 我們曾在本系列的第三篇文章中詳細(xì)闡述了這個(gè)問題
// fnTest是一個(gè)正則表達(dá)式,可能的取值為(/\b_super\b/ 或 /.*/)
// - 對 /xyz/.test(function() { xyz; }) 的測試是為了檢測瀏覽器是否支持test參數(shù)為函數(shù)的情況
// - 不過我對IE7.0,Chrome2.0,FF3.5進(jìn)行了測試,此測試都返回true。
// - 所以我想這樣對fnTest賦值大部分情況下也是對的:fnTest = /\b_super\b/;
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// 基類構(gòu)造函數(shù)
// 這里的this是window,所以這整段代碼就向外界開辟了一扇窗戶 - window.Class
this.Class = function () { };
// 繼承方法定義
Class.extend = function (prop) {
// 這個(gè)地方很是迷惑人,還記得我在本系列的第二篇文章中提到的么
// - this具體指向什么不是定義時(shí)能決定的,而是要看此函數(shù)是怎么被調(diào)用的
// - 我們已經(jīng)知道extend肯定是作為方法調(diào)用的,而不是作為構(gòu)造函數(shù)
// - 所以這里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父類的原型對象
// - 注意:_super指向父類的原型對象,我們會在后面的代碼中多次碰見這個(gè)變量
var _super = this.prototype;
// 通過將子類的原型指向父類的一個(gè)實(shí)例對象來完成繼承
// - 注意:this是基類構(gòu)造函數(shù)(即是Class)
initializing = true;
var prototype = new this();
initializing = false;
// 我覺得這段代碼是經(jīng)過作者優(yōu)化過的,所以讀起來非常生硬,我會在后面詳解
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function (name, fn) {
return function () {
var tmp = this._super; // 這里是必要的,第91行注釋代碼可說明之。
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// 這個(gè)地方可以看出,Resig很會偽裝哦
// - 使用一個(gè)同名的局部變量來覆蓋全局變量,很是迷惑人
// - 如果你覺得拗口的話,完全可以使用另外一個(gè)名字,比如function F()來代替function Class()
// - 注意:這里的Class不是在最外層定義的那個(gè)基類構(gòu)造函數(shù)
// 這里的Class和上邊的window.Class函數(shù)不一樣,這里是window.Class內(nèi)部的函數(shù)局部變量
function Class() {
// 在類的實(shí)例化時(shí),調(diào)用原型方法init
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// 子類的prototype指向父類的實(shí)例(完成繼承的關(guān)鍵)
Class.prototype = prototype; // Class指代上邊的Class,并非一開始的window.Class
// 修正constructor指向錯(cuò)誤
// 是否可用Class.prototype.constructor = Class;來修正???
Class.constructor = Class;
// 子類自動獲取extend方法,arguments.callee指向當(dāng)前正在執(zhí)行的函數(shù)
Class.extend = arguments.callee;
return Class;
};
})();
JS代碼預(yù)解析原理(包括三個(gè)段落);
函數(shù)相關(guān)(包括 函數(shù)傳參,帶參數(shù)函數(shù)的調(diào)用方式,閉包);
面向?qū)ο螅ò?對象創(chuàng)建、原型鏈,數(shù)據(jù)類型的檢測,繼承)。
JS代碼預(yù)解析原理
復(fù)制代碼 代碼如下:
/****************** JS代碼預(yù)解析原理 ******************/
/*
JS代碼預(yù)解析、變量作用域、作用域鏈等 應(yīng)該能作為學(xué)習(xí)JS語言的入門必備知識。
下邊給出些簡要解釋和一些典型的代碼段,若要了解更多,能從網(wǎng)上搜索到更多相關(guān)示例。
引用網(wǎng)上的一段有關(guān) “JS的執(zhí)行順序” 的解釋:
如果一個(gè)文檔流中包含多個(gè)script代碼段(用script標(biāo)簽分隔的js代碼或引入的js文件),它們的運(yùn)行順序是:
步驟1. 讀入第一個(gè)代碼段(js執(zhí)行引擎并非一行一行地執(zhí)行程序,而是一段一段地分析執(zhí)行的)
步驟2. 做語法分析,有錯(cuò)則報(bào)語法錯(cuò)誤(比如括號不匹配等),并跳轉(zhuǎn)到步驟5
步驟3. 對var變量和function定義做“預(yù)解析”(永遠(yuǎn)不會報(bào)錯(cuò)的,因?yàn)橹唤馕稣_的聲明)
步驟4. 執(zhí)行代碼段,有錯(cuò)則報(bào)錯(cuò)(比如變量未定義)
步驟5. 如果還有下一個(gè)代碼段,則讀入下一個(gè)代碼段,重復(fù)步驟2
步驟6. 結(jié)束
*/
// 下邊給出 三段覺得比較典型的代碼示例:
/********** 一:基本的幾條語句 **********/
alert(num); // undefined
var num = 0;
alert(str); // 錯(cuò)誤:str未定義
str = "string";
alert(func); // undefined
var func = function (){ alert('exec func'); }
test(); // exec test
alert(test()); // 先exec test 后undefined
function test(){ alert('exec test'); }
/********** 二:函數(shù)名與變量名相同 **********/
//var mark = 1;
function mark(x) {
return x * 2;
}
var mark;
alert(mark); // function mark(x) { return x * 2; }
// 去掉前邊的var mark = 1;則會返回1
/********** 三:把第二段包括在語句塊中 **********/
// 當(dāng)有條件時(shí)候(代碼包含在條件語句塊里)
if (false) {
var mark1 = 1;
function mark1() {
alert("exec mark1");
}
//var mark1;
alert(mark1);
}
alert(mark1);
mark1();
// 由于解析瀏覽器解析不同,這段代碼在不同瀏覽器里執(zhí)行的結(jié)果不一致,具體原因可從網(wǎng)上查找答案
函數(shù)相關(guān)(包括 函數(shù)傳參,帶參數(shù)函數(shù)的調(diào)用方式,閉包)
復(fù)制代碼 代碼如下:
/****************** 函數(shù)相關(guān) ******************/
/********** 一:函數(shù)傳參 **********/
/*
編程語言大概都有 值類型與引用類型 的區(qū)別,JS也不例外。
原始類型:undefined null number boolean 均為值類型。
string比較特殊,因?yàn)樗遣豢筛淖兊?,String類定義的方法都不能改變字符串的內(nèi)容。
function object array 這三種為引用類型。
*/
/* JavaScript 函數(shù)傳遞參數(shù)時(shí),是值傳遞。
ECMAScript 中,所有函數(shù)的參數(shù)都是按值來傳遞的。
基本類型值的傳遞和基本類型變量復(fù)制一致(采用在棧內(nèi)新建值),
引用類型值的傳遞和引用類型變量的復(fù)制一致(棧內(nèi)存放的是指針,指向堆中同一對象)。
具體參考:http://www.xiaoxiaozi.com/2010/03/05/1719/
*/
function setName(obj){
//obj拷貝了person的值(person是一個(gè)對象的引用地址),所以obj也指向了person所指向的對象。
obj.name = "xiaoxiaozi";
obj = {}; // 讓obj 指向了另一個(gè)對象
obj.name = "admin";
}
var person = {};
setName(person);
alert(person.name); // xiaoxiaozi
/********** 二:帶參數(shù)函數(shù)的調(diào)用方式 **********/
/* 在DOM不同版本中,函數(shù)調(diào)用方式不太一樣。標(biāo)準(zhǔn)推薦的是addEventListener和attachEvent
這兩種方式有很多資料可查。但是有些已經(jīng)不被推薦的函數(shù)調(diào)用仍舊有實(shí)際應(yīng)用,相關(guān)資料發(fā)現(xiàn)的不多。
這里主要討論這些函數(shù)調(diào)用方式
*/
var g = "全局變量";
function show(str) {
alert("my site: " + str);
}
setTimeout("show(g);",100); // g是全局變量,函數(shù)正確執(zhí)行
function t() {
var url = "www.xujiwei.cn";
var num = 2;
//setTimeout("alert("+url+")", 3000); // 解析錯(cuò)誤,www未定義
//setTimeout("alert("+num+")", 3000); // 解析正確,注意與上句對比
//setTimeout("show('url');", 2000); // url
//setTimeout("show("+ url +");", 2000); // 解析錯(cuò)誤,www未定義
//setTimeout("show(url);", 2000); // 解析錯(cuò)誤,url未定義
//setTimeout('"show("+ url +");"', 2000); // 解析錯(cuò)誤,url未定義
//setTimeout("show('"+ url +"');", 2000); // 正確
//setTimeout(function(){show(url);},1000); // 正確
}
t();
/* 結(jié)論:
諸如onclick="xx();"等函數(shù)調(diào)用方式,在雙引號內(nèi)的內(nèi)容直接解析為js語句執(zhí)行。
若調(diào)用的函數(shù)帶有參數(shù),注意對比以上各種寫法,保證傳遞進(jìn)去的參數(shù)為正確的。
*/
/********** 三:閉包 **********/
/*
閉包,幾乎是每個(gè)學(xué)習(xí)JS的朋友都要討論的問題,因此各種相關(guān)資料應(yīng)有盡有。
它的作用很大,但也有弊端,例如如果使用不當(dāng),容易引起內(nèi)存泄漏等問題,因此有不少人
提倡少用閉包。
這里列出閉包的一種經(jīng)典應(yīng)用,一個(gè)有爭議的應(yīng)用。
*/
function test1() { //通過閉包,每次能傳入不同的j值。
for (var j = 0; j < 3; j++) {
(function (j) {
setTimeout(function () { alert(j) }, 3000);
})(j);
}
}
test1();
/* 這個(gè)是閉包的典型應(yīng)用 */
(function tt() {
for (var i = 1; i < 4; i++) {
document.getElementById("b" + i).attachEvent("onclick",
new Function('alert("This is button' + i + '");')); // 在IE中測試
}
})() // 立即執(zhí)行函數(shù),一個(gè)文件是否只能有一個(gè)?把上邊函數(shù)寫成立即執(zhí)行出問題,怎么回事?
/* 這個(gè)問題出現(xiàn)在論壇里,有很多爭議
有說是new Function動態(tài)生成個(gè)閉包結(jié)構(gòu)的函數(shù),所以能保存外部變量。
有說是跟閉包無關(guān),new Function,就是新定義了一個(gè)function,
i的值也作為這個(gè)新的function的參數(shù)固化在其內(nèi)部了。
*/
面向?qū)ο螅ò?對象創(chuàng)建、原型鏈,數(shù)據(jù)類型的檢測,繼承)
復(fù)制代碼 代碼如下:
/****************** 面向?qū)ο?******************/
/********** 一:對象創(chuàng)建、原型鏈 **********/
/* 討論 構(gòu)造函數(shù)(類方式)創(chuàng)建對象 ,深入理解這些內(nèi)容,是很重要的
*/
function MyFunc() { }; //定義一個(gè)空函數(shù)
var anObj = new MyFunc(); //使用new操作符,借助MyFun函數(shù),就創(chuàng)建了一個(gè)對象
// 等價(jià)于:
function MyFunc() { };
var anObj = {}; //創(chuàng)建一個(gè)對象
anObj.__proto__ = MyFunc.prototype;
MyFunc.call(anObj); //將anObj對象作為this指針調(diào)用MyFunc函數(shù)
/*
用 var anObject = new aFunction() 形式創(chuàng)建對象的過程實(shí)際上可以分為三步:
第一步:建立一個(gè)新對象(anObject);
第二步:將該對象內(nèi)置的原型對象(__proto__)設(shè)置為構(gòu)造函數(shù)prototype引用的那個(gè)原型對象;
第三步:將該對象作為this參數(shù)調(diào)用構(gòu)造函數(shù),完成成員設(shè)置等初始化工作。
對象建立之后,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關(guān),
與構(gòu)造函數(shù)再扯不上關(guān)系了。
換句話說,構(gòu)造函數(shù)只是在創(chuàng)建對象時(shí)起到介紹原型對象和初始化對象兩個(gè)作用。
原型鏈:(參考:http://hi.baidu.com/fegro/blog/item/41ec7ca70cdb98e59152eed0.html)
每個(gè)對象(此處對象應(yīng)該僅指大括號括起來的object,不包括function、array。待驗(yàn)證?)
都會在其內(nèi)部初始化一個(gè)屬性,就是__proto__,當(dāng)我們訪問一個(gè)對象的屬性時(shí),
如果這個(gè)對象內(nèi)部不存在這個(gè)屬性,那么他就會去__proto__里找這個(gè)屬性,
這個(gè)__proto__又會有自己的__proto__,于是就這樣 一直找下去,也就是我們平時(shí)所說的原型鏈的概念。
*/
/* 理解了對象創(chuàng)建的原理,可試著分析下邊兩個(gè)示例的結(jié)果 */
var yx01 = new function() {return "圓心"};
alert(yx01); // [object Object]
var yx02 = new function() {return new String("圓心")};
alert(yx02); // “圓心”
/* 解釋:
"圓心"是基本的字符串類型,new String("圓心")則創(chuàng)建了一個(gè)string對象。
只要new表達(dá)式之后的構(gòu)造函數(shù)返回一個(gè)引用對象(數(shù)組,對象,函數(shù)等),都將覆蓋new創(chuàng)建的對象,
如果返回一個(gè)原始類型(無 return 時(shí)其實(shí)為 return 原始類型 undefined),
那么就返回 new 創(chuàng)建的對象。
參考:http://www.planabc.net/2008/02/20/javascript_new_function/
*/
/********** 二:數(shù)據(jù)類型的檢測 **********/
/* 判斷數(shù)據(jù)類型可能想到的方法:
constructor、typeof、instanceof、Object.prototype.toString.call()
*/
/***** 1、通過constructor屬性 *****/
var myvar= new Array("a","b","c","d");
function A(){}
myvar.constructor = A;
var c = myvar.constructor;
alert(c); // function A(){}
//可見,通過constructor屬性獲取類型的方法很容易被修改,不應(yīng)該用來判斷類型。
/***** 2、通過typeof *****/
/*
typeof是一個(gè)操作符,而不是個(gè)函數(shù)。
typeof的實(shí)際應(yīng)用是用來檢測一個(gè)對象是否已經(jīng)定義或者是否已經(jīng)賦值。
如if(typeof a!="undefined"){},而不要去使用if(a)因?yàn)槿绻鸻不存在(未聲明)則會出錯(cuò)。
typeof檢測對象類型時(shí)一般只能返回如下幾個(gè)結(jié)果:
number,boolean,string,function,object,undefined。
對于Array,Null,自定義對象 等使用typeof一律返回object,
這正是typeof的局限性。
*/
var num = new Number(1);
var arr = [1,2,3];
alert(typeof num); //object 而不是number
alert(typeof arr); //object 而不是Array
alert(typeof null); // object
/***** 3、通過 instanceof *****/
/* 用instanceof操作符來判斷對象是否是某個(gè)類的實(shí)例。
如果obj instanceof Class返回true,那么Class的原型與obj原型鏈上的某個(gè)原型是同一個(gè)對象,
即obj要么由Class創(chuàng)建,要么由Class的子類創(chuàng)建。
*/
function t(){};
t.prototype = Array.prototype;
//t.prototype = [];
var x = new t();
alert(x instanceof t);//彈出true
alert(x instanceof Array);//彈出true
alert(x instanceof Object);//彈出true
/*
由此可知,通過 instanceof 判斷數(shù)據(jù)類型也不可靠。
因?yàn)橐粋€(gè)對象(此處x)的原型鏈可以很長,每個(gè)原型的類型可以不同。
另外在iframe內(nèi)也會容易出錯(cuò):
即有個(gè)頁面定義了一個(gè)數(shù)組a,頁面又嵌套了一個(gè)IFrame,在Iframe里面通過 top.a instanceof Array, 是返回false的。
這個(gè)說明 父頁面和內(nèi)嵌iframe里的對象是不同的,不能混合在一起使用。
改成top.a instanceof top.Array 就會返回true
*/
/***** 4、通過 Object.prototype.toString.call() *****/
/*
Object.prototype.toString.call() 作用是:
1、獲取對象的類名(對象類型)。
2、然后將[object、獲取的類名]組合并返回。
可應(yīng)用于判斷Array,Date,Function等類型的對象
*/
var num = new Number(1);
var arr = [1,2,3];
alert(Object.prototype.toString.call(num)); // [object Number]
alert(Object.prototype.toString.call(arr)); // [object Array]
// 擴(kuò)展示例:(apply等價(jià)于call)
window.utils = {
toString: Object.prototype.toString,
isObject: function (obj) {
return this.toString.apply(obj) === '[object Object]';
},
isFunction: function (obj) {
return this.toString.apply(obj) === '[object Function]';
},
isArray: function (obj) {
return this.toString.apply(obj) === '[object Array]';
}
}
function A() { }
window.utils.isFunction(A); //true
window.utils.isObject(new A()); //true
window.utils.isArray([]); //true
/*
jQuery等框架 就是用這個(gè)方法判斷對象的類型的,因此可以把這種方法作為權(quán)威的判斷方法。
但是,如果重寫了Object.prototype.toString方法,這時(shí)候再用來判斷數(shù)據(jù)類型可能就會出錯(cuò),
所以,一般不要去重寫Object.prototype.toString方法。
*/
/********** 三:繼承 **********/
/*
JS繼承和閉包一樣,幾乎是每個(gè)想深入學(xué)習(xí)JS的朋友都要討論的問題,因此各種相關(guān)資料應(yīng)有盡有。
JS繼承代碼的版本非常多,但原理都是一樣的,核心都是利用了prototype對象。
為了和其他面向?qū)ο笳Z言的風(fēng)格相似,大多數(shù)都采用“類式”風(fēng)格模擬。
繼承的詳細(xì)原理不再贅述,網(wǎng)上有許多資料介紹。
這里給出一個(gè)示例:Jquery作者John Resig寫的繼承。
(其中的詳細(xì)注釋是來自某個(gè)博客,不知道是誰原創(chuàng),這里私自轉(zhuǎn)帖出來)
*/
(function () {
// initializing變量用來標(biāo)示當(dāng)前是否處于類的創(chuàng)建階段,
// - 在類的創(chuàng)建階段是不能調(diào)用原型方法init的
// - 我們曾在本系列的第三篇文章中詳細(xì)闡述了這個(gè)問題
// fnTest是一個(gè)正則表達(dá)式,可能的取值為(/\b_super\b/ 或 /.*/)
// - 對 /xyz/.test(function() { xyz; }) 的測試是為了檢測瀏覽器是否支持test參數(shù)為函數(shù)的情況
// - 不過我對IE7.0,Chrome2.0,FF3.5進(jìn)行了測試,此測試都返回true。
// - 所以我想這樣對fnTest賦值大部分情況下也是對的:fnTest = /\b_super\b/;
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// 基類構(gòu)造函數(shù)
// 這里的this是window,所以這整段代碼就向外界開辟了一扇窗戶 - window.Class
this.Class = function () { };
// 繼承方法定義
Class.extend = function (prop) {
// 這個(gè)地方很是迷惑人,還記得我在本系列的第二篇文章中提到的么
// - this具體指向什么不是定義時(shí)能決定的,而是要看此函數(shù)是怎么被調(diào)用的
// - 我們已經(jīng)知道extend肯定是作為方法調(diào)用的,而不是作為構(gòu)造函數(shù)
// - 所以這里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父類的原型對象
// - 注意:_super指向父類的原型對象,我們會在后面的代碼中多次碰見這個(gè)變量
var _super = this.prototype;
// 通過將子類的原型指向父類的一個(gè)實(shí)例對象來完成繼承
// - 注意:this是基類構(gòu)造函數(shù)(即是Class)
initializing = true;
var prototype = new this();
initializing = false;
// 我覺得這段代碼是經(jīng)過作者優(yōu)化過的,所以讀起來非常生硬,我會在后面詳解
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function (name, fn) {
return function () {
var tmp = this._super; // 這里是必要的,第91行注釋代碼可說明之。
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// 這個(gè)地方可以看出,Resig很會偽裝哦
// - 使用一個(gè)同名的局部變量來覆蓋全局變量,很是迷惑人
// - 如果你覺得拗口的話,完全可以使用另外一個(gè)名字,比如function F()來代替function Class()
// - 注意:這里的Class不是在最外層定義的那個(gè)基類構(gòu)造函數(shù)
// 這里的Class和上邊的window.Class函數(shù)不一樣,這里是window.Class內(nèi)部的函數(shù)局部變量
function Class() {
// 在類的實(shí)例化時(shí),調(diào)用原型方法init
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// 子類的prototype指向父類的實(shí)例(完成繼承的關(guān)鍵)
Class.prototype = prototype; // Class指代上邊的Class,并非一開始的window.Class
// 修正constructor指向錯(cuò)誤
// 是否可用Class.prototype.constructor = Class;來修正???
Class.constructor = Class;
// 子類自動獲取extend方法,arguments.callee指向當(dāng)前正在執(zhí)行的函數(shù)
Class.extend = arguments.callee;
return Class;
};
})();
您可能感興趣的文章:
- JavaScript易錯(cuò)知識點(diǎn)整理
- JavaScript面試出現(xiàn)頻繁的一些易錯(cuò)點(diǎn)整理
- 總結(jié)js中的一些兼容性易錯(cuò)的問題
- javascript幾個(gè)易錯(cuò)點(diǎn)記錄
- javaScript知識點(diǎn)總結(jié)(必看篇)
- 關(guān)于vue.js彈窗組件的知識點(diǎn)總結(jié)
- js+jquery常用知識點(diǎn)匯總
- JavaScript基礎(chǔ)知識點(diǎn)歸納(推薦)
- JS中BOM相關(guān)知識點(diǎn)總結(jié)(必看篇)
- javascript 易錯(cuò)知識點(diǎn)實(shí)例小結(jié)
相關(guān)文章
javascript實(shí)現(xiàn)自動輸出文本(打字特效)
文字如何實(shí)現(xiàn)打字的效果呢?在瀏覽網(wǎng)頁的時(shí)候也經(jīng)常能看到這種效果。本文給大家匯總介紹了幾種打字效果的文字特效,文字一個(gè)一個(gè)地打印在頁面上。2015-08-08document.all與getElementById、getElementsByName、getElementsByT
HTML DOM 定義了多種查找元素的方法,除了 getElementById() 之外,還有 getElementsByName() 和 getElementsByTagName()。2008-12-12基于JavaScript實(shí)現(xiàn)簡單的輪播圖
這篇文章主要為大家詳細(xì)介紹了基于JavaScript實(shí)現(xiàn)簡單的輪播圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03一文帶你詳細(xì)理解uni-app如何構(gòu)建小程序
uni-app是近年來一種新興的多端混合開發(fā)框架,適合開發(fā)跨平臺應(yīng)用,方便多端運(yùn)行,下面這篇文章主要給大家介紹了關(guān)于uni-app如何構(gòu)建小程序的相關(guān)資料,需要的朋友可以參考下2022-11-11小程序多圖列表實(shí)現(xiàn)性能優(yōu)化的方法步驟
這篇文章主要介紹了小程序多圖列表實(shí)現(xiàn)性能優(yōu)化的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05利用NodeJS和PhantomJS抓取網(wǎng)站頁面信息以及網(wǎng)站截圖
這篇文章主要介紹了利用NodeJS和PhantomJS抓取網(wǎng)站頁面信息以及網(wǎng)站截圖的方法,提供實(shí)例代碼供大家參考2013-11-11實(shí)用Javascript調(diào)試技巧分享(小結(jié))
這篇文章主要介紹了實(shí)用Javascript調(diào)試技巧分享(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06