摘要:繼承每個構造函數都有一個原型對象,而原型對象中都有一個默認指向構造函數的指針構造函數實例化后,實例化的對象又有一個內部指針指向原型對象所以他們是存在自內而外的一個逐層調用的關系。
面向對象設計
面向對象都有類的概念,所以都可以通過類創建相同屬性方法(同一類嘛)的若干個對象,但是ECMAScript中沒有類,所以它的對象和基于類的語言中的對象有所不同。對象的基本表現形式:
var person = { name:"Nicholas", age:29, job:"Soft Engineer", sayName() { alert(this.name); //this.name被解析為person.name } }屬性類型
JavaScript中為了實現JavaScript引擎規定了內部屬性,內部屬性是不可直接訪問的,用[[property]]表示。ECMA有兩種屬性:數據屬性和訪問器屬性
數據屬性利用這些數據屬性更改操作對象如下:
var person = { name:"liming", // [[Value]]特性將被設置為liming,值得改變反應在[[Value]]上 age:"23", // 同上 } Object.defineProperty(person,"age",{ writable:false, // 不可改值 configurable:false // 不可刪除值, })
注意configurable只能修改一次,沒后悔藥可吃。還有Object.defineProperty()如果默認不制定第三個參數,那么將屬性的特性默認全部為false,這個方法在IE8上盡量不要用。
訪問器屬性訪問器屬性不包含數據值,他包含一對兒getter和setter(不過這兩個都不是必須的).他的核心作用是當一個屬性改變的時候,另一個屬性也隨著他改變。他倆必須同時出現,一下代碼可以很好的理解上面的話:
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}創建對象 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.構造函數模型
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");
在創建Person實例的過程中,必須要new出來,以這種方式調用構造函數會經歷如下四個過程:
1)創建一個新對象
2)把構造函數中的this指向這個新對象
3)執行構造函數的代碼
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
這條規則,也是它勝過工廠模式的一個原因
原型模式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是對的,因為他們共同指向一個引用,而構造函數類型就不一樣,所以可以說在一定方面節省了內存。想知道構造函數實例化的對象和構造函數之間原型的關系,要看書上圖6-1,當能默寫出來基本就理解了。文字描述如下:
當創建一個新函數時,都會創建一個prototype屬性->這個屬性指向函數的原型對象(理解為prototype指向的是一個對象) -> 所有原型對象又會自動獲得一個constructor屬性,而它又指向剛才創建的這個函數(繞回來了)。所以上面的過程可以理解為Person === Person.prototype.constructor // true
第二點,我們通過構造函數創建的實例有一個私有屬性__proto__,這個私有屬性指向構造函數的原型對象,即person1.__proto__ === Person.prototype // true。同理我們可以再繼續往上推,構造函數Person是個函數對象,那么Person也應該有__proto__,他的私有屬性應該是Function.prototype 即Person.__proto__ === Function.prototype //true
創建實例屬性和原型中的屬性重名的時候,他們會同時存在,但是你獲取對象的屬性時,會按照先在實例中搜索再從原型中搜索的順序為搜索原則,即使將實例中的同名屬性設置為null,也會讀取實例屬性,而用delete這個屬性后,實例中搜不到了,才會去原型中獲取。
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
用于檢測屬性是存在于原型中還是實例中。因為是Object繼承下來的方法,所以當然是存在于對象中返回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,無論在實例中,還是在原型中in都能找到,而hasOwnProperty()只能檢測出在實例中,而對象中有沒有就不清楚了。所以引申出了一個檢測只存在于原型中的方法,如下:
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
想獲取對象中實例和原型中的所有可枚舉屬性:for-in([[Enumerable]]為true)的,constructor,hasOwnProperty(),toString()...這類默認都設置成了false,所以枚舉不出來他們,例如:
function Person() {}; Person.prototype.msg = "I"m only in prototype"; person1.__proto__.age = 25; // 和上面定義方式的結果一樣 var person1 = new Person(); person1.name = "andy"; for(var prop in person1) { console.log(prop) // name msg age //沒有constructor,因為不可枚舉 }
想獲取對象中或實例和原型中的所有可枚舉屬性:Object.keys()
//接上個例子 Object.keys(Person.prototype); // ["msg","age"] Object.keys(person1) // ["name"]
想獲取對象中實例或原型中的所有可枚舉和不可枚舉屬性:Object.getOwnPropertyNames()
Object.hasOwnPropertyNames(Person.prototype) // ?["constructor", "msg", "age"]字面量形式定義對象原型
上面的方式中,定義原型屬性的時候都太過繁瑣,可以用對象字面量的形式來定義,如下:
function Person() {}; Person.prototype = { age:25, name:"andy", job:"Soft engineer", sex:"man" }
但這樣定義產生了一個問題 -> constructor的指向不再是Person了,因為我們重寫了Person.prototype(constrctor是原型對象中自動生成的屬性),如果constructor真的很重要,需要我們這樣寫原型,再以上基礎添加一個constructor屬性:
function Person() {}; Person.prototype = { constructor:Person, age:25, name:"andy", job:"Soft engineer", sex:"man" }
但這樣寫又有一個問題 -> constructor的[[Enumerable]] 從默認的false變成true了。所以還要運用前面的知識,Object.defineProperty(Person.prototype,"constructor"),就和之前一樣了。
原型的動態性:用字面量重寫了對象的原型后,實際上會改變對象原型的指針,如高程書中6-3,描述的非常形象,這里看一個例子就能深刻明白了:
var friend = new Person(); Person.prototype.sayHi = function() { alert("hi"); }; friend.sayHi(); // hi
實例與原型存在松散關系,所以引用這個方法不會有問題;
function Person() {} var friend = new Person(); Person.prototype = { ......, sayHi: function() { alert("hi"); }; } friend.sayHi(); // error原生對象的原型
要理解,我們的引用類型,比如arr = new Array 的Array,Object,String...背后也都有prototype原型的,里面有不少方法,當然我們可以為它添加方法,例如:
String.prototype.startWith = function(word) { return this.indexOf(word) === 0; }; "kdsfkjs".startWith("abc") // false; "kdsfkjs".startWith("kdsf") // true;
這種寫法雖然很好,但是不建議用,因為可能產生命名沖突等問題。
原型對象的缺點它最大的問題在于共享引用類型的屬性上面,如下例子
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屬性也引用該地址,所以就會產生我們不想要的結果。
組合使用構造函數和原型構造函數用來實例化獨特的屬性,而原型方法可以創建公有的方法,節省內存,所以他倆組合使用是很好的方式。
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"]繼承
每個構造函數都有一個原型對象(prototype),而原型對象中都有一個默認指向構造函數的指針(constructor),構造函數實例化后,實例化的對象又有一個內部指針(__proto__)指向原型對象所以他們是存在自內而外的一個逐層調用的關系。需要把繼承鏈的圖捯飭清楚就和上面原型鏈的知識沒什么區別了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96635.html
摘要:設計模式是以面向對象編程為基礎的,的面向對象編程和傳統的的面向對象編程有些差別,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續了解設計模式必須要先搞懂面向對象編程,否則只會讓你自己更痛苦。 JavaScript 中的構造函數 學習總結。知識只有分享才有存在的意義。 是時候替換你的 for 循環大法了~ 《小分享》JavaScript中數組的那些迭代方法~ ...
摘要:組合構造原型模式將自身屬性于構造函數中定義,公用的方法綁定至原型對象上原型對象的解釋每一個函數創建時本身內部會有一個固有的原型對象,可以通過函數名去訪問,而其原型對象又有一個屬性指針指向該函數。 每次遇到JS面對對象這個概念,關于繼承及原型,腦海里大概有個知識框架,但是很不系統化,復習下,將其系統化,內容涉及到對象的創建,原型鏈,以及繼承。 創建對象 兩種常用方式,其余的比較少見工廠模...
摘要:每個原型對象都有一個屬性指向關聯的構造函數為了驗證這一說話,舉個例子。 本文共 1475 字,讀完只需 6 分鐘 一、概述 在 JavaScript 中,是一種面向對象的程序設計語言,但是 JS 本身是沒有 類 的概念,JS 是靠原型和原型鏈實現對象屬性的繼承。 在理解原型前,需要先知道對象的構造函數是什么,構造函數都有什么特點? 1. 構造函數 // 構造函數 Person() ...
摘要:面向對象中有三大特征,封裝,繼承,多態。這不僅無法做到數據共享,也是極大的資源浪費,那么引入對象實例對象的屬性指向其構造函數,這樣看起來實例對象好像繼承了對象一樣。實例對象的原型指向其構造函數的對象構造器的指向。 前言 為什么說是再談呢,網上講解這個的博客的很多,我開始學習也是看過,敲過就沒了,自以為理解了就結束了,書到用時方恨少啊。實際開發中一用就打磕巴,于是在重新學習了之后分享出來...
摘要:在創建子類實例時,不能向超類型的構造函數中傳遞參數。構造函數繼承子類傳進的值是基本思想是在子類構造函數的內部調用超類或父類型構造函數。繼承保證構造函數指針指向如果想同時繼承多個,還可使用添加屬性的方式類繼承, OOP:Object Oriented Programming 面向對象編程。 題外話:面向對象的范圍實在太大,先把這些大的東西理解理解。 1.什么是對象? 根據高程和權威指南上...
閱讀 982·2021-11-23 09:51
閱讀 2695·2021-08-23 09:44
閱讀 656·2019-08-30 15:54
閱讀 1433·2019-08-30 13:53
閱讀 3101·2019-08-29 16:54
閱讀 2527·2019-08-29 16:26
閱讀 1186·2019-08-29 13:04
閱讀 2313·2019-08-26 13:50