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