国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

重新認(rèn)識(shí)JavaScript面向?qū)ο? 從ES5到ES6

用戶83 / 3579人閱讀

摘要:基于原型的面向?qū)ο笤诨谠偷恼Z(yǔ)言中如并不存在這種區(qū)別它只有對(duì)象不論是構(gòu)造函數(shù),實(shí)例,原型本身都是對(duì)象。允許動(dòng)態(tài)地向單個(gè)的對(duì)象或者整個(gè)對(duì)象集中添加或移除屬性。為了解決以上兩個(gè)問(wèn)題,提供了構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式。

一. 重新認(rèn)識(shí)面向?qū)ο?/strong> 1. JavaScript是一門(mén)面向?qū)ο蟮恼Z(yǔ)言

在說(shuō)明JavaScript是一個(gè)面向?qū)ο蟮恼Z(yǔ)言之前, 我們來(lái)探討一下面向?qū)ο蟮娜蠡咎卣? 封裝, 繼承, 多態(tài)

封裝

把抽象出來(lái)的屬性和對(duì)方法組合在一起, 且屬性值被保護(hù)在內(nèi)部, 只有通過(guò)特定的方法進(jìn)行改變和讀取稱為封裝

我們以代碼舉例, 首先我們構(gòu)造一個(gè)Person構(gòu)造函數(shù), 它有nameid兩個(gè)屬性, 并有一個(gè)sayHi方法用于打招呼:

//定義Person構(gòu)造函數(shù)
function Person(name, id) {
  this.name = name;
  this.id = id;
}

//在Person.prototype中加入方法
Person.prototype.sayHi = function() {
  console.log("你好, 我是" +  this.name);
}

現(xiàn)在我們生成一個(gè)實(shí)例對(duì)象p1, 并調(diào)用sayHi()方法

//實(shí)例化對(duì)象
let p1 = new Person("阿輝", 1234);

//調(diào)用sayHi方法
p1.sayHi();

在上述的代碼中, p1這個(gè)對(duì)象并不知道sayHi()這個(gè)方法是如何實(shí)現(xiàn)的, 但是仍然可以使用這個(gè)方法. 這其實(shí)就是封裝. 你也可以實(shí)現(xiàn)對(duì)象屬性的私有和公有, 我們?cè)跇?gòu)造函數(shù)中聲明一個(gè)salary作為私有屬性, 有且只有通過(guò)getSalary()方法查詢到薪資.

function Person(name, id) {
  this.name = name;
  this.id = id;
  let salary = 20000;
  this.getSalary = function (pwd) {
    pwd === 123456 ? console.log(salary) : console.log("對(duì)不起, 你沒(méi)有權(quán)限查看密碼");
  }
}

繼承

可以讓某個(gè)類型的對(duì)象獲得另一個(gè)類型的對(duì)象的屬性和方法稱為繼承

以剛才的Person作為父類構(gòu)造器, 我們來(lái)新建一個(gè)子類構(gòu)造器Student, 這里我們使用call()方法實(shí)現(xiàn)繼承

function Student(name, id, subject) {
  //使用call實(shí)現(xiàn)父類繼承
  Person.call(this, name, id);
  //添加子類的屬性
  this.subject = subject;
}

let s1 = new Student("阿輝", 1234, "前端開(kāi)發(fā)");

多態(tài)

同一操作作用于不同的對(duì)象產(chǎn)生不同的執(zhí)行結(jié)果, 這稱為多態(tài)

JavaScript中函數(shù)沒(méi)有重載, 所以JavaScript中的多態(tài)是靠函數(shù)覆蓋實(shí)現(xiàn)的。

同樣以剛才的Person構(gòu)造函數(shù)為例, 我們?yōu)?b>Person構(gòu)造函數(shù)添加一個(gè)study方法

function Person(name, id) {
  this.name = name;
  this.id = id;
  this.study = function() {
    console.log(name + "在學(xué)習(xí)");
  }
}

同樣, 我們新建一個(gè)StudentTeacher構(gòu)造函數(shù), 該構(gòu)造函數(shù)繼承Person, 并也添加study方法

