Javascript基礎(chǔ)回顧之(三) js面向?qū)ο?/h1>
更新時間:2017年01月31日 23:50:39 作者:Jesse
本篇是你必須知道的Javascript系列第三篇,我們主要來看看Javascript是如何面向?qū)ο蟮木幊痰?需要的朋友可以參考下
本來是要繼續(xù)由淺入深表達(dá)式系列最后一篇的,但是最近團(tuán)隊突然就忙起來了,從來沒有過的忙!不過喜歡表達(dá)式的朋友請放心,已經(jīng)在寫了:) 在工作當(dāng)中發(fā)現(xiàn)大家對Javascript的一些基本原理普遍存在這里或者那里的一知半解,所以決定先花一些時間整理一下這些基礎(chǔ)知識和大家分享。 后面會附上培訓(xùn)用的PPT。剛開始是打算寫一篇的,但是后來寫著寫著就發(fā)現(xiàn)越來越多,所以決定還是寫一個系列吧。本系列所有內(nèi)容都是涉及Javascript基礎(chǔ)的,沒有時髦的玩意兒,但是我相信這些基礎(chǔ)的東西會有助于你理解那些有趣的東西的。
本篇是你必須知道的Javascript系列第三篇,我們主要來看看Javascript是如何面向?qū)ο蟮木幊痰?。主要涉及以下?nèi)容 :
- Javascript中的對象
- 什么是對象
- 遍歷屬性
- 創(chuàng)建對象
- 工廠模式
- 構(gòu)造函數(shù)模式
- 詳解this
- 在函數(shù)中
- 在對象方法中
- 在構(gòu)造函數(shù)中
- 在call和apply中
- 在bind中
- 在dom元素事件處理函數(shù)中
- 詳解prototype
- 什么是原型
- 什么是原型鏈
- 利用原型鏈實現(xiàn)繼承
- 原型鏈中的問題
Javascript中的對象
什么是對象
我們可以把Javascript中對象理解為一組無序的鍵值對,就好像C#中的Dictionary<string,Object>一樣。Key是屬性的名稱,而value可以為以下3種類型:
- 基本值(string, number, boolean, null, undefined)
- 對象
- 函數(shù)
var o = new Object();
o["name"] = "jesse"; //基本值作為對象屬性
o["location"] = { //對象作為對象屬性
"city": "Shanghai",
"district":"minhang"
};
// 函數(shù) 作為對象屬性
o["sayHello"] = function () {
alert("Hello, I am "+ this.name + " from " + this.location.city);
}
o.sayHello();
遍歷屬性
在C#中我們是可以用foreach對Dictionary<string,Object>進(jìn)行遍歷的,如果說對象在Javascript中是一組鍵值對的話,那我們?nèi)绾芜M(jìn)行遍歷呢?
for (var p in o) {
alert('name:'+ p +
' type:' + typeof o[p]
);
}
// name:name type:string
// name:location type:object
// name:sayHello type:function
上面的這種遍歷方式會把原型中的屬性也包括進(jìn)來,關(guān)于什么是原型,以及如何區(qū)分原型和實例中的屬性我們下面會講到。
創(chuàng)建對象
其實在上面我們已經(jīng)創(chuàng)建了一個對象,并且使用了以下兩種創(chuàng)建對象的方式。
- 利用new創(chuàng)建一個Object的實例。
- 字面量
我們上面的o是用第一種方式創(chuàng)建的,而o中的location屬性則是用字面量的方式創(chuàng)建的。而第一種方式其實也有一種名字叫做構(gòu)造函數(shù)模式,因為Object實際上是一個構(gòu)造函數(shù),為我們產(chǎn)生了一個Object的實例。如果對于構(gòu)造函數(shù)這一塊還有不清楚的話,趕緊去看我的第一篇 類型基礎(chǔ)Object與object吧。
除了以上兩種方式以外,我們一些創(chuàng)建對象的方式,我們也來一起看一下:
工廠模式
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson('Jesse', 29, 'Software Engineer');
var person2 = createPerson('Carol', 27, 'Designer');
這種模式創(chuàng)建的對象有一個問題,那就是它在函數(shù)的內(nèi)部為我創(chuàng)建了一個Object的實例,這個實例跟我們的構(gòu)造函數(shù)createPerson是沒有任何關(guān)系的。

