构造函数、原型对象、实例
1、每个函数都有一个prototype属性,该属性指向函数的原型对象,原型对象的用途是可以让所有对象实例共享它所包含的属性和方法。
2、默认情况下,每个原型对象都会自动获得一个constructor属性,该属性指向prototype属性所在的函数。constructor属性最初是用来标识对象类型的,但检测对象类型,还是instanceof更可靠些(因为对象的constructor可被改写,指向的函数不同于原型对象construtor指向的函数):
person1 instanceof Person // 检测Person.prototype是否在person1的原型链上,在就返回true。
3、当调用构造函数创建一个对象实例后,该实例的内部将包含一个指针[[Prototype]](内部属性),指向构造函数的原型对象。Firefox、Safari和Chrome等浏览器在每个对象上都加了一个__proto__,来访问[[Prototype]],而在其他实现中,这个属性则完全不可见。对象实例和构造函数没有直接关系。可以通过isPrototypeOf()来确定是否为对象的原型对象:
Person.prototype.isPrototypeOf(person1);
ES5中增加了新方法Object.getPrototypeOf(),返回[[Prototype]]的值(IE 9+,Firefox 3.5+,Safari 5+,Opera 12+,Chrome):
Object.getPrototypeOf(person1) === Person.prototype // true
4、读取对象某个属性时,会先搜索该对象实例,如果找到同名属性,返回该属性值;如果没有找到,继续搜索该对象的原型对象,这是多个对象实例共享原型对象属性和方法的基本原理。对象实例中的属性会屏蔽原型对象中的同名属性,使用delete操作符可以删除实例属性,从而能重新访问原型中的属性。
function Person() {}
Person.prototype.name = 'Nicholas';
Person.prototype.sayName = function () {
alert(this.name);
}
let person1 = new Person();
let person2 = new Person();
person1.name = 'Greg';
alert(person1.name); // 'Greg'
alert(person2.name); // 'Nicholas'
delete person1.name;
alert(person1.name); // 'Nicholas
hasOwnProperty():若属性为实例属性,返回true:
person1.hasOwnProperty('name') // 如果person1实例本身有name属性,返回true
in操作符:无论该属性在实例中还是原型中,只要能通过对象访问,就返回true。
'name' in person1 // 检测person1是否有name属性,无论是实例还是原型属性
for-in: 返回的是所有能通过对象访问的、可枚举的属性,包括实例属性和原型属性。屏蔽了原型中不可枚举属性的实例属性也会被返回,因为按规定,开发人员定义的属性都是可枚举的(IE8及更早版本中例外)。
Object.keys():返回对象上所有可枚举的实例属性(IE 9+,Firefox 4+,Safari 5+,Opera 12+ 和 Chrome)。
Object.getOwnPropertyNames():返回对象上所有实例属性,包括不可枚举的(IE 9+,Firefox 4+,Safari 5+,Opera 12+ 和 Chrome)。
用new创建实例时经历的步骤
- 创建一个空的js对象,即{};
- 将该空对象[[Prototype]]属性链接到构造函数的原型对象 ;
- 将步骤1新创建的对象作为
this
的上下文 ; - 执行构造函数内的代码(为这个新对象添加属性);
- 如果该函数没有返回对象,则返回
this
。
用代码示意,记函数A为构造函数, new A() 实际做了如下事情:
let obj = {};
obj.__proto__ = A.prototype;
return A.call(obj) || obj;
1、new的过程中并不涉及constructor属性。改变A.prototype.constructor,也不影响new A()的执行过程:
function A () { console.log('A'); }
function B () { console.log('B'); }
B.prototype.constructor = A;
let b = new B(); // 'B'
console.log(b.__proto__ === B.prototype); // true
console.log(b.construtor === A); // true
2、new A也可以创建实例,和new A()的区别是new A不可以传入参数。