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