JavaScript必須瞭解的知識點總結。

来源:http://www.cnblogs.com/webmark2016/archive/2016/11/20/6082625.html
-Advertisement-
Play Games

整理的知識點不全面但是很實用。 主要分三塊: (1)JS代碼預解析原理(包括三個段落); (2)函數相關(包括 函數傳參,帶參數函數的調用方式,閉包); (3)面向對象(包括 對象創建、原型鏈,數據類型的檢測,繼承)。 JS代碼預解析原理 函數相關(包括 函數傳參,帶參數函數的調用方式,閉包) 面向 ...


整理的知識點不全面但是很實用。

主要分三塊:

(1)JS代碼預解析原理(包括三個段落);

(2)函數相關(包括 函數傳參,帶參數函數的調用方式,閉包);

(3)面向對象(包括 對象創建、原型鏈,數據類型的檢測,繼承)。

 

JS代碼預解析原理

複製代碼
/****************** JS代碼預解析原理 ******************/
/*
JS代碼預解析、變數作用域、作用域鏈等 應該能作為學習JS語言的入門必備知識。
下邊給出些簡要解釋和一些典型的代碼段,若要瞭解更多,能從網上搜索到更多相關示例。

引用網上的一段有關 “JS的執行順序” 的解釋:
如果一個文檔流中包含多個script代碼段(用script標簽分隔的js代碼或引入的js文件),它們的運行順序是:
步驟1. 讀入第一個代碼段(js執行引擎並非一行一行地執行程式,而是一段一段地分析執行的)
步驟2. 做語法分析,有錯則報語法錯誤(比如括弧不匹配等),並跳轉到步驟5
步驟3. 對var變數和function定義做“預解析”(永遠不會報錯的,因為只解析正確的聲明)
步驟4. 執行代碼段,有錯則報錯(比如變數未定義)
步驟5. 如果還有下一個代碼段,則讀入下一個代碼段,重覆步驟2
步驟6. 結束
*/
// 下邊給出 三段覺得比較典型的代碼示例:
/********** 一:基本的幾條語句 **********/
alert(num); // undefined
var num = 0;
alert(str); // 錯誤: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'); }

/********** 二:函數名與變數名相同 **********/
//var mark = 1;
function mark(x) {
return x * 2;
}
var mark;
alert(mark); // function mark(x) { return x * 2; }
// 去掉前邊的var mark = 1;則會返回1

/********** 三:把第二段包括在語句塊中 **********/
// 當有條件時候(代碼包含在條件語句塊里)
if (false) {
var mark1 = 1;
function mark1() {
alert("exec mark1");
}
//var mark1;
alert(mark1);
}
alert(mark1);
mark1();
// 由於解析瀏覽器解析不同,這段代碼在不同瀏覽器里執行的結果不一致,具體原因可從網上查找答案
複製代碼

 

函數相關(包括 函數傳參,帶參數函數的調用方式,閉包)

複製代碼
/****************** 函數相關 ******************/

/********** 一:函數傳參 **********/
/*
編程語言大概都有 值類型與引用類型 的區別,JS也不例外。
原始類型:undefined null number boolean 均為值類型。
string比較特殊,因為它是不可改變的,String類定義的方法都不能改變字元串的內容。
function object array 這三種為引用類型。
*/
/* JavaScript 函數傳遞參數時,是值傳遞。

ECMAScript 中,所有函數的參數都是按值來傳遞的。
基本類型值的傳遞和基本類型變數複製一致(採用在棧內新建值),
引用類型值的傳遞和引用類型變數的複製一致(棧記憶體放的是指針,指向堆中同一對象)。
具體參考:http://www.xiaoxiaozi.com/2010/03/05/1719/
*/
function setName(obj){
//obj拷貝了person的值(person是一個對象的引用地址),所以obj也指向了person所指向的對象。
obj.name = "xiaoxiaozi";
obj = {}; // 讓obj 指向了另一個對象
obj.name = "admin";
}
var person = {};
setName(person);
alert(person.name); // xiaoxiaozi