function Student(subject) {
  this.subject = subject;
  this.study = function() {
    console.log(this.name + "在學(xué)習(xí)" + this.subject);
  }
}
Student.prototype = new Person("阿輝", 1234);
Student.prototype.constructor = Student;

function Teacher(subject) {
  this.subject = subject;
  this.study = function() {
    console.log(this.name + "為了教學(xué)而學(xué)習(xí)" + this.subject);
  }
}
Teacher.prototype = new Person("老夫子", 4567);
Teacher.prototype.constructor = Teacher;

測(cè)試我們新建一個(gè)函數(shù)doStudy

function doStudy(role) {
  if(role instanceof Person) {
    role.study();
  }
}

此時(shí)我們分別實(shí)例化StudentTeacher, 并調(diào)用doStudy方法

let student = new Student("前端開(kāi)發(fā)");
let teacher = new Teacher("前端開(kāi)發(fā)");

doStudy(student); //阿輝在學(xué)習(xí)前端開(kāi)發(fā)
doStudy(teacher); //老夫子為了教學(xué)在學(xué)習(xí)前端開(kāi)發(fā)

對(duì)于同一函數(shù)doStudy, 由于參數(shù)的不同, 導(dǎo)致不同的調(diào)用結(jié)果,這就實(shí)現(xiàn)了多態(tài).
JavaScript的面向?qū)ο?/strong>
從上面的分析可以論證出, JavaScript是一門(mén)面向?qū)ο蟮恼Z(yǔ)言, 因?yàn)樗鼘?shí)現(xiàn)了面向?qū)ο蟮乃刑匦? 其實(shí), 面向?qū)ο髢H僅是一個(gè)概念或者一個(gè)編程思想而已, 它不應(yīng)該依賴于某個(gè)語(yǔ)言存在, 比如Java采用面向?qū)ο笏枷霕?gòu)造其語(yǔ)言, 它實(shí)現(xiàn)了類, 繼承, 派生, 多態(tài), 接口等機(jī)制. 但是這些機(jī)制,只是實(shí)現(xiàn)面向?qū)ο蟮囊环N手段, 而非必須。換言之, 一門(mén)語(yǔ)言可以根據(jù)自身特性選擇合適的方式來(lái)實(shí)現(xiàn)面向?qū)ο蟆?由于大多數(shù)程序員首先學(xué)習(xí)的是Java, C++等高級(jí)編程語(yǔ)言, 因而先入為主的接受了“類”這個(gè)面向?qū)ο髮?shí)際方式,所以習(xí)慣性的用類式面向?qū)ο笳Z(yǔ)言中的概念來(lái)判斷該語(yǔ)言是否是面向?qū)ο蟮恼Z(yǔ)言。這也是很多有其他編程語(yǔ)言經(jīng)驗(yàn)的人在學(xué)習(xí)JavaScript對(duì)象時(shí),感覺(jué)到很困難的地方。

實(shí)際上, JavaScript是通過(guò)一種叫原型(prototype)的方式來(lái)實(shí)現(xiàn)面向?qū)ο缶幊痰?。下面我們就?lái)討論一下基于類(class-basesd)的面向?qū)ο?/strong>和基于原型(protoype-based)的面向?qū)ο?/strong>這兩者的差別。

2. 基于類的面向?qū)ο蠛突谠偷拿嫦驅(qū)ο蟮谋容^

基于類的面向?qū)ο?/strong>

在基于的面向?qū)ο笳Z(yǔ)言中(比如Java和C++), 是構(gòu)建在類(class)實(shí)例(instance)上的。其中定義了所有用于具有某一特征對(duì)象的屬性。是抽象的事物, 而不是其所描述的全部對(duì)象中的任何特定的個(gè)體。另一方面, 一個(gè)實(shí)例是一個(gè)的實(shí)例化,是其中的一個(gè)成員。

基于原型的面向?qū)ο?/strong>
在基于原型的語(yǔ)言中(如JavaScript)并不存在這種區(qū)別:它只有對(duì)象!不論是構(gòu)造函數(shù)(constructor),實(shí)例(instance),原型(prototype)本身都是對(duì)象?;谠偷恼Z(yǔ)言具有所謂的原型對(duì)象的概念,新對(duì)象可以從中獲得原始的屬性。

