這是一篇簡單介紹js面向對象的編程筆記html
一,js解析和執行編程
js解析時,會先把var和function聲明的變量和函數放到一個詞法對象裏,變量的值是undefined,函數則是一個引用,這是js變量提高的根本機制。閉包
若是一個變量沒有聲明就開始使用了,不論它是在局部仍是全局使用,它都是掛在了window下,是個全局變量,這點和this指向很相似,this找不到直接調用者,也是直接指向window。app
若是變量名和函數名命名衝突了,則該命名最後指向的是函數。若是兩個函數名重複了,則後面一個函數覆蓋前面的,由於在js中,函數纔是第一等公民。dom
e.g. console.log(a)//undefined var a = 1 console.log(a)//1 e.g. console.log(aa)//function aa() {console.log('bbbbb')} var aa = 1 function aa() { console.log('aaaaa') } function aa() { console.log('bbbbb') }
二,做用域函數
js只有全局做用域和局部(函數)做用域,而且內部能夠由內到外,從近到遠,訪問即止地層層訪問外部變量,造成一條做用域鏈,而外部不能直接訪問內部變量。性能
e.g. var a = 1 var b = function() { var c = 2+a console.log(c) } b()//3 console.log(c)//undefined
三,閉包this
一種訪問局部變量的手段,一般指的是一個能夠訪問局部變量的函數。es5
閉包一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中,所以要注意控制性能,提升網頁性能。spa
e.g. var name = "The Window"; var object = { name : "My Object", // 這個方法是返回了一個函數 getNameFunc : function(){ return function(){ return this.name; }; } }; // 執行了object.getNameFunc()返回的函數,此時this找不到直接調用者,直接指向window alert(object.getNameFunc()()); e.g. var name = "The Window"; var object = { name : "My Object", // 這個方法是返回了一個函數 getNameFunc : function(){ // that指向的是object var that = this; return function(){ return that.name; }; } }; // 執行了object.getNameFunc()返回的函數,此時this固定化成了object,彈出object.name alert(object.getNameFunc()());
四,類與對象
搞清楚js中的類,就須要明白三個概念:構造函數、原型對象,實例化和實例。
e.g. function Person(name,age) { this.name = name; this.age = age } Person.prototype.hello = function () { console.log('Hello,I am '+this.name+',and I am '+this.age) } var xm = new Person('xiaoming',12) xm.hello()//Hello,I am xiaoming,and I am 12 Person.prototype = { asset:12345 } var xmm = new Person('xiaomingming',34) console.log(xm.asset)//undefined console.log(xmm.asset)//12345
其中Person函數就是構造函數,Person.prototype指向的對象就是原型對象,xm就是實例
那麼在關鍵詞new的過程當中,大概發生了什麼呢?
1,一個新對象被建立了,繼承Person.prototype原型對象裏的屬性。
2,使用指定的參數調用構造函數Person,並將this指向新建立的對象。
若是咱們用自定義函數模擬過程,則:
e.g. function Person(name,age) { this.name = name; this.age = age } function New(f) { return function() { var o = { '__proto__':f.prototype } f.apply(o,arguments) return o } } var xmmm = New(Person)('xmmm',232)
在js中,並無真正的類的概念,只是用構造函數和原型對象模擬了類,其中原型對象是類的惟一標識,當且僅當兩個對象繼承自同一個原型對象的時候,咱們才說它們是同一個類的實例。而初始化對象的構造函數則不能做爲類的標識,一般構造函數的名字被用來當作類名了而已。
instanceof 運算符
實例 instanceof 類名 被用來判斷一個實例是否繼承自某個原型對象,返回布爾值。
在上述例子中,原型對象也是對象,也具備原型對象,即原型對象的原型對象,以此類推。訪問一個實例自己沒有的屬性或方法時,js會從原型對象上去找,原型對象上沒有時,會從原型對象上的原型對象上去找,......這種像鏈式同樣地繼承機制就是原型鏈,和做用域鏈很相似。
原型對象中constructor屬性則是指向該原型對象的構造函數,在修改原型對象時,好比增刪其屬性時,可採用.符號操做,如Person.prototype.hello = s =>{console.log(s)},在實例person初始化的時候,該原型對象的constructor默認指向構造函數.
可是若是直接給Person.prototype賦值對象型數據時,則須要顯式指定constructor屬性,不然原有的原型對象會被新對象所替代,而經過構造函數實例化的實例的__protoo__屬性都會指向最新的Person.prototype對象。在這個改動以後實例化的實例均不能繼承原有的原型對象的屬性,改動以前的實例亦不能繼承新原型對象的屬性。
實例,構造函數,原型對象,原型鏈之間的關係能夠藉助一張神圖來講明關係
this指針問題:
1,老是指向方法的直接調用對象,在事件觸發中指向觸發該事件的dom節點對象
2,指向new出來的實例
apply和call都可以改變this指針,其區別僅僅是第二個參數的不一樣而已:
f.call(o,a,b) === o.f(a,b) === f.apply(o,[a,b])
五,封裝
一段代碼可以實現某種功能,隱藏該代碼的細節,僅對外開放接口去使用該功能代碼,便是對該功能代碼的封裝。
六,繼承
繼承可使得子類具備父類的屬性和方法或者從新定義、追加屬性和方法等。具備的方式多是複製,多是引用。
從對象層面上來看繼承:
1,複製
var zsh = { name :'zsh', age:23 } var job = { job:'programer', language:'fe' } for (var key in job){ zsh[key] = job[key] } console.log(zsh)//{name: "zsh", age: 23, job: "programer", language: "fe"}
涉及到深淺複製參考:http://www.cnblogs.com/zhouxiaohouer/p/8037729.html
2,Object.create(proto[, propertiesObject])
es5裏的方法,注意兼容性。
參數
proto 新建立對象的原型對象。
propertiesObject 可選。若是沒有指定爲 undefined,則是要添加到新建立對象的可枚舉屬性(即其自身定義的屬性,而不是其原型鏈上的枚舉屬性)對象的屬性描述符以及相應的屬性名稱。這些屬性對應Object.defineProperties()的第二個參數。
返回值
在指定原型對象上添加新屬性後的對象。
從類的層面上來看繼承:
// 建立父類 // 建立子類 // 創建聯繫,修正構造函數 // 實例化 function Father() {} function Son() {} Son.prototype = Object.create(Father.prototype) // 此時,Son.prototype.constructor指向的是function Father() {},咱們最好是將其修正過來 Son.prototype.constructor = Son
七,多態
'見人說人話,見鬼說鬼話。'
根據不一樣的參數個數,調用不一樣的方法,實現不一樣的功能
本質上是檢測arguments中數據類型來作對應的操做,將差別化的處理方式通用封裝處理。
e.g. function duotai(a,b,c,d,e,f) { console.log(duotai.length)//返回形參個數 console.log(arguments.length)//返回實參個數 } duotai(1,2,3,4)//6 4 e.g. function add() { var result = 0 for (var i = 0 ;i<arguments.length-1;i++){ result += arguments[i] } return result } add(1,2,3,4,6,7,8)//31 add(343,567,8456,234)//9600