精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
JavaScript 面向?qū)ο缶幊趟枷牒喗?/div>

JavaScript 面向?qū)ο缶幊?/h3>

JavaScript 執(zhí)行過程

JavaScript 運行分為兩個階段:

  • 預(yù)解析
    • 全局預(yù)解析(所有變量和函數(shù)聲明都會提前;同名的函數(shù)和變量函數(shù)的優(yōu)先級高)
    • 函數(shù)內(nèi)部預(yù)解析(所有的變量、函數(shù)和形參都會參與預(yù)解析)
      • 函數(shù)
      • 形參
      • 普通變量
  • 執(zhí)行

先預(yù)解析全局作用域,然后執(zhí)行全局作用域中的代碼,
在執(zhí)行全局代碼的過程中遇到函數(shù)調(diào)用就會先進行函數(shù)預(yù)解析,然后再執(zhí)行函數(shù)內(nèi)代碼。


JavaScript 面向?qū)ο缶幊?/h1>

面向?qū)ο蠼榻B

什么是對象

Everything is object (萬物皆對象)

對象到底是什么,我們可以從兩次層次來理解。

(1) 對象是單個事物的抽象。

一本書、一輛汽車、一個人都可以是對象,一個數(shù)據(jù)庫、一張網(wǎng)頁、一個與遠程服務(wù)器的連接也可以是對象。當實物被抽象成對象,實物之間的關(guān)系就變成了對象之間的關(guān)系,從而就可以模擬現(xiàn)實情況,針對對象進行編程。

(2) 對象是一個容器,封裝了屬性(property)和方法(method)。

屬性是對象的狀態(tài),方法是對象的行為(完成某種任務(wù))。比如,我們可以把動物抽象為animal對象,使用“屬性”記錄具體是那一種動物,使用“方法”表示動物的某種行為(奔跑、捕獵、休息等等)。

在實際開發(fā)中,對象是一個抽象的概念,可以將其簡單理解為:數(shù)據(jù)集或功能集

ECMAScript-262 把對象定義為:無序?qū)傩缘募?#xff0c;其屬性可以包含基本值、對象或者函數(shù)
嚴格來講,這就相當于說對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都
映射到一個值。

提示:每個對象都是基于一個引用類型創(chuàng)建的,這些類型可以是系統(tǒng)內(nèi)置的原生類型,也可以是開發(fā)人員自定義的類型

什么是面向?qū)ο?/h3>

面向?qū)ο蟛皇切碌臇|西,它只是過程式代碼的一種高度封裝,目的在于提高代碼的開發(fā)效率和可維護性。

面向?qū)ο缶幊?—— Object Oriented Programming,簡稱 OOP ,是一種編程開發(fā)思想。

它將真實世界各種復雜的關(guān)系,抽象為一個個對象,然后由對象之間的分工與合作,完成對真實世界的模擬。

在面向?qū)ο蟪绦蜷_發(fā)思想中,每一個對象都是功能中心,具有明確分工,可以完成接受信息、處理數(shù)據(jù)、發(fā)出信息等任務(wù)。

因此,面向?qū)ο缶幊叹哂?mark>靈活、代碼可復用高度模塊化等特點,容易維護和開發(fā),比起由一系列函數(shù)或指令組成的傳統(tǒng)的過程式編程(procedural programming),更適合多人合作的大型軟件項目。

面向?qū)ο笈c面向過程:

  • 面向過程就是親力親為,事無巨細,面面俱到,步步緊跟,有條不紊
  • 面向?qū)ο缶褪钦乙粋€對象,指揮得結(jié)果
  • 面向?qū)ο髮?zhí)行者轉(zhuǎn)變成指揮者
  • 面向?qū)ο蟛皇敲嫦蜻^程的替代,而是面向過程的封裝

面向?qū)ο蟮奶匦?#xff1a;

  • 封裝性
  • 繼承性
  • [多態(tài)性]

擴展閱讀:

  • 維基百科 - 面向?qū)ο蟪绦蛟O(shè)計
  • 知乎:如何用一句話說明什么是面向?qū)ο笏枷?#xff1f;
  • 知乎:什么是面向?qū)ο缶幊趟枷?#xff1f;

程序中面向?qū)ο蟮幕倔w現(xiàn)