所以,在JavaScript中有一個(gè)很有意思的__proto__屬性(ES6以下是非標(biāo)準(zhǔn)屬性)用于訪問(wèn)其原型對(duì)象, 你會(huì)發(fā)現(xiàn),上面提到的構(gòu)造函數(shù),實(shí)例,原型本身都有__proto__指向原型對(duì)象。其最后順著原型鏈都會(huì)指向Object這個(gè)構(gòu)造函數(shù),然而Object的原型對(duì)象的原型是null,不信, 你可以嘗試一下Object.prototype.__proto__ === nulltrue。然而typeof null === "object"true。到這里, 我相信你應(yīng)該就能明白為什么JavaScript這類基于原型的語(yǔ)言中沒(méi)有類和實(shí)例的區(qū)別, 而是萬(wàn)物皆對(duì)象!

差異總結(jié)

基于類的(Java) 基于原型的(JavaScript)
類和實(shí)例是不同的事物。 所有對(duì)象均為實(shí)例。
通過(guò)類定義來(lái)定義類;通過(guò)構(gòu)造器方法來(lái)實(shí)例化類。 通過(guò)構(gòu)造器函數(shù)來(lái)定義和創(chuàng)建一組對(duì)象。
通過(guò) new 操作符創(chuàng)建單個(gè)對(duì)象。 相同
通過(guò)類定義來(lái)定義現(xiàn)存類的子類, 從而構(gòu)建對(duì)象的層級(jí)結(jié)構(gòu) 指定一個(gè)對(duì)象作為原型并且與構(gòu)造函數(shù)一起構(gòu)建對(duì)象的層級(jí)結(jié)構(gòu)
遵循類鏈接繼承屬性 遵循原型鏈繼承屬性
類定義指定類的所有實(shí)例的所有屬性。無(wú)法在運(yùn)行時(shí)動(dòng)態(tài)添加屬性 構(gòu)造器函數(shù)或原型指定初始的屬性集。允許動(dòng)態(tài)地向單個(gè)的對(duì)象或者整個(gè)對(duì)象集中添加或移除屬性。
二. ES5中的面向?qū)ο?/strong>
*這里的ES5并不特指ECMAScript 5, 而是代表ECMAScript 6 之前的ECMAScript!
(一) ES5中對(duì)象的創(chuàng)建

在ES5中創(chuàng)建對(duì)象有兩種方式, 第一種是使用對(duì)象字面量的方式, 第二種是使用構(gòu)造函數(shù)的方式。該兩種方法在特定的使用場(chǎng)景分別有其優(yōu)點(diǎn)和缺點(diǎn), 下面我們來(lái)分別介紹這兩種創(chuàng)建對(duì)象的方式。

1. 使用對(duì)象字面量的方式

我們通過(guò)對(duì)象字面量的方式創(chuàng)建兩個(gè)student對(duì)象,分別是student1student2。

var student1 = {
  name: "阿輝",
  age: 22,
  subject: "前端開(kāi)發(fā)"
};

var student2 = {
  name: "阿傻",
  age: 22,
  subject: "大數(shù)據(jù)開(kāi)發(fā)"
};

上面的代碼就是使用對(duì)象字面量的方式創(chuàng)建實(shí)例對(duì)象, 使用對(duì)象字面量的方式在創(chuàng)建單一簡(jiǎn)單對(duì)象的時(shí)候是非常方便的。但是,它也有其缺點(diǎn):

在生成多個(gè)實(shí)例對(duì)象時(shí), 我們需要每次重復(fù)寫(xiě)name,age,subject屬性,寫(xiě)起來(lái)特別的麻煩

雖然都是學(xué)生的對(duì)象, 但是看不出student1student2之間有什么聯(lián)系。

為了解決以上兩個(gè)問(wèn)題, JavaScript提供了構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式。

2. 使用構(gòu)造函數(shù)的方式

