对象是
JavaScript
中的基本数据结构,与许多面向对象的语言一样,JavaScript
也支持继承,但不同的是JavaScript
的继承机制基于 原型 ,而不是 类,虽然原型使用了很多传统面向对象语言的概念,但是使用原型和使用类依旧存在很大差异。
1、 prototype
、 getPrototypeOf
和 __proto__
之间的区别
原型包括三个独立但相关的访问器,即 prototype
、 getPrototypeOf
和 __proto__
:
C.prototype属性是new C()`创建的对象的原型。
Object.getPrototypeOf(obj)是ES5中检索对象原型的标准函数。
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
构造函数及其实例的原型关系
2、尽量使用 Object.getPropertyOf
函数而不要使用 __proto__
属性
使用符合标准的
Object.getPrototypeOf
函数而不要使用非标准的__proto__
属性。在支持
__proto__
属性的非ES5环境中实现Object.getPrototypeOf
函数。不要修改
__proto__
属性使用
Object.create()
函数给新对象设置自定义的原型。
如下:
//==========================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
3、在原型中存储方法
将方法存储在实例对象中将创建函数的多个副本,因为每个实例对象都有一份副本。
将方法存储于原型中优于存储在实例对象中。
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] }
4、只将实例属性(状态)储存在实例对象中
从上一个例子可以看出,储存在父对象原型中的方法或者属性不会拷贝到实例对象中,而是由所有实例对象公用,这就会出现一个问题,假如属性设置不当,就会导致不同实例调用同一个方法时可能会修改到共用的属性,如下:
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]
5、在子类的构造函数中调用父类的构造函数
在子类的构造函数中显式地传入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"
6、其他的一些注意项
不要重用父类的属性名
避免继承标准类
避免轻率地使用猴子补丁(例如随意为Array原型添加一个方法,但可以通过测试条件为原型添加polyfills)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。