摘要:上面的代碼,運(yùn)行以后,我們可以看到因?yàn)榈脑褪侵赶虻膶?shí)例上的,所以可以訪問他的屬性值,那如果我不想讓訪問的構(gòu)造函數(shù)里聲明的屬性值,那怎么辦呢只需要將指向的原型而不是實(shí)例就行了。
走在前端的大道上
本篇將自己讀過的相關(guān) javascript原型和原型鏈 文章中,對自己有啟發(fā)的章節(jié)片段總結(jié)在這(會(huì)對原文進(jìn)行刪改),會(huì)不斷豐富提煉總結(jié)更新。
文章——深入理解javascript之原型一般的初學(xué)者,在剛剛學(xué)習(xí)了基本的javascript語法后,都是通過面向函數(shù)來編程的。如下代碼:
var decimalDigits = 2, tax = 5; function add(x, y) { return x + y; } function subtract(x, y) { return x - y; } //alert(add(1, 3));
通過執(zhí)行各個(gè)函數(shù)來得到最后的結(jié)果。但是利用原型,我們可以優(yōu)化一些我們的代碼,使用構(gòu)造函數(shù):
首先,函數(shù)本體中只存放變量:
var Calculator = function (decimalDigits, tax) { this.decimalDigits = decimalDigits; this.tax = tax; };
其具體的方法通過prototype屬性來設(shè)置:
Calculator.prototype = { add: function (x, y) { return x + y; }, subtract: function (x, y) { return x - y; } }; //alert((new Calculator()).add(1, 3));
這樣就可以通過實(shí)例化對象后進(jìn)行相應(yīng)的函數(shù)操作。這也是一般的js框架采用的方法。
原型還有一個(gè)作用就是用來實(shí)現(xiàn)繼承。首先,定義父對象:
var BaseCalculator = function() { this.decimalDigits = 2; }; BaseCalculator.prototype = { add: function(x, y) { return x + y; }, subtract: function(x, y) { return x - y; } };
然后定義子對象,將子對象的原型指向父元素的實(shí)例化:
var Calculator = function () { //為每個(gè)實(shí)例都聲明一個(gè)稅收數(shù)字 this.tax = 5; }; Calculator.prototype = new BaseCalculator();
我們可以看到Calculator的原型是指向到BaseCalculator的一個(gè)實(shí)例上,目的是讓Calculator集成它的add(x,y)和subtract(x,y)這2個(gè)function,還有一點(diǎn)要說的是,由于它的原型是BaseCalculator的一個(gè)實(shí)例,所以不管你創(chuàng)建多少個(gè)Calculator對象實(shí)例,他們的原型指向的都是同一個(gè)實(shí)例。
上面的代碼,運(yùn)行以后,我們可以看到因?yàn)镃alculator的原型是指向BaseCalculator的實(shí)例上的,所以可以訪問他的decimalDigits屬性值,那如果我不想讓Calculator訪問BaseCalculator的構(gòu)造函數(shù)里聲明的屬性值,那怎么辦呢?只需要將Calculator指向BaseCalculator的原型而不是實(shí)例就行了。代碼如下:
var Calculator = function () { this.tax= 5; }; Calculator.prototype = BaseCalculator.prototype;
在使用第三方庫的時(shí)候,有時(shí)候他們定義的原型方法不能滿足我們的需要,我們就可以自己添加一些方法,代碼如下:
//覆蓋前面Calculator的add() function Calculator.prototype.add = function (x, y) { return x + y + this.tax; }; var calc = new Calculator(); alert(calc.add(1, 1));
原型鏈
對象的原型指向?qū)ο蟮母福傅脑陀种赶蚋傅母福@種原型層層的關(guān)系,叫做原型鏈。
在查找一個(gè)對象的屬性時(shí),javascript會(huì)向上遍歷原型鏈,直到找到給定名稱的屬性為止,當(dāng)查找到達(dá)原型鏈的頂部,也即是Object.prototype,仍然沒有找到指定的屬性,就會(huì)返回undefined。
示例如下:
function foo() { this.add = function (x, y) { return x + y; } } foo.prototype.add = function (x, y) { return x + y + 10; } Object.prototype.subtract = function (x, y) { return x - y; } var f = new foo(); alert(f.add(1, 2)); //結(jié)果是3,而不是13 alert(f.subtract(1, 2)); //結(jié)果是-1
我們可以發(fā)現(xiàn),subtrace是按照向上找的原則,而add則出了意外。原因就是,屬性在查找的時(shí)候是先查找自身的屬性,如果沒有再查找原型。
說到Object.prototype,就不得不提它的一個(gè)方法,hasOwnProperty。它能判斷一個(gè)對象是否包含自定義屬性而不是原型鏈上的屬性,它是javascript中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)。使用代碼如下:
// 修改Object.prototype Object.prototype.bar = 1; var foo = {goo: undefined}; foo.bar; // 1 "bar" in foo; // true foo.hasOwnProperty("bar"); // false foo.hasOwnProperty("goo"); // true
而為了判斷prototype對象和某個(gè)實(shí)例之間的關(guān)系,又不得不介紹isPrototyleOf方法,演示如下:
alert(Cat.prototype.isPrototypeOf(cat2)); //true文章——白話原型和原型鏈 1. 背景知識(shí)
JavaScript和Java、C++等傳統(tǒng)面向?qū)ο蟮木幊陶Z言不同,它是沒有類(class)的概念的(ES6 中的class也只不過是語法糖,并非真正意義上的類),而在JavaScript中,一切皆是對象(object)。在基于類的傳統(tǒng)面向?qū)ο蟮木幊陶Z言中,對象由類實(shí)例化而來,實(shí)例化的過程中,類的屬性和方法會(huì)拷貝到這個(gè)對象中;對象的繼承實(shí)際上是類的繼承,在定義子類繼承于父類時(shí),子類會(huì)將父類的屬性和方法拷貝到自身當(dāng)中。因此,這類語言中,對象創(chuàng)建和繼承行為都是通過拷貝完成的。但在JavaScript中,對象的創(chuàng)建、對象的繼承(更好的叫法是對象的代理,因?yàn)樗⒉皇莻鹘y(tǒng)意義上的繼承)是不存在拷貝行為的。現(xiàn)在讓我們忘掉類、忘掉繼承,這一切都不屬于JavaScript。
2. 原型和原型鏈其實(shí),原型這個(gè)名字本身就很容易產(chǎn)生誤解,原型在百度詞條中的釋義是:指原來的類型或模型。按照這個(gè)定義解釋的話,對象的原型是對象創(chuàng)建自身的模子,模子具備的特點(diǎn)對象都要具有,這儼然就是拷貝的概念。我們已經(jīng)說過, JavaScript的對象創(chuàng)建不存在拷貝,對象的原型實(shí)際上也是一個(gè)對象,它和對象本身是完全獨(dú)立的兩個(gè)對象。既然如此,原型存在的意義又是什么呢?原型是為了共享多個(gè)對象之間的一些共有特性(屬性或方法),這個(gè)功能也是任何一門面向?qū)ο蟮木幊陶Z言必須具備的。A、B兩個(gè)對象的原型相同,那么它們必然有一些相同的特征。
JavaScript中的對象,都有一個(gè)內(nèi)置屬性[[Prototype]],指向這個(gè)對象的原型對象。當(dāng)查找一個(gè)屬性或方法時(shí),如果在當(dāng)前對象中找不到定義,會(huì)繼續(xù)在當(dāng)前對象的原型對象中查找;如果原型對象中依然沒有找到,會(huì)繼續(xù)在原型對象的原型中查找(原型也是對象,也有它自己的原型);如此繼續(xù),直到找到為止,或者查找到最頂層的原型對象中也沒有找到,就結(jié)束查找,返回undefined。可以看出,這個(gè)查找過程是一個(gè)鏈?zhǔn)降牟檎遥總€(gè)對象都有一個(gè)到它自身原型對象的鏈接,這些鏈接組成的整個(gè)鏈條就是原型鏈。擁有相同原型的多個(gè)對象,他們的共同特征正是通過這種查找模式體現(xiàn)出來的。
在上面的查找過程,我們提到了最頂層的原型對象,這個(gè)對象就是Object.prototype,這個(gè)對象中保存了最常用的方法,如toString、valueOf、hasOwnProperty等,因此我們才能在任何對象中使用這些方法。
3.創(chuàng)建對象常見的三種方式1.字面量方式
當(dāng)通過字面量方式創(chuàng)建對象時(shí),它的原型就是Object.prototype。雖然我們無法直接訪問內(nèi)置屬性[[Prototype]],但我們可以通過Object.getPrototypeOf()或?qū)ο蟮腳_proto__獲取對象的原型。
var obj = {}; Object.getPrototypeOf(obj) === Object.prototype; // true obj.__proto__ === Object.prototype; // true
2.函數(shù)的構(gòu)造調(diào)用
通過函數(shù)的構(gòu)造調(diào)用(注意,我們不把它叫做構(gòu)造函數(shù),因?yàn)镴avaScript中同樣沒有構(gòu)造函數(shù)的概念,所有的函數(shù)都是平等的,只不過用來創(chuàng)建對象時(shí),函數(shù)的調(diào)用方式不同而已)也是一種常用的創(chuàng)建對象的方式。基于同一個(gè)函數(shù)創(chuàng)建出來的對象,理應(yīng)可以共享一些相同的屬性或方法,但這些屬性或方法如果放在Object.prototype里,那么所有的對象都可以使用它們了,作用域太大,顯然不合適。于是,JavaScript在定義一個(gè)函數(shù)時(shí),同時(shí)為這個(gè)函數(shù)定義了一個(gè) 默認(rèn)的prototype屬性,所有共享的屬性或方法,都放到這個(gè)屬性所指向的對象中。由此看出,通過一個(gè)函數(shù)的構(gòu)造調(diào)用創(chuàng)建的對象,它的原型就是這個(gè)函數(shù)的prototype指向的對象。
var f = function(name) { this.name = name }; f.prototype.getName = function() { return this.name; } //在prototype下存放所有對象的共享方法 var obj = new f("JavaScript"); obj.getName(); // JavaScript obj.__proto__ === f.prototype; // true
//創(chuàng)建構(gòu)造函數(shù) function Person(name){ this.name = name } //每個(gè)構(gòu)造函數(shù)JS引擎都會(huì)自動(dòng)添加一個(gè)prototype屬性,我們稱之為原型,這是一個(gè)對象 //每個(gè)由構(gòu)造函數(shù)創(chuàng)建的對象都會(huì)共享prototype上面的屬性與方法 console.log(typeof Person.prototype) // "object" //我們?yōu)镻erson.prototype添加sayName方法 Person.prototype.sayName = function(){ console.log(this.name) } //創(chuàng)建實(shí)例 var person1 = new Person("Messi") var person2 = new Person("Suarez") person1.sayName() // "Messi" person2.sayName() // "Suarez" person1.sayName === person2.sayName //true
我們借助上面的例子來理解構(gòu)造函數(shù)-原型-實(shí)例,三者之間的關(guān)系,主要有幾個(gè)基本概念
構(gòu)造函數(shù)默認(rèn)會(huì)有一個(gè)protoype屬性指向它的原型
構(gòu)造函數(shù)的原型會(huì)有一個(gè)consctructor的屬性指向構(gòu)造函數(shù)本身, 即
Person.prototype.constructor === Person
每一個(gè)new出來的實(shí)例都有一個(gè)隱式的__proto__屬性,指向它們的構(gòu)造函數(shù)的原型,即
person1.__proto__ === Person.prototype person1.__proto__.constructor === Person
Oject本身是一個(gè)構(gòu)造函數(shù),它也是一個(gè)對象,那么
Object.__proto__ === Function.prototype
還有幾個(gè)需要我們知道的特殊概念:
Function的原型屬性與Function的原型指向同一個(gè)對象. 即
Function.__proto__ == Function.prototype
Object.prototype.__proto__ === null
typeof Function.prototype === "function"
3.Object.create()
第三種常用的創(chuàng)建對象的方式是使用Object.create()。這個(gè)方法會(huì)以你傳入的對象作為創(chuàng)建出來的對象的原型。
var obj = {}; var obj2 = Object.create(obj); obj2.__proto__ === obj; // true
這種方式還可以模擬對象的“繼承”行為。
function Foo(name) { this.name = name; } Foo.prototype.myName = function() { return this.name; }; function Bar(name,label) { Foo.call( this, name ); // this.label = label; } // temp對象的原型是Foo.prototype var temp = Object.create( Foo.prototype ); // 通過new Bar() 創(chuàng)建的對象,其原型是temp, 而temp的原型是Foo.prototype, // 從而兩個(gè)原型對象Bar.prototype和Foo.prototype 有了"繼承"關(guān)系 Bar.prototype = temp; Bar.prototype.myLabel = function() { return this.label; }; var a = new Bar( "a", "obj a" ); a.myName(); // "a" a.myLabel(); // "obj a" a.__proto__.__proto__ === Foo.prototype; //true4. __proto__和prototype
這是容易混淆的兩個(gè)屬性。__proto__指向當(dāng)前對象的原型,prototype是函數(shù)才具有的屬性,默認(rèn)情況下,new 一個(gè)函數(shù)創(chuàng)建出的對象,其原型都指向這個(gè)函數(shù)的prototype屬性。
5. 三種特殊情況1.對于JavaScript中的內(nèi)置對象,如String、Number、Array、Object、Function等,因?yàn)樗麄兪莕ative代碼實(shí)現(xiàn)的,他們的原型打印出來都是? () { [native code] }。
2.內(nèi)置對象本質(zhì)上也是函數(shù),所以可以通過他們創(chuàng)建對象,創(chuàng)建出的對象的原型指向?qū)?yīng)內(nèi)置對象的prototype屬性,最頂層的原型對象依然指向Object.prototype。
"abc".__proto__ === String.prototype; // true new String("abc").__proto__ === String.prototype; //true new Number(1).__proto__ ==== Number.prototype; // true [1,2,3].__proto__ === Array.prototype; // true new Array(1,2,3).__proto__ === Array.prototype; // true ({}).__proto__ === Object.prototype; //true new Object({}).__proto__ === Object.prototype; // true var f = function() {}; f.__proto__ === Function.prototype; // true var f = new Function("{}"); f.__proto__ === Function.prototype; // true
3.Object.create(null) 創(chuàng)建出的對象,不存在原型。
var a = Object.create(null); a.__proto__; // undefined
此外函數(shù)的prototype中還有一個(gè)constructor方法,建議大家就當(dāng)它不存在,它的存在讓JavaScript原型的概念變得更加混亂,而且這個(gè)方法也幾乎沒有作用。
推薦閱讀Javascript中的原型鏈、prototype、__proto__的關(guān)系
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/90164.html
摘要:原文鏈接關(guān)于的原型和原型鏈,看我就夠了一參考鏈接闖關(guān)記之原型及原型鏈之原型與原型鏈一篇文章帶你理解原型和原型鏈徹底理解原型鏈一的默認(rèn)指向圖解和的三角關(guān)系原型和原型鏈三張圖搞懂的原型對象與原型鏈 溫故 創(chuàng)建對象的三種方式 通過對象直接量 通過new創(chuàng)建對象 通過Object.create() js中對象分為兩種 函數(shù)對象 普通對象 仔細(xì)觀察如下代碼 function Foo(na...
摘要:走在前端的大道上本篇是一篇文章帶你理解原型和原型鏈一篇文章帶你完全理解的查漏補(bǔ)缺,會(huì)不斷豐富提煉總結(jié)更新。 走在前端的大道上 本篇是 一篇文章帶你理解原型和原型鏈 、一篇文章帶你完全理解this的查漏補(bǔ)缺,會(huì)不斷豐富提煉總結(jié)更新。 什么是原型鏈 原型鏈 是針對構(gòu)造函數(shù)的,比如我先創(chuàng)建了一個(gè)函數(shù),然后通過一個(gè)變量new了這個(gè)函數(shù),那么這個(gè)被new出來的函數(shù)就會(huì)繼承創(chuàng)建出來的那個(gè)函數(shù)的屬性...
摘要:返回值在指定原型對象上添加新屬性后的對象。該方法返回值被用作屬性值。這個(gè)方法返回值就是屬性存取表達(dá)式返回的值。 走在前端的大道上 最后更新 2018.12.27 本篇將自己讀過的相關(guān) javascript Object方法 文章中,對自己有啟發(fā)的章節(jié)片段總結(jié)在這(會(huì)對原文進(jìn)行刪改),會(huì)不斷豐富提煉總結(jié)更新。 1.Object.keys遍歷 返回一個(gè)數(shù)組,包括對象自身的(不含繼承的)所有...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:除了以上介紹的幾種對象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點(diǎn):封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個(gè)類繼承(復(fù)制)另一個(gè)類的屬性/方法 多態(tài):方...
閱讀 1915·2023-04-26 01:56
閱讀 3112·2021-11-18 10:02
閱讀 3051·2021-09-09 11:35
閱讀 1284·2021-09-03 10:28
閱讀 3408·2019-08-29 18:36
閱讀 2846·2019-08-29 17:14
閱讀 833·2019-08-29 16:10
閱讀 1616·2019-08-26 13:45