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