對象是
JavaScript
中的基本數據結構,與許多面向對象的語言同樣,JavaScript
也支持繼承,但不一樣的是JavaScript
的繼承機制基於 原型 ,而不是 類,雖然原型使用了不少傳統面嚮對象語言的概念,可是使用原型和使用類依舊存在很大差別。javascript
prototype
、 getPrototypeOf
和 __proto__
之間的區別原型包括三個獨立但相關的訪問器,即 prototype
、 getPrototypeOf
和 __proto__
:java
C.prototype屬性是new C()`建立的對象的原型。數據結構
Object.getPrototypeOf(obj)是ES5中檢索對象原型的標準函數。app
obj.__proto__是檢索對象原型的非標準方法。函數
// 建立一個類 function User(name, passwordHash) { this.name = name; this. passwordHash = age; } // 建立原型 User.prototype.toString = function() { return 'User:' + this.name; }; User.prototype.checkPassword = function() { return hash(password) === this.passwordHash; }; var u = new User('dreamapple', '0eajkjfkahjfh7as7d678as6'); // User.prototype == new User().__proto_ User.prototype == Object.getPrototypeOf(u) console.log(User.prototype === u.__proto__); // true console.log(User.prototype === Object.getPrototypeOf(u)); // true
構造函數及其實例的原型關係測試
Object.getPropertyOf
函數而不要使用 __proto__
屬性使用符合標準的 Object.getPrototypeOf
函數而不要使用非標準的 __proto__
屬性。this
在支持 __proto__
屬性的非ES5環境中實現 Object.getPrototypeOf
函數。spa
不要修改 __proto__
屬性prototype
使用 Object.create()
函數給新對象設置自定義的原型。code
以下:
//==========================eg.2========================= var obj = Object.create(null); console.log('__proto__' in obj); // false console.log(Object.getPrototypeOf(obj)); // null // 可使用 __proto__屬性來模仿 Object.getPropertyOf() 函數 if('undefined' === typeof Object.getPrototypeOf) { Object.getPrototypeOf = function(obj) { var t = typeof obj; if(!obj || (t !== 'object' && t !== 'function')) { throw new Error('not an object'); } return obj.__proto__; } } //==========================eg.2========================= function User(name) { this.name = name; } var user = new User('dreamapple'); console.log(user); // User { name: 'dreamapple' } console.log(user.__proto__); /* Object constructor:User(name) __proto__:Object */ // 使用user對象的原型 經過使用Object.create()方法建立一個新的對象 var user1 = Object.create(Object.getPrototypeOf(user)); console.log(user1); // User {} // 永遠不要修改 __proto__ 屬性 user1.__proto__ = {}; // X
將方法存儲在實例對象中將建立函數的多個副本,由於每一個實例對象都有一份副本。
將方法存儲於原型中優於存儲在實例對象中。
function User(name, age) { // 通常屬性 this.name = name; this.age = age; // 在實例屬性上的方法 this.getName = function() { console.log('My name is ' + this.name); return this.name; } } // 在原型上的方法 User.prototype = { getAge: function() { console.log('My age is ' + this.age); return this.age; } } var user = new User('dreamapple', 22); // 方法 getName 在 user 的實例對象中複製了一份,而方法 getAge 在User的原型上供給全部實例公用 console.log(user); // User { name: 'dreamapple', age: 22, getName: [Function] }
從上一個例子能夠看出,儲存在父對象原型中的方法或者屬性不會拷貝到實例對象中,而是由全部實例對象公用,這就會出現一個問題,假如屬性設置不當,就會致使不一樣實例調用同一個方法時可能會修改到共用的屬性,以下:
function Tree(name){ this.name = name; } Tree.prototype = { children : [], addChild : function(x) { this.children.push(x); } }; var left = new Tree('left'); left.addChild(1); left.addChild(3); var right = new Tree('right'); right.addChild(5); right.addChild(7); console.log(Tree.prototype.children); //[1, 3, 5, 7] console.log(right); //[1, 3, 5, 7] console.log(left); //[1, 3, 5, 7]
在子類的構造函數中顯式地傳入this做爲顯式的接受者調用父類構造函數。
使用 Object.create()
函數來構造子類的原型對象以免調用父類的構造函數。
示例以下:
//定義一個父類 function Animal(cate, food) { this.cate = cate; this.food = food; } Animal.prototype = { sayHello : function(){ console.log(this.cate + ' eat ' + this.food); } } //定義子類 function Dog (cate, food, name, age) { Animal.call(this,cate,food); this.name = name; this.age = age; } //將子類與父類原型關聯 Dog.prototype = Object.create(Animal.prototype); Dog.prototype.wang = function() { console.log(this.name + ' do not like ' + '?'); } var tom = new Dog('dog','meat','old tom',15); tom.wang(); //"old tom do not like ?" tom.sayHello(); //"dog eat meat"
不要重用父類的屬性名
避免繼承標準類
避免輕率地使用猴子補丁(例如隨意爲Array原型添加一個方法,但能夠經過測試條件爲原型添加polyfills)