JS — 物件的基本操作

IT丶Danccer發表於2019-01-16

JS物件導向系列教程 — 物件的基本操作

物件導向概述

物件導向(Object Oriented)簡稱OO,它是一種程式設計思維,用於指導我們如何應對各種複雜的開發場景。

這裡說的物件(Object),意思就是事物,在物件導向的思維中,它將一切都看作是物件,並以物件為切入點去思考問題。

使用物件導向的思維去開發程式,我們首先思考的是這個系統中有哪些物件(事物),它們各自有什麼屬性(特徵),又有什麼方法(行為),這樣一來,就可以把系統分解為一個一個的物件,然後對每個物件進行單獨研究,以降低系統的整體複雜度。

學習物件導向,我們不僅要學習它的技術知識,更重要的是學習這種思考程式的方式,這種思維方式跟我們過去開發程式有很大的區別。在過去,我們完成一個功能的時候,往往思考的是完成該功能的步驟,先做什麼,再做什麼,如果怎麼樣,就怎麼怎麼樣……,這種過去的思維模式,我們稱之為程式導向。

程式導向並不是錯誤的,只是它面對複雜的問題時顯得有些捉襟見肘。

程式導向和麵向物件最大的區別在於,程式導向思考的重心和切入點是事情,物件導向思考的重心和切入點是事物。

在物件導向的世界中,它將一切都看作是物件。這裡的物件,包羅永珍,它可以是現實世界中看得見摸得著的實體:人、小貓、小狗、飛機、課桌、鉛筆、電視等等等等,都可以看作是物件。它也可以是某些領域中的抽象體:訂單、價格計算器、dom元素、日期等等等等,它們也被看作是物件。

因此,在OO的世界裡,有一句至理名言——一切皆為物件。

javascript語言是支援物件導向開發的語言,本系列教程中,將一步步講解如何使用它進行物件導向開發,同時會進一步探尋它背後深層次的原理。

注意:本系列教程的重心,將放在物件導向開發的技術層面(即物件導向程式設計,Object Oriented Programming,OOP)。

對於物件導向思想層面(即物件導向設計,Object Oriented Design,OOD),本教程不做過多講解。因為物件導向思想的建立並非一朝一夕,它不是靠讀文章讀出來的,雖然好的文章可以對你有啟發作用,但更多的,這種思想是靠常年累月的程式碼量練出來的,是靠不斷的分析、重構想出來的。

本教程希望的是,你能夠通過學習物件導向開發,激發你的思考,引起你對另一種開發方式的關注,逐漸建立起物件導向的思維。

建立物件

物件即事物,一切皆物件。

其實你在之前的開發中,應該不止一次的接觸過物件,有些是瀏覽器自帶的物件,比如:dom物件、window物件、document物件等;有些則是你根據功能需要自己建立的,比如:使用者物件、學生物件等。

在JS中,建立一個物件非常的簡單,通過一對{}即可建立一個物件,下面的程式碼你應該並不會陌生:

const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”};const user2 = {    loginid: “xiaobaitu”,    loginpwd: “654321”,    name: “小白兔”};

上面的程式碼應該這樣理解,建立了兩個物件,分別把它們賦值給了變數user1和user2。

為什麼要強調這一點呢?因為真正的物件不是user1和user2,而是那一對大括號及其裡面的內容{…},在後面講解物件賦值原理時還會詳細講解這一點。

上面這種建立物件的寫法,即使用兩個大括號來表示物件{…},叫做物件的字面量表示法。

使用字面量表示法來建立物件的好處是簡單易懂,但它不好的地方在於無法應用物件導向開發中的一些高階特性。

下面的程式碼,展現了另一種建立物件的方式,即使用關鍵字new來建立物件:

const user1 = new Object(); //相當於:const user1 = {};user1.loginid = “bangbangji”; user1.loginpwd = “123456”; user1.name = “棒棒雞”;const user2 = new Object(); //相當於:const user2 = {};user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”;

這樣的程式碼雖然看似繁瑣了很多,但實際上它才是物件原本的建立方式。上面的程式碼和第一種方式效果完全一致。

實際上,在JS語言中,所有的物件都是使用new關鍵字建立的。即便你使用的是字面量表示法,JS引擎最終也會將其轉換為這種方式。因此,物件的字面量表示法僅僅是JS語言的一個語法糖,要建立物件,最終都是要使用new關鍵字的。因此,在後文中,每一個示例,我都會用兩種方式來實現物件的建立。

語法糖(Syntactic sugar),也譯為糖衣語法,是指語言層面的一些便捷語法,它僅僅是為開發者提供的一種高效的編碼方式,並不改變底層的實現原理。

屬性和方法

我們過去使用物件,往往是為了將多個變數整合到一個變數上,其實,這種思想就是物件導向的一種核心思想——封裝。

物件導向有三大顯著特徵:封裝、繼承、多型

那些整合到一個物件中的變數,我們稱之為物件的屬性。屬性往往是一個名詞,表示物件所擁有的特徵。

