js中面向對象(創建對象的幾種方式)


1、面向對象編程(OOP)的特點

  抽象:抓住核心問題

  封裝:只能通過對象來訪問方法

  繼承:從已有的對象下繼承出新的對象

  多態:多對象的不同形態

 


 

一、創建對象的幾種方式

javascript 創建對象簡單的來說,無非就是使用內置對象或各種自定義對象,當然還可以使用JSON,但寫法有很多,也能混合使用。

1、工廠方式創建對象:面向對象中的封裝函數(內置對象)

function createPerson(name){
   //1、原料
    var obj=new Object();
   //2、加工
    obj.name=name;
    obj.showName=function(){
       alert(this.name);
    }     
    //3、出場
     return obj; 
} 
var p1=createPerson('小米');
p1.showName();

與系統對象的區別:

    var arr=new Array();//生成一個系統數組對象

    1、系統對象是直接用 new 在外面生成,而工廠定義的是在函數內部生成

    2、工廠定義的函數名稱第一個是小寫開頭,而系統定義的是大寫開頭

工廠模式的優缺點:雖然解決了創建相似對象的問題,但是卻沒有解決對象識別問題(即怎樣知道一個對象的類型)。

2、構造函數創建對象

  當new去調用一個函數,這個時候函數中的this就是創建出來的對象,而且函數的返回值就是this(隱式返回)

  new后面的函數叫做構造函數

  <1>有參數的構造函數

function CreatePerson(name){
  this.name=name;
  this.showName=function(){
    alert(this.name);
  }
}
    var p1=new CreatePerson('小米');

  <2>無參數的構造函數

  function CreatePerson(){}
    var p1=new CreatePerson();
    p1.name="小米";
    p1.showName=function(){
     alert(p1.name);
    }
    p1.showName();

構造函數模式的優缺點:

  1、優點:創建自定義函數意味着將來可以將它的實例標識為一種特定的類型,這是構造函數勝過工廠模式的地方

  2、缺點:每個方法都要在每個實例上重新創建一遍

3、對象字面量方式創建對象

person={
  name:"小米",
  age:23
};

4、用原型方式 

  1、原型對象:只要創建了一個新函數,就會為該函數創建一個prototype屬性,這個屬性指向函數的原型對象。在默認情況下,所有的原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性是一個指向prototype屬性所在函數的指針

  2、可以通過isPrototypeOf()方法來確定對象之間是否存在這種關系

function Person(){}
Person.prototype.name="小米";
Person.prototype.showName=function(){
alert(this.name);
}
var p1=new Person();
p1.showName();

原型模式的優缺點:

  1、優點:可以讓所有的對象實例共享它所包含的屬性和方法

  2、缺點:原型中是所有屬性都是共享的,但是實例一般都是要有自己的單獨屬性的。所以一般很少單獨使用原型模式。

5.混合模型

  構造函數模式定義實例屬性,而原型模式用於定義方法和共享的屬性

function CreatePerson(name){
  this.name=name;
}
  Create.prototype.showName=function(){
    alert(this.name);
  }
    var p1=new CreatePerson('小米');
    p1.showName();
   var p2=new CreatePerson('小米'); p2.showName();
  alert(p1.showName==p2.showName);//true;原因:都是在原型下面,在內存中只存在一份,地址相同

  總結:

  function 構造函數(){

    this.屬性;

  }

  構造函數.原型.方法=function(){};

  var 對象1=new 構造函數();

  對象1.方法();

 


 

原型:去改寫對象下面公用的的方法或屬性,讓公用的方法或屬性在內存中存在一份(提高性能)

原型:prototype:要寫在構造函數的下面

var  arr=[];
arr.number=10;
Array.prototype.number=20;
alert(arr.number);//10,
//原因:普通定義的要比原型定義的權重大,先會找自身的,自身沒有的話再沿着原型鏈找原型上是否有

屬性是否要放在原型下面,就要看該屬性是否是可變的,如果不是可變的,就可以放在原型下面,用來公用屬性,可變的話放在構造函數下面。

 


 

this的指向問題:在事件或者定時器下比較容易出問題

 


 

 二、包裝對象

1、我們把系統自帶的對象,叫做系統對象。例如:Array,Date

2、包裝對象:基本類型都有自己對應的包裝對象:String,Number,Boolean

  var str='hello';//基本類型:字符串類型

  str.charAt(0);//基本類型會找到對應的包裝對象類型,然后包裝對象把所有的屬性和方法給了基本類型,然后包裝對象消失。

  str.number=10;//在包裝對象下創一個對象,將屬性創建在對象下面,然后包裝對象就消失了,

  alert(str.number);//會彈出undefined;原因:會在包裝對象下重新創建一個對象

 


 