構(gòu)造函數(shù)就其實(shí)就是一個(gè)普通的函數(shù),當(dāng)對(duì)構(gòu)造函數(shù)使用new進(jìn)行實(shí)例化時(shí),會(huì)將其內(nèi)部this的指向綁定實(shí)例對(duì)象上,下面我們來(lái)創(chuàng)建一個(gè)Student構(gòu)造函數(shù)(構(gòu)造函數(shù)約定使用大寫(xiě)開(kāi)頭,和普通函數(shù)做區(qū)分)。

function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
  console.log(this);
}

我特意在構(gòu)造函數(shù)中打印出this的指向。上面我們提到,構(gòu)造函數(shù)其實(shí)就是一個(gè)普通的函數(shù), 那么我們使用普通函數(shù)的調(diào)用方式嘗試調(diào)用Student

Student("阿輝", 22, "前端開(kāi)發(fā)"); //window{}

采用普通方式調(diào)用Student時(shí), this的指向是window。下面使用new來(lái)實(shí)例化該構(gòu)造函數(shù), 生成一個(gè)實(shí)例對(duì)象student1。

let student1 = new Student("阿輝", 22, "前端開(kāi)發(fā)"); //Student?{name: "阿輝", age: 22, subject: "前端開(kāi)發(fā)"}

當(dāng)我們采用new生成實(shí)例化對(duì)象student1時(shí), this不再指向window, 而是指向的實(shí)例對(duì)象本身。這些, 都是new幫我們做的。上面的就是采用構(gòu)造函數(shù)的方式生成實(shí)例對(duì)象的方式, 并且當(dāng)我們生成其他實(shí)例對(duì)象時(shí),由于都是采用Student這個(gè)構(gòu)造函數(shù)實(shí)例化而來(lái)的, 我們能夠清楚的知道各實(shí)例對(duì)象之間的聯(lián)系。

let student1 = new Student("阿輝", 22, "前端開(kāi)發(fā)");
let student2 = new Student("阿傻", 22, "大數(shù)據(jù)開(kāi)發(fā)");
let student3 = new Student("阿呆", 22, "Python");
let student4 = new Student("阿笨", 22, "Java");
(二) ES5中對(duì)象的繼承
1. prototype的原型繼承

prototype是JavaScript這類基于原型繼承的核心, 只要弄明白了原型和原型鏈, 就基本上完全理解了JavaScript中對(duì)象的繼承。下面我將著重的講解為什么要使用prototype和使用prototype實(shí)現(xiàn)繼承的方式。

為什么要使用prototype

我們給之前的Student構(gòu)造函數(shù)新增一個(gè)study方法

function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
  this.study = function() {
    console.log("我在學(xué)習(xí)" + this.subject);
  }
}

