前言
最近一直在看typescript,主要的一個(gè)目的是為vue3.x做準(zhǔn)備。3.x版本尤雨溪大神采用typescript重寫底層,具體內(nèi)容可以查看[譯] 尤雨溪:Vue 3.0 計(jì)劃-掘金。在typescript里,幾乎完全采用ES6語(yǔ)法,其中最為重要的還是class類的使用,學(xué)到這塊就有點(diǎn)懵逼了。什么?繼承還可以這樣玩?什么共有私有什么派生類???我之前學(xué)了個(gè)假js吧???
于是回頭來重新梳理學(xué)習(xí)一下面對(duì)對(duì)象編程,這一系列文章全部是基于ES5語(yǔ)法規(guī)則,別跟我說為啥不直接用ES6,ES6它最終也是編譯成ES5運(yùn)行。
小子只是個(gè)前端菜鳥,對(duì)編程還沒有太深入的了解,只是個(gè)代碼搬運(yùn)工。如果文章有什么錯(cuò)誤或者是理解上的錯(cuò)誤,還請(qǐng)指條明路,感激不盡。
這一系列將以面對(duì)對(duì)象三個(gè)特性封裝、繼承、多態(tài)為題進(jìn)行講解,更好的理解面對(duì)對(duì)象編程。
面對(duì)過程編程和面對(duì)對(duì)象編程(OOP)
面對(duì)過程編程
面向過程就是分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)依次調(diào)用就可以了。
面對(duì)對(duì)象編程
面對(duì)對(duì)象編程就是將你的需求抽象成一個(gè)對(duì)象,針對(duì)這個(gè)對(duì)象分析其特征和動(dòng)作,這個(gè)對(duì)象我們稱之為類
對(duì)象基礎(chǔ)知識(shí)
對(duì)象理解
程序員經(jīng)常會(huì)拿對(duì)象來開玩笑:沒對(duì)象?自己new一個(gè)唄,要啥樣都可以。那到底什么是對(duì)象呢?js中有一個(gè)說法是萬物皆對(duì)象,對(duì)象就是可以擁有屬性和方法的一個(gè)集合。人,是一個(gè)對(duì)象,他有吃飯睡覺的屬性,有上班賺錢的方法。所有的一切都可以抽象為一個(gè)對(duì)象,只不過每個(gè)對(duì)象都屬于自己的屬性和方法。
什么叫面對(duì)對(duì)象編程???在js中,有兩種編程風(fēng)格。面對(duì)過程編程和面對(duì)對(duì)象編程,我們寫輪播圖寫動(dòng)畫寫頁(yè)面,大部分都是面對(duì)過程編程(反正我個(gè)人目前是這樣)。
對(duì)象三大特征
封裝、繼承、多態(tài)
一個(gè)對(duì)象被創(chuàng)建,這中間發(fā)生什么 | new 關(guān)鍵字做了什么(面試題)
1、創(chuàng)建一個(gè)新的對(duì)象,這個(gè)對(duì)象的類型是 `object`
2、設(shè)置這個(gè)新的對(duì)象的內(nèi)部、可訪問性和[[prototype]]屬性為構(gòu)造函數(shù)(指prototype.construtor所指向的構(gòu)造函數(shù))中設(shè)置的
3、將步驟1新創(chuàng)建的對(duì)象作為this的上下文
4、果該函數(shù)沒有返回對(duì)象,則返回this。
原型和原型鏈
- 原型
JavaScript 規(guī)定,每一個(gè)構(gòu)造函數(shù)都有一個(gè) prototype 屬性,指向另一個(gè)對(duì)象。我們可以把所有對(duì)象實(shí)例需要共享的屬性和方法直接定義在 prototype 對(duì)象上。這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的所擁有。
prototype作為對(duì)象的內(nèi)部屬性,我們一般通過實(shí)例化對(duì)象的__proto__這個(gè)屬性進(jìn)行訪問。
在原型對(duì)象中還有一個(gè)屬性constructor,這個(gè)屬性對(duì)應(yīng)創(chuàng)建所有指向該原型的實(shí)例的構(gòu)造函數(shù)

