2014年3月1日 星期六

你可能看不懂我在寫什麼的 JavaScript 的物件導向基礎筆記...

前言

這篇筆記,其實要感謝 Jace Ju (小鐵) 的細心教導,

在此做一下當天學習的心得跟領悟,其實我不太寫物件導向教學文,因為網路資源已經夠多了,二來自己也不夠熟,不知道從何說起...,不過由於小鐵的教學讓我有更多的體會,突然有頓悟的感覺xd,且有些地方也讓我心有戚戚焉...,在此用語言記錄下來。


第一段,建立一個 Object
(1)物件 (Object) 表達式:

其實在 JavaScript 的世界中,『Everything is an Object』,此外,JavaScript 也允許你定義自己的 Object。在了解一些物件導向之前,先了解 JavaScript 的物件 (Object) 是如何表示的:


1
2
3
4
var Object = {
      property1 : true,
      method1 : function (msg) { alert(msg) }
};

  • Object 的定義格式就是 property : value 或是 method : function( )....,在物件的定義,只有屬性(property) 或是方法 (method) 這兩種定義。
  • 你會發現方法的定義跟定義函式 (function) 一樣,沒錯,只是定義在物件的函式,稱為方法(method)
以下為一個人類的物件:

1
2
3
4
5
6
7
8
9
 var frontendGirl = {
    name : 'Win Wu',
    gender : 'girl',
    age : 18,
    //forever 18 XD
    sayHello: function(){
      alert('Hello');
    }
  };
  • frontendGirl 這個物件有 name, gender, age 這些屬性以及一個 sayHello 方法。
(2)存取物件的屬性與使用物件的方法
  • 存取物件的屬性通常是用一個『.』來表示。
    • 取得屬性的值:  objectName.propertyName
    • 更改屬性的值:  objectName.propertyName = 'your new value';
  • example:
  • 1
    2
    3
    4
    5
    6
     //取得 frontendGril 的 name 屬性的值
      console.log(frontendGirl.name);
    
      //改變 frontendGirl 的 name 屬性
      frontendGirl.name = '小winwin';
      console.log(frontendGirl.name);
  • 如果是要使用物件的方法,物件名稱加上方法名稱: objectName.methodName( )
    • console.log(frontendGirl.sayHello());

Q1: 成之前定義的 frontendGirl,以下的表達式,你會覺得 frontendGirl 跟 normalPerson 是同一個物件嗎?

1
var normalGirl = frontendGirl;

答案是一樣的,我們已經指定 normalGirl 的值為 frontendGirl 這個物件,並且試著改變 normalGirl 的 name 屬性為 AAA,但是當我們 log 出 frontendGirl 的 name 時,也被改變為 AAA 這個值,當然這樣的驗證還不足夠,我們使用嚴格相等運算子 (===),比較兩者的內容,以及是否為相同的資料型態,當我們得到 true 時,表示 normalGirl 跟 frontendGirl 其實是指到同一個物件。


1
2
3
4
5
6
var normalGirl = frontendGirl;
normalGirl.name = 'AAA';
console.log(frontendGirl.name);

console.log(frontendGirl === normalGirl);
// 你會得到 true



第二段,what is "this"
第二段,首先我寫了一個 who 的 function,目的是用來回傳 frontendGirl 的屬性:

1
2
3
4
5
function who(){
  return 'i am ' + frontendGirl.name
         +' and age is '+ frontendGirl.age;
}
console.log(who());

但是這樣寫不太好,為什麼? 如果你沒有建立 frontendGirl 這個物件,那麼要這個 funtion 也就沒意義了,讓我們把它改的更合理些。

1
2
3
4
5
function who(person){
  return 'i am ' + person.name
         +' and age is ' + person.age;
}
console.log(who(frontendGirl));

這是第二個 who function 的版本,必須帶一個參數給 who 函式,然後這個參數會被轉換為 person 變數(也就是說你的物件不用永遠都叫 frontendGirl,也可以傳給 who 函式,而不管是哪個物件傳給 who 當作參數,都會被轉換成 person 變數),接著在回傳 person 的屬性。

