javascript
浅谈面向对象的javascript几个特性
javascript中的this和new
javascript是一門很靈活的語言,尤其是function。他即可以以面向過程的方式來用,比如:
function getName() {return '張三' } getName()也可以以面向對象的方式來用,比如:
function User() {this.name = '張三' }var user = new User()javascript是如何實現面向對象編程的呢?他提供了new這個關健字,有了new就可以把對象進行實例化,比如:
function User(name, age){this.name = namethis.age = age } var zs = new User('zs', 20) var ls = new User('ls', 30)new出來的兩個實例,會開辟兩塊新的內存區域,來保存這些數據,同時有指針指向對象User。所以就有instanceof這個運算符,這個運算符的意思就是:a是不是A的實例。比如上例:zs instanceof User的返回值是true。
即然是面向對象的編程語言,那this也是不可或缺的。在javascript中,this永遠指向的是他的調用者。要理解這句話,我們舉幾個例子:
例子1
function test(){this.name = 'zs' } test()當執行完成之后,這個name會直接掛載到window下面,因為是這樣執行的:winow.test()。
例子2
var game = document.getElementById('game') game.addEventListener('click', function () {setTimeout(function () {this.innerText = 'Clicked'}, 1000) })這個例子很簡單,點擊某個元素的時候,1秒后,讓他里面的html改成Clicked,可是你發現這樣不好使,就是因為this指向的問題,因為這里面的this也指向window了,所以你執行window.innerText會返回Clicked。
例子3
function User(name) {this.name = namethis.getName = function () {console.log(this.name)} } var u = new User('zs') u.getName()這里面的this的指向沒有問題,因為按照之前的原則,調用者是u,也就是User的實例,所以在方法getName中,this.name相當于u.name,所以打印出zs。
prototype和__proto__
prototype
javascript是面向對象的語言,這個上面已經提過了,其他面向對象語言有一個必備我就是繼承,很顯然在ES6之前,沒有extends這個關鍵字,那么,javascript就是利用prototype的原型鏈來實現繼承。先記住這句話,我們一會會說到繼承。prototype其實只是對象的一個屬性,在Chrome控制臺下,可以直接看出來,但是這個屬性很特殊,這個屬性下可以掛載任何的對象、方法、屬性,而且掛載的東西可以反映到實例下對象上。說的比較繞,我們看個例子:
function User(name) {this.name = name } User.prototype.getName = function () {console.log(this.name) } var u = new User('zs') u.getName()我們在User.prototype上面掛載了getName的方法,在下面實例化User之后的u,就可以訪問這個方法。
看到這,你可以有個疑問,既然是給實例化對象用的,那下面這種方式豈不是更好、更直觀?
如果我們和Java語言進行對應,User相當是Class,name相當于屬性,getName相當于里面的方法,完美映射!可以這樣有一個問題啊,就是太費內存了。因為每new一個對象,都會占用一塊內存區域,這樣User里面方法屬性越多,那么每個實例化的對象都會對這些進行 深復制,那么占用的內存空間就越大。那么javascript是如何通過prototype來解決內存占用的問題的呢?這就需要引用__proto__。
__proto__
定義:__proto__是存在于實例化后對象的一個屬性,并且指向原對象的prototype屬性。
比如上例中的u.__proto__ === User.prototype返回的是true。可以在Chrome控制臺下查看u.__proto__。
你會發現,不對吧,User對象下也有__proto__啊。那是因為User也是Function的實例,不信你可以試一下User.__proto__ === Function.prototype的返回值。其實我們這樣定義函數:function test(){}是一個語法糖的寫法,全拼應該是這樣:var test = new Function('alert(1)')。
現在我們來解釋為什么使用prototype能節省內存。不知道你有沒有注意到上面一句代碼u.__proto__ === User.prototype,我為什么要使用三等?因為三等號除了值、類型外,內存地址也必須是相等的,也就是說User不管實例化多少對象,他們的prototype只有一份,放在User里。客戶端的瀏覽器環境不像服務器,內存還是比較緊張的,所以javascript通過這種方式,來解決內存占用問題。
繼承
方式一:直接繼承
先舉個例子:
var Animal = function (name) {this.name = name } Animal.prototype.walk = function () {console.log('I can walk!') } Animal.prototype.getName = function () {console.log('My name is ' + this.name + '!') }var Dog = function (name) {this.name = name } Dog.prototype = Animal.prototypevar d = new Dog('Tom') d.getName() d.walk()我們建立一個父類Animal對象,建立一個子類Dog,我們想讓Dog也有walk方法和getName方法,通過上面對prototype的了解,我們最先想到的是Dog.prototype = Animal.prototype,這樣子類和父類的prototype相等,那子類就有父類所有方法嘍,繼承鏈條是這樣的:d.__proto__ === Dog.prototype === Animal.prototype。
這樣很直觀,但是也有一個比較嚴重的問題。我們在擴展Dog的時候,同時父類也會有對應的方法,這很顯然是一個很嚴重的問題。
方式二:實例化繼承
為了解決上面的問題,我們需要引入一個空函數,這個空函數做為橋梁,把子類和父類之間的連接切斷。實現如下:
var F = function () {} F.prototype = Animal.prototype Dog.prototype = new F()Dog.prototype.say = function () {console.log('Say') }- 為什么是Dog.prototype = new F()呢?因為這樣即可以繼承Animal的所有方法,他的原型鏈是這樣的:
執行walk方法,F已經有了,所以就不會再找Animal了
- 新增加的方法又不影響父類,這句怎么講?因實例化的對象沒有prototype屬性!所以不會影響
總結
以上是生活随笔為你收集整理的浅谈面向对象的javascript几个特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux安装java的脚本吗,Linu
- 下一篇: linux 每周日执行脚本,Linux任