const user1 = {    loginid: “bangbangji”, //loginid為物件的屬性,表示使用者賬號    loginpwd: “123456”, //loginpwd為物件的屬性,表示使用者密碼    name: “棒棒雞” //name為物件的屬性,表示使用者姓名};const user2 = new Object(); user2.loginid = “xiaobaitu”;//loginid為物件的屬性,表示使用者賬號user2.loginpwd = “654321”;//loginpwd為物件的屬性,表示使用者密碼user2.name = “小白兔”;//name為物件的屬性,表示使用者姓名

物件的屬性還可以是一個物件或者陣列或者其他任意型別,用於表示多層次的結構:

const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”,    //屬性address也是一個物件    address: {        province: “四川省”,        city: “成都市”    } };//輸出:四川省-成都市console.log(`${user1.address.province}-${user1.address.city}`); const user2 = new Object(); user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”; user2.address = new Object(); //屬性address也是一個物件user2.address.province = “四川省”; user2.address.city = “南充市”;//輸出:四川省-南充市console.log(`${user2.address.province}-${user2.address.city}`);

物件除了有特徵,還會有一些行為,這些行為我們稱之為物件的方法。方法往往是一個動詞,表示物件所擁有的行為,在程式碼層面,方法表現為函式。

const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”,    //sayHello(打招呼)屬於物件方法,它是一個函式,表示物件的行為    sayHello: function () {        console.log(“你好!”);    } }; user1.sayHello(); //呼叫物件的方法,輸出:你好!const user2 = new Object(); user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”;//sayHello(打招呼)屬於物件方法,它是一個函式,表示物件的行為user2.sayHello = function () {    console.log(“你好!”); }; user2.sayHello(); //呼叫物件的方法,輸出:你好!

可以看出,方法和屬性在書寫上並沒有本質的差別,只不過屬性是一個普通的值,方法是一個函式而已。

this關鍵字

我們現在考慮一下,在之前的示例中出現的sayHello方法,假設我們現在要對其功能做一點改造,我們希望該方法要輸出物件的姓名和賬號,於是,得到了下面的程式碼:

const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”,    //sayHello(打招呼)屬於物件方法,它是一個函式,表示物件的行為    sayHello: function () {        console.log(“你好!我是棒棒雞,我的賬號是bangbangji”);    } }; user1.sayHello(); //呼叫物件的方法,輸出:你好!我是棒棒雞,我的賬號是bangbangjiconst user2 = new Object(); user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”;//sayHello(打招呼)屬於物件方法,它是一個函式,表示物件的行為user2.sayHello = function () {    console.log(“你好!我是小白兔,我的賬號是xiaobaitu”); }; user2.sayHello(); //呼叫物件的方法,輸出:你好!我是小白兔,我的賬號是xiaobaitu

這種方式結果固然正確,但是考慮一下100個使用者的場景……不敢想象是不?這還僅僅是一個打招呼的方法,如果方法裡面程式碼多一點,複雜一點,會更加棘手。

由於同一類物件(比如這裡的使用者物件)都具有共同的行為——打招呼,我們何不把這個方法函式提出來呢?就像下面這樣:

//提取出來的共同方法function sayHello(){    //怎麼辦?輸出哪個物件的姓名和賬號?    console.log(“你好!我是???,我的賬號是???”); }const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”,    sayHello }; user1.sayHello(); //輸出:你好!我是???,我的賬號是???const user2 = new Object(); user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”; user2.sayHello = sayHello; user2.sayHello(); //輸出:你好!我是???,我的賬號是???

這樣雖然解決了程式碼重複的問題,但新的問題又來了,雖然我們知道每個使用者物件都具有打招呼的方法,但是每個物件的屬性值不一樣,打招呼的時候,我到底輸出哪個物件的屬性值呢?

於是,JS給我們提供了一個關鍵字this,它指代的是當前物件,即呼叫方法的物件,於是我們就可以解決這個問題了:

//提取出來的共同方法function sayHello(){    //this指代當前呼叫方法的物件    console.log(`你好!我是${this.name},我的賬號是${this.loginid}`); }const user1 = {    loginid: “bangbangji”,    loginpwd: “123456”,    name: “棒棒雞”,    sayHello };//user1呼叫sayHello時,方法執行過程中的this指代user1user1.sayHello(); //輸出:你好!我是棒棒雞,我的賬號是bangbangjiconst user2 = new Object(); user2.loginid = “xiaobaitu”; user2.loginpwd = “654321”; user2.name = “小白兔”; user2.sayHello = sayHello;//user2呼叫sayHello時,方法執行過程中的this指代user2user2.sayHello(); //輸出:你好!我是小白兔,我的賬號是xiaobaitu

我們在sayHello函式中使用了this關鍵字,當函式執行時,該關鍵字指代呼叫該函式的物件,即誰在呼叫我,我裡面的this就指代誰。在閱讀程式碼的時候,你可以把this讀作我的,以便於你理解它。

藉助了神奇的this關鍵字,就解決了重複程式碼的問題,之後無論建立多少個使用者物件,我們都可以使用同一個方法了。

關於this關鍵字,目前你需要記住以下兩點:

this關鍵字只能書寫在函式內。

this關鍵字在函式執行的時候才能確定指代的是誰,函式執行前誰也無法知道它將指代誰。

由於javascript語言本身的特性,函式中的this關鍵字會帶來很多坑(比如直接呼叫sayHello函式,this指代誰呢?)。後面會專門拿一章出來講解this。

總結

物件導向(OO)是一種程式設計思維,以物件為切入點分析解決問題,這種思維需要長期的練習才能逐漸建立,而我們學習的是物件導向技術層面的東西。

建立物件可以使用字面量表示法和new關鍵字,字面量表示法是一個語法糖,JS引擎最終會將其變為new關鍵字方式建立物件。

物件包含屬性和方法,分別表示物件的特徵和行為,它們沒有本質的區別,只是行為提現為一個函式

在函式中可以使用this關鍵字來指代**當前物件(呼叫函式的物件)

相關文章