我是菜鳥,老鳥勿看,繼承多態等太多概念難以理解,還是從實踐中慢慢學吧!爭取能大致看懂網上的開源的代碼。 對象的組成:方法和屬性 屬性關鍵詞:靜止的,狀態 方法關鍵詞:動態的,過程,處理,帶括弧 js中的面向對象不是其他語言那樣的面向對象。 結果是數組有個number屬性和test方法,但是數組內容為 ...
純屬筆記,加強記憶,不是教程,歡迎糾錯,沒有邏輯,不太適合學習使用。
--------------
繼承多態等太多概念難以理解,還是從實踐中慢慢學吧!爭取能大致看懂網上的開源的代碼。
--------------
對象的組成:方法和屬性
屬性關鍵詞:靜止的,狀態
方法關鍵詞:動態的,過程,處理,帶括弧
--------------
js中的面向對象不是其他語言那樣的面向對象。
1 <script type="text/javascript"> 2 //定義arr為一個數組(數組也是一個對象實例,所以arr是個對象實例) 3 var my_arr=[]; 4 //以前變數這樣用,var number=10; 5 //現在定義屬性,arr.number = 10; 就是用“點”即可! 6 my_arr.number = 10; 7 //定義對象的方法 8 my_arr.test = function(){ 9 console.log(this); 10 //這裡this就是這個數組,直接用數組名也可以 11 //alert(my_arr.number); 12 //alert(this.number); 13 } 14 //這樣使用方法 15 my_arr.test(); 16 </script> 17 18
這裡需要註意的是,能不能將第6行放在第3行前面?
不可以,沒有到第3行賦值,my_arr還是undefined,而undefined是不能有特性的。
1 <script type="text/javascript"> 2 var a ='ddd'; 3 a.kkk = 'eee'; 4 console.log(a.kkk); //火狐,列印出undefined ,可見,a不是一個對象類型的數據時,還不可以直接加“點”呢 5 a.ka = function (){ 6 console.log('aaaaaaaaaaaaaaaaaa'); 7 } 8 a.ka(); //報錯,a.ka is not a function 可見,a不是一個對象類型的數據時,還不可以直接加“點”賦予方法呢 9 </script>
1 <script type="text/javascript"> 2 var a ='ddd'; 3 String.prototype.kkk = 'eee'; 4 a.kkk = 'eeeddd'; 5 console.log(a.kkk); //火狐,列印出eee ,可見,a不是一個對象類型的數據時,還不可以直接加“點”呢,但是從字元串的原型上可以加 6 String.prototype.ka = function (){ 7 console.log('aaaaaaaaaaaaaaaaaa'); 8 } 9 a.ka(); //aaaaaaaaaaaaaaaaaa 10 </script>
結果是數組有個number屬性和test方法,但是數組內容為空,length為0,但是這樣alert(my_arr['number']);也可以彈出10
//TODO 以後再研究數組,但是可見,數組的內容是內容,屬性和內容不是一回事,內容是數組裡面存了啥,屬性是這個數組實例有什麼屬性。如同,一個是汽車裡裝了誰誰,一個是汽車有車大燈的感覺。
------------
新建一個純凈的對象實例
1 <script type="text/javascript"> 2 var my_obj = new Object(); //新建一個對象實例my_obj,換成 var my_obj = {}; 也行,一個意思 3 my_obj.name = '張三'; //加個屬性 4 my_obj.test = function(){ //加個方法 5 this.test2(); //對象的A方法中用到對象的B方法 6 } 7 my_obj.test2 = function(){ 8 console.log(this.name); 9 } 10 my_obj.test(); //使用 11 </script>
對象中思考變數作用域的問題
以前學到變數作用域,都是函數和變數,現在加入對象,看看是怎麼回事
1 <script type="text/javascript"> 2 console.log(my_obj); // 顯示undefined,說明解析器中和其他類型一樣,根據var,先放個undefined再說 3 var my_obj = {}; 4 console.log(my_obj); // 顯示 Object {} 5 my_obj.name = '張三'; 6 console.log(my_obj); // 顯示Object {name:"張三"} 7 </script>
其實可以這樣理解,js解析器,預解析,根據var找到了my_obj,賦值undefined,如果放到不用對象的時候,出個name,需要var name一下,這裡由於對象var了,對象下各個目前未定義的屬性就相當於已經var但沒賦值了,一旦測試都是undefined。第4行,由於還沒有賦予name屬性,如果查看my_obj.name的話,就是undefined,而第5行賦值,第6行再看,就是“張三”了。
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = '張三'; 4 console.log(my_obj.test); //undefined 5 my_obj.test(); //瀏覽器提示出錯,說test不是一個函數。可見my_obj.test = function(){...} 只是一個賦值,並沒有因為是function,就扔到預解析倉庫中 6 my_obj.test = function(){ 7 console.log(this.name); 8 } 9 </script>
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = '張三'; 4 my_obj.test = xxx; 5 my_obj.test(); //這樣寫是可以執行的,因為函數xxx扔到預解析倉庫了,上一行又賦予test這個函數了,顯示張三 6 function xxx(){ 7 console.log(this.name); 8 } 9 </script>
<script type="text/javascript"> var my_obj = {}; my_obj.name = '張三'; my_obj.test = function xxx(){ console.log(this.name); }; my_obj.test(); // 正常運行 </script>
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = '張三'; 4 my_obj.test = function xxx(){ 5 console.log(this.name); 6 }; 7 xxx(); //瀏覽器報錯,火狐認為,xxx未定義,也就是說,在等號後面的函數不會被預解析,這裡不求預解析,連解析都沒有 8 </script>
1 <script type="text/javascript"> 2 var my_obj = { 3 name : "張三", 4 test : function(){ 5 //對象也是個變數作用域,這裡面是可以使用對象外面的函數的 6 //console.log(name); 不可以這樣用,不能因為name和test平級,就不加this,屬性名和變數是不同的,要加上對象名才能用 7 console.log(this.name); //console.log(my_obj.name); 這樣也行 8 } 9 }; 10 my_obj.test(); //顯示張三 11 console.log(my_obj.name); //想使用對象的屬性,就要加對象名 12 13 </script>
-----------------------
這裡新建對象不夠靈活,如果要建的對象名不是張三呢,而是好幾個,張三,李四,王二,麻子等很多人,那麼就要用到工廠方式
還按照上面的方式就是如下代碼
1 <script type="text/javascript"> 2 var my_obj1 = { 3 name: "張三", 4 test: function() { 5 console.log(this.name); //如果是console.log(my_obj2.name); 將顯示李四,也就是用到了其他對象的屬性。 6 } 7 }; 8 9 var my_obj2 = { 10 name : "李四", 11 test : function(){ 12 console.log(this.name); 13 } 14 } 15 my_obj1.test(); //顯示張三 16 my_obj2.test(); //顯示李四 17 </script>
js並不是其他語言那樣的面向對象,這裡的對象,其實是其他語言中 對象實例的意思。
上面代碼怎麼精簡呢,共同部分用一個函數來生成即可,也就是所謂的工廠模式,如下
1 <script type="text/javascript"> 2 function CreatePerson(name){ //這個function就相當於一個生產對象的工廠 3 var obj = new Object(); 4 obj.name = name; 5 obj.test = function() { 6 console.log(this.name); 7 } 8 return obj; 9 } 10 11 var my_obj1 = CreatePerson('張三'); //開始生產了 12 var my_obj2 = CreatePerson('李四'); 13 my_obj1.test();//使用工廠生產出來的對象 14 my_obj2.test(); 15 </script>
在js中,用new去調用一個函數的話,這個函數中的this就是創建出來的對象,而且函數的返回值直接就是this 隱式返回
難以理解這句吧,就是js規定的,函數加new, 函數中的this就是對象實例本身,並且函數返回該對象實例(不用你操作了,js幫你搞定)。
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 console.log(this); //列印出的this就是張三 這裡就相當於js幫你做了 var my_obj1 = new Object(); 最後return my_obj1; 5 } 6 var my_obj1 = new CreatePerson('張三'); 7 console.log(my_obj1); //列印出的my_obj1就是張三 8 </script>
這樣調用
var my_obj1 = new CreatePerson('張三'); 是不是很像 var date = new Date();
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 this.test = function(){ 5 console.log(this.name); 6 } 7 } 8 9 var my_obj1 = new CreatePerson('張三'); 10 my_obj1.test(); 11 </script>
但是這樣還是有問題,每個對象實例的方法,都是在記憶體總新建的,用prototype來對相同的對象實例方法進行定義,使之共用記憶體。改造如下
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 } 5 6 CreatePerson.prototype.test = function(){ 7 console.log(this.name); 8 } 9 10 var my_obj1 = new CreatePerson('張三'); 11 var my_obj2 = new CreatePerson('李四'); 12 13 alert(my_obj1.test == my_obj2.test); //true 14 my_obj1.test(); // 張三 15 my_obj2.test(); // 李四 16 17 </script>
這裡面的對象這樣去記憶理解容易點。 my_obj1和my_obj2是具體的某個人,是對象的實例。而CreatePerson是人這一類,是構造函數。在CSS中,有行內樣式 <div style="...."></div> 或者寫成 <div class="..."></div>這裡。用構造函數加上prototype的方式,類似於class,能廣泛用於很多個個體對象實例,而直接加在個體上的方法就只能用於個體,同時,二者有重名衝突的時候,個體上的方法優先順序較高。
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 } 5 6 CreatePerson.prototype.test = function(){ 7 console.log(this.name); 8 } 9 10 var my_obj1 = new CreatePerson('張三'); 11 var my_obj2 = new CreatePerson('李四'); 12 13 //為my_obj1這個個體又單獨加個test。prototype定義的test對該個體來說失效 14 my_obj1.test = function(){ 15 console.log(this.name+'111'); 16 } 17 18 alert(my_obj1.test == my_obj2.test); //false 19 my_obj1.test(); // 張三111 20 my_obj2.test(); // 李四 21 22 </script>
再看看最開始怎麼定義my_obj1,直接 var my_obj1 = new Object(); 或者寫 var obj = {};這也是js的面向對象,到現在用構造函數的方式,一個是直接定義到個體,一個是先定義一個種類,然後再出個體。
使用原型再舉個例子,求數組中元素的和,如下
1 <script type="text/javascript"> 2 var arr_1 = [1,9,3,5]; 3 Array.prototype.sum = function(){ 4 var result = 0; 5 for (var i=0;i<this.length;i++) { 6 result += this[i]; 7 } 8 return result; 9 } 10 11 console.log(arr_1.sum());//18 12 </script>
原型要加在構造函數上
1 <script type="text/javascript"> 2 var arr_1 = [1,9,3,5]; 3 var arr_2 = [3,5,7]; 4 Array.prototype.sum = function(){ 5 var result = 0; 6 for (var i=0;i<this.length;i++) { 7 result += this[i]; 8 } 9 return result; 10 } 11 arr_1.sum = function(){ 12 return '我要覆蓋prototype的方法!'; 13 } 14 15 console.log(arr_1.sum());//我要覆蓋prototype的方法! 16 console.log(arr_2.sum());//15 17 </script>
----------------
js是基於原型的程式
每個基本類型數據都有一個原型,字元串 String 數字 Number 布爾值 Boolean 數組 Array
儘量不要去修改系統對象下麵的方法和屬性。
例子:重寫數組的push方法
1 <script type="text/javascript"> 2 var arr = [1,2,3]; 3 Array.prototype.push = function(){//這裡系統自帶的push就被覆蓋了 4 for(var i=0; i<arguments.length;i++){ 5 this[this.length] = arguments[i]; 6 } 7 return this.length; 8 } 9 10 arr.push(9,8,7,6,5); 11 console.log(arr); 12 </script>
---------------
包裝對象:基本類型(除了null 和 undefined)都有自己對應的包裝對象,如String Number Boolean,這裡也就知道了,null空對象和undefined未定義是不能再添加屬性和方法的
<script type="text/javascript"> var str1 ='hello123'; var str2 = new String('hello123');//通過new創建的都是對象 console.log(typeof str1);//string console.log(typeof str2);//object </script>
1 <script type="text/javascript"> 2 var str1 = 'hello123'; 3 var str2 = new String('hello123'); //通過new創建的都是對象 4 5 console.log(typeof str1); //string 6 console.log(typeof str2); //object 7 str2.lastvalue = function() { 8 return this.charAt(this.length - 1); 9 } 10 console.log(str2.lastvalue());//最後一個字元3 11 12 str1.lastvalue = function() { //報錯,因為str1是字元串,不是對象,不能添加方法,只能使用其包裝對象給予的方法 13 return this.charAt(this.length - 1); 14 } 15 console.log(str1.lastvalue()); 16 </script>
如果str1這個字元串類型數據,要添加方法,那可以添加到它的包裝對象上。如下
1 <script type="text/javascript"> 2 var str1 = 'hello123'; 3 String.prototype.lastvalue = function(){ 4 return this.charAt(this.length-1); 5 } 6 console.log(str1.lastvalue()); 7 </script>
-----------------------------
原型鏈:個體實例和原型之間的鏈接 , 原型鏈的最外層是Object.prototype
舉例:個體人物:張三 / 原型:男人
張三具有屬性:姓名:張三;
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = name; 4 } 5 Man.prototype.name = '男人'; 6 Object.prototype.name = '人類'; 7 8 var Zhanshan = new Man('張三'); 9 console.log(Zhanshan.name); //顯示張三,如果沒有第3行代碼,則根據原型鏈,顯示為男人,如果連第5行也沒有,則顯示為人類 10 //console.log(Man.prototype.name); //顯示男人,若無第5行,則顯示為人類 11 </script>
也就是註意層級關係。沒有找到的屬性就往原型鏈上找,優先順序當然是內層優先。
-------------------
------------------
面向對象的一些屬性和方法
hasOwnProperty : 對象個體實例自身是不是有某個屬性?如果是個體自身的屬性返回true; 如果屬性是原型下的,返回false;
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = name; 4 } 5 Man.prototype.name = '男人'; 6 7 var Zhanshan = new Man('張三'); 8 console.log(Zhanshan.hasOwnProperty('name')); //true 9 </script>
1 <script type="text/javascript"> 2 function Man(name){ 3 //this.name = name; 4 } 5 Man.prototype.name = '男人'; 6 7 var Zhanshan = new Man('張三'); 8 console.log(Zhanshan.hasOwnProperty('name')); //false 9 </script>
<script type="text/javascript"> function Man(name){ //this.name = name; } Man.prototype.name = '男人'; var Zhanshan = new Man('張三'); Zhanshan.name = '張三三'; console.log(Zhanshan.hasOwnProperty('name')); //true </script>
<script type="text/javascript"> function Man(){ this.name = '張三'; } Man.prototype.name = '男人'; var Zhanshan = new Man('張三'); console.log(Zhanshan.hasOwnProperty('name')); //true 雖然新建個其他的個體實例,name也會是張三,但是該個體實例自身有name屬性,所以還是true </script>
constructor : 查看對象的構造函數
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = '張三'; 4 } 5 Man.prototype.name = '男人'; 6 7 var Zhanshan = new Man('張三'); 8 console.log(Zhanshan.constructor == Man); //true 9 </script>
1 <script type="text/javascript"> 2 var a1 = [1,2,3]; 3 console.log(a1.constructor = Array); //true 4 </script>
當我們寫一個函數時,程式自動會生成constructor
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //註意,下麵這句話是系統內部自動實現的,這裡寫出來,好看到系統做了什麼,只要定義一個函數,系統都會"自動生成"下麵一句 5 //Aaa.prototype.constructor = Aaa; //這裡可以自行更改,將覆蓋,如Aaa.prototype.constructor = Array;但是亂改後將影響使用 6 console.log(Aaa.prototype); 7 //只有constructor是函數原型自身的,hasOwnProperty這個方法是Object上的,是通過原型鏈找到的 8 </script>
現在知道不要更改constructor,但是有時候,不註意我們就更改了,如下:
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 Aaa.prototype.name = '張三'; 5 Aaa.prototype.age = 30; 6 7 var a1 = new Aaa(); 8 console.log(a1.constructor); //Aaa 9 </script>
上面我們知道,Aaa.prototype也是一個對象,上面的改寫為如下形式,就更改了constructor
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //這裡更改了,不是更改屬性,而是重新對Aaa.prototype進行了賦值,也就弄丟了constructor屬性 5 Aaa.prototype = { 6 name:"張三", 7 age :30 8 } 9 var a1 = new Aaa(); 10 console.log(a1.constructor); //Object 11 12 </script>
所以,當我們按照上面這張方法寫的時候,要加上修正的,變成如下
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //弄丟的constructor屬性再找回來 5 Aaa.prototype = { 6 constructor:Aaa, //註意不要加引號,註意這句話就是為了修正對象constructor屬性 7 name:"張三", 8 age :30 9 } 10 var a1 = new Aaa(); 11 console.log(a1.constructor); //Aaa 12 </script>
還有一個地方註意:用for in迴圈對象的屬性的時候,系統自帶的屬性是迴圈不到的
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 Aaa.prototype.name = '張三'; 5 Aaa.prototype.age = 30; 6 Aaa.prototype.constructor = Aaa; 7 var a1 = new Aaa(); 8 for (var attr in Aaa.prototype) { 9 console.log(attr); //顯示了name和age,但是不顯示constructor,即使還單獨寫一次 10 } 11 </script>
但是,如果用= 重新賦值的方式,再迴圈列印是可以列印出來的哦!如下
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //這種方式是重新賦值 5 Aaa.prototype = { 6 constructor:Aaa, //註意不要加引號,註意這句話就是為了修正對象constructor屬性 7 name:"張三", 8 age :30 9 } 10 var a1 = new Aaa(); 11 for (var attr in Aaa.prototype) { 12 console.log(attr); //顯示constructor name age 13 } 14 </script>
instanceof 運算符,判斷個體對象和原型函數 是否有原型鏈上的關係,返回true或false 如下
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = name; 4 } 5 function Woman(name){ 6 this.name = name; 7 } 8 var zhanshan = new Man(); 9 console.log(zhanshan instanceof Man); //true 10 console.log(zhanshan instanceof Object); //true 11 console.log(zhanshan instanceof Woman); //false 12 </script>
用instanceof判斷某變數是否是數組
1 <script type="text/javascript"> 2 var a1 = [1,2,3,4,5]; 3 console.log(a1 instanceof Array); //true; 4 5 var b1 = 'hi'; 6 console.log(b1 instanceof Array); //false; 7 </script>
toString 方法 把對象轉成字元串 。 系統對象下麵都是自帶的,而自己創建的對象toString在object上
如下
1 <script type="text/javascript"> 2 var a1 = [1,2,3,4,5]; 3 console.log(a1.toString == Object.prototype.toString); //false 系統對象下麵都是自帶的,不是Object上的; 4 console.log(a1.toString == Array.prototype.toString); //true 系統對象下麵都是自帶的,在Array上 不在Object 5 function Bbb(){ 6 } 7 var b1 = new Bbb(); 8 console.log(b1.toString == Object.prototype.toString); //true 自己創建的對象toString在object上 9 </script>
1 <script type="text/javascript"> 2 var a1 = [1,2,3,4,5]; 3 console.log(a1.toString()); //1,2,3,4,5 4 console.log(typeof a1.toString()); //string 5 </script>
1 <script type="text/javascript"> 2 var a1 = [1,2,3,4,5]; 3 console.log(a1.toString()); //1,2,3,4,5 4 console.log(typeof a1.toString()); //string 5 6 //按照系統toString生成的1,2,3,4,5 如果不符合我們使用的格式的話,我們還可以自己去改寫 7 Array.prototype.toString = function(){ 8 return this.join('+'); //將數組內容按照加號連接 9 } 10 console.log(a1.toString()); //1+2+3+4+5 11 </script>
1 <script type="text/javascript"> 2 var num = 255; 3 console.log(num.toString(16)); //ff, 參數16是16進位的意思,輸出16進位的數據 rgb色彩可以用~~ 4 console.log(num.toString(2)); // 11111111 參數2是2進位的意思 5 </script>
上面是利用toString轉換數字進位
toString還可以用來判斷變數的數據類型,比typeof instanceof 更精準,如下
1 <script type="text/javascript"> 2 var num = 255; 3 console.log(Object.prototype.toString.call(num)); // [object Number] 4 console.log(Object.prototype.toString.call(num) == '[object Number]'); // true; 5 6 var str = 'hello'; 7 console.log(Object.prototype.toString.call(str)); // [object String] 8 var arr = [1,2,5]; 9 console.log(Object.prototype.toString.call(arr)); // [object Array] 10 11 console.log(Object.prototype.toString.call({name:'張三'})); //[object Object] 12 13 </script>