因為我在內(nèi)部用new Object()來創(chuàng)建了這個對象,所以它是Object的實例。所以如果我們想知道它是具體哪個function的實例,那就不可能了。
構(gòu)造函數(shù)模式
工廠模式?jīng)]有解決對象識別的問題,但是我們可以想一下,Object()實際上也是一個函數(shù),只不過當(dāng)我在它前面加上一個new的時候,它就變成了一個構(gòu)造函數(shù)為我們產(chǎn)生一個Object的實例。那么我同樣也可以在其它函數(shù)前面加上new這樣就可以產(chǎn)生這個函數(shù)的實例了,這就是所謂的構(gòu)造函數(shù)模式。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var p1 = new Person('Jesse', 18, 'coder');
alert(p1 instanceof Person); // true
詳解this
this在Javascript中也可以算是一個很神奇對象,沒錯this是一個對象。我們在上一篇作用域和作用域鏈中講到了變量對象,變量對象決定了在當(dāng)前的執(zhí)行環(huán)境中有哪些屬性和函數(shù)是可以被訪問到的,從某種程度上來說我們就可以把this看作是這個變量對象。我們之前提到了最大的執(zhí)行環(huán)境是全局執(zhí)行環(huán)境,而window就是全局執(zhí)行環(huán)境中的變量對象,那么我們在全局環(huán)境中this===window是會返回true的。

除了全局執(zhí)行環(huán)境以外,我們還提到了另外一種執(zhí)行環(huán)境,也就是函數(shù)。每一個函數(shù)都有一個this對象,但有時候他們所代表的值是不一樣的,主要是這個函數(shù)的調(diào)用者來決定的。我們來看一下以下幾種場景:
函數(shù)
function f1(){
return this;
}
f1() === window; // global object
因為當(dāng)前的函數(shù)在全局函數(shù)中運(yùn)行,所以函數(shù)中的this對象指向了全局變量對象,也就是window。這種方式在嚴(yán)格模式下會返回undefined。
對象方法
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
在對象方法中,this對象指向了當(dāng)前這個實例對象。注意: 不管這個函數(shù)在哪里什么時候或者怎么樣定義,只要它是一個對象實例的方法,那么它的this都是指向這個對象實例的。
var o = { prop: 37 };
var prop = 15;
function independent() {
return this.prop;
}
o.f = independent;
console.log(independent()); // logs 15
console.log(o.f()); // logs 37
區(qū)別:上面的函數(shù)independent如果直接執(zhí)行,this是指向全局執(zhí)行環(huán)境,那么this.prop是指向我們的全局變量prop的。但是如果將independent設(shè)為對象o的一個屬性,那么independent中的this就指向了這個實例,同理this.prop就變成了對象o的prop屬性。
構(gòu)造函數(shù)
我們上面講到了用構(gòu)造函數(shù)創(chuàng)建對象,其實是利用了this的這種特性。在構(gòu)造函數(shù)中,this對象是指向這個構(gòu)造函數(shù)實例化出來的對象。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
};
}
var p1 = new Person('Jesse', 18, 'coder');
var p2 = new Person('Carol',16,'designer');
當(dāng)我們實例化Person得到p1的時候,this指向p1。而當(dāng)我們實例化Person得到p2的時候,this是指向p2的。
利用call和apply
當(dāng)我們用call和apply去調(diào)用某一個函數(shù)的時候,這個函數(shù)中的this對象會被綁定到我們指定的對象上。而call和apply的主要區(qū)別就是apply要求傳入一個數(shù)組作為參數(shù)列表。
function add(c, d) {
return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
// 第一個參數(shù)會被綁定成函數(shù)add的this對象
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// 第二個參數(shù)是數(shù)組作為arguments傳入方法add
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
在bind方法中
bind方法是 存在于function的原型中的 Function.prototype.bind,也就是說所有的function都會有這個方法。但我們調(diào)用某一個方法的bind的時候,會產(chǎn)生一個和原來那個方法一樣的新方法,只不過this是指向我們傳得bind的第一個參數(shù)。
function f() {
return this.a;
}
var g = f.bind({ a: "azerty" });
console.log(g()); // azerty
var o = { a: 37, f: f, g: g };
console.log(o.f(), o.g()); // 37, azerty
在dom元素事件處理器中
在事件處理函數(shù)中,我們的this是指向觸發(fā)這個事件的dom元素的。
HTML代碼
<html>
<body>
<div id="mydiv" style="width:400px; height:400px; border:1px solid red;"></div>
<script type="text/javascript" src="essence.js"></script>
</body>
</html>
JavaScript代碼
function click(e) {
alert(this.nodeName);
}
var myDiv = document.getElementById("mydiv");
myDiv.addEventListener('click', click, false);
當(dāng)我們點擊頁面那個div的時候,毫無疑問,它是會顯示DIV的。

詳解prototype
prototype即原型,也是Javascrip中一個比較重要的概念。在說原型之前呢,我們需要回顧一下之前的構(gòu)造函數(shù)模式。在我們用構(gòu)造函數(shù)去創(chuàng)建對象的時候主要是利用了this的特性。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
};
}
var p1 = new Person('Jesse', 18, 'coder');
var p2 = new Person('Carol', 17, 'designer');
我們上面還講到了當(dāng)用Person實例化p1的時候Person中的this是指向p1的,當(dāng)實例化p2的時候呢,this是指向p2的。那也就是說,p1和p2中的sayName雖然起到了同樣的作用,但是實際上他們并非是一個函數(shù)。

