构造函数
构造函数就是为了创建出很多具有相同属性和方法的实例对象
function Person(name) {
this.mName = name;
this.sayName = function() {
return this.mName;
}
};
// 使用构造函数创建实例化对象
var p1 = new Person('张三');
var p2 = new Person('李四');
每一个实例对象都有sayName方法。但是这样每次创建一个对象都会产生一个一样的sayName方法,这很占用空间,而且也不符合代码书写规范(减少重复性)。
当我们这样写时
// 创建构造函数
function Person(name) {
this.mName = name;
// this.sayName = function() {
// return this.mName;
// }
};
Person.prototype.sayName = function () {
return this.mName;
}
// 使用构造函数创建实例化对象
var p1 = new Person('张三');
var p2 = new Person('李四');
p1.sayName();
发现p1.sayName()依然能够执行。
检测p1中是否有sayName这个属性
console.log(p1.hasOwnProperty('sayName'));// false
返回为false,说明p1中确实没有sayName属性。
这是因为p1作为构造函数Person的实例化对象,它继承了来自Person的prototype属性里面的方法。
这里就要看一下实例对象的__proto__属性,
先打印一下
console.log(p1.__proto__);
console.log(Person.prototype);
两个属性一摸一样。
再进行一下对比
console.log(Person.prototype === p1.__proto__);
结果是true。
说明两个属性是完全相等的。
获取对象的__proto__属性指向的构造函数的prototype属性。也就是原型对象
**使用Object.getPrototypeOf()获取对象的原型对象
console.log(Object.getPrototypeOf(p1));
就是Person的prototype属性。
这个对象里sayName是我们自己添加的,constructor构造函数的意思,里面的构造函数指向自身,__proto__这个属性指向另一个原型对象。
总结:
- 每一个实例化成员都有一个__proto__属性,这个属性里面指向向其构造函数的prototype属性。
- 而每一个构造函数都有一个prototype属性,属性值是一个对象,称为原型对象。
当调用某种方法或者查找某种属性时,首先会先在自身查找,如果自身没有这个属性或者方法,就会去对象的__proto__属性中去查找,也就是构造函数的prototype中调用查找。
原型链
任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;
每一个实例对象都有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。
原型的修改或改变
可以通过给构造函数的prototype属性添加属性和方法来让创建的实例对象继承。
1、普通的暴力方法
Person.prototype = {
sayName:function(){
return this.mName;
}
直接用一个新的对象替换掉原来的原型对象,但是这样不建议使用,因为原型对象中还保存这其他的信息,这样就会将其他的属性给抹除掉了。
2、使用 . 添加属性和方法
Person.prototype.sayName = function () {
return this.mName;
}
这种添加方式可以使用,但是还是不够完美,因为这样添加上去的属性是可以被实例对象使用for in遍历时遍历到的,正常的属于继承的属性是不应该被子元素遍历到的。
3、使用Object.defineProperty()方法,可以添加属性时,定义属性内部的特性。
Object.defineProperty(Person.prototype,'sayName',{
configurable:false,
enumerable:false,
writable:false,
value:function(){
return this.mName;
}
})
//定义多个
Object.defineProperties(Person.prototype, {
sayName: {
configurable: false,
enumerable: false,
writable: false,
value: function () {
return this.mName;
},
fullNmae: {
configurable: false,
enumerable: false,
writable: false,
value: '尼古拉丝儿'
}
}
})
这样添加的属性就不会被遍历出来。
4、Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
语法:Object.create(proto, [propertiesObject])
proto:新创建对象的原型对象。
propertiesObject:可选。若没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数
返回值:一个新对象,带着指定的原型对象和属性。
例外:如果propertiesObject参数不是 null 或一个对象,则抛出一个 TypeError 异常。
var p2 = Object.create(p1,{
fullNmae: {
configurable: false,
enumerable: false,
writable: false,
value: '尼古拉丝儿'
//这些属性不写默认是false
}
});
还可以利用这个创建一个空的对象。
var obj3 = Object.create({});
创建的空对象的原型也是一个空对象。
Object的原型对象给我们提供的方法
1.hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
console.log(p1.getOwnProper('mName'));// true
与他相似的还有in操作符,但是in会算上原型链中可以访问的属性
2.propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
console.log(p1.propertyIsEnumerable('mName'));//true
3、 isPrototypeOf() 方法用于在person的原型链上搜寻是否有object原型
console.log(Person.prototype.isPrototypeOf(p1));//true
4、 toString:把对象用字符串的形式表示出来
console.log(p1.toString());//[object Object]
5、 valueOf() 方法返回指定对象的原始值。一般只有js内置的对象才会有原始值。没有原始值的对象会返回自身。
console.log(p1.valueOf());// 自身