三、原型鏈

  原型鏈:實例對象與原型之間的連接,叫做原型鏈  

  _proto_(隱式連接)

   Object對象類型是原型鏈的最外層

  

  實例對象->先查找自己本身下面的屬性和方法->自身沒找到會沿着原型鏈找到該對象的原型,再查看原型上是否有要查找的屬性或方法->依次繼續查找如果找到的話則返回,否則找到最頂層Object上還沒有就真沒有了

 


 

四、面向對象中的屬性和方法 

 1、hasOwnProperty():看是否為對象自身下面的屬性和方法

  只有對象自己定義的屬性和方法則返回true,如果在prototype下定義發屬性和方法為公用的,所以返回為false;

2、constructor:查看對象的構造函數

  (可以用來檢測函數類型例如檢測是否是數組)

  每個原型函數都會自動添加constructor屬性(只會生成這一個屬性)

  for in的時候有些屬性是找不到的(系統自帶的屬性是for in找不到的,自己定義的可以找到)

  避免修改constructor屬性

function Aaa(){}
//Aaa.prototype.name='小米';
//Aaa.prototype.age=6;
//alert(a1.constructor);//Aaa 原因:只是給原型添加了屬性,並不是重新賦值了,自動添加的constructor屬性還在。
Aaa.prototype={
//  constructor:'Aaa',//需要手動修正指向問題
  name:'小米',
  age:6
}
var a1=new Aaa();
alert(a1.constructor);//Object 原因:將原型的prototype重新賦值了,但里面沒有constructor

 注意:以這種方式重設constructor屬性會使它的[Enumerable]特性被設置為true,默認情況下,原生的constructor屬性是不可枚舉的。可以通過Object.defineProperty()來修改。

3、instanceof:運算符

  對象與構造函數在原型鏈上是否有關系,也可以用作類型判斷但不是最好的方案,最好的方案是用toString 方法來判斷。

4、toString():object上的方法,把對象轉化為字符串

var arr=[];
alert(arr.toString==Object.prototype.toString);//false
//原因:系統對象下面都是自帶的(例如數組的toString在Array.prototype下),自己寫的對象都是通過原型鏈找到object下面的toString
function Aaa(){}
var a1=new Aaa();
alert(a1.toString==Object.prototype.toString);//true

1>利用toString 進制轉化

 Number.toString(進制);

var num=255;
alert(num.toString(16));//ff---轉化為16進制,默認不寫轉化為十進制

2>利用toString做類型判斷:

  //跨頁面的情況下上面兩種情況會失效

 var oF=document.createElement('iframe');

  document.body.appendChild('oF');

  var ifArray=windows.frames[0].Array;//iframe下的Array數組

  var arr=new ifArray();

  alert(arr.constructor==Array);//false

  alert(arr instanceof Array);//false

  alert(Object.prototype.toString.call(arr)=='[object Array]');//true

 

var arr=[];
alert(Object.prototype.toString.call(arr));//[Object Array] 
var arr={};
alert(Object.prototype.toString.call(arr));//[Object Object] 
var arr=new Date;
alert(Object.prototype.toString.call(arr));//[Object Date] 
var arr=new RegExp;
alert(Object.prototype.toString.call(arr));//[Object RegExp]  
var arr=null;
alert(Object.prototype.toString.call(arr));//[Object Null] 

 


 

 

五 、繼承

1、繼承方式:

  1、拷貝繼承:通用型  有new無new都可以用

  2、類式繼承:new構造函數---利用構造函數(類)繼承的方式

  3、原型繼承:無new的對象---借助原型來實現對象繼承對象

  屬性繼承:調用父類的構造函數call

  方法繼承:用for in的形式 拷貝繼承(jq也用拷貝繼承)

        var a = {
            name: '小米'
        };
        //拷貝繼承
        function extend(obj1, obj2) {
            for (var attr in obj2) {
                obj1[attr] = obj2[attr];
            }
        }
        //原型繼承
            var b=cloneObj(a);
            b.name='小喬';
            alert(a.name);
            alert(b.name);
            function cloneObj(obj) {
                var F=function () {};
                F.prototype=obj;
                return new F();
            }
        //類式繼承
        function A() {//父類
            this.name='小米';
        }
        A.prototype.showName=function () {
            alert(this.name);
        }
        function B() {//子類
            A.call(this);//屬性和方法分開繼承
        }
    //B.prototype=new A();//一句話實現繼承,但會有很多問題,比如指向問題,屬性會互相影響
    //類式繼承改進:至少由以下四句實現方法的繼承,屬性需要分開繼承
var F=function () {}; F.prototype=A.prototype; B.prototype=new F(); B.prototype.constructor=A;//修正指向問題 var b1=new B(); b1.name='笑笑'; b1.showName();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM