[轉]JavaScript 學習 - 提高篇-

一. JavaScript中的對象.

JavaScript中的Object是一組數據的key-value的集合, 有點類似於Java中的HashMap, 所有這些數據都是Object裏的property. 通常情況下, JavaScript中建立一個對象用"new"加上constructor function來實現. 如new Date(), new Object()等.

var book = new Object();
book.name = "JavaScript is Cool";
book.author = "tom";
book.pages = 514;

上面例子中的name和page就是名爲book的對象中的property. 我們可以用delete來刪除Object中的property: "delete book.name;". 除了Object, Date等buildin的對象外, 我們可以寫自己的constructor function, 然後使用new就可以建立自己的對象. 如上面的book可以寫成:

function Book (name, author, page) {
this.name = name;
this.author = author;
this.page = page;
}
var abook = new Book("JavaScript is Cool", "tom", 514);

二. function的用法

在JavaScript中, function是一種數據類型, 所有的function都是從buildin的Function object 衍生的對象. 所以在JavaScript 中function可以作爲參數傳遞, 可以作爲Object的property, 也可以當作函數返回值. function在JavaScript中有兩種用法, 一種是當作constructor, 前面加上new keyword用來建立對象. 一種是當作method, 爲其他對象調用.

注意function和method在中文裏的意思相當, 在有些語言裏也可以通用. 但是在JavaScript中, 它們還是有所區別的. function本身是是一個對象, 而當作爲一個方法他屬於一個對象時, 就成爲了一個這個對象的method, 相當於一個對象種的屬性. 也就是說method是相對於一個對象而言的, function在某些情況下成爲了一個對象的method.

function Book(name, author, page) {
this.name = name;
this.author = author;
this.page = page;
this.getReader = Book_getReader;
}

function Book_getReader() {
//....
}

上面的例子種, function Book_getReader()就成爲了Book的一個名爲getReader的method. call()和apply()是Function object 的兩個方法, 它們也可以使一個function作爲另一個對象的method來調用用. call()和apply()都需要參數, 而第一個參數就是調用對象, 也就是當function內部出現this時, this所指的對象. call()和apply()的區別在於call()可以傳遞任意長度參數, 只要第一個參數時調用對象. 而apply只接受兩個參數, 需要將除調用對象外的所有參數放入一個數組中. 即:

function getBooksWithSameAuthor(form, to) {
var name = this.author;
var books = ...
//get books written by name and from year "from" to year "to"
return books;
}

var abook = new Book("JavaScript is Cool", "tom", 514);
var books = getBooksWithSameAuthor.call(abook, 1990, 2005);

var books = getBooksWithSameAuthor.apply(abook, [1990, 2005]);

當一個function不作爲一個對象的method時, JavaScript會認爲它是屬於一個Globle Object對象的method, 這個Globle Object在Browser中就是window類. 所以從這個角度來說, function和method又可以統一起來了.

Function object 還有一個非常重要的property: prototype. 它是一個predefined的prototype object. 當一個Function用作對象的constructor時, protptype property將發揮作用,中文翻譯叫原型. JavaScript的新對象就是通過function的原型來建立的. 同時我們還可以利用prototype來動態的向對象中添加屬性, 如:

function Book (name, author, page) {
this.name = name;
this.author = author;
this.page = page;
}
var abook = new Book("JavaScript is Cool", "tom", 514);

Book.prototype.getInfo = getInfo;
function getInfo() {
return this.name + " written by " + this.author + " with " + this.page + " pages";
}

alert(abook.getInfo());

這裏有一個例子, 用prototype方法來實現callback:

Function.prototype.andThen=function(g) {
var f=this;
return function() {
f();g();
}
};

function Manager() {
this.callback=function () {}; // do nothing
this.registerCallback=function(callbackFunction) {
this.callback=(this.callback).andThen(callbackFunction);
}
}

var manager=new Manager();
manager.registerCallback(sayHi);
manager.registerCallback(sayBye);
manager.callback();

三. JavaScript中的OO
JavaScript中的對象是一個屬性和方法的集合. JavaScript也有對應於Java的Class和Instance的概念.我們可以把JavaScript中定義的

function(Function類的子類)看作是Class, 而利用這個function構造的對象看是Instance. 對應於Java的Instance Property, Instance

