面向對象是一種編程思想編程
對象:能夠添加屬性(變量)和方法(函數);
面向對象寫法特色:數組
一、把功能寫成一個函數; 二、函數必須寫在對象身上,調用的時候直接拿對象.方法名(); 三、對象身上的功能能夠繼承; 四、this指向,在事件或者定時器裏面是訪問不到真正的this的,須要在外面存一下;
寫法示例: function 構造函數 (){ this.屬性=??; } 構造函數.prototype.方法=??; var 實例=new 構造函數(); 實例.方法();
一元運算符,後面只能接函數,用來調用函數;數據結構
用new調用函數的過程:模塊化
一、構建一個空對象; 二、把函數內部的this指向建立的對象; 三、在函數執行完之後自動返回剛纔建立的那個對象,即便函數裏面沒有return;假如函數後面有return的話會有兩種狀況: A 、return後面是對象的話,則返回這個對象; B 、return後面是非對象的話,則返回一開始new建立的對象;注意:函數
一、用new調用的函數永遠返回一個對象,無論有沒有返回值; 二、用new調用函數,這個函數必定是用來建立對象的,叫作構造函數;
建立並初始化對象的函數,而且必須用new調用,否則和普通函數沒有區別;性能
functon Person(name,age){ this.name=name; this.age=age; this.say=function{ console.log("my name is"+this.name); }; };
new出來的都是構造函數的實例,構造函數就是實例化的過程;
構造函數的問題:性能問題會形成資源浪費;this
概念:函數身上的屬性,每一個函數都有,它的值是一個對象;prototype
用途:由於它是一個對象,因此身上能夠放屬性和方法,若是與構造函數相結合,經過構造函數建立的對象就會具備原型身上的屬性和方法;code
建議把一些共用的屬性和方法放在構造函數的原型身上;對象
對象身上的屬性,每一個對象都有這個屬性;它的值也是一個對象,它的值就是它對應的構造函數的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構造函數身上的屬性和方法;
對象與原型之間的關係(連接)
原型鏈查找規則:
包裝對象有:
注意:
做用:判斷一個屬性是否是本身對象身上的;
語法:對象.hasOwnProperty(屬性);
參數:要檢測的屬性;
返回值:
**true** *自身屬性* **false** *非自身屬性*
注意:
一、這個方法是Object身上的方法 二、不會順着原型鏈往外面去查找屬性,只查找自身
概念: 每一個對象身上都會有這個屬性,默認指向該對象對應的構造函數; 這個屬性不是放在對象身上,放在對應的原型對象身上;
做用:查看對象的構造函數;
語法:對象.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
做用:把對象類型轉成字符串;
注意:系統對象身上的這個方法都是在對應的原型身上;而本身寫的構造函數這個方法在達的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));
基本數據類型複製:
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; }
讓一個對象擁有另外一個對象的屬性和方法,而且本身添加的屬性和方法不會影響原來的對象;
經過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]; } }
把一個效果或者方法用面向對象的方法封裝起來,只提供給用戶一些相關的方法和數據接口;(模塊化)
易擴展、易維護、相互之間沒有影響
放在初始化函數裏;
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); } //用戶傳進來的就叫作配置參數;是一個對象;
放在原型裏的函數;
概念:
自定義事件三要素:
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; } } }