/********** 二:帶參數函數的調用方式 **********/
/* 在DOM不同版本中,函數調用方式不太一樣。標準推薦的是addEventListener和attachEvent
這兩種方式有很多資料可查。但是有些已經不被推薦的函數調用仍舊有實際應用,相關資料發現的不多。
這裡主要討論這些函數調用方式
*/
var g = "全局變數";
function show(str) {
alert("my site: " + str);
}
setTimeout("show(g);",100); // g是全局變數,函數正確執行
function t() {
var url = "www.xujiwei.cn";
var num = 2;
//setTimeout("alert("+url+")", 3000); // 解析錯誤,www未定義
//setTimeout("alert("+num+")", 3000); // 解析正確,註意與上句對比
//setTimeout("show('url');", 2000); // url
//setTimeout("show("+ url +");", 2000); // 解析錯誤,www未定義
//setTimeout("show(url);", 2000); // 解析錯誤,url未定義
//setTimeout('"show("+ url +");"', 2000); // 解析錯誤,url未定義
//setTimeout("show('"+ url +"');", 2000); // 正確
//setTimeout(function(){show(url);},1000); // 正確
}
t();
/* 結論:
諸如onclick="xx();"等函數調用方式,在雙引號內的內容直接解析為js語句執行。
若調用的函數帶有參數,註意對比以上各種寫法,保證傳遞進去的參數為正確的。
*/


/********** 三:閉包 **********/
/*
閉包,幾乎是每個學習JS的朋友都要討論的問題,因此各種相關資料應有盡有。
它的作用很大,但也有弊端,例如如果使用不當,容易引起記憶體泄漏等問題,因此有不少人
提倡少用閉包。

這裡列出閉包的一種經典應用,一個有爭議的應用。
*/
function test1() { //通過閉包,每次能傳入不同的j值。
for (var j = 0; j < 3; j++) {
(function (j) {
setTimeout(function () { alert(j) }, 3000);
})(j);
}
}
test1();
/* 這個是閉包的典型應用 */

(function tt() {
for (var i = 1; i < 4; i++) {
document.getElementById("b" + i).attachEvent("onclick",
new Function('alert("This is button' + i + '");')); // 在IE中測試
}
})() // 立即執行函數,一個文件是否只能有一個?把上邊函數寫成立即執行出問題,怎麼回事?

/* 這個問題出現在論壇里,有很多爭議
有說是new Function動態生成個閉包結構的函數,所以能保存外部變數。
有說是跟閉包無關,new Function,就是新定義了一個function,
i的值也作為這個新的function的參數固化在其內部了。
*/
複製代碼

 

面向對象(包括 對象創建、原型鏈,數據類型的檢測,繼承)

複製代碼
/****************** 面向對象 ******************/

/********** 一:對象創建、原型鏈 **********/
/* 討論 構造函數(類方式)創建對象 ,深入理解這些內容,是很重要的
*/
function MyFunc() { }; //定義一個空函數
var anObj = new MyFunc(); //使用new操作符,藉助MyFun函數,就創建了一個對象
// 等價於:
function MyFunc() { };
var anObj = {}; //創建一個對象
anObj.__proto__ = MyFunc.prototype;
MyFunc.call(anObj); //將anObj對象作為this指針調用MyFunc函數
/*
用 var anObject = new aFunction() 形式創建對象的過程實際上可以分為三步:
第一步:建立一個新對象(anObject);
第二步:將該對象內置的原型對象(__proto__)設置為構造函數prototype引用的那個原型對象;
第三步:將該對象作為this參數調用構造函數,完成成員設置等初始化工作。

對象建立之後,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關,
與構造函數再扯不上關係了。
換句話說,構造函數只是在創建對象時起到介紹原型對象和初始化對象兩個作用。

原型鏈:(參考:http://hi.baidu.com/fegro/blog/item/41ec7ca70cdb98e59152eed0.html)
每個對象(此處對象應該僅指大括弧括起來的object,不包括function、array。待驗證?)
都會在其內部初始化一個屬性,就是__proto__,當我們訪問一個對象的屬性時,
如果這個對象內部不存在這個屬性,那麼他就會去__proto__里找這個屬性,
這個__proto__又會有自己的__proto__,於是就這樣 一直找下去,也就是我們平時所說的原型鏈的概念。
*/

