摘要:繼承每個構(gòu)造函數(shù)都有一個原型對象,而原型對象中都有一個默認(rèn)指向構(gòu)造函數(shù)的指針構(gòu)造函數(shù)實(shí)例化后,實(shí)例化的對象又有一個內(nèi)部指針指向原型對象所以他們是存在自內(nèi)而外的一個逐層調(diào)用的關(guān)系。
面向?qū)ο笤O(shè)計
面向?qū)ο蠖加?strong>類的概念,所以都可以通過類創(chuàng)建相同屬性方法(同一類嘛)的若干個對象,但是ECMAScript中沒有類,所以它的對象和基于類的語言中的對象有所不同。對象的基本表現(xiàn)形式:
var person = { name:"Nicholas", age:29, job:"Soft Engineer", sayName() { alert(this.name); //this.name被解析為person.name } }屬性類型
JavaScript中為了實(shí)現(xiàn)JavaScript引擎規(guī)定了內(nèi)部屬性,內(nèi)部屬性是不可直接訪問的,用[[property]]表示。ECMA有兩種屬性:數(shù)據(jù)屬性和訪問器屬性
數(shù)據(jù)屬性利用這些數(shù)據(jù)屬性更改操作對象如下:
var person = { name:"liming", // [[Value]]特性將被設(shè)置為liming,值得改變反應(yīng)在[[Value]]上 age:"23", // 同上 } Object.defineProperty(person,"age",{ writable:false, // 不可改值 configurable:false // 不可刪除值, })
注意configurable只能修改一次,沒后悔藥可吃。還有Object.defineProperty()如果默認(rèn)不制定第三個參數(shù),那么將屬性的特性默認(rèn)全部為false,這個方法在IE8上盡量不要用。
訪問器屬性訪問器屬性不包含數(shù)據(jù)值,他包含一對兒getter和setter(不過這兩個都不是必須的).他的核心作用是當(dāng)一個屬性改變的時候,另一個屬性也隨著他改變。他倆必須同時出現(xiàn),一下代碼可以很好的理解上面的話:
var article = { _year:2018, edition: 1 } Object.defineProperty(article,"year",{ get: function() { return _this.year } set:function(val) { if(val > 2018) { this._year = val; edition += val - 2018; } } })讀取屬性的特性
在這個例子中
var book = {}; Object.defineProperties(book,{ _year: { writable:true, value:2018 }, edition: { writable:true, value:1 }, year: { get:function() { return this._year }, set:function(val) { if(val > 2018) { this._year = val; this.edition += val - 2018; } } } })
我們可以使用Object.getOwnPropertyDescriptor的方法獲得屬性的描述符。它返回一個對象,因此在上面的代碼中,加入如下代碼:
var descriptor = Object.getOwnPropertyDescriptor(book,"_year"); // {value: 2018, writable: true, enumerable: false, configurable: false} var descriptor2 = Object.getOwnPropertyDescriptor(book,"year"); // {get: ?, set: ?, enumerable: false, configurable: false}創(chuàng)建對象 1.工廠模式
function createPerson(name,age,job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name) } return o; } var person1 = createPerson("namea",27,"joba") // {name: "namea", age: 27, job: "joba", sayName: ?} var person2 = createPerson("nameb",17,"jobb") // {name: "nameb", age: 17, job: "jobb", sayName: ?}2.構(gòu)造函數(shù)模型
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); }; } person1 = new Person("namea",27,"joba"); person2 = new Person("nameb",17,"jobb");
在創(chuàng)建Person實(shí)例的過程中,必須要new出來,以這種方式調(diào)用構(gòu)造函數(shù)會經(jīng)歷如下四個過程:
1)創(chuàng)建一個新對象
2)把構(gòu)造函數(shù)中的this指向這個新對象
3)執(zhí)行構(gòu)造函數(shù)的代碼
4)返回新對象
function Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); }; } person1 = new Person("namea",27,"joba"); console.log(person1 instanceof Person) // true console.log(person1 instanceof Object) // true
這條規(guī)則,也是它勝過工廠模式的一個原因
原型模式function Person (){} Person.prototype.name ="andy"; Person.prototype.age = 25; Person.prototype.sayName = function() { alert(this.name) } var person1 = new Person(); var person2 = new Person();
person1.sayName === person2.sayName是對的,因?yàn)樗麄児餐赶蛞粋€引用,而構(gòu)造函數(shù)類型就不一樣,所以可以說在一定方面節(jié)省了內(nèi)存。想知道構(gòu)造函數(shù)實(shí)例化的對象和構(gòu)造函數(shù)之間原型的關(guān)系,要看書上圖6-1,當(dāng)能默寫出來基本就理解了。文字描述如下:
當(dāng)創(chuàng)建一個新函數(shù)時,都會創(chuàng)建一個prototype屬性->這個屬性指向函數(shù)的原型對象(理解為prototype指向的是一個對象) -> 所有原型對象又會自動獲得一個constructor屬性,而它又指向剛才創(chuàng)建的這個函數(shù)(繞回來了)。所以上面的過程可以理解為Person === Person.prototype.constructor // true
第二點(diǎn),我們通過構(gòu)造函數(shù)創(chuàng)建的實(shí)例有一個私有屬性__proto__,這個私有屬性指向構(gòu)造函數(shù)的原型對象,即person1.__proto__ === Person.prototype // true。同理我們可以再繼續(xù)往上推,構(gòu)造函數(shù)Person是個函數(shù)對象,那么Person也應(yīng)該有__proto__,他的私有屬性應(yīng)該是Function.prototype 即Person.__proto__ === Function.prototype //true
創(chuàng)建實(shí)例屬性和原型中的屬性重名的時候,他們會同時存在,但是你獲取對象的屬性時,會按照先在實(shí)例中搜索再從原型中搜索的順序?yàn)樗阉髟瓌t,即使將實(shí)例中的同名屬性設(shè)置為null,也會讀取實(shí)例屬性,而用delete這個屬性后,實(shí)例中搜不到了,才會去原型中獲取。
function Person() {} Person.prototype.name = "andy"; Person.prototype.sayName = function() { console.log(this.name); }; Person.prototype.age = 25; var person1 = new Person(); var person2 = new Person(); person1.name = "liming"; console.log(person1.name) // liming console.log(person2.name) // andy person1.name = null; console.log(person1.name) // null delete person1.name // andyhasOwnProperty
用于檢測屬性是存在于原型中還是實(shí)例中。因?yàn)槭荗bject繼承下來的方法,所以當(dāng)然是存在于對象中返回true,上面的例子加上如下處理:
function Person() {} Person.prototype.name = "andy"; Person.prototype.sayName = function() { console.log(this.name); }; Person.prototype.age = 25; var person1 = new Person(); var person2 = new Person(); person1.hasOwnProperty("name") // false person1.name = "liming"; person1.hasOwnProperty("name") // true原型與in操作符(in與for in)
in操作符返回的是true和false,無論在實(shí)例中,還是在原型中in都能找到,而hasOwnProperty()只能檢測出在實(shí)例中,而對象中有沒有就不清楚了。所以引申出了一個檢測只存在于原型中的方法,如下:
function hasPrototypeProperty(obj,property) { return !obj.hasOwnProperty(property) && (property in obj); } function Person() {}; Person.prototype.msg = "I"m only in prototype"; var person1 = new Person(); person1.name = "andy"; hasPrototypeProperty(person1,"msg"); // true hasPrototypeProperty(person1,"name"); // false
想獲取對象中實(shí)例和原型中的所有可枚舉屬性:for-in([[Enumerable]]為true)的,constructor,hasOwnProperty(),toString()...這類默認(rèn)都設(shè)置成了false,所以枚舉不出來他們,例如:
function Person() {}; Person.prototype.msg = "I"m only in prototype"; person1.__proto__.age = 25; // 和上面定義方式的結(jié)果一樣 var person1 = new Person(); person1.name = "andy"; for(var prop in person1) { console.log(prop) // name msg age //沒有constructor,因?yàn)椴豢擅杜e }
想獲取對象中或?qū)嵗驮椭械乃锌擅杜e屬性:Object.keys()
//接上個例子 Object.keys(Person.prototype); // ["msg","age"] Object.keys(person1) // ["name"]
想獲取對象中實(shí)例或原型中的所有可枚舉和不可枚舉屬性:Object.getOwnPropertyNames()
Object.hasOwnPropertyNames(Person.prototype) // ?["constructor", "msg", "age"]字面量形式定義對象原型
上面的方式中,定義原型屬性的時候都太過繁瑣,可以用對象字面量的形式來定義,如下:
function Person() {}; Person.prototype = { age:25, name:"andy", job:"Soft engineer", sex:"man" }
但這樣定義產(chǎn)生了一個問題 -> constructor的指向不再是Person了,因?yàn)槲覀冎貙懥薖erson.prototype(constrctor是原型對象中自動生成的屬性),如果constructor真的很重要,需要我們這樣寫原型,再以上基礎(chǔ)添加一個constructor屬性:
function Person() {}; Person.prototype = { constructor:Person, age:25, name:"andy", job:"Soft engineer", sex:"man" }
但這樣寫又有一個問題 -> constructor的[[Enumerable]] 從默認(rèn)的false變成true了。所以還要運(yùn)用前面的知識,Object.defineProperty(Person.prototype,"constructor"),就和之前一樣了。
原型的動態(tài)性:用字面量重寫了對象的原型后,實(shí)際上會改變對象原型的指針,如高程書中6-3,描述的非常形象,這里看一個例子就能深刻明白了:
var friend = new Person(); Person.prototype.sayHi = function() { alert("hi"); }; friend.sayHi(); // hi
實(shí)例與原型存在松散關(guān)系,所以引用這個方法不會有問題;
function Person() {} var friend = new Person(); Person.prototype = { ......, sayHi: function() { alert("hi"); }; } friend.sayHi(); // error原生對象的原型
要理解,我們的引用類型,比如arr = new Array 的Array,Object,String...背后也都有prototype原型的,里面有不少方法,當(dāng)然我們可以為它添加方法,例如:
String.prototype.startWith = function(word) { return this.indexOf(word) === 0; }; "kdsfkjs".startWith("abc") // false; "kdsfkjs".startWith("kdsf") // true;
這種寫法雖然很好,但是不建議用,因?yàn)榭赡墚a(chǎn)生命名沖突等問題。
原型對象的缺點(diǎn)它最大的問題在于共享引用類型的屬性上面,如下例子
function Person() {} Person.prototype = { constructor: Person, name: "andy", age: 25, friend : ["shelby","jony"], sayHi : function() { alert("Hi"); } } var person1 = new Person(); var person2 = new Person(); person1.friend.push("lee") // 目的是給person1這個對象里的friend增加自己的一個值。 alert(person1.friend) //["shelby","jony","lee"] alert(person2.friend) //["shelby","jony","lee"]
由于friend是引用類型屬性,指向一個地址,而friend最初還在prototype中,所以給引用類型操作后,只是在引用地址不變的前提下修改屬性值,而person2的friend屬性也引用該地址,所以就會產(chǎn)生我們不想要的結(jié)果。
組合使用構(gòu)造函數(shù)和原型構(gòu)造函數(shù)用來實(shí)例化獨(dú)特的屬性,而原型方法可以創(chuàng)建公有的方法,節(jié)省內(nèi)存,所以他倆組合使用是很好的方式。
function Person(name,age,job) { this.age = age; this.name = name; this.job = job; this.friends = ["lee","jony"]; } Person.prototype = { constructor:Person, sayName:function() { alert(this.name) } } var person1 = new Person("Nicholas",29,"Software engineer"); var person2 = new Person("Greg",27,"Doctor"); person1.friends.push("marry"); console.log(person1.friends) // ["lee","jony","marry"]; console.log(person2.friends) // ["lee","jony"]繼承
每個構(gòu)造函數(shù)都有一個原型對象(prototype),而原型對象中都有一個默認(rèn)指向構(gòu)造函數(shù)的指針(constructor),構(gòu)造函數(shù)實(shí)例化后,實(shí)例化的對象又有一個內(nèi)部指針(__proto__)指向原型對象所以他們是存在自內(nèi)而外的一個逐層調(diào)用的關(guān)系。需要把繼承鏈的圖捯飭清楚就和上面原型鏈的知識沒什么區(qū)別了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/96635.html
摘要:設(shè)計模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計模式必須要先搞懂面向?qū)ο缶幊?,否則只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學(xué)習(xí)總結(jié)。知識只有分享才有存在的意義。 是時候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...
摘要:組合構(gòu)造原型模式將自身屬性于構(gòu)造函數(shù)中定義,公用的方法綁定至原型對象上原型對象的解釋每一個函數(shù)創(chuàng)建時本身內(nèi)部會有一個固有的原型對象,可以通過函數(shù)名去訪問,而其原型對象又有一個屬性指針指向該函數(shù)。 每次遇到JS面對對象這個概念,關(guān)于繼承及原型,腦海里大概有個知識框架,但是很不系統(tǒng)化,復(fù)習(xí)下,將其系統(tǒng)化,內(nèi)容涉及到對象的創(chuàng)建,原型鏈,以及繼承。 創(chuàng)建對象 兩種常用方式,其余的比較少見工廠模...
摘要:每個原型對象都有一個屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)為了驗(yàn)證這一說話,舉個例子。 本文共 1475 字,讀完只需 6 分鐘 一、概述 在 JavaScript 中,是一種面向?qū)ο蟮某绦蛟O(shè)計語言,但是 JS 本身是沒有 類 的概念,JS 是靠原型和原型鏈實(shí)現(xiàn)對象屬性的繼承。 在理解原型前,需要先知道對象的構(gòu)造函數(shù)是什么,構(gòu)造函數(shù)都有什么特點(diǎn)? 1. 構(gòu)造函數(shù) // 構(gòu)造函數(shù) Person() ...
摘要:面向?qū)ο笾杏腥筇卣?,封裝,繼承,多態(tài)。這不僅無法做到數(shù)據(jù)共享,也是極大的資源浪費(fèi),那么引入對象實(shí)例對象的屬性指向其構(gòu)造函數(shù),這樣看起來實(shí)例對象好像繼承了對象一樣。實(shí)例對象的原型指向其構(gòu)造函數(shù)的對象構(gòu)造器的指向。 前言 為什么說是再談呢,網(wǎng)上講解這個的博客的很多,我開始學(xué)習(xí)也是看過,敲過就沒了,自以為理解了就結(jié)束了,書到用時方恨少啊。實(shí)際開發(fā)中一用就打磕巴,于是在重新學(xué)習(xí)了之后分享出來...
摘要:在創(chuàng)建子類實(shí)例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。構(gòu)造函數(shù)繼承子類傳進(jìn)的值是基本思想是在子類構(gòu)造函數(shù)的內(nèi)部調(diào)用超類或父類型構(gòu)造函數(shù)。繼承保證構(gòu)造函數(shù)指針指向如果想同時繼承多個,還可使用添加屬性的方式類繼承, OOP:Object Oriented Programming 面向?qū)ο缶幊獭?題外話:面向?qū)ο蟮姆秶鷮?shí)在太大,先把這些大的東西理解理解。 1.什么是對象? 根據(jù)高程和權(quán)威指南上...
閱讀 996·2021-11-23 09:51
閱讀 2710·2021-08-23 09:44
閱讀 670·2019-08-30 15:54
閱讀 1443·2019-08-30 13:53
閱讀 3118·2019-08-29 16:54
閱讀 2536·2019-08-29 16:26
閱讀 1205·2019-08-29 13:04
閱讀 2328·2019-08-26 13:50