現(xiàn)在我們來(lái)實(shí)例化Student構(gòu)造函數(shù), 生成student1`student2, 并分別調(diào)用其study方法。

let student1 = new Student("阿輝", 22, "前端開(kāi)發(fā)");
let student2 = new Student("阿傻", 22, "大數(shù)據(jù)開(kāi)發(fā)");

student1.study(); //我在學(xué)習(xí)前端開(kāi)發(fā)
student2.study(); //我在學(xué)習(xí)大數(shù)據(jù)開(kāi)發(fā)

這樣生成的實(shí)例對(duì)象表面上看沒(méi)有任何問(wèn)題, 但是其實(shí)是有很大的性能問(wèn)題!我們來(lái)看下面一段代碼:

console.log(student1.study === student2.study); //false

其實(shí)對(duì)于每一個(gè)實(shí)例對(duì)象studentx,其study方法的函數(shù)體是一模一樣的,方法的執(zhí)行結(jié)果只根據(jù)其實(shí)例對(duì)象決定,然而生成的每個(gè)實(shí)例都需要生成一個(gè)study方法去占用一份內(nèi)存。這樣是非常不經(jīng)濟(jì)的做法。新手可能會(huì)認(rèn)為, 上面的代碼中也就多生成了一個(gè)study方法, 對(duì)于內(nèi)存的占用可以忽略不計(jì)。

那么我們?cè)贛DN中看一下在JavaScript中我們使用的String實(shí)例對(duì)象有多少方法?

上面的方法只是String實(shí)例對(duì)象中的一部分方法(我一個(gè)屏幕截取不完!), 這也就是為什么我們的字符串能夠使用如此多便利的原生方法的原因。設(shè)想一下, 如果這些方法不是掛載在String.prototype上, 而是像上面Student一樣寫(xiě)在String構(gòu)造函數(shù)上呢?那么我們項(xiàng)目中的每一個(gè)字符串,都會(huì)去生成這幾十種方法去占用內(nèi)存,這還沒(méi)考慮Math,Array,Number,Object等對(duì)象!

現(xiàn)在我們應(yīng)該知道應(yīng)該將study方法掛載到Student.prototype原型對(duì)象上才是正確的寫(xiě)法,所有的studentx實(shí)例都能繼承該方法。

function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
}
Student.prototype.study = function() {
  console.log("我在學(xué)習(xí)" + this.subject);
}

現(xiàn)在我們實(shí)例化student1student2

let student1 = new Student("阿輝", 22, "前端開(kāi)發(fā)");
let student2 = new Student("阿傻", 22, "大數(shù)據(jù)開(kāi)發(fā)");

student1.study(); //我在學(xué)習(xí)前端開(kāi)發(fā)
student2.study(); //我在學(xué)習(xí)大數(shù)據(jù)開(kāi)發(fā)

console.log(student1.study === student2.study); //true

從上面的代碼我們可以看出, student1student2study方法執(zhí)行結(jié)果沒(méi)有發(fā)生變化,但是study本身指向了一個(gè)內(nèi)存地址。這就是為什么我們要使用prototype進(jìn)行掛載方法的原因。接下來(lái)我們來(lái)講解一下如何使用prototype來(lái)實(shí)現(xiàn)繼承。

如何使用prototype實(shí)現(xiàn)繼承?

“學(xué)生”這個(gè)對(duì)象可以分為小學(xué)生, 中學(xué)生和大學(xué)生等。我們現(xiàn)在新建一個(gè)小學(xué)生的構(gòu)造函數(shù)Pupil

function Pupil(school) {
  this.school = school;
}

那么如何讓Pupil使用prototype繼承Student呢? 其實(shí)我們只要將Pupilprototype指向Student的一個(gè)實(shí)例即可。

Pupil.prototype = new Student("小輝", 8, "小學(xué)義務(wù)教育課程");
Pupil.prototype.constructor = Pupil;

let pupil1 = new Pupil("北大附小");

代碼的第一行, 我們將Pupil的原型對(duì)象(Pupil.prototype)指向了Student的實(shí)例對(duì)象。

Pupil.prototype = new Student("小輝", 8, "小學(xué)義務(wù)教育課程");

代碼的第二行也許有的讀者會(huì)不能理解是什么意思。

Pupil.prototype.constructor = Pupil;

Pupil作為構(gòu)造函數(shù)有一個(gè)protoype屬性指向原型對(duì)象Pupil.prototype,而原型對(duì)象Pupil.prototype也有一個(gè)constructor屬性指回它的構(gòu)造函數(shù)Pupil。如下圖所示:

然而, 當(dāng)我們使用實(shí)例化Student去覆蓋Pupil.prototype后, 如果沒(méi)有第二行代碼的情況下, Pupil.prototype.constructor指向了Student構(gòu)造函數(shù), 如下圖所示:

而且, pupil1.constructor會(huì)默認(rèn)調(diào)用Pupil.prototype.constructor, 這個(gè)時(shí)候pupil1.constructor指向了Student

Pupil.prototype = new Student("小輝", 8, "小學(xué)義務(wù)教育課程");
let pupil1 = new Pupil("北大附小");

console.log(pupil1.constructor === Student); //true

這明顯是錯(cuò)誤的, pupil1明明是用Pupil構(gòu)造函數(shù)實(shí)例化出來(lái)的, 怎么其constructor指向了Student構(gòu)造函數(shù)呢。所以, 我們就需要加入第二行, 修正其錯(cuò)誤:

Pupil.prototype = new Student("小輝", 8, "小學(xué)義務(wù)教育課程");

//修正constructor的指向錯(cuò)誤
Pupil.prototype.constructor = Pupil;

let pupil1 = new Pupil("北大附小");

console.log(pupil1.constructor === Student); //false
console.log(pupil1.constructor === Pupil); //ture

上面就是我們的如何使用prototype實(shí)現(xiàn)繼承的例子, 需要特別注意的: 如果替換了prototype對(duì)象, 必須手動(dòng)將prototype.constructor重新指向其構(gòu)造函數(shù)。

2. 使用callapply方法實(shí)現(xiàn)繼承

使用callapply是我個(gè)人比較喜歡的繼承方式, 因?yàn)橹恍枰恍写a就可以實(shí)現(xiàn)繼承。但是該方法也有其局限性,callapply不能繼承原型上的屬性和方法, 下面會(huì)有詳細(xì)說(shuō)明。

使用call實(shí)現(xiàn)繼承

同樣對(duì)于上面的Student構(gòu)造函數(shù), 我們使用call實(shí)現(xiàn)Pupil繼承Student的全部屬性和方法:

//父類構(gòu)造函數(shù)
function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
}

//子類構(gòu)造函數(shù)
function Pupil(name, age, subject, school) {
  //使用call實(shí)現(xiàn)繼承
  Student.call(this, name, age, subject);
  this.school = school;
}

//實(shí)例化Pupil
let pupil2 = new Pupil("小輝", 8, "小學(xué)義務(wù)教育課程", "北大附小");

需要注意的是, callapply只能繼承本地屬性和方法, 而不能繼承原型上的屬性和方法,如下面的代碼所示, 我們給Student掛載study方法,Pupil使用call繼承Student后, 調(diào)用pupil2.study()會(huì)報(bào)錯(cuò):

//父類構(gòu)造函數(shù)
function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
}
//原型上掛載study方法
Student.prototype.study = function() {
  console.log("我在學(xué)習(xí)" + this.subject);
}

//子類構(gòu)造函數(shù)
function Pupil(name, age, subject, school) {
  //使用call實(shí)現(xiàn)繼承
  Student.call(this, name, age, subject);
  this.school = school;
}

let pupil2 = new Pupil("小輝", 8, "小學(xué)義務(wù)教育課程", "北大附小");

//報(bào)錯(cuò)
pupil2.study(); //Uncaught TypeError: pupil2.study is not a function

使用apply實(shí)現(xiàn)繼承
使用apply實(shí)現(xiàn)繼承的方式和call類似, 唯一的不同只是參數(shù)需要使用數(shù)組的方法。下面我們使用apply來(lái)實(shí)現(xiàn)上面Pupil繼承Student的例子。

//父類構(gòu)造函數(shù)
function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
}

//子類構(gòu)造函數(shù)
function Pupil(name, age, subject, school) {
  //使用applay實(shí)現(xiàn)繼承
  Student.apply(this, [name, age, subject]);
  this.school = school;
}

//實(shí)例化Pupil
let pupil2 = new Pupil("小輝", 8, "小學(xué)義務(wù)教育課程", "北大附小");
3. 其他繼承方式

JavaScript中的繼承方式不僅僅只有上面提到的幾種方法, 在《JavaScript高級(jí)程序設(shè)計(jì)》中, 還有實(shí)例繼承,拷貝繼承,組合繼承,寄生組合繼承等眾多繼承方式。在寄生組合繼承中, 就很好的彌補(bǔ)了callapply無(wú)法繼承原型屬性和方法的缺陷,是最完美的繼承方法。這里就不詳細(xì)的展開(kāi)論述,感興趣的可以自行閱讀《JavaScript高級(jí)程序設(shè)計(jì)》。

三. ES6中的面向?qū)ο?/strong>

基于原型的繼承方式,雖然實(shí)現(xiàn)了代碼復(fù)用,但是行文松散且不夠流暢,可閱讀性差,不利于實(shí)現(xiàn)擴(kuò)展和對(duì)源代碼進(jìn)行有效的組織管理。不得不承認(rèn),基于類的繼承方式在語(yǔ)言實(shí)現(xiàn)上更健壯,且在構(gòu)建可服用代碼和組織架構(gòu)程序方面具有明顯的優(yōu)勢(shì)。所以,ES6中提供了基于類class的語(yǔ)法。但class本質(zhì)上是ES6提供的一顆語(yǔ)法糖,正如我們前面提到的,JavaScript是一門(mén)基于原型的面向?qū)ο笳Z(yǔ)言。

(一) ES6中對(duì)象的創(chuàng)建

我們使用ES6的class來(lái)創(chuàng)建Student

//定義類
class Student {
  //構(gòu)造方法
  constructor(name, age, subject) {
    this.name = name;
    this.age = age;
    this.subject = subject;
  }

  //類中的方法
  study(){
    console.log("我在學(xué)習(xí)" + this.subject);
  }
}

//實(shí)例化類
let student3 = new Student("阿輝", 24, "前端開(kāi)發(fā)");
student3.study(); //我在學(xué)習(xí)前端開(kāi)發(fā)

上面的代碼定義了一個(gè)Student類, 可以看到里面有一個(gè)constructor方法, 這就是構(gòu)造方法,而this關(guān)鍵字則代表實(shí)例對(duì)象。也就是說(shuō),ES5中的構(gòu)造函數(shù)Student, 對(duì)應(yīng)的是E6中Student類中的constructor方法。

Student類除了構(gòu)造函數(shù)方法,還定義了一個(gè)study方法。需要特別注意的是,在ES6中定義類中的方法的時(shí)候,前面不需要加上function關(guān)鍵字,直接把函數(shù)定義進(jìn)去就可以了。另外,方法之間不要用逗號(hào)分隔,加了會(huì)報(bào)錯(cuò)。而且,類中的方法全部是定義在原型上的,我們可以用下面的代碼進(jìn)行驗(yàn)證。

console.log(student3.__proto__.study === Student.prototype.study); //true
console.log(student3.hasOwnProperty("study")); // false

上面的第一行的代碼中, student3.__proto__是指向的原型對(duì)象,其中Student.prototype也是指向的原型的對(duì)象,結(jié)果為true就能很好的說(shuō)明上面的結(jié)論: 類中的方法全部是定義在原型上的。第二行代碼是驗(yàn)證student3實(shí)例中是否有study方法,結(jié)果為false, 表明實(shí)例中沒(méi)有study方法,這也更好的說(shuō)明了上面的結(jié)論。其實(shí),只要理解了ES5中的構(gòu)造函數(shù)對(duì)應(yīng)的是類中的constructor方法,就能推斷出上面的結(jié)論。

(二) ES6中對(duì)象的繼承

E6中class可以通過(guò)extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承, 這比前面提到的ES5中使用原型鏈來(lái)實(shí)現(xiàn)繼承, 要清晰和方便很多。下面我們使用ES6的語(yǔ)法來(lái)實(shí)現(xiàn)Pupil。

//子類
class Pupil extends Student{
  constructor(name, age, subject, school) {
    //調(diào)用父類的constructor
    super(name, age, subject); 
    this.school = school;
  }
}

let pupil = new Pupil("小輝", 8, "小學(xué)義務(wù)教育課程", "北大附小");
pupil.study(); //我在學(xué)習(xí)小學(xué)義務(wù)教育課程

上面代碼代碼中, 我們通過(guò)了extends實(shí)現(xiàn)Pupil子類繼承Student父類。需要特別注意的是,子類必須在constructor方法中首先調(diào)用super方法,否則實(shí)例化時(shí)會(huì)報(bào)錯(cuò)。這是因?yàn)樽宇悰](méi)有自己的this對(duì)象, 而是繼承父類的this對(duì)象,然后對(duì)其加工。如果不調(diào)用super方法,子類就得不到this對(duì)象。

四.結(jié)束語(yǔ)

JavaScript 被認(rèn)為是世界上最受誤解的編程語(yǔ)言,因?yàn)樗砼?c 語(yǔ)言家族的外衣,表現(xiàn)的卻是 LISP 風(fēng)格的函數(shù)式語(yǔ)言特性;沒(méi)有類,卻實(shí)也徹底實(shí)現(xiàn)了面向?qū)ο?。要?duì)這門(mén)語(yǔ)言有透徹的理解,就必須扒開(kāi)其 c 語(yǔ)言的外衣,從新回到函數(shù)式編程的角度,同時(shí)摒棄原有類的面向?qū)ο蟾拍钊W(xué)習(xí)領(lǐng)悟它(摘自參考目錄1)。現(xiàn)在的前端中不僅普遍的使用了ES6的新語(yǔ)法,而且在JavaScript的基礎(chǔ)上還出現(xiàn)了TypeScript、CoffeeScript這樣的超集??梢灶A(yù)見(jiàn)的是,目前在前端生態(tài)圈一片繁榮的情況下,對(duì)JSer的需求也會(huì)越來(lái)越多,但同時(shí)也對(duì)前端開(kāi)發(fā)者的JavaScript的水平提出了更加嚴(yán)苛的要求。使用面向?qū)ο蟮乃枷肴ラ_(kāi)發(fā)前端項(xiàng)目也是未來(lái)對(duì)JSer的基本要求之一!

五.參考文章

IBM: 全面理解面向?qū)ο蟮腏avaScript

MDN: 對(duì)象模型的細(xì)節(jié)

阮一峰: Javascript面向?qū)ο缶幊滔盗?/p>

阮一峰: ECMASciprt6入門(mén)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/107287.html

相關(guān)文章

  • 重新認(rèn)識(shí)JavaScript面向對(duì)象: ES5ES6

    摘要:基于原型的面向?qū)ο笤诨谠偷恼Z(yǔ)言中如并不存在這種區(qū)別它只有對(duì)象不論是構(gòu)造函數(shù),實(shí)例,原型本身都是對(duì)象。允許動(dòng)態(tài)地向單個(gè)的對(duì)象或者整個(gè)對(duì)象集中添加或移除屬性。為了解決以上兩個(gè)問(wèn)題,提供了構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認(rèn)識(shí)面向?qū)ο?1. JavaScript...

    VishKozus 評(píng)論0 收藏0
  • 如何JavaScript跨越TypeScript [基礎(chǔ)進(jìn)階知識(shí)點(diǎn)]

    摘要:接口前端程序員很難理解的點(diǎn)也是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,但是中它是基于原型實(shí)現(xiàn)的,中使用了類,這樣會(huì)更清晰的體會(huì)到面向?qū)ο筮@一說(shuō)法,但是實(shí)際在中的面向?qū)ο蟾油暾?,它跟這些語(yǔ)言一樣,通過(guò)接口和類去完整的面向?qū)ο缶幊獭? 從入門(mén)到放棄的java 初中時(shí)自學(xué)過(guò)JAVA,學(xué)了大概一個(gè)多月吧, 學(xué)了一個(gè)多月,看視頻這些,后面放棄了編程。 依稀記得,那段日子極度苦逼,我想如果當(dāng)時(shí)是學(xué)javaScrip...

    wangym 評(píng)論0 收藏0
  • 如何JavaScript跨越TypeScript [基礎(chǔ)進(jìn)階知識(shí)點(diǎn)]

    摘要:接口前端程序員很難理解的點(diǎn)也是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,但是中它是基于原型實(shí)現(xiàn)的,中使用了類,這樣會(huì)更清晰的體會(huì)到面向?qū)ο筮@一說(shuō)法,但是實(shí)際在中的面向?qū)ο蟾油暾?,它跟這些語(yǔ)言一樣,通過(guò)接口和類去完整的面向?qū)ο缶幊獭? 從入門(mén)到放棄的java 初中時(shí)自學(xué)過(guò)JAVA,學(xué)了大概一個(gè)多月吧, 學(xué)了一個(gè)多月,看視頻這些,后面放棄了編程。 依稀記得,那段日子極度苦逼,我想如果當(dāng)時(shí)是學(xué)javaScrip...

    zhunjiee 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<