// 建立對象,賦給屬性 var person = new Object(); person.name = "Nicholas"; person.age = 29; person.job = "Sofware Engineer"; person.sayName = function() { alert(this.name); } // 字面量方式建立對象 var person = { name: "Nicholas", age: 29, job: "Sofware Engineer", sayName: function() { alert(this.name); } }
數據屬性包含一個數值的位置。在這個位置能夠讀取和寫入值。數據屬性有4個描述其行爲的特性javascript
要修改屬性默認的特性,必須使用ECMAScript5的Object.defineProperty()方法。java
var person = {}; // 建立了一個name屬性,它的值"Nicholas"是隻讀的。 Object.defineProperty(person, "name", { writable: false, value: "Nicholas" }); console.log(person.name); // "Nicholas" // 在非嚴格模式下,賦值操做會被忽略 // 而在嚴格模式下,會致使錯誤 person.name = "Greg"; console.log(person.name); // "Nicholas"
var person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Nicholas" }); // 拋出錯誤 Object.defineProperty(person, "name", { configurable: true, value: "Nicholas" })
訪問器屬性有以下4個特性:數組
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, "year", { get: function () { return this._year; }, set: function (newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }); // 這是使用訪問器屬性的常見方式,即設置一個屬性的值會致使其餘屬性發生變化。 book.year = 2005; console.log(book.edition); // 2
var book = {} Object.defineProperties(book, { _year: { writable: true, value: 2004 }, edition: { writable: true, value: 1 }, year: { get: function() { return this._year; }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.editio += newValue - 2004; } } } })
var book = {}; Object.defineProperties(book, { _year: { writable: true, value: 2004 }, edition: { writable: true, value: 1 }, year: { get: function() { return this._year; }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.editio += newValue - 2004; } } } }); var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); console.log(descriptor.value); // 2004 console.log(descriptor.configurable); // false console.log(typeof descriptor.get); // undefined var descriptor = Object.getOwnPropertyDescriptor(book, "year"); console.log(descriptor.value); // undefined console.log(descriptor.enumerable); // false console.log(typeof descriptor.get); // "function"
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("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
構造函數模式對比工廠模式存在如下不一樣之處瀏覽器
console.log(person1.constructor == Person); // true console.log(person2.constructor == Person); // true
console.log(person1 instanceof Object); // true console.log(person2 instanceof Object); // true console.log(person1 instanceof Person); // true console.log(person2 instanceof Person); // true
// 做爲構造函數使用 var person = new Person("Nicholas", 29, "Software Engineer"); person.sayName(); // "Nicholas" // 做爲普通函數調用,此時this指向window對象 Person("Greg", 27, "Doctor"); // 添加到window對象上 window.sayName(); // "Greg" // 在另外一個對象的做用域中調用 var o = new Object(); Person.call(o, "Kristen", 25, "Nurse"); o.sayName(); // "Kristen"
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; // 與聲明函數再路基上是等價的 this.sayName = new Function("alert(this.name)"); } console.log(person1.sayName == person2.sayName); // false
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName } function sayName () { alert(this.name); } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); person1.sayName(); // "Nicholas" var person2 = new Person(); person2.sayName(); // "Nicholas" console.log(person1.sayName == person2.sayName); // true
console.log(Person.prototype.isPrototypeOf(person1)); // true console.log(Person.prototype.isPrototypeOf(person2)); // true console.log(Person.isPrototypeOf(person1)); // false console.log(Person.isPrototypeOf(person2)); // false
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true console.log(Object.getPrototypeOf(person1).name); // "Nicholas"
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); console.log(person1.hasOwnProperty("name")); // false person1.name = "Greg"; console.log(person1.name); // "Greg" ——來自實例 console.log(person1.hasOwnProperty("name")); // true console.log(person2.name); // "Nicholas" ——來自原型 console.log(person2.hasOwnProperty("name")); // false // 使用delete操做符刪除實例中的屬性 delete person1.name; console.log(person1.name); // "Nicholas" ——來自實例 console.log(person1.hasOwnProperty("name")); // false
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); console.log(person1.hasOwnProperty("name")); // false console.log("name" in person1); // true person1.name = "Greg"; console.log("name" in person1); // true console.log(person2.name); // "Nicholas" ——來自原型 console.log("name" in person2); // true // 使用delete操做符刪除實例中的屬性 delete person1.name; console.log("name" in person1); // true
function hasPrototypeProperty(object, name) { return !object.hasOwnProperty(name) && (name in object); }
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person = new Person(); console.log(hasPrototypeProperty(person, "name")); // true person.name = "Greg"; console.log(hasPrototypeProperty(person, "name")); // false
var o = { toString: function() { return "My Object"; } }; for (var prop in o) { if (prop == "toString") { console.log("Found toString"); // 在IE中不會顯示 } }
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "SoftWare Engineer"; Person.prototype.sayName = function() { console.log(this.name); }; var keys = object.keys(Person.prototype); console.log(keys); // "name,age,job,sayName" var p1 = new Person(); p1.name = "Rob"; p1.age = 31; var p1kyes = Object.keys(p1); console.log(p1kyes); // "name,age"
var keys = Object.getOwnPropertyNames(Person.prototype); // "constructor,name,age,job,sayName"
function Person() {} Person.prototype = { name: "Nicholas", age: 29, job: "Software Engineer", sayName: function () { console.log(this.name); } };
var friend = new Person(); console.log(friend instanceof Object); // true console.log(friend instanceof Person); // true console.log(friend.constructor == Person); // false console.log(friend.constructor == Object); // Object
function Person() {} Person.prototype = { constructor: Person, // 讓 prototype的constructor從新指向Person name: "Nicholas", age: 29, job: "Software Engineer", sayName: function () { console.log(this.name); } };
function Person() {} Person.prototype = { name: "Nicholas", age: 29, job: "Software Engineer", sayName: function () { console.log(this.name); } }; // 重設構造函數,只適用於ECMASCript 5 兼容的瀏覽器 Object.defineProperty(Person.prototype, "constructor", { enumerable: false, value: Person });
var friend = new Person(); Person.prototype.sayHi = function() { console.log("Hi"); }; friend.sayHi(); // "Hi"
function Person() {} var friend = new Person(); // 重寫整個原型對象,就等於切斷了構造函數與最初原型之間的聯繫 Person.prototype = { constructor: Person, age: 29, job: "Software Engineer", sayName: function () { console.log(this.name); } }; friend.sayName(); // error
console.log(typeof Array.prototype.sort); // "function" console.log(typeof String.prototype.substring); // "function"
String.prototype.startsWith = function (text) { return this.indexOf(text) == 0; }; var msg = "Hello world!"; console.log(msg.startsWith("Hello")); // true
原型模式也不是沒有缺點。安全
function Person() {} Person.prototype = { constructor: Person, // 讓 prototype的constructor從新指向Person name: "Nicholas", age: 29, job: "Software Engineer", friend: ["Shelby", "Court"], sayName: function () { console.log(this.name); } }; var person1 = new Person(); var person2 = new Person(); // 這裏修改的其實是Person.prototype.friends person1.friends.push("Van"); // 不但person1的friends屬性被修改,person2也作了一樣的改動 console.log(person1.friends); // "Shelby,Court,Van" console.log(person1.friends); // "Shelby,Court,Van" // 由於兩個實例的friends屬性指向的都是Person.prototype.friends console.log(person1.friends === person2.friends); // true
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor: Person, sayName: function() { console.log(this.name); } }; var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); console.log(person1.friends); // "Shelby,Court,Van" console.log(person1.friends); // "Shelby,Court" console.log(person1.friends === person2.friends); // false console.log(person1.sayName === person2.sayName); // true
function Person(name, age, job) { // 屬性 this.name = name; this.age = age; this.job = job; // 方法 if (typeof this.sayName != "function") { Person.perototype.sayName = function() { console.log(this.name); }; } } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); // "Nicholas"
function Person(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { console.log(this.name); }; return o } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); // "Nicholas"
function SpecialArray() { // 建立數組 var values = new Array(); // 添加值 values.push.apply(values, arguments); // 添加方法 values.toPipedString = function() { return this.join("|"); }; // 返回數組 return values; } var colors = new SpecialArray("red", "blue", "green"); console.log(colors.toPipedString()); // "red|blue|green"
道格拉斯·克羅克福德(Douglas Crockford)發明了JavaScript中的穩妥對象(durable objects)這個概念。穩妥對象,指的是沒有公共屬性並且其方法不引用this的對象。穩妥對象最適合在一些安全的環境中(這些環境中會禁止使用this和new),或者在防止數據被其餘應用程序(如Mashup程序)改動時使用。穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:app
this
function Person(name, age, job) { // 建立要返回的對象 var o = new Object(); // 這裏定義私有變量和函數 ... // 添加方法 o.sayName = function() { console.log(name); }; // 返回對象 return o; } var friend = Person("Nicholas", 29, "Software Engineer"); friend.sayName(); // "Nicholas"
許多OO語言都支持兩種繼承方式函數
原型鏈 實現繼承的主要方法。基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。簡單回顧一下構造函數、原型和實例的關係:測試
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } // 繼承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); //true
最終:this
經過實現原型鏈,本質上拓展了原型搜索機制。當讀取模式訪問一個實例屬性時,首先會在實例中搜索該屬性。若是沒有找到該屬性,則會繼續搜索實例的原型。在經過原型鏈實現繼承的狀況下,搜索過程就得以沿着原型鏈繼續向上。調用instance.getSuperValue()會經歷三個搜索步驟spa
// 因爲原型鏈,咱們能夠是instance是Object,SuperType, SubType中任何一個類型的實例 console.log(instance instanceof Object); // true console.log(instance instanceof SuperType); // true console.log(instance instanceof SubType); // true
console.log(Object.prototype.isPrototypeOf(instance)); // true console.log(SuperType.prototype.isPrototypeOf(instance)); // true console.log(SubType.prototype.isPrototypeOf(instance)); // true
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } // 繼承了SuperType,原來默認的原型被替換 SubType.protoype = new SuperType(); // 添加新方法 SubType.prototype.getSubValue = function () { return this.subproperty; }; // 重寫超類型中的方法 SubType.protoype.getSuperValue = function () { return false; }; var instance = new SubType(); console.log(instance.getSuperValue()); // false
function SuperType() { this.property = true; } SuperType.prototype. getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } // 繼承了SuperType SubType.protype = new SuperType(); // 使用字面量添加新方法,會致使上一行代碼無效 // 原型鏈被切斷——SubType 和 SuperType 之間已經沒有關係 SubType.prototype = { getSubValue: function () { return this.subproperty; }, someOtherMethod: function () { return false; } }; var instance = new SubType(); console.log(instance.getSuperValue())); // error
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { } // SubType 繼承了 SuperType 以後 // SubType.prototype 就變成了 SuperType的一個實例 // 所以 SubType.prototype 也擁有了本身的colors屬性 等價於建立了一個SubType.prototype.colors SubType.protype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" // 結果就是全部SubType實例都會共享這個colors屬性 var instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green,black"
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { // 繼承了SuperType // "借調「了超類型的構造函數 SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" // SubType實例都不會共享這個colors屬性 var instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green"
function SuperType(name) { this.name = name; } function SubType() { // 繼承了SuperType 同時傳遞了參數 SuperType.call(this. "Nicholas"); // 實例屬性 this.age = 29; } var instance = new SubType(); console.log(instance.anme); // "Nicholas" console.log(instance.age); // 29
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { // 繼承屬性 SuperType.call(this, name); // 子類型本身的屬性 this.age = age } // 繼承方法 SubType.prototype = new SuperType(); // 若是不指定constructor,SubType.prototype.constructor 爲 SuperType SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" instance1.sayName(); // "Nicholas" instance1.sayAge(); // 29 var instance2 = new SubType("Greg", 27); console.log(instance2.colors); // "red,blue,green" instance2.sayName(); // "Greg" instance2.sayAge(); // 27
function object(o) { function F() {} F.prototype = o; return new F(); }
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"], }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); console.log(anotherPerson.friends); // "Shelby,Court,Van,Rob" var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(yetAnotherPerson.friends); // "Shelby,Court,Van,Rob,Barbie" console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
var person = { name: "Nicholoas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); console.log(anotherPerson.friends); // "Shelby,Court,Van,Rob" var yetAnotherPerson = Object.object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(yetAnotherPerson.friends); // "Shelby,Court,Van,Rob,Barbie" console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
var person = { name: "Nicholoas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person, { name: { value: "Greg" } }); console.log(anotherPerson.name); // "Greg"
function createAnother(original) { var clone = object(original); // 經過調用函數建立一個新對象 clone.sayHi = function() { // 以某種方式加強這個對象 console.log("hi"); }; return clone; // 返回這個對象 }
組合繼承是最經常使用的繼承模式;不過最大的問題就是不管什麼狀況下,都會調用兩次超類型構造函數:
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); // 第二次調用SuperType() this.age = age; } // 實例化SuperType做爲SubType的原型 // 當即觸發 SubType.prototype = new SuperType(); // 第一次調用SuperType() SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); }; // 此時觸發第二次調用 var instance = new SubType("Nicholas", 29); console.log(instance.name); // "Nicholas" console.log(SubType.prototype.name); // undefined
function inheritPrototype(subType, superType) { // 建立對象 - 超類型的對象原型的副本 // 這裏沒有使用new操做符,因此沒有生成SuperType的實例 // 這裏沒有調用SuperType構造函數 var prototype = Object(superType.prototype) console.log(prototype == superType.prototype) // 加強對象 - 彌補因重寫原型而失去的默認constructor屬性 // 而這也將致使supert.prototype.constructor指向了subType prototype.constructor = subType; // 指定對象 - 將新建立的對象(即副本)賦值給子類型的原型。 subType.prototype = prototype; } function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); };