第一章、引言 1.5 面向對象的程式設計常用概念 對象 (名詞):是指“事物”在程式設計語言中的表現形式。 這裡的事物可以是任何東西,我們可以看到它們具有某些明確特征,能執行某些動作。 這些對象特征就叫做屬性(形容詞),動作稱之為方法(動詞)。 類: 實際上就是對象的設計藍圖或製作配方。類更多的是一 ...
第一章、引言
1.5 面向對象的程式設計常用概念
對象(名詞):是指“事物”在程式設計語言中的表現形式。
這裡的事物可以是任何東西,我們可以看到它們具有某些明確特征,能執行某些動作。
這些對象特征就叫做屬性(形容詞),動作稱之為方法(動詞)。
類:實際上就是對象的設計藍圖或製作配方。類更多的是一種模板,而對象就是在這些模版的基礎上被創建出來的。
封裝:主要闡述對象中包含的內容。通常包括:
- 相關數據(用於存儲屬性)
- 基於這些數據所能做的事(所能調用的方法)
聚合:將幾個現有對象合併成一個新對象的過程
繼承:實現代碼重用
多態:不同對象通過相同的方法調用來實現各自行為的能力
第二章、基本數據類型、數組、迴圈及條件表達式
2.3.2 指數表示法
2e+3表示在數字2後面加3個0,即2000
typeof Infinity // number
typeof NaN // number
typeof null // object null值表示一個空指針對象
2.3.4 惰性求值
Javascript引擎在一個邏輯表達式中遇到一個非布爾類型操作數,
那麼該操作數的值就會成為該表達式返回的結果。
true || "something" //true
true && "something" //"something"
2.9 練習題
var s = "1s"; //隱式轉換Number()用於任何數據類型
s++ //NaN
10 % "0" //NaN 如果被除數是有限大的數值而除數是零,則結果是NaN
//乘法口訣程式代碼
for(var i=1;i<10;i++){
for(var j=1;j<=i;j++){
document.write(j+"*"+i+"="+i*j+" ");
}
document.write("<br>");
}
第三章、函數
3.1.2 函數參數
函數內部都有一個內建的arguments數組,能返回函數所接收的所有參數
function sumOnSteroids(){
var i,res = 0;
for(i = 0; i < arguments.length; i++){
res += arguments[i];
}
return res;
}
sumOnSteroids(1,2,3,4,5,6); //21
3.3 函數的作用域
var a = 123;
function f(){
alert(a); //undefined 這是因為函數域優先於全局域
var a = 1;
alert(a); //1
}
f();
3.4.2 回調函數
當我們將函數B傳遞給函數A,並由A來執行B時,B就成了一個回調函數
function A(a,b,c,callback){
var i=0,ar = [];
for(i=0;i<3;i++){
ar[i] = callback(arguments[i]*2);
}
return ar;
}
function B(a){ //回調函數
return a+1;
}
A(a,b,c,B)
3.4.4 自調函數
(function(name){
alert('Hello '+name+' !');
})('Jesse') //Hello Jesse !
//第二對括弧起到的是立即調用的作用,同時也是向函數傳遞參數的地方
使用自調函數的好處是不會產生任何全局變數,缺點是無法重覆執行,這使得匿名自調函數最合適於執行一些一次性的或者初始化的任務
3.4.5 私有函數
function a(param){
function b(theinput){ //私有函數
return theinput * 2
};
return 'The result is '+b(param)
}
使用私有函數的好處:
- 有助於全局命名空間的純凈性(命名衝突的機會很小)
- 私有性--不被外部其他用用程式所用
3.4.7 能重寫自己的函數
這對於要執行某些一次性初始化工作的函數非常有用
function a(){
alert('A'); //第一次調用該函數時該語句會被執行
a = function(){ //第一次調用時a被賦予新函數
alert('B'); //第二次調用該函數時該語句會被執行
};
}
var a = function(){
function someSetup(){
var setup = 'done'
}
function actualWork(){
alert('work')
}
someSetup();
return actualWork;
}();
3.5 閉包
3.5.1 作用域鏈
在函數內定義的變數在函數外是不可見的,
但是如果該變數是在某個代碼塊中定義的(if或for語句中),它在代碼塊外是可見的。
3.5.2 詞法作用域
在javascript中每個函數都有一個屬於自己的詞法作用域,也就是說每個函數
在被定義時(而非執行時)都會創建一個屬於自己的環境(即作用域)
function f1(){var a = 1;f2();}
function f2(){return a;} //f2()被定義時a是不可見的,只能訪問自身作用域和全局作用域
f1(); //a is not defined
3.5.3 利用閉包突破作用域鏈
3.5.3.1 閉包#1
function f(){
var b = "m";
return function(){ //有著私有作用域,可以訪問f()的作用域和全局作用域
return b;
}
}
var n = f();
n(); //m
f()是全局函數,我們可以將它的返回值賦值給另一個全局變數,
從而生成一個可以訪問f()私有空間的新全局函數
3.5.3.3 相關定義與閉包#3
如果一個函數在其父函數返回之後想留住對父級作用域的鏈接,就必須要為此建立一個閉包
3.5.3.4 迴圈中的閉包
function f(){
var a = [];
for(var i = 0; i < 3; i++){
a[i] = function(){
return i;
}
}
return a;
}
var s = f();
s[0](); //3
s[1](); //3
s[2](); //3
我們在這裡創建了3個閉包,它們都指向一個共同的局部變數i,
但是閉包不會記錄它們的值,他們所擁有的只是一個i的引用,
因此只能返回i的當前值(迴圈結束時i=3).
function f(){
var a = [];
for(var i = 0; i < 3; i++){
a[i] = (function(x){
return function(){
return x;
}
})(i);
}
return a;
}
var s = f();
s[0](); //0
s[1](); //1
s[2](); //2
3.7 練習題
1.十六進位值轉為顏色函數getRGB()
function getRGB(hex){
var rgb=[0,0,0];
if(/#(..)(..)(..)/g.test(hex)){
rgb=[parseInt(RegExp.$1,16),parseInt(RegExp.$2,16),parseInt(RegExp.$3,16)];
};
return "rgb("+rgb.join(",")+")";
}
getRGB('#00ff00'); //"rgb(0,255,0)"
第四章、對象
4.1 從數組到對象
用[]定義數組的方法我們稱之為數組文本標識法
用{}定義對象的方法我們稱之為對象文本標識法
4.1.2 哈希表、關聯型數組
在javascript中我們用數組表示索引型數組,用對象表示關聯型數組
4.1.3 訪問對象屬性
一般通過以下兩種方式訪問對象的屬性:
- 中括弧表示法:hero['name']
- 點號表示法:hero.name
4.1.7 構造器函數
function Hero(name){ //構造器函數首字母大寫
this.name = name;
this.occupation = 'ninja'
}
var hero = new Hero('jesse'); //使用new操作符創建新對象
hero.name; //ninja
使用構造器函數的好處是能利用同一個構造器函數通過傳參從而創建出不同的對象。
4.1.8 全局對象
事實上程式所在的宿主環境一般都會為其提供一個全局對象,
而所謂的全局變數其實只不過是該對象的屬性
4.1.9 構造器屬性
構造器屬性實際上是一個指向用於創建該對象的構造器函數的引用
hero.contructor //Hero
通過instanceof操作符,我們可以測試一個對象是不是由某個指定的構造器函數所創建的
hero instanceof Hero; //true
4.1.12 傳遞對象
引用類型,因為其值大小不固定,因此棧記憶體中存放的只是該對象的訪問地址,(即該對象的引用)
堆記憶體為這個值分配空間。因此我們在引用上所做的任何改動,都會影響到他所引用的源對象。
4.2 內建對象
4.2.1 Object
所有對象都繼承自Object對象,因此都具有toLocaleString()、toString()和valueOf()方法。
var o = new Object();
var o = {};
alert(o); //[object,Object]
由於alert()要接收字元串參數,所以它會在後臺調用toString()方法
Object構造器的成員:
用Object()構造器所建對象的成員:
4.2.2 Array
var a = new Array();
var a = [];
typeof a; //'object' 數組也是對象
值得關註的數組的屬性與方法
- length屬性:定義數組時會自動生成length屬性,而一般對象中沒有
- sort()、join()、slice()、splice()
Array對象的成員:
4.2.3 Function
函數實際上也是一種對象,函數對象的內建構造器是Function()
定義函數的三種方式:
function sum(a,b){return a + b;}; //函數聲明
var sum = function(a,b){return a + b;}; //函數表達式
var sum = new Function('a','b','return a + b;'); //Function構造器 避免使用
4.2.3.1 Function對象的屬性:
- prototype屬性詳見第五章
- length:用於記錄該函數所擁有的參數數量
caller:返回一個調用該函數對象的外層函數引用
function A(){return A.caller;} function B(){return A();} B(); //function B(){return A();}
4.2.3.2 Function對象的方法
Function對象繼承自Object對象,預設擁有Object對象的所有方法
call()、apply()方法都能讓對象去借用其他對象中的方法為己所用,這也是一種代碼重用的方式。
call()方法:
var someObj = { name: 'Ninja', say: function(who){ return 'Hello '+who+', my name is '+ this.name; } }; var myObj = { name:'Jesse' }; someObj.say.call(myObj, 'Dude');//"Hello Dude, my name is Jesse" //當say()被調用時其中的this就被自動設置成myObj對象的引用
如果我們調用call方法時需要傳遞更多的參數,可以在後面依次加入他們
someObj.say.call(myObj,'a','b','c')
如果我們沒有將對象傳遞給call()的首參數,或者傳遞的是null,則它的調用對象預設為全局對象
- apply()方法:
apply()的工作方式與call()基本相同,唯一的不同之處在於第二個參數的傳遞形式apply()方法的第二個參數是通過一個數組來傳遞的
someObj.say.apply(myObj,['a','b','c'])
4.2.3.3 重新認識arguments對象
- 在函數中通過arguments訪問傳遞給函數的所有參數
- arguments對象的callee屬性,該屬性返回的是當前被調用的函數對象
通過arguments.callee屬性實現匿名函數的遞歸調用
(function(count){ if(count < 5){ console.log(count); arguments.callee(++count); } })(1) //1,2,3,4
Function對象的成員
4.2.4 Boolean
var b = new Boolean();
typeof b; //'object'
typeof b.valueOf();// 'boolean'
4.2.5 Number
Number對象的toString()方法有一個可選的radix參數(預設10)
var n =new Number(255);
n.toString(16); // 'ff'
Number()構造器的成員
Number對象的成員
4.2.6 String
當我們將一個基本字元串當做對象來使用時,後臺會執行相應的String對象創建操作
String()構造器的成員
String對象的成員
4.2.7 Math
Math對象既不能當做一般函數來使用,也不能用new操作符創建對象,只是一個包含一系列方法和屬性的內建對象
獲取某個max和min之間的值,公式((max-min)*Math.random())+min
Math對象的成員
4.2.8 Date
Date()構造器成員
Date對象的成員
4.2.9 RegExp
var reg = new RegExp('j.*t');
var reg = /j.*t/; //匹配任何以j開頭t結尾的字元串,且這倆字元之間包含1個或多個字元
4.2.9.1 RegExp對象的屬性
- global:如果該值為false(預設),相關搜索在找到第一個匹配位置時就會停止,如果為true則會找出所有匹配位置,簡寫為‘g’
- ignoreCase:設置是否忽略大小寫,預設為false,簡寫為'i'
- multiline:設置是否跨行搜索,預設為false,簡寫為'm'
- lastIndex:搜索開始的索引位置,預設為0。在對象創建之後可以修改
- source:用於存儲正則表達式匹配模式
var reg = /j.*t/img;
4.2.9.2 RegExp對象的方法
- test():返回的是一個布爾值(找到匹配內容為true,否則為false)
- exec():返回的是一個由匹配字元串組成的數組
/j.*t/i.test('Javascript') //true
/j.*t/i.exec('Javascript')[0] //Javascript
4.2.9.3 以正則表達式為參數的字元串方法
- match():返回的是一個包含匹配內容的數組
- search():返回的是第一個匹配內容所在的位置
- replace():將匹配的文本替換成指定的字元串
split():根據指定的正則表達式將目標字元串分割成若幹數組元素
var s = new String('HelloJavascriptWorld'); s.match(/j.*a/ig); //['Java'] s.search(/j.*a/i); //5 //當某個匹配對象被找到時,如果我們想讓相關的替換字元串中包含匹配的文本,可以使用$&修飾符 s.replace(/[A-Z]/g,'_$&'); //"_Hello_Javascript_World" //如果正則表達式分了組(即帶括弧),那麼可以用$1代表匹配分組中的第一組,$2代表第二組 s.replace(/[A-Z]/g,'_$1'); //"_Hello_Javascript_World" var csv = 'one, two,three ,four'; csv.split(/\s*,\s*/) //["one", "two", "three", "four"] // \s*匹配0個或多個空格 //以上的4個方法可以接受的參數也包括字元串 "test".replace('t','r'); //"rest"
RegExp對象的成員
4.2.10 Error對象
try{
//可能會導致錯誤的代碼
}catch(e){
//在發生錯誤時處理代碼
}finally{ //可選的
//無論如何都會執行的代碼
}
4.4 練習題
c = [1,2,[1,2]];
c.sort(); //[1, [1,2], 2]
4、在String()構造器不存在時,自定義MyString()構造器函數並通過以下測試
function MyString(string){
//this.length =
this.toString = function(){
return string.toString();
}
this.valueOf = function(){
return string.valueOf();
}
this.reverse = function(){
return Array.prototype.reverse.apply(string.split('')).join('');
}
}
var s = new MyString('hello');
s.length; //5
s[0]; //'h'
s.toString(); //'hello'
s.valueOf(); //'hello'
s.chatAt(1); //'e'
s.concat(' world!'); //'hello world!'
s.slice(0,-1); //'hell'
s.split('l'); //['he','','o']
6、在Array()不存在時,創建MyArray()構造器並通過以下測試
var a = new MyArray(1,2,3,'test');
a.length;//4
a.toString();//'1,2,3,test'
a[a.length - 1];//'test'
a.push('boo');//5
a.pop();//'1,2,3,test'
a.join(',');//'1,2,3,test'
function MyArray(){
this.length =
}
第五章、原型
5.1 原型屬性
5.1.4 利用自身屬性重寫原型屬性
如果在一個對象自身屬性中沒有找到指定的屬性,就可以去原型鏈中查找相關屬性。但是如果遇上對象自身屬性與原型鏈屬性同名時,那麼對象自身屬性的優先順序高於原型鏈屬性。
function Gadget(name,color){
this.name = name;
this.color = color;
this.method =function(){
return 1;
}
}
Gadget.prototype.price = 10;
Gadget.prototype.rating = 3;
var newtoy = new Gadget('webcam','back');
for(var prop in newtoy){
console.log(prop + '=' + newtoy[prop]);
}
//name=webcam
//color=back
//method=function (){return 1;}
//price=10
//rating=3
hasOwnProperty()方法用於區分對象自身屬性(返回true)與原型屬性(返回false)
newtoy.hasOwnProperty('name'); //true
newtoy.hasOwnProperty('price'); //false
propertyIsEnumerable()方法對所有非內建對象屬性返回true,表示可通過for-in枚舉;
newtoy.propertyIsEnumerable('name'); //true
isPrototypeOf()方法會告訴我們當前對象是否是另一個對象的原型
var monkey = {
hair:true,
feeds:'bananas',
breathes:'air'
};
function Human(name){
this.name = name;
}
Human.prototype = monkey;
var jesse = new Human('Jesse');
monkey.isPrototypeOf(jesse); //true
5.2 擴展內建對象
為Array對象添加inArray()方法,用於查詢數組中是否存在某個特定的值
if(!Array.prototype.inArray){ //如果想通過原型為某個對象添加新屬性,請務必檢查該屬性是否已存在
Array.prototype.inArray = function (needle){
for(var i = 0;i < this.length; i++){
if(this[i] == needle){
return true;
}
}
return false;
}
}
var a = ['a','b','c'];
a.inArray('d'); //false
為String對象添加reverse()方法,用於反向輸出該字元串
if(!String.prototype.reverse){ //如果想通過原型為某個對象添加新屬性,請務必檢查該屬性是否已存在
String.prototype.reverse = function(){
return Array.prototype.reverse.apply(this.split('')).join('');
}
}
"Jesse".reverse(); //"esseJ"
//首先利用this.split('')將目標字元串轉為數組,並作為apply()的第二個參數,第一個參數不傳值時預設為全局對象
//再調用數組的reverse()方法生成反向數組
//最後通過join()方法將數組轉化為字元串
5.2.2 原型陷阱
- 當我們對原型對象完全替換時,可能會觸發異常
- prototype.constructor屬性不可靠
解決方法:當我們重寫某對象的prototype時,必須重置相應的constructor屬性
5.4 練習題
var shape = {
type:"triangle",
getType:function(){
return this.type;
}
}
function Triangle(a,b,c){
this.a = a;
this.b = b;
this.c = c;
}
Triangle.prototype = shape;
//當我們重寫某對象的prototype時,必須重置相應的constructor屬性
Triangle.prototype.constructor = Triangle;
Triangle.prototype.getPerimeter = function(){
return this.a + this.b + this.c;
}
var t = new Triangle(1,2,3);
for(var prop in t){
if(t.hasOwnProperty(prop)){
console.log(prop + '=' + t[prop]);
}
}
t.constructor; //Trianle(a,b,c)
shape.isPrototypeOf(t); //true
t.getPerimeter(); //6
t.getType(); //"triangle"
第六章、繼承
6.1.1 原型鏈示例
function Shape(){
this.name = 'shape';
this.toString = function(){return this.name;};
}
function TwoDShape(){
this.name = '2D shape';
}
function Triangle(side,height){
this.name = 'Triangle';
this.side = side;
this.height = height;
this.getArea = function(){
return this.side*this.height/2;
}
}
TwoDShape.prototype = new Shape();
Triangle.prototype = new TwoDShape();
//我們用構造器Shape()另建了一個新的實體,然後用它去覆蓋該對象的原型
//這確保了在繼承實現之後,我們對Shape()所進行的任何修改、重寫、刪除都不會對TwoShape()產生影響
TwoDShape.prototype.constructor = TwoDShape;
Triangle.prototype.constructor = Triangle;
var my = new Triangle(5,10);
my.getArea();//25
my.toString(); // Triangle
6.1.2 將共用屬性遷移到原型中
function Shape(){};
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){return this.name;};
function TwoDShape(){};
TwoDShape.prototype = new Shape();
TwoDShape.prototype.constructor = TwoDShape;
//我們需要在對原型對象進行擴展之前,先完成相關繼承關係的構建
TwoDShape.prototype.name = '2D shape';
function Triangle(side,height){
this.side = side;
this.height = height;
}
Triangle.prototype = new TwoDShape();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
return this.side*this.height/2;
}
6.2 只繼承於原型
function Shape(){};
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){return this.name;};
function TwoDShape(){};
//TwoDShape.prototype = new Shape(); new Shape()會將Shape的屬性設定為對象自身屬性,這樣的代碼是不可重用的
TwoDShape.prototype = Shape.prototype;
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = '2D shape';
function Triangle(side,height){
this.side = side;
this.height = height;
}
Triangle.prototype = TwoDShape.prototype;
//這樣固然可以提高效率,但是子對象和父對象都指向同一對象,一旦對原型屬性進行修改,繼承的對象相關屬性也隨之改變
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
return this.side*this.height/2;
}
var s = new Shape();
s.name;//"Triangle"
6.3 uber--子對象訪問父對象的方式
在構建繼承關係的過程中引入一個uber屬性,並令其指向其父級原型對象
6.8 深拷貝
當對象類型的屬性拷貝時,實際上拷貝的只是該對象在記憶體中的位置指針,這一過程就是淺拷貝
//淺拷貝
function extendCopy(p){
var c = {};
for(var i in p){
c[i] = p[i];
}
c.uber = p;