在 JavaScript 中,所有數(shù)據(jù)類型都可以視為對象,當然也可以自定義對象。
自定義的對象數(shù)據(jù)類型就是面向?qū)ο笾械念?#xff08; Class )的概念。

我們以一個例子來說明面向過程和面向?qū)ο笤诔绦蛄鞒躺系牟煌帯?/p>

假設(shè)我們要處理學生的成績表,為了表示一個學生的成績,面向過程的程序可以用一個對象表示:

var std1 = { name: 'Michael', score: 98 }
var std2 = { name: 'Bob', score: 81 }

而處理學生成績可以通過函數(shù)實現(xiàn),比如打印學生的成績:

function printScore (student) {
  console.log('姓名:' + student.name + '  ' + '成績:' + student.score)
}

如果采用面向?qū)ο蟮某绦蛟O(shè)計思想,我們首選思考的不是程序的執(zhí)行流程,
而是 Student 這種數(shù)據(jù)類型應(yīng)該被視為一個對象,這個對象擁有 namescore 這兩個屬性(Property)。
如果要打印一個學生的成績,首先必須創(chuàng)建出這個學生對應(yīng)的對象,然后,給對象發(fā)一個 printScore 消息,讓對象自己把自己的數(shù)據(jù)打印出來。

抽象數(shù)據(jù)行為模板(Class):

function Student (name, score) {
  this.name = name
  this.score = score
}

Student.prototype.printScore = function () {
  console.log('姓名:' + this.name + '  ' + '成績:' + this.score)
}

根據(jù)模板創(chuàng)建具體實例對象(Instance):

var std1 = new Student('Michael', 98)
var std2 = new Student('Bob', 81)

實例對象具有自己的具體行為(給對象發(fā)消息):

std1.printScore() // => 姓名:Michael  成績:98
std2.printScore() // => 姓名:Bob  成績 81

面向?qū)ο蟮脑O(shè)計思想是從自然界中來的,因為在自然界中,類(Class)和實例(Instance)的概念是很自然的。
Class 是一種抽象概念,比如我們定義的 Class——Student ,是指學生這個概念,
而實例(Instance)則是一個個具體的 Student ,比如, Michael 和 Bob 是兩個具體的 Student 。

所以,面向?qū)ο蟮脑O(shè)計思想是:

  • 抽象出 Class
  • 根據(jù) Class 創(chuàng)建 Instance
  • 指揮 Instance 得結(jié)果

面向?qū)ο蟮某橄蟪潭扔直群瘮?shù)要高,因為一個 Class 既包含數(shù)據(jù),又包含操作數(shù)據(jù)的方法。

創(chuàng)建對象

簡單方式

我們可以直接通過 new Object() 創(chuàng)建:

var person = new Object()
person.name = 'Jack'
person.age = 18

person.sayName = function () {
  console.log(this.name)
}

每次創(chuàng)建通過 new Object() 比較麻煩,所以可以通過它的簡寫形式對象字面量來創(chuàng)建:

var person = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

對于上面的寫法固然沒有問題,但是假如我們要生成兩個 person 實例對象呢?

var person1 = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

var person2 = {
  name: 'Mike',
  age: 16,
  sayName: function () {
    console.log(this.name)
  }
}

通過上面的代碼我們不難看出,這樣寫的代碼太過冗余,重復性太高。

簡單方式的改進:工廠函數(shù)

我們可以寫一個函數(shù),解決代碼重復問題:

