JavaScript 學習 - 提高篇_javascript技巧

來源:互聯網
上載者:User

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子類執行個體的時候, 父類的所有非基礎資料型別 (Elementary Data Type)(見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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.