认识对象的原型
JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]],这个特殊的对象可以指向另外一个对象。
那么这个对象有什么用呢?
当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
如果对象中没有该属性,那么会访问对象[[prototype]]内置属性指向的对象上的属性;
获取对象的原型
obj = {
name: 'John',
age: 30,
}
console.log(obj.__proto__); // [Object: null prototype] {}
console.log(Object.getPrototypeOf(obj)); // [Object: null prototype] {}
原型作用
当我们从一个对象中获取某一个属性时, 它会触发 [[get]] 操作
- 在当前对象中去查找对应的属性, 如果找到就直接使用
- 如果没有找到, 那么会沿着它的原型去查找 [[prototype]]
函数的原型 prototype
所有的函数都有一个prototype的属性:
function Person() {
this.name = 'John';
this.age = 30;
}
console.log(Person.prototype); // [Object: null prototype] {}
// 因为它是一个函数,才有了这个特殊的属性,而不是因为它是一个对象
new 操作符
- 在内存中创建一个新对象
- 将新对象的原型指向构造函数的prototype属性
function Person() {
}
var p1 = new Person()
var p2 = new Person()
console.log(p1.__proto__ === p2.__proto__); // true
console.log(p1.__proto__ === Person.prototype); // true
constructor 属性
事实上原型对象上面是有一个属性的:constructor,默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象;
function Person() {
}
p1 = new Person();
console.log(Person.prototype); // [Object: null prototype] {}
console.log(p1.__proto__.constructor); // [Function: Person]
console.log(p1.__proto__.constructor.name); // Person
利用getOwnPropertyDescriptors方法可以获取到所有的属性和属性描述符
通过这种方法我们可以发现constructor属性是一个属性描述符,它有一个value属性,指向当前的函数对象
console.log(Object.getOwnPropertyDescriptors(foo.prototype)) // {constructor: {value: [Function: Person], writable: true, enumerable: false, configurable: true}}
Object.defineProperty(foo.prototype, "constructor", {
enumerable: true,
configurable: true,
writable: true,
value: "哈哈哈哈"
})
console.log(foo.prototype) // {constructor: "哈哈哈哈"}
原型对象的constructor
如果我们需要在原型上添加多种属性,那么可以重新定义整个原型对象,这样就可以添加多个属性了;
function Person() {
}
Person.prototype = {
name: 'John',
age: 30,
sayHello: function() {
console.log('Hello');
}
};
var p1 = new Person();
console.log(p1.name, p1.age); // John 30
p1.sayHello(); // Hello
每创建一个函数, 就会同时创建它的prototype对象, 这个对象也会自动获取constructor属性;而我们这里相当于给prototype重新赋值了一个对象, 那么这个新对象的constructor属性, 会指向Object构造函数, 而不是Person构造函数了
console.log(f1.constructor) // [Function: Object]
// 真实开发中我们可以通过Object.defineProperty方式添加constructor
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: Person
})
console.log(f1.constructor) ; // [Function: Person]
利用原型和构造函数创建对象
上一篇中我讲到通过构造函数创建对象时,会创造出重复的函数,这里我们可以通过原型对象来创建对象,这样就不会创造出重复的函数了;
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(this.name + 'Hello');
}
Person.prototype.sayGoodbye = function() {
console.log(this.name + 'Goodbye');
}
var p1 = new Person('John', 30);
var p2 = new Person('Mary', 25);
p1.sayHello(); // JohnHello
p2.sayGoodbye(); //MaryGoodbye