function createPerson (name, age) {
  return {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

然后生成實例對象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

這樣封裝確實爽多了,通過工廠模式我們解決了創(chuàng)建多個相似對象代碼冗余的問題,
但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。

構(gòu)造函數(shù)

更優(yōu)雅的工廠函數(shù):構(gòu)造函數(shù)

一種更優(yōu)雅的工廠函數(shù)就是下面這樣,構(gòu)造函數(shù):

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike

解析構(gòu)造函數(shù)代碼的執(zhí)行

在上面的示例中,Person() 函數(shù)取代了 createPerson() 函數(shù),但是實現(xiàn)效果是一樣的。
這是為什么呢?

我們注意到,Person() 中的代碼與 createPerson() 有以下幾點不同之處:

  • 沒有顯示的創(chuàng)建對象
  • 直接將屬性和方法賦給了 this 對象
  • 沒有 return 語句
  • 函數(shù)名使用的是大寫的 Person

而要創(chuàng)建 Person 實例,則必須使用 new 操作符。
以這種方式調(diào)用構(gòu)造函數(shù)會經(jīng)歷以下 4 個步驟:

  1. 創(chuàng)建一個新對象
  2. 將構(gòu)造函數(shù)的作用域賦給新對象(因此 this 就指向了這個新對象)
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼
  4. 返回新對象

下面是具體的偽代碼:

function Person (name, age) {
  // 當使用 new 操作符調(diào)用 Person() 的時候,實際上這里會先創(chuàng)建一個對象
  // var instance = {}
  // 然后讓內(nèi)部的 this 指向 instance 對象
  // this = instance
  // 接下來所有針對 this 的操作實際上操作的就是 instance

  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }

  // 在函數(shù)的結(jié)尾處會將 this 返回,也就是 instance
  // return this
}

構(gòu)造函數(shù)和實例對象的關(guān)系

使用構(gòu)造函數(shù)的好處不僅僅在于代碼的簡潔性,更重要的是我們可以識別對象的具體類型了。
在每一個實例對象中的__proto__中同時有一個 constructor 屬性,該屬性指向創(chuàng)建該實例的構(gòu)造函數(shù):

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

對象的 constructor 屬性最初是用來標識對象類型的,
但是,如果要檢測對象的類型,還是使用 instanceof 操作符更可靠一些:

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true

總結(jié):

  • 構(gòu)造函數(shù)是根據(jù)具體的事物抽象出來的抽象模板
  • 實例對象是根據(jù)抽象的構(gòu)造函數(shù)模板得到的具體實例對象
  • 每一個實例對象都具有一個 constructor 屬性,指向創(chuàng)建該實例的構(gòu)造函數(shù)
    • 注意: constructor 是實例的屬性的說法不嚴謹,具體后面的原型會講到
  • 可以通過實例的 constructor 屬性判斷實例和構(gòu)造函數(shù)之間的關(guān)系
    • 注意:這種方式不嚴謹,推薦使用 instanceof 操作符,后面學原型會解釋為什么

構(gòu)造函數(shù)的問題

使用構(gòu)造函數(shù)帶來的最大的好處就是創(chuàng)建對象更方便了,但是其本身也存在一個浪費內(nèi)存的問題:

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = function () {
    console.log('hello ' + this.name)
  }
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

在該示例中,從表面上好像沒什么問題,但是實際上這樣做,有一個很大的弊端。
那就是對于每一個實例對象,typesayHello 都是一模一樣的內(nèi)容,
每一次生成一個實例,都必須為重復的內(nèi)容,多占用一些內(nèi)存,如果實例對象很多,會造成極大的內(nèi)存浪費。

console.log(p1.sayHello === p2.sayHello) // => false

對于這種問題我們可以把需要共享的函數(shù)定義到構(gòu)造函數(shù)外部:

function sayHello = function () {
  console.log('hello ' + this.name)
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = sayHello
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true

這樣確實可以了,但是如果有多個需要共享的函數(shù)的話就會造成全局命名空間沖突的問題。

你肯定想到了可以把多個函數(shù)放到一個對象中用來避免全局命名空間沖突的問題:

var fns = {
  sayHello: function () {
    console.log('hello ' + this.name)
  },
  sayAge: function () {
    console.log(this.age)
  }
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = fns.sayHello
  this.sayAge = fns.sayAge
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true

至此,我們利用自己的方式基本上解決了構(gòu)造函數(shù)的內(nèi)存浪費問題。
但是代碼看起來還是那么的格格不入,那有沒有更好的方式呢?

那就要通過原型對象的方法來建立,下面這篇博文進行介紹

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服

主站蜘蛛池模板: 苍南县| 甘洛县| 民乐县| 绥化市| 沙雅县| 崇明县| 西充县| 南涧| 桦南县| 广昌县| 武定县| 娱乐| 砀山县| 泗水县| 莎车县| 宁夏| 灵丘县| 达拉特旗| 周宁县| 乐安县| 濮阳市| 利津县| 海兴县| 城市| 女性| 广汉市| 饶平县| 天津市| 桃园市| 聂拉木县| 九龙城区| 巩留县| 江川县| 丰顺县| 湘阴县| 蓬莱市| 清原| 丹江口市| 天门市| 公主岭市| 武强县|