但是這個版本的 who 函式一樣有些不合理,怎麼說呢? 如果你今天隨便傳一個沒有 name 屬性跟 age 屬性的物件給 who 函式,這就不合理了,也就是說 who 函式的使用時機是有限制的。
接下來是三個版本的 who 函式,既然 who 函示的使用時機其實是比較適合當某個物件裡面剛好都有 name 跟 age 屬性的時候,那麼為何不把它寫在該 Object 裡面作為 method 呢?因此我們要把 who 函式移到 frontendGirl 裡面作為一個 method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var frontendGirl = {
  name : 'Win Wu',
  gender : 'girl',
  age : 18, 
  sayHello: function(){
    alert('Hello');
  },
  who: function(){
    return 'i am ' + this.name + ' and age is ' + this.age;
  }
};

當我們把 who 函式定義在 Object 裡面,就成了方法,因此只有該物件可以呼叫 who 這個方法。 who 函式裡面的 this,指的就是物件本身,this.name 等於本身的 name 屬性,因此就會得到 name 屬性的『Win Wu』 的值,現在我們要做另外一件事情,就是在 who 方法裡面在寫另一個 function :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var frontendGirl = {
  name : 'Win Wu',
  gender : 'girl',
  age : 18, 
  sayHello: function(){
    alert('Hello');
  },
  who: function(){
    function getName(){
      //思考一下這個 this  是誰?
      //為什麼不會得到 Win Wu
      return this.name;
    }
    return 'i am ' + getName() + ' and age is ' + this.age;
  }
};


console.log( frontendGirl.who() );
  • 你會發現 console.log 的結果不如我們所預期,你會得到 "i am 1393075739532 and age is 18" 這樣奇怪的值
  • 為什麼在 who 方法裡面的 getName() 函式得到的 this.name 不是 Win Wu? 這是因為 getName 函式的 this 並不是物件本身,而是 global 的 window,那要怎麼樣才能讓這個 this 成為我們真正想要的物件本身的 this 呢?
    1. 帶參數給 getName( )
    2. 在 who 方法指定 this 給一個變數
帶你看一下 1. 的方法:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
     var frontendGirl = {
          name : 'Win Wu',
          gender : 'girl',
          age : 18, 
          sayHello: function(){
              alert('Hello');
          },
          who: function(){
              function getName(person){
                  return person.name;
              }
              return 'i am ' + getName(this) 
                    + ' and age is ' + this.age;
          }
      };
    
      console.log( frontendGirl.who() );
    

接著這是 2. 的方法:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
      var frontendGirl = {    
        name : 'Win Wu',
        gender : 'girl',
        age : 18, 
        sayHello: function(){
          alert('Hello');
        },
        who: function(){
          var that = this;
          function getName(){
            return that.name;
          }
        return 'i am ' + getName()
              + ' and age is ' + this.age;
        }
      };
      console.log( frontendGirl.who() );
    

  • 另外,其實我們也能將 getName() 變成方法(method),這樣就不用定義成 who 方法裡面的 function 了:
    1. 如此一來 getName 方法可以直接使用 this.name,因為它可以直接使用物件本身的屬性。
    2. 而我們也可以直接呼叫物件的 getName 方法 (console.log( frontendGirl.getName() );)。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var frontendGirl = {
  name : 'Win Wu',
  gender : 'girl',
  age : 18, 
  getName: function(){
    return this.name;
  },
  who: function(){
    return 'i am ' + this.getName()
         + ' and age is ' + this.age;
  }
};
console.log( frontendGirl.who() );

//直接呼叫 getName 方法
console.log( frontendGirl.getName() );




第三段,建構函式,建構一個物件的函式...

