摘要:所以,可以創(chuàng)建自定義的構造函數(shù),從而定義自定義對象類型的屬性和方法。如對于構造函數(shù)原型屬性以及實例之間的關系,參見高級程序設計一書中第章節(jié)。穩(wěn)妥構造函數(shù)模式穩(wěn)妥對象,指的是沒有公共屬性,且其方法也不引用的對象如
創(chuàng)建對象
Object 構造函數(shù)或對象字面量都可以用來創(chuàng)建單個對象。但這個方法的缺點非常明顯:同一個接口創(chuàng)建很可耐多對象會產生大量的重復代碼。為了解決這個問題,人們開始使用工廠模式的一種變體。
工廠模式(摒棄,不推薦)這個模式沒有解決對象識別的問題(即怎樣知道一個對象的類型)。如:
具體的創(chuàng)建單個對象:
var person = {}; person.name = "Oliver"; person.age = 18; person.sayName = function(){ return this.Name; };
改變成工廠模式:
function createPerson(name,age){ var obj = {}; obj.name = name; obj.age = age; obj.sayName = function(){ return this.name }; return obj; //注意這里要返回obj 對象,這樣才能把obj 對象傳給createPerson 變量。 } var newPerson = createPerson("Oliver",18);構造函數(shù)模式
構造函數(shù)可以創(chuàng)建特定類型的對象。所以,可以創(chuàng)建自定義的構造函數(shù),從而定義自定義對象類型的屬性和方法。如:
function Person(name,age){ //注意大小寫,構造函數(shù)應該把第一個字幕大寫化 this.name = name; this.age = age; this.sayName = function (){ return this.name; }; } var newPerson = new Person("Oliver",18); document.write(newPerson.name); //Oliver document.write(newPerson.age); //18 document.write(newPerson.sayName()); //Oliver
確實相當方便
這里一定要記住,構造函數(shù)都應該以一個大寫字母開頭,用來區(qū)分其他的函數(shù)
又如:
function Person(name,age){ //注意大小寫,構造函數(shù)應該把第一個字幕大寫化 this.name = name; this.age = age; this.sayName = function (){ return this.name; }; } var person1 = new Person("Oliver",18); var person2 = new Person("Troy",24); document.write(person1.constructor == Person); //true document.write(person2.constructor == Person); //true
這里的person1 和person2 分別保存著Person 的一個不同的實例。兩個對象都有一個constructor(構造函數(shù))屬性,該屬性指向Person。
在上面這個例子中,person1 和person2 即是Object 的實例,同時也是Person 的實例。可以通過instanceof 操作符來驗證:
console.log((person1 instanceof Object) && (person2 instanceof Person)); //true
以這種方式創(chuàng)建構造函數(shù)是定義在Global 中的(window 對象)
將構造函數(shù)當做函數(shù)任何函數(shù),只要通過new 操作符來調用,那它就可以座位構造函數(shù);而任何函數(shù),如果不通過new 操作符來調用,那它就跟普通函數(shù)沒區(qū)別。如下面這個構造函數(shù):
function Car(name,color,sound){ this.name = name; this.color = color; this.sound = function(){ return sound; }; console.log(this.name + " " + this.color + " " + this.sound()); }
如果當做構造函數(shù)來使用:
var benz = new Car("C200","White","Boom Boom"); //C200 White Boom Boom
如果作為普通函數(shù)來調用:
Car("Benz","White","Boom!"); //Benz White Boom! console.log(window.name + window.color + window.sound()); //BenzWhiteBoom!
如果在另一個對象的作用域中調用:
var cars = {}; Car.call(cars,"Benz","White","Boom Boom!"); document.write(cars.sound()); //Boom Boom!構造函數(shù)的問題
問題是每個方法都要在每個實例是重新創(chuàng)建一遍。可用通過把內部的函數(shù)轉移到外部來解決這些問題。如:
function Car(name,color){ this.name = name; this.color = color; this.show = show; } function show(){ console.log(this.name + this.color); } var benz = new Car("Benz","white"); benz.show(); //Benzwhite
但這個問題是完全沒有了封裝性可言。不過可以通過原型模式來解決。
原型模式function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.sayName(); //Oliver var person2 = new Person(); person2.sayName(); //Oliver; console.log(person1.sayName == person2.sayName); //true
與構造函數(shù)不同的是,新對象的這些屬性和方法是由所有實例共享的。這里兩個新的person 訪問的都是同一組屬性和同一個sayName() 函數(shù)。
理解原型對象以上面的Person 為例,Person 構造函數(shù)里面存在一個prototype 屬性,這個屬性指向原型對象Person Prototype,該Person Prototype 里面包含了constructor 屬性,該屬性又指向構造函數(shù)Person。構造函數(shù)的實例包含了一個[[Prototype]]的內部屬性,該內部屬性則指向Person Prototype。如:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.sayName(); //Oliver var person2 = new Person(); person2.sayName(); //Oliver; console.log(Person.prototype); /* age: 18 constructor: function Person() {} name: "Oliver" sayName: function () { __proto__: Object */ console.log(Person.prototype.constructor); //function Person() {} console.log(Object.getPrototypeOf(person1)); /* age: 18 constructor: function Person() {} name: "Oliver" sayName: function () { __proto__: Object */
對于構造函數(shù)、原型屬性以及實例之間的關系,參見《js高級程序設計》一書中第6.2.3 章節(jié)。
兩個方法:isPrototypeOf()和Object.getProtytypeOf()(ECMAScript 5)。前者是用來確定[[Prototype]];后者是用來返回[[Prototype]]值。如:
console.log(Person.prototype.isPrototypeOf(person1)); //true console.log(Object.getPrototypeOf(person1).name); //Oliver console.log(Object.getPrototypeOf(person1)); /* age: 18 constructor: function Person() {} name: "Oliver" sayName: function () { __proto__: Object */
為對象添加一個屬性時,這個屬性會屏蔽原型對象中的同名屬性,但是并不會修改那個屬性。如果使用delete 刪除這個屬性,就可以重新訪問原型中的屬性。如:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.sayName(); //Oliver 原型中的Name person1.name = "Troy"; person1.sayName(); //Troy 實例中的Name delete person1.name; person1.sayName(); //Oliver 原型中的Name
每次讀取某個對象的某個屬性,都會從實例本身開始搜索,如果沒有找到給定名字的屬性,則會在原型對象中再次搜索。
方法hasOwnProperty()檢測屬性如果在對象實例中時,返回true。如:
console.log(person1.hasOwnProperty("age")); //false age屬性來自于原型 console.log(person1.hasOwnProperty("name")); //true name屬性來自于實例原型與in 操作符
兩種方法使用in 操作符:多帶帶使用和for-in 循環(huán)中使用。
多帶帶使用時,in 返回true 說明該屬性存在于實例或原型中。如:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.name = "Troy"; person1.sayName(); //Troy 實例中的Name console.log("name" in person1); //true name屬性在實例或原型中 console.log(person1.hasOwnProperty("name")); //true name屬性在實例中 //上面兩個就說明name屬性一定在實例中
又如:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.name = "Troy"; person1.sayName(); //Troy 實例中的Name var person2 = new Person(); console.log("name" in person1); //true name屬性在實例或原型中 console.log(person1.hasOwnProperty("name")); //true name屬性在實例中 //上面兩個就說明name屬性一定在實例中 console.log("name" in person2); //true console.log(person2.hasOwnProperty("name")); //false //上面兩個說明name屬性一定在原型中
自定義一個函數(shù)hasPrototypeProperty(object,name);即同時使用上面兩個方法來確定屬性到底是不是存在于實例中。如:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.age = 18; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.name = "Troy"; person1.sayName(); //Troy 實例中的Name var person2 = new Person(); function hasPrototypeProperty(object,name){ console.log((name in object) && !(object.hasOwnProperty(name))) } hasPrototypeProperty(person2,"name"); //true name屬性是在原型中 hasPrototypeProperty(person1,"name"); //false name屬性是在實例中
用Object.defineProperty()方法定義的屬性:
function Person(){}; Person.prototype.name = "Oliver"; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); Object.defineProperty(person1, "age", { value: 18 }) console.log(person1.hasOwnProperty("age")); //true age屬性是實例屬性
關于for-in、[[enumerable]]、defineProperty、hasOwnProperty 的例子:
var person1 = { age: 18 }; Object.defineProperty(person1, "name", { value: "Oliver", enumerable: true }) for(var x in person1){ console.log(x); } console.log(person1.hasOwnProperty("name")); //true
又如:
function Person(age){ this.age = age; } var person1 = new Person(18); Object.defineProperty(person1, "name", { value: "Oliver", enumerable: false }) for(var x in person1){ console.log(x); //用defineProperty 定義的name 屬性是實例屬性,這里不會枚舉到 } console.log(person1.hasOwnProperty("name")); //true
又如:
function Person(){}; Person.prototype.age = 18; var person1 = new Person(); Object.defineProperty(person1, "name", { value: "Oliver", enumerable: false }) for(x in person1){ console.log(x); //這里仍然不回枚舉到自定義的name 實例屬性 }
但是:
function Person(){}; Person.prototype.age = 18; Person.prototype.name = "Oliver"; var person1 = new Person(); Object.defineProperty(person1, "name", { enumerable: false }) for(x in person1){ console.log(x); //這里則返回枚舉到自定義的name 原型屬性 }
原型屬性的[[enumerable]]設置為false,調用for-in 仍然可以被枚舉到。
另外,Object.keys()方法可以返回所有可枚舉屬性的字符串數(shù)組:
function Person(){}; Person.prototype.age = 18; Person.prototype.name = "Oliver"; var person1 = new Person(); Object.defineProperty(person1, "sound", { value: "miao~", enumerable: true //可枚舉 }); Object.defineProperty(person1, "sound2", { value: "wang~", enumerable: false //不可枚舉 }); console.log(Object.keys(Person.prototype)); //["age", "name"] console.log(Object.keys(person1)); //["sound"]
Object.getOwnPropertyName()方法,則可以返回無論可否枚舉的所有實例屬性:
function Person(){}; Person.prototype.age = 18; Person.prototype.name = "Oliver"; var person1 = new Person(); Object.defineProperty(person1, "sound", { value: "miao~", enumerable: true //可枚舉 }); Object.defineProperty(person1, "sound2", { value: "wang~", enumerable: false //不可枚舉 }); console.log(Object.keys(Person.prototype)); //["age", "name"] console.log(Object.keys(person1)); //["sound"] console.log(Object.getOwnPropertyNames(Person.prototype)); //["constructor", "age", "name"] console.log(Object.getOwnPropertyNames(person1)); //["sound","sound2"]更簡單的原型語法
即字面量方法:
function Person(){}; Person.prototype = { name: "Oliver", age: 18, sayName: function(){ console.log(this.name); } }; var person1 = new Person(); console.log(Person.prototype.constructor); //不再指向Person()構造函數(shù) function People(){}; People.prototype.name = "Troy"; People.prototype.age = 26; People.prototype.sayName = function(){ console.log(this.name); }; var people1 = new People(); console.log(People.prototype.constructor); //這里則指向People()構造函數(shù)
上面第一種就是字面量方法。但是由此帶來的問題是,他的原型對象中的constructor 屬性將不再指向上個例子中的Person() 構造函數(shù)了。(其實我們本質上是重寫了prototype對象)
如果constructor 值真的非常重要,則只需要把它設置回適當?shù)闹稻涂梢粤耍?/p>
function Person(){}; Person.prototype = { constructor: Person, name: "Oliver", age: 18, sayName: function(){ console.log(this.name); } }; var person1 = new Person(); console.log(Person.prototype.constructor); //重新指向Person()構造函數(shù) function People(){}; People.prototype.name = "Troy"; People.prototype.age = 26; People.prototype.sayName = function(){ console.log(this.name); }; var people1 = new People(); console.log(People.prototype.constructor); //這里則指向People()構造函數(shù)
然而用字面量的方法導致的問題仍然沒有結束,以上面這種方式重設constructor 屬性會導致[[Enumerable]]特性被設置為true。因此在支持ECMAScript 5 的js 引擎中可以用Object.defineProperty()方法把它修改為false:
function Person(){}; Person.prototype = { constructor: Person, name: "Oliver", age: 18, sayName: function(){ console.log(this.name); } }; var person1 = new Person(); console.log(Person.prototype.constructor); for (var x in person1){ console.log(x); //這里會出現(xiàn)constructor,但是我們實際上不應該讓他能夠被枚舉出 } Object.defineProperty(Person.prototype, "constructor", { enumerable: false }); for (var x in person1){ console.log(x); //這里就不會出現(xiàn)constructor 了,但是這種方法只支持ECMAScript 5的js 引擎 } /* [Log] function Person() {} (repetition.html, line 130) [Log] constructor (repetition.html, line 132) [Log] name (repetition.html, line 132) [Log] age (repetition.html, line 132) [Log] sayName (repetition.html, line 132) [Log] name (repetition.html, line 140) [Log] age (repetition.html, line 140) [Log] sayName (repetition.html, line 140) */原型的動態(tài)性
我們對原型對象所做的任何修改都能立即從實例上反應出來。因為實例與原型之間的鏈接只不過是一個指針而不是副本:
function Person(){}; var person = new Person(); //person在Person()構造函數(shù)修改之前創(chuàng)建的 Person.prototype.name = "Oliver"; console.log(person.name); //仍然會出現(xiàn)實時的變化
但是如果重寫了prototype 則就不同了,因為實例的[[Prototype]]會指向原型對象,如果修改了原來的原型對象,則就是切斷了構造函數(shù)與最初原型之間的聯(lián)系:
function Person(){}; var person = new Person(); Person.prototype = { //這里重寫了Person.prototype,屬于新的Person.prototype constructor: Person, name: "Oliver" } console.log(person.name); //原型對象被修改了,指針仍然指向舊的Person.prototype
從這里就可以很明顯看出,Person.prototype={},實際上字面量方法就是重寫了原型對象。如果是Person.prototype.name="Oliver",則并不是重寫而是修改,不會創(chuàng)建“新的原型對象。”
原生對象的原型《js高級程序設計》一書中6.2.3 中的圖6-3 很清楚的描述了該原理
所有原生的引用類型(Object、Array、String等等)都在其構造函數(shù)的原型上定義了方法。同時,我們也可以給原生對象自定義方法:
var array = new Array(); Array.prototype.name = function(){ console.log("Array") }; array.push("hello ","there"); console.log(array); array.name();
也可以修改:
var array = new Array(); Array.prototype.toString = function(){ return("Array") }; array.push("hello ","there"); console.log(array.toString()); //這樣就抹去了toString()方法
強烈不推薦修改和重寫原生對象的原型
原型對象的問題就是包含引用類型值的屬性來說,問題比較嚴重。具體體現(xiàn)在原型中的屬性被實例共享:
function Person(){}; Person.prototype = { constructor: Person, name: "Oliver", age: 18, friends: ["Troy","Alice"] } var person1 = new Person(); var person2 = new Person(); person1.friends.push("Ellen"); console.log(person1.friends); //["Troy", "Alice", "Ellen"] console.log(person2.friends); //["Troy", "Alice", "Ellen"] //兩者完全相同,因為原型中的該屬性被實例所共享,push()方法只是修改了person1.friends,沒有重寫 person1.friends = ["Troy", "Alice"]; console.log(person1.friends); //["Troy", "Alice"] console.log(person2.friends); //["Troy", "Alice", "Ellen"] //雖然可以通過重寫和覆蓋來解決該問題,但是仍然非常麻煩 console.log(person1.hasOwnProperty("friends")); //true; console.log(person2.hasOwnProperty("friends")); //false; //這里就可以看到,重寫能解決問題是因為重寫導致它創(chuàng)建了實例屬性"friends"
這里可以看出,如果我們的初衷像這樣只是想創(chuàng)建一個共享的數(shù)組,那么當然不會有什么問題;但是實例一般都會有自己的屬性,所以不應該多帶帶使用原型模式。而是組合使用構造函數(shù)模式和原型模式。
組合使用構造函數(shù)模式和原型模式這是一種用來定義引用類型的一種默認模式:
function Person(name,age){ this.name = name; this.age = age; this.friends = []; } //獨享的部分 Person.prototype = { constructor: Person, sayName: function(){ return this.name; } } //共享的部分 var person1 = new Person("Oliver",18); var person2 = new Person("Troy",24); person1.friends.push("Alice","Mark"); person2.friends.push("Mac"); console.log(person1.friends.toString()); console.log(person2.friends.toString()); /* [Log] Alice,Mark (repetition.html, line 228) [Log] Mac (repetition.html, line 229) */動態(tài)原型模式
可以通過檢查某個應該存在的方法是否有效,來決定是否需要初始化原型:
function Person(name,age){ this.name = name; this.age = age; if (typeof this.sayName != "function"){ Person.prototype.sayName = function(){ return (this.name); }; } } var person = new Person("Oliver",18); console.log(person.sayName()); //Oliver
實際上就是把下面代碼封裝在了構造函數(shù)中:
function Person(name,age){ this.name = name; this.age = age; } Person.prototype.sayName = function(){ return(this.name); }; var person = new Person("Troy",24); console.log(person.sayName()); //Troy寄生構造函數(shù)模式
世紀撒好難過跟工廠模式一樣。建議在可以使用其他模式的情況下,不要使用該模式。
穩(wěn)妥構造函數(shù)模式穩(wěn)妥對象,指的是沒有公共屬性,且其方法也不引用this 的對象如:
function Person(name,age){ var obj = new Object(); obj.sayName = function(){ console.log(name); }; return obj; } var person1 = Person("Oliver",18); person1.sayName(); //Oliver
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86248.html
摘要:如繼承了這里就不必寫該方法的主要優(yōu)勢就是可以在子類型構造函數(shù)中向超類型構造函數(shù)傳遞參數(shù)。以上原型式繼承通常只是想讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。 繼承 繼承分為接口繼承和實現(xiàn)繼承;接口繼承只繼承方法簽名,實現(xiàn)繼承則繼承實際的方法;由于函數(shù)沒有簽名,所以ECMAScript 中沒有接口繼承,只能依靠原型鏈來實現(xiàn)實現(xiàn)繼承。 原型鏈 基本思想是利用原型鏈讓...
摘要:描述符對象就是上面提到的個描述其行為的特性和。真是奇怪讀取屬性的特征使用的方法,可以取得給定屬性的描述符。接收兩個參數(shù)所在的對象和要讀取其描述符的屬性名稱。 對象的基本概念 面向對象(Object-Oriented,OO),的語言最大的特征就是它們都有類的概念,通過類可以創(chuàng)建任意多個具有相同屬性和方法的對象。 創(chuàng)建自定義對象最簡單的方式就是創(chuàng)建一個Object 的實例,然后再給他添加屬...
摘要:當談到語言與其他編程語言相比時,你可能會聽到一些令人困惑東西,其中之一是工廠函數(shù)和構造函數(shù)。好的,讓我們用構造函數(shù)做同樣的實驗。當我們使用工廠函數(shù)創(chuàng)建對象時,它的指向,而當從構造函數(shù)創(chuàng)建對象時,它指向它的構造函數(shù)原型對象。 showImg(https://segmentfault.com/img/bVbr58T?w=1600&h=900); 當談到JavaScript語言與其他編程語言...
摘要:被覆蓋級事件處理事件名,事件處理函數(shù),事件捕獲事件冒泡清除事件處理要使用級事件處理程序不會被覆蓋而是會一步一步的解析執(zhí)行。 一,變量1.可以用new Array(1,2);來定義數(shù)組。2.可以通過為變量賦值為null來清除變量,如: //首先定義一個變量 var i1=10; i1=null; //此時的i1就被清除了 在函數(shù)里面這樣定義變量的時候要注意 funtion demo()...
摘要:構造函數(shù)的兩個特征函數(shù)內部使用了,指向所要生成的對象實例。將一個空對象的指向構造函數(shù)的屬性,這個對象就是要返回的實例對象。用面向對象開發(fā)時,把要生成的實例對象的特有屬性放到構造函數(shù)內,把共有的方法放到構造函數(shù)的里面。 JS中面向對象的概念 面向對象OOP是一種組織代碼結構、實現(xiàn)功能過程的思維方式。它將真實世界各種復雜的關系,抽象為一個個對象,然后由對象之間的分工與合作,完成對真實世界的...
閱讀 3921·2021-11-17 09:33
閱讀 3283·2021-10-08 10:05
閱讀 3111·2021-09-22 15:36
閱讀 1140·2021-09-06 15:02
閱讀 2772·2019-08-29 12:45
閱讀 1590·2019-08-26 13:40
閱讀 3399·2019-08-26 13:37
閱讀 420·2019-08-26 13:37