- 原型鏈
當(dāng)我們?cè)L問對(duì)象的一個(gè)屬性或方法時(shí),它會(huì)先在對(duì)象自身中尋找,如果有則直接使用,如果沒有則會(huì)去原型對(duì)象中尋找,如果找到則直接使用。如果沒有則去原型的原型中尋找,直到找到Object對(duì)象的原型,Object對(duì)象的原型沒有原型,如果在Object原型中依然沒有找到,則返回null。

function Person(name, age) {
this.name = name
this.age= age
this.action = function () {
alert('hello word')
}
}
var p = new Person('尤雨溪', 18) // 實(shí)例化Person這個(gè)對(duì)象
console.log(p.name) // 尤雨溪
/*
他們之間的關(guān)系
構(gòu)造函數(shù)的原型 == 實(shí)例化p.__proto__
構(gòu)造函數(shù) == 構(gòu)造函數(shù)原型的constructor
*/
console.log(Person.prototype === p.__proto__) // true
console.log(Person.prototype.constructor === Person) // true
console.log(p.constructor === Person) // true
面對(duì)對(duì)象—封裝
什么是封裝
封裝就是把客觀事物封裝成抽象的類,隱藏屬性和方法的實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外公開接口。也就是說,封裝就是將屬性和方法組成一個(gè)類的過程就稱之為封裝。
對(duì)象封裝的幾種形式
對(duì)象字面量
- 優(yōu)點(diǎn):代碼簡(jiǎn)單易懂
- 缺點(diǎn):創(chuàng)建多個(gè)對(duì)象會(huì)產(chǎn)生大量的代碼,編寫麻煩,且并沒有實(shí)例與原型的概念
// 創(chuàng)建對(duì)象
var Person = {
name: "尤雨溪",
age: "18",
action : function() {
alert('hello word!');
}
}
Person.name
Person.action()
// 創(chuàng)建一個(gè)空對(duì)象 給空對(duì)象添加方法屬性
var Person = {}
Person.name = '尤雨溪'
Person.age= 18
Person.action = function() {
alert('hello word!');
}
工廠模式
- 對(duì)象字面量的形式在創(chuàng)建多個(gè)對(duì)象的時(shí)候會(huì)產(chǎn)生大量代碼,如果我們把創(chuàng)建新對(duì)象、添加對(duì)象屬性、返回對(duì)象的過程放到這個(gè)函數(shù)中,每次需要?jiǎng)?chuàng)建對(duì)象的時(shí)候調(diào)用函數(shù)即可。
function createPerson(name, age) {
var person = new Object();
person.name = name;
person.age = age;
person.action= function() {
alert('hello word!');
};
return person;
}
//生成實(shí)例
var p = createPerson('尤雨溪', 18);
var p2 = createPerson('阮一峰',20)
console.log(p.name)
p.action()
- 優(yōu)點(diǎn):避免創(chuàng)建大量對(duì)象時(shí)代碼的臃腫
- 缺點(diǎn): p1與p2之間沒有內(nèi)在聯(lián)系
構(gòu)造函數(shù)
所謂"構(gòu)造函數(shù)",其實(shí)就是一個(gè)普通函數(shù),但是內(nèi)部使用了this變量。對(duì)構(gòu)造函數(shù)使用new運(yùn)算符,就能生成實(shí)例,并且this變量會(huì)綁定在實(shí)例對(duì)象上。
function Person(name, age) {
// 通過this來添加屬性方法
this.name = name
this.age = age
this.action = function () {
alert('hello word')
}
}
// 實(shí)例化對(duì)象
var p1 = new Person('尤雨溪', 18)
var p2 = new Person('阮一峰', 20)
上面我們說過,通過new關(guān)鍵字實(shí)例化出來的對(duì)象,都會(huì)有一個(gè)
constructor屬性指向他們的構(gòu)造函數(shù)
console.log(p1.constructor === Person) // true
console.log(p2.constructor === Person) // true
- 優(yōu)點(diǎn):實(shí)例化對(duì)象和構(gòu)造函數(shù)之間存在關(guān)聯(lián)
- 缺點(diǎn):浪費(fèi)內(nèi)存,構(gòu)造函數(shù)中定義的方法名
action一樣,但實(shí)例化出來的對(duì)象名不一樣,造成一個(gè)內(nèi)存的浪費(fèi)。
原型模式
上面我們說過,每個(gè)對(duì)象被實(shí)例化的時(shí)候,都會(huì)有一個(gè)原型prototype屬性,這個(gè)屬性是可以被其他對(duì)象繼承的。所以我們可以將對(duì)象共有的屬性方法,放到原型上,這樣每次通過
new出來的對(duì)象,在構(gòu)造函數(shù)中的方法被多次創(chuàng)建。
function Person(name, age) {
// 通過this來添加屬性方法
this.name = name
this.age = age
}
// 將共同擁有的方法掛在到原型上
Person.prototype.action = function () {
alert('hello wodr')
}
// 實(shí)例化對(duì)象
var p1 = new Person('尤雨溪', 18)
var p2 = new Person('阮一峰', 20)
p1.action()
p2.action()
屬性和方法的類型
通過原型模式我們可以將方法屬性直接放在原型上,所有的實(shí)例對(duì)象都可以訪問這個(gè)屬性方法,我們稱為公有方法/屬性。熟悉ES6的朋友知道,在用class定義類的時(shí)候,我們可以定義里面屬性方法的類型。是保護(hù)類型,還是公開類型,還是私有類型,不同類型之間是不能相互訪問的。在ES6中我們可以通過public等關(guān)鍵字來定義,那我們ES5是怎樣區(qū)分他們的一個(gè)類型呢?
我之前在學(xué)習(xí)面對(duì)對(duì)象這一塊的時(shí)候沒有太過注意這一塊內(nèi)容,現(xiàn)在我們開發(fā)也很少注意到這一塊內(nèi)容。其實(shí)在快速開發(fā)的時(shí)期,我們很少注意到這些javascrip基礎(chǔ)理論知識(shí),更缺乏對(duì)原生js的理解。
在面向?qū)ο笾校總€(gè)屬性都有它的一個(gè)類型,可以分為共有、私有、靜態(tài)三種。
共有屬性和方法
從字面量上的意思理解,共有,就是所有對(duì)象都有的屬性或方法。
function Person(name, age) {
// 通過this來添加屬性方法
this.name = name // 共有屬性
this.age = age
}
// 將共同擁有的方法掛在到原型上
Person.prototype.action = function () { // 共有方法
console.log(`${this.name}是干${this.job}的`)
}
// 實(shí)例化對(duì)象
var p1 = new Person('尤雨溪', '程序員')
p1.action()
從上面代碼可以看出,通過this添加的屬性或者在原型上添加的屬性和方法都是共有的
- 調(diào)用規(guī)則: 調(diào)用公有方法,我們必需先實(shí)例化對(duì)象
私有屬性和方法
私有屬性的理解可以從函數(shù)的作用域出發(fā),函數(shù)內(nèi)部定義的變量在函數(shù)外部無法訪問。我們看下面栗子
function Person(name, job) {
// 通過this來添加屬性方法
var name = name // 私有屬性
var job = job
function action () { // 私有方法
console.log(`${name}是干${job}的`)
}
}
// 實(shí)例化對(duì)象
var p = new Person('尤雨溪', '程序員')
console.log(p.name) // undefined
p.action() // p.action is not a function 報(bào)錯(cuò)
當(dāng)實(shí)例化創(chuàng)建對(duì)象的時(shí)候,通過var定義的局部變量是無法在外界被訪問,他只能在構(gòu)造函數(shù)內(nèi)部進(jìn)行使用,并且不能通過this訪問,我們吧代碼變動(dòng)一下再看。
function Person(name, job) {
// 通過this來添加屬性方法
var name = name // 私有屬性
var job = job
function action1 () { // 私有方法
console.log(`${name}是干${job}的`, '通過this調(diào)用')
}
function action2 () { // 私有方法
console.log(`${name}是干${job}的`, '在內(nèi)部調(diào)用')
}
// 這樣是不能調(diào)用的
this.action1()
this.name
action()
}
// 實(shí)例化對(duì)象
var p = new Person('尤雨溪', '程序員') //尤雨溪是干程序員的 在內(nèi)部調(diào)用
- 調(diào)用規(guī)則: 對(duì)象的私有方法和屬性,外部是不可以訪問的,只能在內(nèi)部使用
特權(quán)方法
在函數(shù)內(nèi)部通過this創(chuàng)建的屬性和方法,在創(chuàng)建對(duì)象時(shí),每個(gè)對(duì)象自身都會(huì)擁有一份并且可以在外部可以訪問到。通過this創(chuàng)建的屬性可以看作對(duì)象的共有屬性,而通過this創(chuàng)建的方法不但可以訪問這些對(duì)象的共有屬性方法,還能訪問自身的私有屬性方法,被稱之為特權(quán)方法
function Person(name, job) {
// 通過this來添加屬性方法
var name = name // 私有屬性
this.job = job // 共有屬性
this.action = function () { // 特權(quán)方法
console.log(`${name}是干${this.job}的`)
}
}
// 實(shí)例化對(duì)象
var p = new Person('尤雨溪', '程序員')
console.log(p.name)
p.action() // 尤雨溪是干程序員的
- 調(diào)用規(guī)則:通過實(shí)例化,可以訪問共有屬性方法和私有屬性方法
靜態(tài)屬性和方法
共有屬性方法和私有屬性方法都用通過new創(chuàng)建對(duì)象才能訪問,而靜態(tài)屬性方法不需要通過new關(guān)鍵字創(chuàng)建,就可以直接訪問。
function Person(name, job) {
var name = name // 私有屬性
this.job = job // 共有屬性
this.action = function () { // 特權(quán)方法
console.log(`${name}是干${this.job}的`)
}
}
Person.attr = '靜態(tài)屬性'
Person.say = function () {
console.log('大家好,我是靜態(tài)方法')
}
console.log(Person.attr) // 靜態(tài)屬性
Person.say() // 大家好,我是靜態(tài)方法
// 實(shí)例化對(duì)象
var p = new Person('尤雨溪', '程序員')
console.log(p.attr ) // undefined
p.say() // 報(bào)錯(cuò)
- 調(diào)用規(guī)則:靜態(tài)方法無需實(shí)例化對(duì)象,便可以調(diào)用,對(duì)象實(shí)例不能調(diào)用對(duì)象的靜態(tài)方法,只能調(diào)用實(shí)例自身的靜態(tài)屬性和方法。
靜態(tài)類
前面我們說過,對(duì)象創(chuàng)建有好幾種形式,那我們通過字面量的形式創(chuàng)建的對(duì)象,它的類型時(shí)什么呢?對(duì)象字面量形式創(chuàng)建的對(duì)象添加的方法只能時(shí)靜態(tài)屬性和靜態(tài)方法,而包含靜態(tài)屬性和靜態(tài)方法的類叫靜態(tài)類
var person = {
name: "尤雨溪",
age: "18",
action : function() {
console.log('hello word!');
}
}
person.attr = '我是靜態(tài)屬性'
person.say = function () {
console.log('大家好,我是靜態(tài)方法')
}
console.log(person.name)
person.action()
person.attr
person.say()
- 調(diào)用規(guī)則:不需要通過
new創(chuàng)建對(duì)象
相關(guān)文章
[譯] 尤雨溪:Vue 3.0 計(jì)劃-掘金
js面向過程編程與面向?qū)ο缶幊痰膮^(qū)別
JS面向?qū)ο缶幊讨庋b
Javascript 面向?qū)ο?共有方法,私有方法,特權(quán)方法,靜態(tài)屬性和方法,靜態(tài)類)示例講解
更多文章訪問個(gè)人博客:http://www.lfanliu.top