前端開發工具 1.1、 WebStorm介紹和下載 l 介紹 WebStorm是JetBrains 推出的一款強大的HTML5編輯工具,擁有豐富的代碼快速編輯,可以智能的補全代碼、代碼格式化、html提示以及代碼檢查和快速修複等,支持不同瀏覽器的提示,同時也是一款JavaScript 開發工具,擁有 ...
前端開發工具
1.1、 WebStorm介紹和下載
l 介紹
WebStorm是JetBrains 推出的一款強大的HTML5編輯工具,擁有豐富的代碼快速編輯,可以智能的補全代碼、代碼格式化、html提示以及代碼檢查和快速修複等,支持不同瀏覽器的提示,同時也是一款JavaScript 開發工具,擁有即時編輯(chrome)、自動完成、debugger、Zen Coding、HTML5 支持、JSLint、Less支持、CoffeeScript支持、Node.JS、單元測試、集成git和svn版本控制等特性,推薦前端工程師使用。
WebStorm被廣大中國JS開發者譽為“Web前端開發神器”
l 官網:http://www.jetbrains.com/webstorm/
js的基礎回顧
// 1、JS中數據類型有哪些?
// JS中有6種數據類型,其中五種基本數據類型(Number,Boolean,String,Undefined,Null)和一種複合數據類型(Object)
console.log('--------------------JS中數據類型有哪些END------------------------');
// 2、如何判斷變數數據的類型?
// 使用typeof來判斷變數的數據類型
var i = 12; // 'number'
var s = 'abc'; // 'string'
var b = true; // 'boolean'
var u = undefined; // undefined
var oDate = new Date(); // 'object'
var arr = [12,5]; // 'object'
var o = {}; // 'object'
var fn = function(){alert('xxx')}; // 'function'
var n = null; // 'object'
console.log(typeof i);
console.log(typeof s);
console.log(typeof b);
console.log(typeof u);
console.log(typeof oDate);
console.log(typeof arr);
console.log(typeof o);
console.log(typeof fn);
console.log(typeof n);
// 註意點:
// 1. JS解釋器認為null是object數據類型的一種特殊形式,而null在JS裡面特指空對象
// 2. function(){}的數據類型是function, 也就是說function也是一種基本數據類型,而不是對象的一種特殊形式。實際上在JS中,函數是一個極為容易引起誤解或者引發歧義的數據類型,它可以是獨立的數據類型,也可以被稱為類或者構造函數,還可以作為函數對象等。
console.log('--------------------如何判斷變數數據的類型END------------------------');
// 3、JS中的空類型有哪些?
// 1).null 是一個原始的數據類型,只有一個值null, 特指空對象
// 2).undefined是一個原始的數據類型,只有一個值undefined
// 1、定義變數沒有賦值,此時變數的值是undefined
var a;
console.log(a);// undefined
///2、函數沒有返回值或者沒有返回具體的值都是預設返回undefined
function show() {
console.log('我沒有返回值,預設返回undefined');
}
console.log(show()); // undefined
///3、訪問對象中一個不存在的屬性
console.log(window.abc); // undefined
console.log('--------------------JS中的空類型有哪些END------------------------');
// 4、JS中代表哪些數據代表false?
// 數字0、空字元串''、false、空對象null、NaN、undefined
// 5、JS中 === 和 == 有什麼區別?
// == 用來比較兩個變數的值是否相等,先做數據類型的轉換,在做比較
// === 全等,嚴格的比較,先比較數據類型,數據類型不相等,就立刻返回false
console.log('1' == 1); // true
console.log('1' === 1); // false
console.log('--------------------JS中 === 和 == 有什麼區別END------------------------');
// 6、JS中的in運算符有什麼用?
// 1). 用來迴圈數組,性能低,建議使用普通的for
var arr = [12,5,8];
for (var i in arr) {
console.log(i + '---' + arr[i]); // 0-12 1-5 2-8
}
// 2). 用來迴圈對象
var p = {
name: 'Jack',
age: 20,
gender: 'male'
};
for (var name in p) {
console.log(name + '---' + p[name]); // 'name---Jack' age---20 'gender---male'
}
// 屬性操作:
// i). 點 . 比較方面,不能接收變數
// ii). 中括弧 [] 可以接收變數,可以替代所有點出現的位置
// 3). 用來判斷某個屬性是否屬於某一個對象
console.log('name' in p); // true
console.log('abc' in p); // false
console.log('--------------------JS中的in運算符有什麼用END------------------------');
// 7、JS中的邏輯中斷?
// 且&& 或 ||
// && 當左側為false,直接返回左側的結果;當左側結果為true,返回右側結果
// || 當左側為true,直接返回左側結果; 當左側結果為false,返回右側結果
console.log(1 || 2); // 1
console.log(0 || 2); // 2
console.log(null || 0); // 0
面向對象的介紹:那麼什麼是對象呢?這裡只需要舉出相關例子就是可以不需要準確說出概念
面向過程:完成一件事情的所有步驟都親力親為,一步一步來完成。例如要吃餃子,就必須自己剁餡兒、和麵、擀餃子皮兒、包餃子
面向對象:完成一件事情,直接去找能做這件事的人。例如吃餃子,直接找老婆(如果沒有就去餃子館吧)
面向過程:是事件的執行者
面向對象:是指揮者
一言以蔽之:面向對象就是對面向過程的封裝!
那麼為什麼要面向對象呢?
1) 提高代碼的復用性,簡化開發
2) 使代碼結構清晰
3) 在JS中有一個特殊的作用:防止代碼污染
那麼什麼是代碼污染呢?代碼污染其實就是代碼中含有重覆的變數並且其中後面的代碼吧前面的代碼給覆蓋了,就是再調用的時候不知道找不到自己真實的需要調用的那個對象或者是方法
如何解決代碼污染?
1、我們可以把定義的方法放在對象中裡面,然後使用對象調用自己的方法基本上可以防止代碼污染如下:
創建對象的兩種方式:
第一種就是使用對象的直接量的方式:
在定義對象的時候,直接使用JSON格式來定義一個JS對象,並且為這個對象賦值屬性和方法,稱為對象的直接量(字面量)
這種方式創建的對象,預設是Object類型的對象(這種方式創建object對象)
l 案例代碼:(也就是使用json格式的數據來定義對象稱作為對象的直接量)(也就是使用json格式的數據來定義對象實現這個過程)
效果如下:
l 缺陷:
u 只能使用1次,無法重覆利用,每次都要編寫相同代碼創建新對象
l 使用場景:
u 對象只需要一次使用的時候。比如作為傳遞參數;
u 自己封裝JS框架(函數)的時候。
第二種方式就是使用構造函數創建對象
l 構造函數創建對象的一般步驟:
1) 通過function指定一個構造函數,與普通函數沒差別,只是約定首字母大寫以區分
2) 然後在構造函數中通過this.屬性名=屬性值來完成對象屬性初始化。
3) 最後通過new關鍵字創建對象(實例)
其中的代碼案例:
效果如下:
那麼對象中的函數相同嗎?
測試:
結果顯示:
實際上查看記憶體中對象的屬性發現p1和p2中各有一份 sayHello 方法:
結論:
通過上面的實驗我們發現,對象中的所有屬性,包括定義的方法,都是每個對象獨有的,不會共用。
屬性不共用可以理解,但是方法都是一樣的,如果也不共用,那麼就是對記憶體的極大浪費。
l 優勢:
u 對象可以重覆創建
l 缺點:
u 對象中相同的方法,無法實現共用,記憶體占用高
由上面可以引出來對象原型的概念
對象原型(prototype)
在JS中,每一個構造函數都會有一個prototype(也是一個對象)屬性,任何由同一個構造函數創建的對象,都會共用這個prototype對象,並且可以具備這個prototype的所有屬性(成員)
原型的基本的使用方法
剛開始的時候沒有設置對象原型的時候,定義一個空的構造函數,只給出name屬性,當列印age屬性的時候結果是undefined,因為沒有給屬性
結果:
列印控制臺中是undefined的。
查看對象屬性也是只有name屬性,沒有age:
當使用原型給構造函數添加新的屬性的時候,也就是使用prototype給構造函數添加新的屬性
此時控制臺上已經顯示了屬性
這個時候再次查看對象的屬性:
發現p1和p2對象中雖然依然沒有age,但是構造函數Person的prototype中已經有age了,又因為對象會共用prototype的成員,所以每個對象都可以使用age屬性了!
例外一種情況進行總結:
1.1.1.1、 給對象從原型中繼承的屬性賦值
嘗試修改對象中,繼承自prototype的屬性值age:
結果:p1的age已經變為30, p2沒變
那麼問題來了:
問題,既然是共用的屬性,那麼為什麼p1修改了,p2值沒變?
查看記憶體中的狀態:
我們發現:prototype中的age沒有改變,而是在對象p1中新增了自己的age屬性,並且賦值為30
結論:當原型和對象自身都有相同屬性時,對象中的屬性優先!
1.1.1.1、 總結
1) 當我們訪問對象中的屬性時,預設會先在當前對象中查找;如果在當前對象找不到,就會去對象的prototype中查找
a) 事實上,prototype也是對象,因此在prototype對象中找不到,會到prototype對象的prototype中找,逐級向上形成“原型鏈”,原型鏈的最頂級的是Object.prototype
b) 那麼Object.prototype也是對象,再向上還有嗎?沒有了,再向上是null。Object.prototype就是原型鏈的根
2) 修改對象的屬性,不會修改prototype中的屬性,而是在對象中創建新的屬性
3) 某些瀏覽器支持__proto__屬性,與prototype是同一個東西,但是某些瀏覽器還不支持。所以一般不要用
再有一種情況就是通過原型來更改構造函數
為了減少對象記憶體占用,我們一般創建構造函數的時候,會把經常變化的屬性在構造函數中聲明,每個對象都有自己的屬性。然後把公用的不變化的東西(通常是函數)通過原型來添加。
此時的驗證結果顯示:
每個對象都可以調用sayHello方法,證明共用了方法。 各個對象的sayHello方法判斷是相等的。證明記憶體中只有一份。
總結構造函數創建對象的方法:
1、 構造函數裡面寫變化屬性
2、 原型上去添加方法
通過原型擴展js的原生對象
事實上不僅僅我們自己定義的對象有原型,JS中原生的對象也有原型。我們可以通過原生對象的原型對原生的JS進行擴展!
原生的JS中,Array對象並沒有sum方法,嘗試調用報錯:
我們使用原型進行擴展:
結果顯示:
疑問?
擴展字元串的trim方法
終極總結:判斷一個 對象的類型
那麼如何判斷一個對象的類型呢?
1)instance of
2)通過類的位元組碼Class對象
方式一:instanceof作用是用來判斷一個對象是否屬於一個類
案例代碼:
結果顯示:
由此得出的總結結論:instanceof 可以基本準確的判斷對象的所屬類型,但是當 instanceof後面跟的是類型是父類類型的時候,判斷是不准確的。這一點與Java類似
如果要是更準確的來判斷對象類型的話,是可以通過類的位元組碼對象,也就是和java中的反射技術是差不多的,但是js中是沒有類的概念的,更不要說class對象,但是js中類似的概念是構造函數constructor
也就是所說的方式2 :作用constructor用來檢測直接的構造者
通過constructor來準確判斷對象類型
結果顯示的也是返回的是布爾值
this關鍵字的總結介紹:
在Java中,this關鍵字表示的是當前對象。比如在方法中使用,就是調用方法的對象。
在JavaScript中,this也是類似的。只不過JS中方法無需定義在類中,所以很多人對this的含義就懵逼了。
代碼案例的實現:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>this關鍵字的含義</title>
</head>
<body>
<script>
// 演示: this關鍵字的含義
// this指的是當前的方法屬於誰,只看調用,誰調用就指向誰,所以我們只需要搞清楚調用方法的對象即可
// 1. 全局函數中的this
function show() {
console.log('全局函數中的this:' + this);
}
// 全局函數 預設是註冊到window對象上中
// 而調用window中的任何成員,預設是可以省略window.的
// 這裡的show()完整的寫法其實是:window.show(); 因此肯定當前的方法屬於window這個對象
// show(); // Object window
window.show();
// 2. 對象中的this
var p = {
name: 'Jack',
age: 20,
gender: 'male',
sayHello: function() {
console.log('Hello, I am' + this.name + ',' + this.age + 'years old');
}
};
// 對象中的this,指向的是調用該方法的對象,現在該方法屬於p這個對象,因此this指向了p
p.sayHello(); // 'Hello, I amJack,20years old'
// 3. 事件函數中的this
// 當前的事件註冊到了document對象身上
// 事件觸發時,調用該事件處理函數的對象就是事件源對象,也就是document對象,此時事件函數裡面的this指向了事件源對象
document.onclick = function() {
console.log('事件函數中的this:' + this); //object HTMLDocument
};
// 4. 定時器裡面的this
// setTimeout方法本身就是window對象的一個方法,那麼時間到了以後,
// 定時器裡面的函數有誰來調用? window, 所以定時器裡面的this就是window
setTimeout(function() {
console.log('定時器裡面的this:' + this); // object Window
}, 1000);
</script>
</body>
</html>
關於this關鍵字進行總結:
1) 其實記住一點就夠了:this指的是當前的方法屬於誰,只看調用,誰調用就指向誰
2) 對象外的全局函數,this是window
3) 對象中的方法,被對象調用時,this是對象自己
4) 事件處理函數,this是事件綁定的對象
5) 定時任務函數,this是window
3.8 、對象的繼承(組合式繼承、構造函數繼承、原型繼承)
3.8.1 組合式繼承:組合式繼承就是把 一個對象的屬性 全部加到另一個對象中。也被稱為屬性拷貝。
應用:組合式繼承主要用在 對象的直接量 繼承另一個 對象的直接量時。
案例:
結果顯示:
使用的場景:比如我用對象封裝了一個框架,現在要對該框架的方法進行擴展,新增更多方法。此時就可以把新增的方法定義到新的對象中,使用組合式繼承來完成 方法的擴展。
//比如可以通過一個匿名對象,給o1可以擴展更多的方法
結果顯示
3.8.2 構造函數繼承
如果對象不是 直接量對象,而是通過構造函數new出來的,那麼如何繼承另一個構造函數中的屬性呢?
結果顯示:
我們發現直接在子類的構造函數中直接調用父類的構造函數不能實現屬性的繼承,因為直接調用父類的構造函數,此時父類的構造函數屬於全局函數,所以裡面的this指向了window對象,不是Worker的實例,這個時候我們要去改變父類構造函數裡面this的指向,讓父類構造函數裡面的this指向Worker的實例
函數也是對象,任何函數都可以調用兩個方法:call和apply ,來自於Function的原型
call和apply都會導致調用的函數被執行,但是可以在執行時指定this
名稱 |
用法 |
參數說明 |
call |
fn.call(fn函數裡面this的值,參數1,參數2..); |
第一個參數是被調用函數裡面this的值,真實的參數從第二個開始 |
apply |
fn.apply(fn函數裡面this的值, [參數1,參數2..]); |
同上,實際參數是數組結構 |
結果顯示:
我們發現,worker具備了父類中的所有屬性,但是,Person類在原型中擴展的方法 sayHello 並沒有被繼承。訪問時報錯
並且,在執行instanceof的時候,無法判斷worker屬於Person類的實例
特點:
1) 可以繼承父類構造函數中定義的屬性
2) 父類原型中定義的方法無法訪問,擴展性差
3) instanceof 判斷失效,因為子類原型與父類原型沒有關聯
3.8.3 使用的是原型繼承;
為瞭解決上面的問題我們可以使用的就是原型繼承的方式進行實現這個過程:
總結(非常重要可以查看)
JS的繼承步驟:
1) 在你的構造函數中,調用父類的構造,但是要用call或apply
2) 讓你的構造函數的prototype指向父類的對象
3) 因為原型被覆蓋,因此原型中的constructor也被覆蓋,需要修正constructor指向子類的構造函數
註意:原型繼承,其實是讓子類原型指向了父類對象。 這樣父類對象就 覆蓋了子類prototype中的所有屬性。其中constructor屬性也被覆蓋成了父類的。因此,在使用原型繼承時,必須手動把子類的原型中的constructor屬性修改為子類的構造函數!