/* 理解了對象創建的原理,可試著分析下邊兩個示例的結果 */
var yx01 = new function() {return "圓心"};
alert(yx01); // [object Object]
var yx02 = new function() {return new String("圓心")};
alert(yx02); // “圓心”
/* 解釋:
"圓心"是基本的字元串類型,new String("圓心")則創建了一個string對象。
只要new表達式之後的構造函數返回一個引用對象(數組,對象,函數等),都將覆蓋new創建的對象,
如果返回一個原始類型(無 return 時其實為 return 原始類型 undefined),
那麼就返回 new 創建的對象。
參考:http://www.planabc.net/2008/02/20/javascript_new_function/
*/



/********** 二:數據類型的檢測 **********/
/* 判斷數據類型可能想到的方法:
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屬性獲取類型的方法很容易被修改,不應該用來判斷類型。

/***** 2、通過typeof *****/
/*
typeof是一個操作符,而不是個函數。
typeof的實際應用是用來檢測一個對象是否已經定義或者是否已經賦值。
如if(typeof a!="undefined"){},而不要去使用if(a)因為如果a不存在(未聲明)則會出錯。
typeof檢測對象類型時一般只能返回如下幾個結果:
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操作符來判斷對象是否是某個類的實例。
如果obj instanceof Class返回true,那麼Class的原型與obj原型鏈上的某個原型是同一個對象,
即obj要麼由Class創建,要麼由Class的子類創建。
*/
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 判斷數據類型也不可靠。
因為一個對象(此處x)的原型鏈可以很長,每個原型的類型可以不同。

另外在iframe內也會容易出錯:
即有個頁面定義了一個數組a,頁面又嵌套了一個IFrame,在Iframe裡面通過 top.a instanceof Array, 是返回false的。
這個說明 父頁面和內嵌iframe里的對象是不同的,不能混合在一起使用。
改成top.a instanceof top.Array 就會返回true
*/

/***** 4、通過 Object.prototype.toString.call() *****/
/*
Object.prototype.toString.call() 作用是:
1、獲取對象的類名(對象類型)。
2、然後將[object、獲取的類名]組合併返回。
可應用於判斷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]

// 擴展示例:(apply等價於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等框架 就是用這個方法判斷對象的類型的,因此可以把這種方法作為權威的判斷方法。
但是,如果重寫了Object.prototype.toString方法,這時候再用來判斷數據類型可能就會出錯,
所以,一般不要去重寫Object.prototype.toString方法。
*/