也就是說他們內(nèi)存堆中是存在多份拷貝的,而不是在棧中引用地址的拷貝。先不說這符不符合面向?qū)ο蟮乃枷耄辽龠@對于內(nèi)存來說也是一種浪費(fèi)。而解決辦法就是我們要討論的原型。
什么是原型
在Javascript中的每一個函數(shù),都會有一個原型對象,這個原型對象和我們普通的對象沒有區(qū)別。只不過默認(rèn)會有一個constructor屬性指向這個函數(shù)。 同時,所有這個函數(shù)的實例都會有一個引用指向這個原型對象。如果不太清楚,那就看看下面這張圖吧:

以上就是構(gòu)造函數(shù),構(gòu)造函數(shù)原型,以及實例之間的關(guān)系。以我們的Person構(gòu)造函數(shù)為例,所有Person的實例(p1,p2)都舒服一個prototype屬性指向了Person構(gòu)造函數(shù)prototype對象。如此一來,我們就可以把方法寫在原型上,那么我們所有的實例就會訪問同一個方法了。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
Person.prototype.sayName = function () {
alert(this.name);
}
}
var p1 = new Person('Jesse', 18, 'coder');
var p2 = new Person('Carol', 17, 'designer');
alert(p1.sayName == p2.sayName); // true
什么是原型鏈
大家還記得作用域鏈么?如果不記得,請自覺到第二篇中去復(fù)習(xí)(作用域和作用域鏈)。簡單的來說,我們在一個執(zhí)行環(huán)境中訪問某個變量的時候如果當(dāng)前這個執(zhí)行環(huán)境中不存在這個變量,那么會到這個執(zhí)行環(huán)境的包含環(huán)境也就是它的外層去找這個變量,外層還找不到那就再外一層,一直找到全局執(zhí)行環(huán)境為止,這就是作用域鏈。而原型鏈有點類型,只不過場景換到了我們的對象實例中,如果我在一個實例中找某一個屬性,這個實例中沒有,那就會到它的原型中去找。記住,我們上面說了,原型也是一個對象,它也有自己的原型對象,所以就行成了一個鏈,實例自己的原型中找不到,那就到原型的原型對象中去找,一直向上延伸到Object的原型對象,默認(rèn)我們創(chuàng)建的函數(shù)的原型對象它自己的原型對象是指向Object的原型對象的,所以這就是為什么我們可以在我們的自定義構(gòu)造函數(shù)的實例上調(diào)用Object的方法(toString, valueOf)。

