JavaScript裏的面向對象心得

1、面向對象的概念

面向對象是一種編程思想編程

對象:能夠添加屬性(變量)和方法(函數);
面向對象寫法特色:數組

一、把功能寫成一個函數;
    二、函數必須寫在對象身上,調用的時候直接拿對象.方法名();
    三、對象身上的功能能夠繼承;
    四、this指向,在事件或者定時器裏面是訪問不到真正的this的,須要在外面存一下;
寫法示例:
        function 構造函數 (){
            this.屬性=??;
        }
        構造函數.prototype.方法=??;
        
        var 實例=new 構造函數();
        實例.方法();

2、new的做用

一元運算符,後面只能接函數,用來調用函數;數據結構

用new調用函數的過程:模塊化

一、構建一個空對象;
    二、把函數內部的this指向建立的對象;
    三、在函數執行完之後自動返回剛纔建立的那個對象,即便函數裏面沒有return;假如函數後面有return的話會有兩種狀況:
        A 、return後面是對象的話,則返回這個對象;
        B 、return後面是非對象的話,則返回一開始new建立的對象;

注意:函數

一、用new調用的函數永遠返回一個對象,無論有沒有返回值;
    二、用new調用函數,這個函數必定是用來建立對象的,叫作構造函數;

3、構造函數

建立並初始化對象的函數,而且必須用new調用,否則和普通函數沒有區別;性能

functon Person(name,age){
            this.name=name;
            this.age=age;
            this.say=function{
            console.log("my name is"+this.name);
            };
        };

new出來的都是構造函數的實例,構造函數就是實例化的過程;
構造函數的問題:性能問題會形成資源浪費;this

4、原型 prototype

概念:函數身上的屬性,每一個函數都有,它的值是一個對象;prototype

用途:由於它是一個對象,因此身上能夠放屬性和方法,若是與構造函數相結合,經過構造函數建立的對象就會具備原型身上的屬性和方法;code

建議把一些共用的屬性和方法放在構造函數的原型身上;對象

5、__proto__

對象身上的屬性,每一個對象都有這個屬性;它的值也是一個對象,它的值就是它對應的構造函數的prototype的值;

實例.__proto__===構造函數.prototype;這句話解釋了爲何實例可以繼承構造函數身上的屬性和方法;

functon Person(name,age){
        this.name=name;
        this.age=age;
    };
    this.prototype.say=function{
    console.log("my name is"+this.name);
    }; 
                
    var p1=new Person("peter",18);
在這個例子中:p1.__proto__=Person.prototype;因此p1才能繼承Person構造函數身上的屬性和方法;

6、原型鏈

對象與原型之間的關係(連接)

原型鏈查找規則:

7、包裝對象

  • 在js中,當咱們去調用字符串,布爾值,數字的屬性和方法的時候,js會在內部把這些基本數據類型轉變成一個對應的對象類型(包裝對象),而後再調用這個包裝對象的屬性和方法;
  • 包裝對象有:

    • String
    • Number
    • Boolear
  • 注意:

    • null和undefined沒有包裝對象;
    • 基本數據類型只能使用對應的包裝對象身上的屬性和方法;

8、hasOwnProperty

做用:判斷一個屬性是否是本身對象身上的;

語法:對象.hasOwnProperty(屬性);

參數:要檢測的屬性;

返回值:

**true**         *自身屬性*
**false**        *非自身屬性*

注意:

一、這個方法是Object身上的方法    
二、不會順着原型鏈往外面去查找屬性,只查找自身

9、constructor

概念: 每一個對象身上都會有這個屬性,默認指向該對象對應的構造函數;
這個屬性不是放在對象身上,放在對應的原型對象身上;

做用:查看對象的構造函數;

語法:對象.constructor;

返回值:對象的構造函數;他能夠用來作數據類型的判斷;

constructor的問題    :constructor的值是能夠修改的;
 
    function Person(name){
        this.name=name;
    }
    var p1=new Person('kaivon');
    console.log(p1);
    console.log(p1.constructor==Person);    //true
            
    p1.constructor=Array;
    console.log(p1.constructor==Array);        //true

10、 toString

做用:把對象類型轉成字符串;

注意:系統對象身上的這個方法都是在對應的原型身上;而本身寫的構造函數這個方法在達的Object原型身上

它能夠用來作類型判斷:

Object.prototype.toString.call( );

//用toString作類型判斷
        var num=0;                  //[object Number]
        var str='kaivon';           //[object String]
        var b=true;                 //[object Boolean]
        var n=null;                 //[object Null]
        var u=undefined;            //[object Undefined]
        var arr1=[];                //[object Array]
        var obj1={};                //[object Object]
        var fn=function(){};        //[object Function]
        var d=new Date();           //[object Date]
        var re=new RegExp();        //[object RegExp]
        
        console.log(Object.prototype.toString.call(num));

11、淺拷貝與深拷貝

基本數據類型複製:

var num1=123;
var num2=num1;//那麼num2和num1就是同樣的,**複製了值**

複雜數據類型複製的問題:

var arr1=[1,2,3];
var arr2=arr1;

arr2.push(4);
//注意這個時候arr1和arr2的值;
console.log(arr2);              //[1,2,3,4]
console.log(arr1);              //[1,2,3,4]

arr1和arr2同樣的緣由:複雜數據類型複製的時候不只僅是複製了值,還複製了引用地址,因此修改arr2時,那麼arr1的地址和arr2其實仍是同樣的,因此arr1的值也相應改變了;