Method, Class(static) property, Class Method, JavaScript都可以實現.
1. Instance Property
Instance Property就是cuntructor function中定義的property, 對於每一個instance都會有一份copy.
2. Class property
Class Property其實是cunstructor function作爲對象本身所具有的屬性(和Java不一樣, Java中method不是數據, 更不是對象).
3. Instance Method
Instance Method是指屬於某一對象的method, 方法內的this代表這個method從屬的對象, 注意雖然是Instance Method, 並不是每一個Instance都有一個此方法的copy, 所有Instance 共享一個method.
4. Class Method
Class Method同樣可以理解爲constructor function作爲對象自己具有的方法, 和由這個constructor建立的對象沒有直接聯繫. 所以Class Method方法裏使用this會產生混淆. 因爲這個this指的不是期望操作的對象, 而是一個Function Object(constructor).

下面的例子定義了一個複數的對象, 包含有上述四種域, 值得仔細看看:
/*

* Complex.js:

* This file defines a Complex class to represent complex numbers.

* Recall that a complex number is the sum of a real number and an

* imaginary number and that the imaginary number i is the

* square root of -1.

*/

/*

* The first step in defining a class is defining the constructor

* function of the class. This constructor should initialize any

* instance properties of the object. These are the essential

* "state variables" that make each instance of the class different.

*/

function Complex(real, imaginary) {

this.x = real; // The real part of the number

this.y = imaginary; // The imaginary part of the number

}

/*

* The second step in defining a class is defining its instance

* methods (and possibly other properties) in the prototype object

* of the constructor. Any properties defined in this object will

* be inherited by all instances of the class. Note that instance

* methods operate implicitly on the this keyword. For many methods,

* no other arguments are needed.

*/

// Return the magnitude of a complex number. This is defined

// as its distance from the origin (0,0) of the complex plane.

Complex.prototype.magnitude = function( ) {

return Math.sqrt(this.x*this.x + this.y*this.y);

};

// Return a complex number that is the negative of this one.

Complex.prototype.negative = function( ) {

return new Complex(-this.x, -this.y);

};

// Convert a Complex object to a string in a useful way.

// This is invoked when a Complex object is used as a string.

Complex.prototype.toString = function( ) {

return "{" + this.x + "," + this.y + "}";

};

// Return the real portion of a complex number. This function

// is invoked when a Complex object is treated as a primitive value.

Complex.prototype.valueOf = function( ) { return this.x; }

/*

* The third step in defining a class is to define class methods,

* constants, and any needed class properties as properties of the

* constructor function itself (instead of as properties of the

* prototype object of the constructor). Note that class methods

* do not use the this keyword: they operate only on their arguments.

*/

// Add two complex numbers and return the result.

Complex.add = function (a, b) {

return new Complex(a.x + b.x, a.y + b.y);

};

// Subtract one complex number from another.

Complex.subtract = function (a, b) {

return new Complex(a.x - b.x, a.y - b.y);

};

// Multiply two complex numbers and return the product.

Complex.multiply = function(a, b) {

return new Complex(a.x * b.x - a.y * b.y,

a.x * b.y + a.y * b.x);

};

// Here are some useful predefined complex numbers.

// They are defined as class properties, where they can be used as

// "constants." (Note, though, that they are not actually read-only.)

Complex.zero = new Complex(0,0);

Complex.one = new Complex(1,0);

Complex.i = new Complex(0,1);

四. 對象的繼承

JavaScript有多種方式模擬繼承.
1. 利用function:

function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.inheritFrom = superClass;
this.inheritFrom();
this.bye = subBye;
}

或者:

function subClass() {
superClass.call(this);
}

先定義subClass的inheritFrom方法, 再調用這個方法(方法名稱並不重要), 或者直接使用Function Object 的call 方法將this做參數, 都可以模擬實現從superClass的繼承. 注意調用superClass時的this指向. 這個方法就是在執行subClass的cunstructor function時, 先執行supperClass的cunstructor function.這個方法的缺點在於子類僅僅是在自己的構造函數中, 將this作爲參數調用了父類的構造函數, 將構造函數賦予父類的所有域賦予子類. 所以, 任何父類在構造函數之外(通過prototype)定義的域, 子類都無法繼承. 而且子類的構造函數一定要在定義自己的域之前調用父類的構造函數, 免得子類的定義被父類覆蓋. 使用這種方法子類也儘量不要使用prototype來定義子類的域, 因爲prototype的定義在子類new的之後就執行, 所以它一定會在調用父類構造函數前, 同樣會有被父類的定義覆蓋的危險.


2. 利用prototype:

function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.bye = subBye;
}
subClass.prototype = new superClass();
subClass.prototype.constructor = superClass;