利用原型實現(xiàn)繼承
其實我們上面已經(jīng)講了繼承在Javascript中的實現(xiàn),主要就是依靠原型鏈來實現(xiàn)的。所有的實例是繼承自object就是因為在默認(rèn)情況下,我們所有創(chuàng)建函數(shù)的原型對象的原型都指向了object對象。同理,我們可以定義自己的繼承關(guān)系。
function Person(name, age, job) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
alert(this.name);
}
function Coder(language){
this.language = language;
}
Coder.prototype = new Person(); //將 Coder 的原型指向一個Person實例實現(xiàn)繼Person
Coder.prototype.code = function () {
alert('I am a '+ this.language +' developer, Hello World!');
}
function Designer() {
}
Designer.prototype = new Person(); //將 Desiger 的原型指向一個Person實例實現(xiàn)繼Person
Designer.prototype.design = function () {
alert('其實我只是一個摳圖工而已。。。。');
}
var coder = new Coder('C#');
coder.name = 'Jesse';
coder.sayName(); //Jesse
coder.code(); // I am a C# developer, Hello World!
var designer = new Designer();
designer.name = 'Carol';
designer.sayName(); // Carol
designer.design(); // 其實我只是一個摳圖工而已。。。。
原型鏈中的問題
由于原型對象是以引用的方式保存的,所以我們在賦值的時候要特別注意,一不小心就有可能把之前賦的值給賦蓋了。比如上面的代碼中,我們先寫原型方法,再實現(xiàn)繼承,那我們的原型方法就沒有了。
function Coder(language){
this.language = language;
}
Coder.prototype.code = function () {
alert('I am a '+ this.language +' developer, Hello World!');
}
Coder.prototype = new Person(); //這里會覆蓋上面所有的原型屬性和方法
var coder = new Coder('C#');
coder.name = 'Jesse';
coder.sayName();
coder.code(); // 這里會報錯,找不到code方法。
這樣三篇文章都完成了
您可能感興趣的文章:- Javascript中數(shù)組去重與拍平的方法示例
- 理解javascript中的Function.prototype.bind的方法
- JavaScript數(shù)組復(fù)制詳解
- Javascript基礎(chǔ)回顧之(一) 類型
- JavaScript基礎(chǔ)之AJAX簡單的小demo
- JavaScript Date 知識淺析
- JavaScript實現(xiàn)時鐘滴答聲效果
- Javascript中 帶名 匿名 箭頭函數(shù)的重要區(qū)別(推薦)
- javascript判斷回文數(shù)詳解及實現(xiàn)代碼
- 淺談javascript中的 “ && ” 和 “ || ”
- Javascript中的 “&” 和 “|” 詳解
相關(guān)文章
-
a標(biāo)簽的href與onclick事件的區(qū)別詳解
對于a標(biāo)簽的href與onclick事件,大家都經(jīng)常見到,也經(jīng)常使用,可它們有什么區(qū)別呢?下面就讓小編來給大家詳細(xì)介紹下,感興趣的朋友可以學(xué)習(xí)下,不用謝了,哈哈 2014-11-11
-
淺談JavaScript數(shù)據(jù)類型及轉(zhuǎn)換
本文向大家簡單介紹了javascript的數(shù)據(jù)類型以及他們直接的轉(zhuǎn)換方法,雖然沒有太多示例,但是也是個人的一些經(jīng)驗總結(jié),這里推薦給大家。 2015-02-02
-
script標(biāo)簽的 charset 屬性使用說明
如果外部文件中的字符編碼與主文件中的編碼方式不同,就要用到 charset 屬性。
2010-12-12
最新評論
本來是要繼續(xù)由淺入深表達(dá)式系列最后一篇的,但是最近團(tuán)隊突然就忙起來了,從來沒有過的忙!不過喜歡表達(dá)式的朋友請放心,已經(jīng)在寫了:) 在工作當(dāng)中發(fā)現(xiàn)大家對Javascript的一些基本原理普遍存在這里或者那里的一知半解,所以決定先花一些時間整理一下這些基礎(chǔ)知識和大家分享。 后面會附上培訓(xùn)用的PPT。剛開始是打算寫一篇的,但是后來寫著寫著就發(fā)現(xiàn)越來越多,所以決定還是寫一個系列吧。本系列所有內(nèi)容都是涉及Javascript基礎(chǔ)的,沒有時髦的玩意兒,但是我相信這些基礎(chǔ)的東西會有助于你理解那些有趣的東西的。
本篇是你必須知道的Javascript系列第三篇,我們主要來看看Javascript是如何面向?qū)ο蟮木幊痰?。主要涉及以下?nèi)容 :
- Javascript中的對象
- 什么是對象
- 遍歷屬性
- 創(chuàng)建對象
- 工廠模式
- 構(gòu)造函數(shù)模式
- 詳解this
- 在函數(shù)中
- 在對象方法中
- 在構(gòu)造函數(shù)中
- 在call和apply中
- 在bind中
- 在dom元素事件處理函數(shù)中
- 詳解prototype
- 什么是原型
- 什么是原型鏈
- 利用原型鏈實現(xiàn)繼承
- 原型鏈中的問題
Javascript中的對象
什么是對象
我們可以把Javascript中對象理解為一組無序的鍵值對,就好像C#中的Dictionary<string,Object>一樣。Key是屬性的名稱,而value可以為以下3種類型:
- 基本值(string, number, boolean, null, undefined)
- 對象
- 函數(shù)
var o = new Object(); o["name"] = "jesse"; //基本值作為對象屬性 o["location"] = { //對象作為對象屬性 "city": "Shanghai", "district":"minhang" }; // 函數(shù) 作為對象屬性 o["sayHello"] = function () { alert("Hello, I am "+ this.name + " from " + this.location.city); } o.sayHello();
遍歷屬性
在C#中我們是可以用foreach對Dictionary<string,Object>進(jìn)行遍歷的,如果說對象在Javascript中是一組鍵值對的話,那我們?nèi)绾芜M(jìn)行遍歷呢?
for (var p in o) { alert('name:'+ p + ' type:' + typeof o[p] ); } // name:name type:string // name:location type:object // name:sayHello type:function
上面的這種遍歷方式會把原型中的屬性也包括進(jìn)來,關(guān)于什么是原型,以及如何區(qū)分原型和實例中的屬性我們下面會講到。
創(chuàng)建對象
其實在上面我們已經(jīng)創(chuàng)建了一個對象,并且使用了以下兩種創(chuàng)建對象的方式。
- 利用new創(chuàng)建一個Object的實例。
- 字面量
我們上面的o是用第一種方式創(chuàng)建的,而o中的location屬性則是用字面量的方式創(chuàng)建的。而第一種方式其實也有一種名字叫做構(gòu)造函數(shù)模式,因為Object實際上是一個構(gòu)造函數(shù),為我們產(chǎn)生了一個Object的實例。如果對于構(gòu)造函數(shù)這一塊還有不清楚的話,趕緊去看我的第一篇 類型基礎(chǔ)Object與object吧。
除了以上兩種方式以外,我們一些創(chuàng)建對象的方式,我們也來一起看一下:
工廠模式
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson('Jesse', 29, 'Software Engineer'); var person2 = createPerson('Carol', 27, 'Designer');
這種模式創(chuàng)建的對象有一個問題,那就是它在函數(shù)的內(nèi)部為我創(chuàng)建了一個Object的實例,這個實例跟我們的構(gòu)造函數(shù)createPerson是沒有任何關(guān)系的。
因為我在內(nèi)部用new Object()來創(chuàng)建了這個對象,所以它是Object的實例。所以如果我們想知道它是具體哪個function的實例,那就不可能了。
構(gòu)造函數(shù)模式
工廠模式?jīng)]有解決對象識別的問題,但是我們可以想一下,Object()實際上也是一個函數(shù),只不過當(dāng)我在它前面加上一個new的時候,它就變成了一個構(gòu)造函數(shù)為我們產(chǎn)生一個Object的實例。那么我同樣也可以在其它函數(shù)前面加上new這樣就可以產(chǎn)生這個函數(shù)的實例了,這就是所謂的構(gòu)造函數(shù)模式。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var p1 = new Person('Jesse', 18, 'coder'); alert(p1 instanceof Person); // true
詳解this
this在Javascript中也可以算是一個很神奇對象,沒錯this是一個對象。我們在上一篇作用域和作用域鏈中講到了變量對象,變量對象決定了在當(dāng)前的執(zhí)行環(huán)境中有哪些屬性和函數(shù)是可以被訪問到的,從某種程度上來說我們就可以把this看作是這個變量對象。我們之前提到了最大的執(zhí)行環(huán)境是全局執(zhí)行環(huán)境,而window就是全局執(zhí)行環(huán)境中的變量對象,那么我們在全局環(huán)境中this===window是會返回true的。
除了全局執(zhí)行環(huán)境以外,我們還提到了另外一種執(zhí)行環(huán)境,也就是函數(shù)。每一個函數(shù)都有一個this對象,但有時候他們所代表的值是不一樣的,主要是這個函數(shù)的調(diào)用者來決定的。我們來看一下以下幾種場景:
函數(shù)
function f1(){ return this; } f1() === window; // global object
因為當(dāng)前的函數(shù)在全局函數(shù)中運(yùn)行,所以函數(shù)中的this對象指向了全局變量對象,也就是window。這種方式在嚴(yán)格模式下會返回undefined。
對象方法
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
在對象方法中,this對象指向了當(dāng)前這個實例對象。注意: 不管這個函數(shù)在哪里什么時候或者怎么樣定義,只要它是一個對象實例的方法,那么它的this都是指向這個對象實例的。
var o = { prop: 37 }; var prop = 15; function independent() { return this.prop; } o.f = independent; console.log(independent()); // logs 15 console.log(o.f()); // logs 37
區(qū)別:上面的函數(shù)independent如果直接執(zhí)行,this是指向全局執(zhí)行環(huán)境,那么this.prop是指向我們的全局變量prop的。但是如果將independent設(shè)為對象o的一個屬性,那么independent中的this就指向了這個實例,同理this.prop就變成了對象o的prop屬性。
構(gòu)造函數(shù)
我們上面講到了用構(gòu)造函數(shù)創(chuàng)建對象,其實是利用了this的這種特性。在構(gòu)造函數(shù)中,this對象是指向這個構(gòu)造函數(shù)實例化出來的對象。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol',16,'designer');
當(dāng)我們實例化Person得到p1的時候,this指向p1。而當(dāng)我們實例化Person得到p2的時候,this是指向p2的。
利用call和apply
當(dāng)我們用call和apply去調(diào)用某一個函數(shù)的時候,這個函數(shù)中的this對象會被綁定到我們指定的對象上。而call和apply的主要區(qū)別就是apply要求傳入一個數(shù)組作為參數(shù)列表。
function add(c, d) { return this.a + this.b + c + d; } var o = { a: 1, b: 3 }; // 第一個參數(shù)會被綁定成函數(shù)add的this對象 add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // 第二個參數(shù)是數(shù)組作為arguments傳入方法add add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
在bind方法中
bind方法是 存在于function的原型中的 Function.prototype.bind,也就是說所有的function都會有這個方法。但我們調(diào)用某一個方法的bind的時候,會產(chǎn)生一個和原來那個方法一樣的新方法,只不過this是指向我們傳得bind的第一個參數(shù)。
function f() { return this.a; } var g = f.bind({ a: "azerty" }); console.log(g()); // azerty var o = { a: 37, f: f, g: g }; console.log(o.f(), o.g()); // 37, azerty
在dom元素事件處理器中
在事件處理函數(shù)中,我們的this是指向觸發(fā)這個事件的dom元素的。
HTML代碼
<html> <body> <div id="mydiv" style="width:400px; height:400px; border:1px solid red;"></div> <script type="text/javascript" src="essence.js"></script> </body> </html>
JavaScript代碼
function click(e) { alert(this.nodeName); } var myDiv = document.getElementById("mydiv"); myDiv.addEventListener('click', click, false);
當(dāng)我們點擊頁面那個div的時候,毫無疑問,它是會顯示DIV的。
詳解prototype
prototype即原型,也是Javascrip中一個比較重要的概念。在說原型之前呢,我們需要回顧一下之前的構(gòu)造函數(shù)模式。在我們用構(gòu)造函數(shù)去創(chuàng)建對象的時候主要是利用了this的特性。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol', 17, 'designer');
我們上面還講到了當(dāng)用Person實例化p1的時候Person中的this是指向p1的,當(dāng)實例化p2的時候呢,this是指向p2的。那也就是說,p1和p2中的sayName雖然起到了同樣的作用,但是實際上他們并非是一個函數(shù)。
也就是說他們內(nèi)存堆中是存在多份拷貝的,而不是在棧中引用地址的拷貝。先不說這符不符合面向?qū)ο蟮乃枷耄辽龠@對于內(nèi)存來說也是一種浪費(fèi)。而解決辦法就是我們要討論的原型。
什么是原型
在Javascript中的每一個函數(shù),都會有一個原型對象,這個原型對象和我們普通的對象沒有區(qū)別。只不過默認(rèn)會有一個constructor屬性指向這個函數(shù)。 同時,所有這個函數(shù)的實例都會有一個引用指向這個原型對象。如果不太清楚,那就看看下面這張圖吧:
以上就是構(gòu)造函數(shù),構(gòu)造函數(shù)原型,以及實例之間的關(guān)系。以我們的Person構(gòu)造函數(shù)為例,所有Person的實例(p1,p2)都舒服一個prototype屬性指向了Person構(gòu)造函數(shù)prototype對象。如此一來,我們就可以把方法寫在原型上,那么我們所有的實例就會訪問同一個方法了。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; Person.prototype.sayName = function () { alert(this.name); } } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol', 17, 'designer'); alert(p1.sayName == p2.sayName); // true
什么是原型鏈
大家還記得作用域鏈么?如果不記得,請自覺到第二篇中去復(fù)習(xí)(作用域和作用域鏈)。簡單的來說,我們在一個執(zhí)行環(huán)境中訪問某個變量的時候如果當(dāng)前這個執(zhí)行環(huán)境中不存在這個變量,那么會到這個執(zhí)行環(huán)境的包含環(huán)境也就是它的外層去找這個變量,外層還找不到那就再外一層,一直找到全局執(zhí)行環(huán)境為止,這就是作用域鏈。而原型鏈有點類型,只不過場景換到了我們的對象實例中,如果我在一個實例中找某一個屬性,這個實例中沒有,那就會到它的原型中去找。記住,我們上面說了,原型也是一個對象,它也有自己的原型對象,所以就行成了一個鏈,實例自己的原型中找不到,那就到原型的原型對象中去找,一直向上延伸到Object的原型對象,默認(rèn)我們創(chuàng)建的函數(shù)的原型對象它自己的原型對象是指向Object的原型對象的,所以這就是為什么我們可以在我們的自定義構(gòu)造函數(shù)的實例上調(diào)用Object的方法(toString, valueOf)。
利用原型實現(xiàn)繼承
其實我們上面已經(jīng)講了繼承在Javascript中的實現(xiàn),主要就是依靠原型鏈來實現(xiàn)的。所有的實例是繼承自object就是因為在默認(rèn)情況下,我們所有創(chuàng)建函數(shù)的原型對象的原型都指向了object對象。同理,我們可以定義自己的繼承關(guān)系。
function Person(name, age, job) { this.name = name; this.age = age; } Person.prototype.sayName = function () { alert(this.name); } function Coder(language){ this.language = language; } Coder.prototype = new Person(); //將 Coder 的原型指向一個Person實例實現(xiàn)繼Person Coder.prototype.code = function () { alert('I am a '+ this.language +' developer, Hello World!'); } function Designer() { } Designer.prototype = new Person(); //將 Desiger 的原型指向一個Person實例實現(xiàn)繼Person Designer.prototype.design = function () { alert('其實我只是一個摳圖工而已。。。。'); } var coder = new Coder('C#'); coder.name = 'Jesse'; coder.sayName(); //Jesse coder.code(); // I am a C# developer, Hello World! var designer = new Designer(); designer.name = 'Carol'; designer.sayName(); // Carol designer.design(); // 其實我只是一個摳圖工而已。。。。
原型鏈中的問題
由于原型對象是以引用的方式保存的,所以我們在賦值的時候要特別注意,一不小心就有可能把之前賦的值給賦蓋了。比如上面的代碼中,我們先寫原型方法,再實現(xiàn)繼承,那我們的原型方法就沒有了。
function Coder(language){ this.language = language; } Coder.prototype.code = function () { alert('I am a '+ this.language +' developer, Hello World!'); } Coder.prototype = new Person(); //這里會覆蓋上面所有的原型屬性和方法 var coder = new Coder('C#'); coder.name = 'Jesse'; coder.sayName(); coder.code(); // 這里會報錯,找不到code方法。
這樣三篇文章都完成了
- Javascript中數(shù)組去重與拍平的方法示例
- 理解javascript中的Function.prototype.bind的方法
- JavaScript數(shù)組復(fù)制詳解
- Javascript基礎(chǔ)回顧之(一) 類型
- JavaScript基礎(chǔ)之AJAX簡單的小demo
- JavaScript Date 知識淺析
- JavaScript實現(xiàn)時鐘滴答聲效果
- Javascript中 帶名 匿名 箭頭函數(shù)的重要區(qū)別(推薦)
- javascript判斷回文數(shù)詳解及實現(xiàn)代碼
- 淺談javascript中的 “ && ” 和 “ || ”
- Javascript中的 “&” 和 “|” 詳解
相關(guān)文章
a標(biāo)簽的href與onclick事件的區(qū)別詳解
對于a標(biāo)簽的href與onclick事件,大家都經(jīng)常見到,也經(jīng)常使用,可它們有什么區(qū)別呢?下面就讓小編來給大家詳細(xì)介紹下,感興趣的朋友可以學(xué)習(xí)下,不用謝了,哈哈2014-11-11淺談JavaScript數(shù)據(jù)類型及轉(zhuǎn)換
本文向大家簡單介紹了javascript的數(shù)據(jù)類型以及他們直接的轉(zhuǎn)換方法,雖然沒有太多示例,但是也是個人的一些經(jīng)驗總結(jié),這里推薦給大家。2015-02-02script標(biāo)簽的 charset 屬性使用說明
如果外部文件中的字符編碼與主文件中的編碼方式不同,就要用到 charset 屬性。2010-12-12