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