這一段要講的建構函式,可能跟上面的內容沒有太大的關聯,...,前面提到的,如果我們定義一個 Object 為 frontendGirl,並且指定 normalGirl 為 frontendGirl,則會視為指到同一個物件 (var normalGirl = frontendGirl;)。那要怎樣才能 normalGirl 跟 frontendGirl 是屬於兩個獨立的物件,但卻可以擁有相同的屬性跟方法 ? 這時候就可以想到建構函式(在 JavaScript 因為沒有 Class,所以不是用 class 來建立物件,而是用建構函式)。

建構函式的作用就是模擬 Class(類別) 的功能,用來重複建立物件。

以下為定義一個 Girl 的建構函式: (習慣上字首大寫)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function Girl(name, gender, age){ 
  //這裡的 this 還不確定是什麼
  this.name = name;
  this.gender = gender;
  this.age = age;
  this.getName = function(){
    return this.name;
  };
  this.who =  function(){
    return 'i am ' + this.getName() + ' and age is ' + this.age;
  };
}

使用建構函式來產生一個新的物件,要使用 new 關鍵字來建立新的物件,並且帶入需要的參數作為建構函式內部的 this ,以下為建立 frontendGirl 以及 normalGirl 兩個獨立的物件 :


1
2
var frontendGirl = new Girl('Win Wu','girl',18);
var normalGirl = new Girl('test', 'boy', 22);


測試兩者的 who 方法所回傳的結果:


1
2
console.log(frontendGirl.who());
console.log(normalGirl.who());


得到的結果:

"i am Win Wu and age is 18"
"i am test and age is 22"


Prototype
  • 神奇的 Prototype,JavaScript 是 prototype base 的語言。
  • 以下範例為動態的新增 getNameAndAge 這個方法給 Girl 這個建構函式,雖然上面定義的 Girl 沒看到 getName 方法, 可是使用 Prototype 會幫你變成內建的方法。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Girl.prototype.getNameAndAge = function (label1, label2){
  var s = '';
  s = s + label1 + ': ';
  s += this.name;
  s+='\n'; //換行
  s += label2 + ': ';
  s += this.age;
  return s;
};

console.log(frontendGirl.getNameAndAge('name', 'age'));
console.log(normalGirl.getNameAndAge('姓名', '年齡'));


console.log 結果:


1
2
3
4
"name: Win Wu
age: 18"
"姓名: test
年齡: 22"


如果你 prototype 定義了一個原本就定義在建構函式裡頭的方法,那會以函式內建的方法為主


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function Girl(name){ 
  this.name = name;
  this.getName = function(){
    return this.name;
  };

}
var frontendGirl = new Girl('Win Wu');
Girl.prototype.getName = function(){
  return 'Name is ' + this.name;
};
//這裡的 getName() 會先去找 Gril 原本的函式有沒有定義 getName 方法,
//沒有的話才會去找 prototype 有沒有 getName 這個方法
console.log(frontendGirl.getName());

console.log 結果: "Win Wu" , 而非 "Name is Win Wu"。



把原本定義在建構函式的 getName 拿掉看看結果:


function Girl(name){ 
  this.name = name; 
}
var frontendGirl = new Girl('Win Wu');
Girl.prototype.getName = function(){
  return 'Name is ' + this.name;
};

console.log(frontendGirl.getName());


console.log結果 : "Name is Win Wu"



Apply 可以傳入指定的物件,成為 function 裡的 "this"

function sayHello(){
  console.log (this.name + ' say hello to you');
}

var frontendGirl = new Girl('Win Wu', 'girl', 18);

sayHello.apply(frontendGirl);


執行結果:

"Win Wu say hello to you"

沒有留言:

張貼留言

若你看的文章,時間太久遠的問題就別問了,因為我應該也忘了... XD

Vue multiselect set autofocus and tinymce set autofocus

要在畫面一進來 focus multiselect 的方式: 參考: https://jsfiddle.net/shentao/mnphdt2g/ 主要就是在 multiselect 的 tag 加上 ref (例如: my_multiselect), 另外在 mounted...