思考:那麼咱們應該如何複製複雜數據類型?

淺拷貝

var obj1={a:10,b:20};               //如何複製呢?

function copy(obj){
    var newObj={};
    for(var attr in obj){
        newObj[attr]=obj[attr];
    }
    return newObj;
}
var obj2=copy(obj1);
//這時候的obj2和obj1看起來是同樣的,可是實際上是不一樣的,有不一樣的引用地址;

深拷貝

深拷貝和淺拷貝的區別:
一、當要複製的那個對象裏面全部的值都是非對象的時候,用淺拷貝;
二、當要複製的那個對象裏面有對象的時候,用深拷貝;

var obj1={
    a:10,
    b:{
        c:20    //這時候對象裏面還有對象;這時候怎麼辦呢?
    }
}

//若是這時候還用上面的淺拷貝的話,那麼修改複製後的值,原來對象裏面的值也仍是會改變,因此要用下面的辦法;

function deepCopy(obj){
    if(typeof obj!=="object"){
        return obj;
    }
    //上面的代碼是給了一個跳出條件,當傳入的條件不是對象的時候就不須要遞歸了;
    if(obj instanceof Array){
        var newObj=[];
    }else{
        var newObj={};
    }
    for(var attr in obj){
        newObj[attr]=deepCopy(obj[attr]);
        //遞歸;當對象裏面的值仍是對象的時候須要用到;
    }
    return newObj;
}

12、繼承

概念:

讓一個對象擁有另外一個對象的屬性和方法,而且本身添加的屬性和方法不會影響原來的對象;

屬性繼承:

經過call的方法調用構造函數來繼承屬性;

function Person(name,age){
                this.name=name;
                this.age=age;
            }
Person.prototype.say=function(){
                console.log('我叫'+this.name);
            }
//Person至關於一個大類,Coder至關於一個小類,他們是有共同的地方的;
function Coder(name,age,job){
                this.name=name;
                this.age=age;
                this.job=job;
            }
Coder.prototype.say=function(){
                console.log('我叫'+this.name);
            }
//經過屬性繼承,咱們能夠將小類和大類共同的部分簡化以下:
function Coder(name,age,job){
                Person.call(this,name,age);
                
    //若是不用call的話,那麼this的指向就是window了,因此要用call將指向指到當前的this上面;
                this.job=job;
            }

方法繼承:

經過for in 想要繼承的構造函數的原型的方法去去繼承;

參考屬性繼承?的代碼:

for (var attr in Person.prototype){
    if(Person.prototype.hasOwnProperty(attr)){
        Coder.prototype[attr]=Person.prototype[attr];
    }
}

十3、組件

概念

把一個效果或者方法用面向對象的方法封裝起來,只提供給用戶一些相關的方法和數據接口;(模塊化

特色

易擴展易維護相互之間沒有影響

組件的組成:

1、配置參數

放在初始化函數裏;

  • 初始化函數放在構造函數的原型身上,通常用init表示
  • 須要寫一個默認參數放在構造函數裏面;
function Drag(obj){
    this.disX=0;
    this.disY=0;
    //下面的就是默認參數,下面的函數都要用下面的參數;
    this.settings={
        id:"",          //這是必傳的;
        downFn:function{},
        moveFn:function{},
        upFn:function{}
    }
}
//下面這個叫作初始化函數;
Drag.prototype.init=function (opt) {
    //在初始化函數裏面,拿用戶輸入來的參數覆蓋默認參數;
    for(var attr in this.settings){
        if(this.settings.hasOwnProperty(attr)){
        //若是默認參數裏面有這個屬性的話,纔會去覆蓋;
            this.settings[attr]=opt[attr];
        }
    }
    this.obj=document.getElementById(this.settings.id);
}
//用戶傳進來的就叫作配置參數;是一個對象;

2、方法

放在原型裏的函數;

3、自定義事件

  • 概念:

    • 除了系統自帶之外的事件叫作自定義事件;
    • 有利於多人協做開發,可擴展js事件;
    • 須要用到事件綁定,事件觸發器;
    • 自定義事件其實就是調用函數,在指定的環境下讓事件觸發;
  • 自定義事件三要素:

    • 對象;
    • 事件名;
    • 事件處理函數;
  • 能夠利用對象數據結構裏面的key:value鍵值對的方式去把事件名和事件處理函數聯繫起來; 把多個函數放到一個數組裏面,循環數組裏面的每一個函數;
//定義事件
object.events={
    "事件名1":[fn1,fn2],
    "事件名2":[fn3,fn4,fn5]
}

//調用事件(循環去調用)
  • 代碼實現示例
//事件綁定器,添加自定義事件;
function customEvent(obj,eventName,fn){
    /*  obj         對象名;
     *  eventName   對象的一個事件名,它的值是一個數組;
     *  fn          事件調用函數,它在數組裏面;
     */
    //這裏的"||"邏輯符號的意思是左邊爲真走左邊,左邊爲假走右邊;
    obj.events=obj.events||{};
    obj.events[eventName]=obj.events[eventName]||[];
    obj.events[eventName].push(fn);//push方法把函數添加到數組;
}
//事件觸發器;
function(obj,eventName){
    //觸發的時候要看看對象身上有沒有這個事件;
    if(obj.events[eventName]){
        for(var i=0;i<obj.events[eventName].length;i++){
            obj.events[eventName][i].call(obj);
            //注意this的指向,要指向這個obj;
        }
    }
}
相關文章
相關標籤/搜索