推薦好文:悟透javascript www.cnblogs.com/zhangshiwen…javascript
編程世界裏只存在兩種基本元素,一個是數據,一個是代碼。編程世界就是在數據和代碼千絲萬縷的糾纏中呈現出無限的生機和活力。html
要理解JavaScript,你得首先放下對象和類的概念,回到數據和代碼的本原。前面說過,編程世界只有數據和代碼兩種基本元素,而這兩種元素又有着糾纏不清的關係。JavaScript就是把數據和代碼都簡化到最原始的程度。java
JavaScript中的數據很簡潔的。簡單數據只有 undefined, null, boolean, number和string這五種,而複雜數據只有一種,即object。這就比如中國古典的樸素惟物思想,把世界最基本的元素歸爲金木水火土,其餘複雜 的物質都是由這五種基本元素組成。es6
JavaScript中的代碼只體現爲一種形式,就是function。編程
任何一個JavaScript的標識、常量、變量和參數都只是unfined, null, bool, number, string, object 和 function類型中的一種,也就typeof返回值代表的類型。除此以外沒有其餘類型了。數組
在咱們學習編程語言時,都會遇到‘類’這個概念。咱們知道學習就是掌握知識從無到有的過程,可是有沒有想過爲何會有‘類’,必定要有它嗎?瀏覽器
var life = {};
for (life.age = 1; life.age <= 3; life.age++)
{
switch(life.age)
{
case 1: life.body = "卵細胞";
life.say = function(){alert(this.age+this.body)};
break;
case 2: life.tail = "尾巴";
life.gill = "腮";
life.body = "蝌蚪";
life.say = function(){alert(this.age+this.body+"-"+this.tail+","+this.gill)};
break;
case 3: delete life.tail;
delete life.gill;
life.legs = "四條腿";
life.lung = "肺";
life.body = "青蛙";
life.say = function(){alert(this.age+this.body+"-"+this.legs+","+this.lung)};
break;
};
life.say();
};
複製代碼
看這段代碼一開始產生了一個生命對象life,通過後面的進化後變成了青蛙,固然繼續進化可能變成其它你想不到的東西。bash
在現實世界裏一開始也是沒有」類「的,都是由分子原子組成,但爲何會出來哺乳類,飛禽類嘞?這就是人的聰明之處了,人爲歸類,爲了方便咱們後續處理事情。app
計算機是人創造出來的,編程也是人的邏輯思想,對某一類事物進行歸類,能爲咱們後續處理事物解決很多麻煩。框架
因此js中沒有類這種概念上的強制,也能解決問題。
也能夠有類(es6中已經推出了class概念),用了class後人在編程時能夠更好的抽象編程裏的世界了。
任何一個函數均可覺得其動態地添加或去除屬性,這些屬性能夠是簡單類型,能夠是對象,也能夠是其餘函數。也就是說,函數具備對象的所有特徵,你徹底能夠把函數當對象來用。
其實,函數就是對象,只不過比通常的對象多了一個括號「()」操做符,這個操做符用來執行函數的邏輯。即,函數自己還能夠被調用,通常對 象卻不能夠被調用,除此以外徹底相同。
自從有了對象,編程世界就被劃分紅兩部分,一個是對象內的世界,一個是對象外的世界。 對象天生具備自私的一面,外面的世界未經容許是不可訪問對象內部的。對象也有大方的一面,它對外提供屬性和方法,也爲他人服務。
既然是對象這種具體的事物了,有沒有想過」對象的意識「。
就在對象將世界劃分爲內外兩部分的同時,對象的「自我」也就隨之產生。「自我意識」是生命的最基本特徵!正是因爲對象這種強大的生命力,才使得編程世界充滿無限的生機和活力。this就是對象的意識
通常編程語言的this就是對象本身,而JavaScript的this卻並不必定!this多是我,也多是你,多是他,反正是我中有你,你中有我。因此this就像意識同樣,不是一層不變的,不一樣的時間,地點,場景你可能會有不一樣的意識。看,js多美。
function WhoAmI() //定義一個函數WhoAmI
{
alert("I'm " + this.name + " of " + typeof(this));
};
WhoAmI(); //此時是this當前這段代碼的全局對象,在瀏覽器中就是window對象,其name屬性爲空字符串。輸出:I'm of object var BillGates = {name: "Bill Gates"}; BillGates.WhoAmI = WhoAmI; //將函數WhoAmI做爲BillGates的方法。 BillGates.WhoAmI(); //此時的this是BillGates。輸出:I'm Bill Gates of object
var SteveJobs = {name: "Steve Jobs"};
SteveJobs.WhoAmI = WhoAmI; //將函數WhoAmI做爲SteveJobs的方法。
SteveJobs.WhoAmI(); //此時的this是SteveJobs。輸出:I'm Steve Jobs of object WhoAmI.call(BillGates); //直接將BillGates做爲this,調用WhoAmI。輸出:I'm Bill Gates of object
WhoAmI.call(SteveJobs); //直接將SteveJobs做爲this,調用WhoAmI。輸出:I'm Steve Jobs of object BillGates.WhoAmI.call(SteveJobs); //將SteveJobs做爲this,卻調用BillGates的WhoAmI方法。輸出:I'm Steve Jobs of object
SteveJobs.WhoAmI.call(BillGates); //將BillGates做爲this,卻調用SteveJobs的WhoAmI方法。輸出:I'm Bill Gates of object WhoAmI.WhoAmI = WhoAmI; //將WhoAmI函數設置爲自身的方法。 WhoAmI.name = "WhoAmI"; WhoAmI.WhoAmI(); //此時的this是WhoAmI函數本身。輸出:I'm WhoAmI of function
({name: "nobody", WhoAmI: WhoAmI}).WhoAmI(); //臨時建立一個匿名對象並設置屬性後調用WhoAmI方法。輸出:I'm nobody of object 複製代碼
從上面的代碼能夠看出,同一個函數能夠從不一樣的角度來調用,this並不必定是函數自己所屬的對象。this只是在任意對象和function元素結合時的一個概念。
看科幻電影常常有將本身的意識轉移到其它事物上的身影。看,js就有這種能力。
// 建立一個沒有任何屬性的對象:
var o = {};
// 建立一個對象並設置屬性及初始值:
var person = {
name: "Angel",
age: 18,
married: false
};
// 建立一個對象並設置屬性和方法:
var speaker = {
text: "Hello World",
say: function(){alert(this.text)}
};
// 建立一個更復雜的對象,嵌套其餘對象和對象數組等:
var company = {
name: "Microsoft",
product: "softwares",
chairman: {
name: "Bill Gates",
age: 53,
Married: true
},
employees: [
{name: "Angel", age: 26, Married: false},
{name: "Hanson", age: 32, Marred: true}
],
readme: function() {document.write(this.name + " product " + this.product);}
};
複製代碼
function MyFunc() {}; //定義一個空函數
var anObj = new MyFunc(); //使用new操做符,藉助MyFun函數,就建立了一個對象
複製代碼
這種寫法如何理解
// 等價於這種形式(簡陋版)
function MyFunc(){};
var anObj = {}; //建立一個對象
MyFunc.call(anObj); //將anObj對象做爲this指針調用MyFunc函數
複製代碼
執行new命令會通過如下幾個步驟
function _new (person, ...rest) {
// 建立一個空對象,這個對象將會是返回的對象實例
var obj = {};
// 將這個空對象的原型指向person的prototype屬性;
obj.__prototype__ = person.prototype;
// 上述兩步能夠合爲一步 : var obj = Object.create(person.prototype)
//將person的this指向空對象,並運行person函數,apply命令綁定this後就會運行person
var res = person.apply(obj, rest);
// 判斷res返回的是否是對象,是的話返res,不是的話返回以前建立的obj,沒有返回值默認返回obj
return (typeof res === 'object' && res != null) ? res: obj;
}
複製代碼
看前面知道咱們能夠根據函數new出不少對象。那爲何咱們不能寫一些生成實例對象的函數,做爲對象的模板,描述實例對象的基本結構呢?很顯然js就是這樣的。
咱們稱能夠生成多個實例對象,描述實例對象的基本結構的函數爲構造函數。若是你願意把構造函數看成「類」的話,她就是「類」,由於她原本就有「類」的那些特徵。
function SayHello() { // 先定義一份SayHello函數代碼
alert("Hello, I'm " + this.name);
};
function Person(name) { // 帶參數的構造函數
this.name = name; // 將參數值賦給給this對象的屬性
this.SayHello = SayHello; //給this對象SayHello方法賦值爲前面那份SayHello代碼。
};
var BillGates = new Person("Bill Gates"); //建立BillGates對象
var SteveJobs = new Person("Steve Jobs"); //建立SteveJobs對象
alert(BillGates.SayHello == SteveJobs.SayHello); //顯示:true
複製代碼
看這段代碼雖然達到了共享了一份方法代碼的目的,可是反映不出SayHello方法與Person類的關係,若是將SayHello方法寫到Person構造函數裏,那每次new出來的對象裏都有各自的SayHello方法,在內存上是種浪費。
Javascript創造者在創造時考慮到了這種狀況。因此JavaScript的全部function類型的對象都有一個prototype屬性。這個prototype屬性自己又是一個object類型的對 象,所以咱們也能夠給這個prototype對象添加任意的屬性和方法。
咱們將prototype屬性稱之爲原型
function Person(name)
{
this.name = name; // 設置對象屬性,每一個對象各自一份屬性數據
};
Person.prototype.SayHello = function() // 給Person函數的prototype添加SayHello方法。
{
alert("Hello, I'm " + this.name);
}
var BillGates = new Person("Bill Gates"); // 建立BillGates對象
var SteveJobs = new Person("Steve Jobs"); // 建立SteveJobs對象
BillGates.SayHello(); // 經過BillGates對象直接調用到SayHello方法
SteveJobs.SayHello(); // 經過SteveJobs對象直接調用到SayHello方法
alert(BillGates.SayHello == SteveJobs.SayHello); // 由於兩個對象是共享prototype的SayHello,因此顯示:true
複製代碼
看這段代碼,咱們將SayHello方法寫到Person的原型上,這樣能體現出SayHello方法與Person類的關係。
function Person(name) {
this.name = name;
};
Person.prototype.company = "Microsoft"; // 原型的屬性
Person.prototype.SayHello = function() { // 原型的方法
alert("Hello, I'm " + this.name + " of " + this.company);
};
var BillGates = new Person("Bill Gates");
BillGates.SayHello(); // 因爲繼承了原型的東西,規規矩矩輸出:Hello, I'm Bill Gates. var SteveJobs = new Person("Steve Jobs"); SteveJobs.company = "Apple"; // 設置本身的company屬性,掩蓋了原型的company屬性 SteveJobs.SayHello = function() { // 實現了本身的SayHello方法,掩蓋了原型的SayHello方法 alert("Hi, " + this.name + " like " + this.company + ", ha ha ha "); }; SteveJobs.SayHello(); // 都是本身覆蓋的屬性和方法,輸出:Hi, Steve Jobs like Apple, ha ha ha BillGates.SayHello(); // SteveJobs的覆蓋沒有影響原型對象,BillGates仍是按老樣子輸出 複製代碼
這與面向對象裏的「多態」很像,能夠實現本身的方法。
看原型的多態咱們想到,能夠隨時給原型對象動態添加新的屬性和方法,從而動態地擴展基類的功能特性,這是其它語言所不能具有的。
function Person(name) { // 基類構造函數
this.name = name;
};
Person.prototype.SayHello = function() { // 給基類構造函數的prototype添加方法
alert("Hello, I'm " + this.name);
};
function Employee(name, salary) { // 子類構造函數
Person.call(this, name); // 調用基類構造函數
this.salary = salary;
};
Employee.prototype = new Person(); // 建一個基類的對象做爲子類原型的原型,這裏就是原型鏈繼承了
Employee.prototype.ShowMeTheMoney = function() { //給子類添構造函數的prototype添加方法
alert(this.name + " $" + this.salary);
};
var BillGates = new Person("Bill Gates"); // 建立基類Person的BillGates對象
var SteveJobs = new Employee("Steve Jobs", 1234); // 建立子類Employee的SteveJobs對象
BillGates.SayHello(); // 經過對象直接調用到prototype的方法
SteveJobs.SayHello(); // 經過子類對象直接調用基類prototype的方法,關注!
SteveJobs.ShowMeTheMoney(); // 經過子類對象直接調用子類prototype的方法
alert(BillGates.SayHello == SteveJobs.SayHello); //顯示:true,代表prototype的方法是共享的
複製代碼
看這段代碼,咱們知道Employee子類中沒有SayHello方法,而Person基類中有SayHello方法,由於Employee原型上建一個基類的對象做爲子類原型的原型,因此用Employee類new出來的對象能夠訪問到父親的父親的SayHello方法。
這種對象的屬性和方法追溯機制是經過所謂的prototype鏈(原型鏈)來實現的。
在原型鏈的最末端,就是Object構造函數prototype屬性指向的那一個原型對象。這個原型對象是全部對象的最老祖先,這個老祖宗實現了諸如 toString等全部對象天生就該具備的方法。其餘內置構造函數,如Function, Boolean, String, Date和RegExp等的prototype都是從這個老祖宗傳承下來的,但他們各自又定義了自身的屬性和方法,從而他們的子孫就表現出各自宗族的那些 特徵。
咱們前面說的都是ES5中的構造函數,在ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,做爲對象的模板,經過class關鍵字,能夠定義類。
基本上,ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。
更多詳細介紹請看阮一峯的ECMAScript 6 入門 es6.ruanyifeng.com/#docs/class
謹以此文膜拜李戰(leadzen)的悟透javascript www.cnblogs.com/zhangshiwen… 但願咱們對javascript世界都全部瞭解