/********** 三:繼承 **********/
/*
JS繼承和閉包一樣,幾乎是每個想深入學習JS的朋友都要討論的問題,因此各種相關資料應有盡有。
JS繼承代碼的版本非常多,但原理都是一樣的,核心都是利用了prototype對象。
為了和其他面向對象語言的風格相似,大多數都採用“類式”風格模擬。

繼承的詳細原理不再贅述,網上有許多資料介紹。
這裡給出一個示例:Jquery作者John Resig寫的繼承。
(其中的詳細註釋是來自某個博客,不知道是誰原創,這裡私自轉帖出來)
*/
(function () {
// initializing變數用來標示當前是否處於類的創建階段,
// - 在類的創建階段是不能調用原型方法init的
// - 我們曾在本系列的第三篇文章中詳細闡述了這個問題
// fnTest是一個正則表達式,可能的取值為(/\b_super\b/ 或 /.*/)
// - 對 /xyz/.test(function() { xyz; }) 的測試是為了檢測瀏覽器是否支持test參數為函數的情況
// - 不過我對IE7.0,Chrome2.0,FF3.5進行了測試,此測試都返回true。
// - 所以我想這樣對fnTest賦值大部分情況下也是對的:fnTest = /\b_super\b/;
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// 基類構造函數
// 這裡的this是window,所以這整段代碼就向外界開闢了一扇窗戶 - window.Class
this.Class = function () { };
// 繼承方法定義
Class.extend = function (prop) {
// 這個地方很是迷惑人,還記得我在本系列的第二篇文章中提到的麽
// - this具體指向什麼不是定義時能決定的,而是要看此函數是怎麼被調用的
// - 我們已經知道extend肯定是作為方法調用的,而不是作為構造函數
// - 所以這裡this指向的不是Object,而是Function(即是Class),那麼this.prototype就是父類的原型對象
// - 註意:_super指向父類的原型對象,我們會在後面的代碼中多次碰見這個變數
var _super = this.prototype;
// 通過將子類的原型指向父類的一個實例對象來完成繼承
// - 註意:this是基類構造函數(即是Class)
initializing = true;
var prototype = new this();
initializing = false;
// 我覺得這段代碼是經過作者優化過的,所以讀起來非常生硬,我會在後面詳解
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];
}
// 這個地方可以看出,Resig很會偽裝哦
// - 使用一個同名的局部變數來覆蓋全局變數,很是迷惑人
// - 如果你覺得拗口的話,完全可以使用另外一個名字,比如function F()來代替function Class()
// - 註意:這裡的Class不是在最外層定義的那個基類構造函數
// 這裡的Class和上邊的window.Class函數不一樣,這裡是window.Class內部的函數局部變數
function Class() {
// 在類的實例化時,調用原型方法init
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// 子類的prototype指向父類的實例(完成繼承的關鍵)
Class.prototype = prototype; // Class指代上邊的Class,並非一開始的window.Class
// 修正constructor指向錯誤
// 是否可用Class.prototype.constructor = Class;來修正???
Class.constructor = Class;
// 子類自動獲取extend方法,arguments.callee指向當前正在執行的函數
Class.extend = arguments.callee;
return Class;
};
})();
複製代碼

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.盒子模型 標準CSS盒子模型=content+padding+border+margin; IE盒子模型:content+margin(content裡面已經包含了padding+margin); 2.行內元素?塊級元素?空元素? 行內元素:a、b、span、img、input、strong、s ...
  • 通過對比js、php、c三門語言的for迴圈,加強自己對js變數作用域和php for迴圈的理解。 ...
  • 最近在看《javascript數據結構和演算法》這本書,補一下數據結構和演算法部分的知識,覺得自己這塊是短板。 做點小筆記。 ...
  • JSON序列化輸出 var xiaoming = { name: '小明', age: 14, gender: true, height: 1.65, grade: null, 'middle-school': '\"W3C\" Middle School', skills: ['JavaScrip ...
  • 國內目前關註最高,維護最好的一個關於nodejs的網站應該是http://www.cnodejs.org/ windows系統下簡單nodejs環境配置。 第一步:下載安裝文件 下載地址:官網 https://nodejs.org/en/download/ 這裡用的是 第二步:安裝nodejs 下載 ...
  • 目前網上有很多各種各樣的手風琴插件,但是沒有一個完整實現了的側菜單,今天寫了一個可以無限子節點的手風琴側菜單,有需要的可以參考一下,有什麼好的想法可以留言。(沒有經過徹底測試,不過問題應該不大) 下麵老規矩,直接貼代碼: ...
  • 在上一篇文章小div佈局之卡片堆疊(card-stacking)中有多行文字溢出省略的效果,這篇文章就對這種效果的實現做個簡單的記錄,以防自己忘記。 ...
  • 清除浮動帶來的額外影響 如果對於浮動不熟悉的同學,可以看看介紹float的文章。傳送門: "CSS float" 我們知道,在一個父元素內如果遇到某個浮動元素,此時父元素的高度會發生塌陷。針對父元素高度塌陷的問題,現在已經有了很多的解決方案。針對每一個方案,我們來進行深度的剖析。 添加空塊級元素 這 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...