這裏將一個superClass的實例設置成subclass的原型:protytype, 由於new superClass實例一定會調用父類prototype定義的所有域, 所以這種方法避免了上一種方法的一個問題, 父類可以通過prototype來描述域. 可以實現從superClass的繼承. 而這個方法也有缺點, 由於子類的peototype已經是父類的實例(Object實例), 不能再被實例化, 所以new子類實例的時候, 父類的所有非基本數據類型(見JavaScript數據類型)都將是reference copy而非數據copy. 簡單說就是所有的父類域在子類中雖然存在, 但看起來就像Java中的static域一樣在子類間share.被一個子類改變, 所有子類都會改變.

注意這裏的最後一句, 改變了子類prototype中的constructor屬性. 它對子類使用沒有影響, 僅僅是爲了在調用instanceOf方法時它使得子類實例返回subClass.

3. Parasitic Inheritance (寄生繼承)
function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.base = new supperClass();
base.sayBye = subBye;
return base;
}

這種繼承其實是一種擴展, 因爲在調用instanceOf時, 子類會返回父類名稱, 它的好處在於在構造函數繼承的基礎上解放了父類, 父類可以使用prototype定義自己的域, 但是子類仍然不建議使用prototype, 以免被父類覆蓋. 爲了可以使子類的instanceof返回正確類型, 我們可以再改進一下:

function subClass() {
this.base = new supperClass();
for ( var key in this.base ) {
if ( !this[key] ) {
this[key] = this.base[key];
}
}

this.sayBye = subBye;
}

將所有的父類域拷貝給子類一份, 不再返回父類, instanceof子類實例時就可以返回正確類型.

五. this的用法

通常情況下, this代表的是前面提到的Globle Object.也就是Browser環境時的window Object. 當function作爲某一對象的 method 時, this 代表這個 function 所屬的 object. 下面這段代碼有格錯誤, 涉及到this的使用:

function Employee(a) {
this.name = a;
}

function init(){
John = Employee("Johnson");
alert(John.name);
}

在init()中我們少了一個new keyword. 於是這個代碼就會報錯, 因爲Browser把Employee當作是window obect的一個method, 裏面的this指的就是window object. init()應該改爲:

function init(){
John = new Employee("Johnson");
alert(John.name);
}

同時我們也可以將Employee的constructor函數修改, 防止類似的錯誤:

function Employee(a) {
if (!(this instanceof Employee)) return new Employee(a);
this.name = a;
}

這樣,我們即使使用原來的init()方法, 也不會報錯了.

六. Array in JavaScript
Array和Object本質上是一樣的, 只是Array需要由index來索引它其中的屬性. index爲>=0的整數.
Array有一系列buildin的方法:
1. jion() 將array中的所有element以string的形式連在一起:
var a = [1,2,3];
s = a.join(); // s == "1,2,3"
s = a.join(": "); // s == "1: 2: 3"

2. reverse() 將Array的element順數顛倒
var a = [1,2,3];
a.reverse();
s = a.join(); // s == "3,2,1"

3. sort() 排序, 默認按字母順序排序case sensitive, 可以自定義排序方式.

var a = [111,4,33,2];
a.sort(); // a == [111,2,33,4]

a.sort(function(a,b) { // a == [2,4,33,111]
return a-b; // Returns < 0, 0, or > 0
});

4. concat()連接多個Array
var a = [1,2,3];
a.concat(4,5); // return [1,2,3,4,5]
a.concat([4,5]); // return [1,2,3,4,5]
a.concat([4,5], [6,7]) // return [1,2,3,4,5,6,7]
a.concat(4,[5,[6,7]]); // return [1,2,3,4,5,6,7]

5. slice() 返回Array的切片, 原Array不變.
var a = [1,2,3,4,5];
a.slice(0,3); // Returns [1,2,3]
a.slice(3); // Returns [4,5]
a.slice(1,-1); // Returns [2,3,4], -1 means the last index of the array
a.slice(-3,-2); // Returns [3], from the third last index to the second last index

6. splice 向一個Array中添加或刪除element. 第一個參數表示位置, 第二個參數表示刪除長度, 後面任意長的參數表示在1刪除位置添加的elements.
var a = [1,2,3,4,5,6,7,8];
a.splice(4); // Returns [5,6,7,8]; a is [1,2,3,4]
a.splice(1,2); // Returns [2,3]; a is [1,4]
a.splice(1,1); // Returns [4]; a is [1]

var a = [1,2,3,4,5];
a.splice(2,0,'a','b'); // Returns []; a is [1,2,'a','b',3,4,5]
a.splice(2,2,[1,2],3); // Returns ['a','b']; a is [1,2,[1,2],3,3,4,5]

7. push() and pop() 向Array末尾添加或刪除element
8. unshift() and shift() 向Array的開頭添加或刪除element
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章