摘要:委托上面的代碼結(jié)合了構(gòu)造函數(shù)和原型兩種方式去創(chuàng)建對(duì)象,首先聊聊構(gòu)造函數(shù)構(gòu)造函數(shù)構(gòu)造函數(shù)本質(zhì)上還是函數(shù),只不過(guò)為了區(qū)分將其首字母大寫(xiě)了而已。注意注釋掉的代碼是自動(dòng)執(zhí)行的,但這并不是構(gòu)造函數(shù)獨(dú)有的,每個(gè)函數(shù)在聲明時(shí)都會(huì)自動(dòng)生成。
首先看看下面兩個(gè)"1+1=2"的問(wèn)題:
問(wèn)題一:為什么改變length的值,數(shù)組的內(nèi)容會(huì)變化?var arr = [1]; arr.length = 3; alert(arr); // [1, undefined, undefined]問(wèn)題二:為什么在showScope函數(shù)內(nèi)能訪(fǎng)問(wèn)outter,在函數(shù)外不能訪(fǎng)問(wèn)inner?
var outter = "sunshine"; function showScope() { var inner = "darkness"; console.log(outter); //"sunshine" } console.log(typeof inner) // undefined
好了,接下來(lái)進(jìn)入正文。
一、對(duì)象的屬性var person = { name: "Simon", _age: 21, isYoung: true, friends: ["Johnny", "Carlton", "Amy"], sayName: function() { console.log(this.name); } educate: { primarySch: "", highSch: "", university: "" } };
上面的person對(duì)象是JS對(duì)象的字面量形式,本質(zhì)上是一個(gè)鍵值對(duì)的無(wú)序集合,這些鍵值 對(duì)叫做屬性。屬性的名稱(chēng)只能是字符串形式的,而屬性的值可以是字符串、數(shù)字、布爾值等基本類(lèi)型,也可以是數(shù)組、函數(shù)、對(duì)象等引用類(lèi)型。值得一提的是,如果屬性的名稱(chēng)是JS能夠識(shí)別的標(biāo)識(shí)符,如name、first_name、$name,則在定義屬性時(shí)不用像json那樣為屬性名加上引號(hào);但屬性名稱(chēng)是first-name這種JS無(wú)法識(shí)別的標(biāo)識(shí)符時(shí),就需要為其加上引號(hào)了。這兩種情況也會(huì)造成訪(fǎng)問(wèn)方式不同,前者既可以通過(guò)person.first_name的形式訪(fǎng)問(wèn),也可以通過(guò)person[first_name]的形式訪(fǎng)問(wèn)。但后者只能通過(guò)中括號(hào)的形式訪(fǎng)問(wèn)。
如果要對(duì)屬性分類(lèi)的話(huà),屬性可以分為兩類(lèi):數(shù)據(jù)屬性、訪(fǎng)問(wèn)器屬性。這兩種屬性都分別有著一些特性:
Configurable: 能否修改或刪除屬性,默認(rèn)為true;
Enumerable: 能否通過(guò)for-in循環(huán)遍歷屬性,默認(rèn)為true;
Writable: 能否修改屬性的值;
Value: 存放屬性的值,默認(rèn)為 undefined;
Configurable: 同上;
Enumerable: 同上;
Get: 在讀取屬性的值時(shí)調(diào)用的函數(shù);
Set: 在設(shè)置屬性的值時(shí)調(diào)用的函數(shù);
這些特性無(wú)法直接訪(fǎng)問(wèn),但可以通過(guò)Object.defineProperty(obj, attr, descriptor)函數(shù)定義這些特性。
基于上面的person對(duì)象各舉一個(gè)例子:
// 數(shù)據(jù)屬性 Object.defineProperty(person, "name", { configurable: false }) console.log(person,name); // Simon person.name = "zai"; console.log(person,name); // Simon //訪(fǎng)問(wèn)器屬性 Object.defineProperty(person, "age", { get: function() { return this._age; }, set: function(newValue) { if (newValue > 30) { this._age = newValue; this.isYoung = false; } } })
到這里第一個(gè)問(wèn)題就得到了解決,數(shù)組的length屬性其實(shí)就是一種訪(fǎng)問(wèn)器屬性。
此外操作屬性的方法還有:Object.defineProperties 用來(lái)一次定義多個(gè)屬性,Object.getOwnPropertyDescriptor(obj, attr) 用來(lái)讀取屬性的特性。另外可以通過(guò)delete操作符去刪除Configurable值為true的屬性。
二、如何創(chuàng)建對(duì)象僅僅通過(guò)字面量的方式去創(chuàng)建對(duì)象顯然是不現(xiàn)實(shí)的,因?yàn)楫?dāng)我們需要?jiǎng)?chuàng)建多個(gè)相似的對(duì)象時(shí),這樣做會(huì)產(chǎn)生大量的重復(fù)代碼。需要一種科學(xué)的方式去創(chuàng)建對(duì)象。
function Person(name, age, friends) { this.name = name; this.age = age; this.friends = friends; // this.prototype = { constructor: this }; } Person.prototype = { constructor: Person, sayName: function() { console.log(this.name); } } Person.prototype.sayAge = function() { console.log(this.age); }; var simon = new Person("Simon", 22, ["Amy", "Johnny", "Carlton"]); simon.sayName(); //委托
上面的代碼結(jié)合了構(gòu)造函數(shù)和原型兩種方式去創(chuàng)建對(duì)象,首先聊聊構(gòu)造函數(shù):
構(gòu)造函數(shù)本質(zhì)上還是函數(shù),只不過(guò)為了區(qū)分將其首字母大寫(xiě)了而已。注意注釋掉的代碼是自動(dòng)執(zhí)行的,但這并不是構(gòu)造函數(shù)獨(dú)有的,每個(gè)函數(shù)在聲明時(shí)都會(huì)自動(dòng)生成prototype。構(gòu)造函數(shù)不一樣的地方在于它的調(diào)用方式——new,new調(diào)用構(gòu)造函數(shù)的大致過(guò)程:
產(chǎn)生一個(gè)新對(duì)象;
將構(gòu)造函數(shù)的作用域賦給新對(duì)象;
執(zhí)行構(gòu)造函數(shù)中的代碼;
返回新對(duì)象或者指定返回的對(duì)象;
構(gòu)造函數(shù)本質(zhì)上仍是函數(shù),所以當(dāng)然可以直接調(diào)用,這樣構(gòu)造函數(shù)中的this就指的是全局對(duì)象,顯然不符合預(yù)期。
《JavaScript高級(jí)程序設(shè)計(jì)》上的一幅圖很好的解釋了原型、構(gòu)造函數(shù)、實(shí)例之間的關(guān)系:
執(zhí)行simon.sayName( )時(shí),首先在simon對(duì)象本身的作用域中尋找sayName,沒(méi)有找到之后再去其原型Person.prototype中尋找,這個(gè)過(guò)程叫做委托。那么問(wèn)題就來(lái)了,當(dāng)我們不知道一個(gè)對(duì)象的構(gòu)成時(shí),如何去判斷一個(gè)屬性屬于對(duì)象還是其原型呢?obj.hasOwnProperty(propName)就是做這個(gè)事情的函數(shù),常常被用在for-in循環(huán)遍歷對(duì)象的屬性的過(guò)程中,與for-in類(lèi)似的兩個(gè)方法:Object.keys(obj)、Object.getOwnPropertyNames(obj) 這兩個(gè)方法返回的都是屬性名的數(shù)組,都不包括原型中的屬性,區(qū)別在于前者和for-in一樣只遍歷enumrable為 true的屬性,而后者遍歷所有屬性。
三、繼承這里給出一種JavaScript實(shí)現(xiàn)繼承的方式:
function Vehicle(maxSpeed, wheels) { this.maxSpeed = maxSpeed; this.wheels = wheels; } Vehicle.prototype.checkMaxSpeed = function() { console.log(this.maxSpeed); }; function Car(brand, maxSpeed) { Vehicle.call(this, maxSpeed, 4); this.brand = brand; } Car.prototype = new Vehicle(); Car.prototype.constructor = Car; Car.prototype.checkBrand = function() { console.log(this.brand); }; var panemera = new Car("Panemera", 250);
這里的關(guān)鍵在于在Car中調(diào)用Vehicle,向父類(lèi)構(gòu)造器傳遞參數(shù),初始化子類(lèi)的屬性,再進(jìn)行擴(kuò)充(brand),當(dāng)然僅僅有構(gòu)造函數(shù)還是不行的,還需要原型鏈才能更好地實(shí)現(xiàn)繼承,這里Car的原型是Vehicle的一個(gè)實(shí)例,值得注意的是Car.prototype = new Vehicle();之后,原本的constructor丟失了,新的constructor在這里指向了Vehicle,需要重置為Car。
之前提出的第二個(gè)問(wèn)題其實(shí)就是用繼承來(lái)實(shí)現(xiàn)的:
function showScope() { // scope代表當(dāng)前作用域 var oldScope = scope; var Scope = function() {}; //繼承當(dāng)前作用域 Scope.prototype = scope; scope = new Scope(); // 進(jìn)入函數(shù)作用域,擴(kuò)充作用域 advance("{"); parse(scope); // 用當(dāng)前作用域做解析 advance("}"); scope =oldScope; }
假設(shè)showScope是解析作用域的函數(shù),它的實(shí)現(xiàn)機(jī)制大概是:進(jìn)入函數(shù)作用域之前保存當(dāng)前作用域,新建一個(gè)繼承了當(dāng)前作用域的對(duì)象并用它取代當(dāng)前作用域,解析左括號(hào)進(jìn)入函數(shù)作用域并對(duì)當(dāng)前作用域進(jìn)行擴(kuò)充,使用擴(kuò)充后的作用域進(jìn)行解析,解析右括號(hào)離開(kāi)函數(shù)作用域,恢復(fù)進(jìn)入函數(shù)前的作用域。
四、私有成員的實(shí)現(xiàn)最后說(shuō)說(shuō)JavaScript中私有成員的實(shí)現(xiàn),一個(gè)很有趣的例子:
function AladdinLamp() { var limit = 3; function rubLamp() { if (limit > 0) { limit -= 1; return true; } else { return false; } } this.satisfyWish = function() { return rubLamp() ? Math.random() : null; }; }
這里的limit和rubLamp都是AladdinLamp的私有成員,無(wú)法從外部直接訪(fǎng)問(wèn),只能通過(guò)唯一暴露出來(lái)的satisfyWish調(diào)用,這實(shí)際上是一種閉包,關(guān)于閉包請(qǐng)參考本專(zhuān)欄中的淺談JavaScript中的閉包
五、ES6中的類(lèi)與繼承上文談到的都是ES5,那么ES6有什么不同呢,先來(lái)看看ES6中的類(lèi):
class Vehicle { constructor(maxSpeed, wheels) { this.maxSpeed = maxSpeed; this.wheels = wheels; } checkMaxSpeed() { console.log(this.maxSpeed); } static openDoor() { console.log("Welcome"); } } Vehicle.length = 100; let bike = new Vehicle(40, 2); // TypeError bike.openDoor();
不同之處在于構(gòu)造函數(shù)換成了Class,其實(shí)Class本質(zhì)上也是函數(shù),constructor就相當(dāng)于ES5中的構(gòu)造函數(shù),而直接在類(lèi)中聲明的checkMaxSpeed實(shí)際相當(dāng)于 Vehicle.prototype.checkMaxSpeed = ...
有意思的是ES6中多了靜態(tài)方法的實(shí)現(xiàn),這里的openDoor無(wú)法在實(shí)例中調(diào)用,可以通過(guò)Vehicle.openDoor直接調(diào)用,可以繼承給子類(lèi)。另外通過(guò)Vehicle.props = ...的形式可以定義靜態(tài)變量。最后注意Vehicle只能通過(guò)new調(diào)用,否則會(huì)報(bào)錯(cuò),是因?yàn)樵赾onstructor中檢測(cè)了new.target。
再看看ES6中的繼承:
class Car extends Vehicle { constructor(maxSpeed, wheels, brand) { super(maxSpeed, wheels); this.brand = brand; } checkBrand() { console.log(this.brand); } }
繼承的關(guān)鍵在于constructor中調(diào)用了super,即父類(lèi)的構(gòu)造函數(shù)。這里一定要調(diào)用super,因?yàn)樽宇?lèi)的this是由super創(chuàng)建的,之后再去擴(kuò)充this。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/84097.html
摘要:前言面試中對(duì)于技術(shù)職位,一般分筆試與面談,如果面試官的一些小問(wèn)題你可以立馬找到對(duì)應(yīng)的知識(shí)點(diǎn)擴(kuò)展開(kāi)來(lái),那么這就是你的優(yōu)勢(shì),本系列將講述一些面試中的事,不會(huì)很詳細(xì),但是應(yīng)該比較全面吧。 前言 面試中對(duì)于技術(shù)職位,一般分筆試與面談,如果面試官的一些小問(wèn)題你可以立馬找到對(duì)應(yīng)的知識(shí)點(diǎn)擴(kuò)展開(kāi)來(lái),那么這就是你的優(yōu)勢(shì),本系列將講述一些java面試中的事,不會(huì)很詳細(xì),但是應(yīng)該比較全面吧。 主要內(nèi)容 pa...
摘要:有需要還可以修改指向謙龍寄生組合式繼承思路是通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混合形式來(lái)繼承方法改變執(zhí)行環(huán)境實(shí)現(xiàn)繼承有需要還可以修改指向謙龍謙龍拷貝繼承該方法思路是將另外一個(gè)對(duì)象的屬性和方法拷貝至另一個(gè)對(duì)象使用遞歸 前言 js中實(shí)現(xiàn)繼承的方式只支持實(shí)現(xiàn)繼承,即繼承實(shí)際的方法,而實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)完成的。 原型鏈?zhǔn)嚼^承 該方式實(shí)現(xiàn)的本質(zhì)是重寫(xiě)原型對(duì)象,代之以一個(gè)新類(lèi)型的實(shí)例...
摘要:也可以這么說(shuō),對(duì)象就好像通訊簿中的一筆數(shù)據(jù)。對(duì)象有已知的事物,并能執(zhí)行工作。對(duì)象本身已知道的事物成為實(shí)例變量,它代表對(duì)象的狀態(tài)。對(duì)象可執(zhí)行的動(dòng)作稱(chēng)為方法,它代表對(duì)象的行為。 閱讀本文約2.1分鐘。 當(dāng)你在設(shè)計(jì)類(lèi)時(shí),要記得對(duì)象時(shí)靠類(lèi)的模型塑造出來(lái)的,你可以這樣看: ——對(duì)象是已知事物 ——對(duì)象會(huì)執(zhí)行的動(dòng)作 對(duì)象本身已知的事物稱(chēng)為實(shí)例變量,它們代表對(duì)象的狀態(tài)(數(shù)據(jù)),且該類(lèi)型的每一個(gè)對(duì)象...
摘要:借用構(gòu)造函數(shù)繼承針對(duì)上面的繼承方法的缺點(diǎn),開(kāi)發(fā)人員使用一種叫做借用構(gòu)造函數(shù)的技術(shù),也就是我們平時(shí)說(shuō)的跟繼承。 繼承是 OO 語(yǔ)言中一個(gè)最為津津樂(lè)道的概念,許多 OO 語(yǔ)言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于函數(shù)沒(méi)有簽名,在 ECMAScript 中無(wú)法實(shí)現(xiàn)接口繼承。ECMAScript 只支持實(shí)現(xiàn)繼承而且實(shí)現(xiàn)繼承主要是依靠原型...
摘要:它包含了一組完善而且容易理解的標(biāo)準(zhǔn)庫(kù),能夠輕松完成很多常見(jiàn)的任務(wù)。代碼這是自年恢復(fù)高考以來(lái)到年的高考報(bào)考及錄取數(shù)據(jù)。為了直觀展示,對(duì)錄取率做了尺度上的變換。 Python(發(fā)音:英[?pa?θ?n],美[?pa?θɑ:n]),是一種面向?qū)ο?、直譯式電腦編程語(yǔ)言,也是一種功能強(qiáng)大的通用型語(yǔ)言,已經(jīng)具有近二十年的發(fā)展歷史,成熟且穩(wěn)定。它包含了一組完善而且容易理解的標(biāo)準(zhǔn)庫(kù),能夠輕松完成很多常...
閱讀 1235·2021-11-23 10:04
閱讀 2478·2021-11-22 15:29
閱讀 3147·2021-11-19 09:40
閱讀 792·2021-09-22 15:26
閱讀 2194·2019-08-29 16:27
閱讀 2565·2019-08-29 16:10
閱讀 1979·2019-08-29 15:43
閱讀 3387·2019-08-29 12:43