摘要:面向?qū)ο缶幊陶Z言的對象體系,不是基于類的,而是基于構(gòu)造函數(shù)和原型鏈。一語言使用構(gòu)造函數(shù)作為對象的模板。構(gòu)造函數(shù)之所以叫構(gòu)造函數(shù),就是說這個函數(shù)的目的,就是操作一個空對象即對象,將其構(gòu)造為需要的樣子。
Javascript
前言:關(guān)于javascript我總有些疑問,是不是一門面向?qū)ο蟮恼Z言,總結(jié)了很久:Javascript是一種基于對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向?qū)ο缶幊蹋∣OP)語言,其實主要是因為它沒有提供象抽象、繼承、重載等有關(guān)面向?qū)ο笳Z言的許多功能, 而是把其它語言所創(chuàng)建的復(fù)雜對象統(tǒng)一起來,從而形成一個非常強(qiáng)大的對象系統(tǒng)。 這種獨特性稱它為prototype-basedOO(基于原型的面向?qū)ο?。 (這是針對ES6以前的標(biāo)準(zhǔn))js面向?qū)ο缶幊?/b>在這篇博客中我引用了阮一峰老師的教程:http://javascript.ruanyifeng.com/oop/basic.html 這個教程講得十分詳細(xì),如果有系統(tǒng)學(xué)習(xí)的需要,建議跳過我的文章,直接看這里。
JavaScript 語言的對象體系,不是基于“類”的,而是基于構(gòu)造函數(shù)(constructor)和原型鏈(prototype)。
一、JavaScript 語言使用構(gòu)造函數(shù)(constructor)作為對象的模板。
var User = function () {
this.age = 23; this.name = "wanghaoxin";
};
這是一個構(gòu)造函數(shù)。
構(gòu)造函數(shù)的寫法就是一個普通的函數(shù),但是有自己的特征和用法。
函數(shù)體內(nèi)部使用了this關(guān)鍵字,代表了所要生成的對象實例。
生成對象的時候,必需用new命令,調(diào)用Vehicle函數(shù)。
var user = new User();//這里的等同于 var user = new User;
user.age = 123;
console.log(user.age);
console.log(user.name);
執(zhí)行這段代碼結(jié)果就是 123 、 wanghaoxin,這里的user.age經(jīng)過了賦值,所以是賦值后的值。
由于js并不是java一樣的強(qiáng)類型語言,所以不能再編譯前檢查你的語法錯誤,這里有個問題。 > 一個很自然的問題是,如果忘了使用new命令,直接調(diào)用構(gòu)造函數(shù)會發(fā)生什么事?
這種情況下,構(gòu)造函數(shù)就變成了普通函數(shù),并不會生成實例對象。而且由于后面會說到的原因,this這時代表全局對象,將造成一些意想不到的結(jié)果。
二、new命令的原理
使用new命令時,它后面的函數(shù)調(diào)用就不是正常的調(diào)用,而是依次執(zhí)行下面的步驟。
1. 創(chuàng)建一個空對象,作為將要返回的對象實例. 2. 將這個空對象的原型,指向構(gòu)造函數(shù)的prototype屬性. 3. 將這個空對象賦值給函數(shù)內(nèi)部的this關(guān)鍵字. 4. 開始執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼.
也就是說,構(gòu)造函數(shù)內(nèi)部,this指的是一個新生成的空對象,所有針對this的操作,都會發(fā)生在這個空對象上。構(gòu)造函數(shù)之所以叫“構(gòu)造函數(shù)”,就是說這個函數(shù)的目的,就是操作一個空對象(即this對象),將其“構(gòu)造”為需要的樣子。
如果構(gòu)造函數(shù)內(nèi)部有return語句,而且return后面跟著一個對象,new命令會返回return語句指定的對象;否則,就會不管return語句,返回this對象。
三、使用 Object.create() 創(chuàng)建實例對象
構(gòu)造函數(shù)作為模板,可以生成實例對象。但是,有時只能拿到實例對象,而該對象根本就不是由構(gòu)造函數(shù)生成的,這時可以使用Object.create()方法,直接以某個實例對象作為模板,生成一個新的實例對象。
var person1 = { name: "張三", age: 38, greeting: function() { console.log("Hi! I"m " + this.name + "."); } }; var person2 = Object.create(person1); person2.name // 張三 person2.greeting() // Hi! I"m 張三.
四、javascript使用prototype
JavaScript 的每個對象都繼承另一個對象,后者稱為“原型”(prototype)對象。null也可以充當(dāng)原型,區(qū)別在于它沒有自己的原型對象。JavaScript 繼承機(jī)制的設(shè)計就是,原型的所有屬性和方法,都能被子對象共享。
function Animal(name) { this.name = name; } Animal.prototype.color = "white"; var cat1 = new Animal("大毛"); var cat2 = new Animal("二毛"); cat1.color // "white" cat2.color // "white"
這里的color屬性被兩個貓共享了。
cat1.color = "black"; cat1.color // "black" cat2.color // "yellow" Animal.prototype.color // "yellow";
在這里cat1.color 被賦值為 black ,所以cat1將不再使用原來的原型屬性.
prototype對象有一個constructor屬性,默認(rèn)指向prototype對象所在的構(gòu)造函數(shù)。由于constructor屬性定義在prototype對象上面,意味著可以被所有實例對象繼承。
構(gòu)造函數(shù)就是普通的函數(shù), 所以實際上所有函數(shù)都有prototype屬性。
五、_proto_原型鏈
對象的屬性和方法,有可能定義在自身,也有可能定義在它的原型對象。由于原型本身也是對象,又有自己的原型,所以形成了一條原型鏈(prototype chain)。比如,a對象是b對象的原型,b對象是c對象的原型,以此類推。
如果一層層地上溯,所有對象的原型最終都可以上溯到Object.prototype,即Object構(gòu)造函數(shù)的prototype屬性。那么,Object.prototype對象有沒有它的原型呢?回答是有的,就是沒有任何屬性和方法的null對象,而null對象沒有自己的原型。
“原型鏈”的作用是,讀取對象的某個屬性時,JavaScript 引擎先尋找對象本身的屬性,如果找不到,就到它的原型去找,如果還是找不到,就到原型的原型去找。如果直到最頂層的Object.prototype還是找不到,則返回undefined。
如果對象自身和它的原型,都定義了一個同名屬性,那么優(yōu)先讀取對象自身的屬性,這叫做“覆蓋”(overriding)。
Object.prototype.__proto__
根據(jù)語言標(biāo)準(zhǔn),__proto__屬性只有瀏覽器才需要部署,其他環(huán)境可以沒有這個屬性,而且前后的兩根下劃線,表示它本質(zhì)是一個內(nèi)部屬性,不應(yīng)該對使用者暴露。因此,應(yīng)該盡量少用這個屬性,而是用Object.getPrototypeof()(讀取)和Object.setPrototypeOf()(設(shè)置),進(jìn)行原型對象的讀寫操作。
this及this的綁定this的動態(tài)切換,固然為JavaScript創(chuàng)造了巨大的靈活性,但也使得編程變得困難和模糊。有時,需要把this固定下來,避免出現(xiàn)意想不到的情況。JavaScript提供了call、apply、bind這三個方法,來切換/固定this的指向。
this的使用這個關(guān)鍵字很好的體現(xiàn)了,如果不是面向?qū)ο螅菫槭裁词褂胻his呢,其實這個this還真有點不一樣,在java中this就是指的本類,那么js中呢?
this永遠(yuǎn)指向的是最后調(diào)用它的對象,也就是看它執(zhí)行的時候是誰調(diào)用的。
所以這里需要注意的問題就是,到底是哪個對象最后調(diào)用了this,只有因為this本身指代的就是對象。下面的例子中this的指向變了,變成了誰(誰調(diào)用了), 一目了然。
function f() { return "姓名:"+ this.name; } var A = { name: "張三", describe: f }; var B = { name: "李四", describe: f }; A.describe() // "姓名:張三" B.describe() // "姓名:李四"
如果沒有對象調(diào)用他,默認(rèn)是用window對象調(diào)用的,這也講得通,因為window是js的頂層對象,所有其他對象都是window的下屬對象,JavaScript規(guī)定,瀏覽器環(huán)境的所有全局變量,都是window對象的屬性。如果一個函數(shù)在全局環(huán)境中運行,那么this就是指頂層對象。
具體的例子看下面鏈接,比我寫的好,
這里有詳細(xì)的講解http://www.cnblogs.com/pssp/p/5216085.html
函數(shù)實例的call方法,可以指定函數(shù)內(nèi)部this的指向(即函數(shù)執(zhí)行時所在的作用域),然后在所指定的作用域中,調(diào)用該函數(shù)。call方法的參數(shù),應(yīng)該是一個對象。如果參數(shù)為空、null和undefined,則默認(rèn)傳入全局對象。
var obj = {}; var f = function () { return this; }; f() === this // true f.call(obj) === obj // true
上面代碼中,在全局環(huán)境運行函數(shù)f時,this指向全局環(huán)境;call方法可以改變this的指向,指定this指向?qū)ο髈bj,然后在對象obj的作用域中運行函數(shù)f。
var n = 123; var obj = { n: 456 }; function a() { console.log(this.n); } a.call() // 123 a.call(null) // 123 a.call(undefined) // 123 a.call(window) // 123 a.call(obj) // 456
上面代碼中,a函數(shù)中的this關(guān)鍵字,如果指向全局對象,返回結(jié)果為123。如果使用call方法將this關(guān)鍵字指向obj對象,返回結(jié)果為456。可以看到,如果call方法沒有參數(shù),或者參數(shù)為null或undefined,則等同于指向全局對象。
function add(a, b) { return a + b; } add.call(this, 1, 2) // 3
上面代碼中,call方法指定函數(shù)add內(nèi)部的this綁定當(dāng)前環(huán)境(對象),并且參數(shù)為1和2,因此函數(shù)add運行后得到3。
var obj = {}; obj.hasOwnProperty("toString") // false // 覆蓋掉繼承的 hasOwnProperty 方法 obj.hasOwnProperty = function () { return true; }; obj.hasOwnProperty("toString") // true Object.prototype.hasOwnProperty.call(obj, "toString") // false
上面代碼中,hasOwnProperty是obj對象繼承的方法,如果這個方法一旦被覆蓋,就不會得到正確結(jié)果。call方法可以解決這個問題,它將hasOwnProperty方法的原始定義放到obj對象上執(zhí)行,這樣無論obj上有沒有同名方法,都不會影響結(jié)果。
apply的使用apply方法的作用與call方法類似,也是改變this指向,然后再調(diào)用該函數(shù)。唯一的區(qū)別就是,它接收一個數(shù)組作為函數(shù)執(zhí)行時的參數(shù),使用格式如下。
function f(x,y){ console.log(x+y); } f.call(null,1,1) // 2 f.apply(null,[1,1]) // 2
上面的 f 函數(shù)本來接受兩個參數(shù),使用apply方法以后,就變成可以接受一個數(shù)組作為參數(shù)。
綁定回調(diào)函數(shù)的對象
var o = new Object();
o.f = function () {
console.log(this === o);
}
var f = function (){
o.f.apply(o);
// 或者 o.f.call(o);
};
$("#button").on("click", f);
點擊按鈕以后,控制臺將會顯示true。由于apply方法(或者call方法)不僅綁定函數(shù)執(zhí)行時所在的對象,還會立即執(zhí)行函數(shù),因此不得不把綁定語句寫在一個函數(shù)體內(nèi)。更簡潔的寫法是采用下面介紹的bind方法。
bind的使用bind方法用于將函數(shù)體內(nèi)的this綁定到某個對象,然后返回一個新函數(shù)。
var counter = { count: 0, inc: function () { this.count++; } }; var func = counter.inc; func(); counter.count // 0 count // NaN
上面代碼中,函數(shù)func是在全局環(huán)境中運行的,這時inc內(nèi)部的this指向頂層對象window,所以counter.count是不會變的,反而創(chuàng)建了一個全局變量count。因為window.count原來等于undefined,進(jìn)行遞增運算后undefined++就等于NaN。
為了解決這個問題,可以使用bind方法,將inc內(nèi)部的this綁定到counter對象。
var func = counter.inc.bind(counter); func(); counter.count // 1
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107338.html
摘要:是完全的面向?qū)ο笳Z言,它們通過類的形式組織函數(shù)和變量,使之不能脫離對象存在。而在基于原型的面向?qū)ο蠓绞街校瑢ο髣t是依靠構(gòu)造器利用原型構(gòu)造出來的。 JavaScript 函數(shù)式腳本語言特性以及其看似隨意的編寫風(fēng)格,導(dǎo)致長期以來人們對這一門語言的誤解,即認(rèn)為 JavaScript 不是一門面向?qū)ο蟮恼Z言,或者只是部分具備一些面向?qū)ο蟮奶卣鳌1疚膶⒒貧w面向?qū)ο蟊疽猓瑥膶φZ言感悟的角度闡述為什...
摘要:使用來調(diào)用函數(shù),會自動執(zhí)行下面操作創(chuàng)建一個全新的對象。所以如果是一個函數(shù)的話,會是這樣子的創(chuàng)建一個新對象連接新對象與函數(shù)的原型執(zhí)行函數(shù),改變指向新的對象所以,在使用來調(diào)用函數(shù)時候,我們會構(gòu)造一個新對象并把它綁定到函數(shù)調(diào)用中的上。 歡迎來我的博客閱讀:《加深對 JavaScript This 的理解》 我相信你已經(jīng)看過很多關(guān)于 JavaScript 的 this 的談?wù)摿耍热荒泓c進(jìn)來...
摘要:首先,需要來理清一些基礎(chǔ)的計算機(jī)編程概念編程哲學(xué)與設(shè)計模式計算機(jī)編程理念源自于對現(xiàn)實抽象的哲學(xué)思考,面向?qū)ο缶幊淌瞧湟环N思維方式,與它并駕齊驅(qū)的是另外兩種思路過程式和函數(shù)式編程。 JavaScript 中的原型機(jī)制一直以來都被眾多開發(fā)者(包括本人)低估甚至忽視了,這是因為絕大多數(shù)人沒有想要深刻理解這個機(jī)制的內(nèi)涵,以及越來越多的開發(fā)者缺乏計算機(jī)編程相關(guān)的基礎(chǔ)知識。對于這樣的開發(fā)者來說 J...
摘要:理解作用域高級程序設(shè)計中有說到對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的在全局函數(shù)中,等于,而當(dāng)函數(shù)被作為某個對象調(diào)用時,等于那個對象。指向與匿名函數(shù)沒有關(guān)系如果函數(shù)獨立調(diào)用,那么該函數(shù)內(nèi)部的,則指向。 理解this作用域 《javascript高級程序設(shè)計》中有說到: this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個對象調(diào)用時,t...
摘要:更多資料如果你想了解更多關(guān)于的資料,下面的連接對你絕對有用關(guān)于本文本文轉(zhuǎn)自大叔的深入理解系列。深入理解系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載,整理等各類型文章,原文是大叔的一個非常不錯的專題,現(xiàn)將其重新整理發(fā)布。 前言 寫這篇文章的目的是經(jīng)常看到開發(fā)人員說:把字符串轉(zhuǎn)化為JSON對象,把JSON對象轉(zhuǎn)化成字符串等類似的話題,所以把之前收藏的一篇老外的文章整理翻譯了一下,供大家討論,如有錯誤,...
摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來說只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會問的問題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書籍等書中給出的概念,這些概念雖然...
閱讀 2573·2021-09-02 15:40
閱讀 1576·2019-08-30 15:54
閱讀 1089·2019-08-30 12:48
閱讀 3410·2019-08-29 17:23
閱讀 1057·2019-08-28 18:04
閱讀 3674·2019-08-26 13:54
閱讀 617·2019-08-26 11:40
閱讀 2406·2019-08-26 10:15