再了解一下构造函数:
构造函数
对象是某个特定引用类型的实例,新对象是使用new操作符后跟一个构造函数来创建的。构造函数本身是一个函数,只不过该函数是出于创建新对象的目的而定义的。例如:
var person = new Object();
这里就是使用构造函数Object()来创建对象。简单来说就是:ECMAScritp中的构造函数,可以用来创建特定类型的对象。
像Object、Array是原生构造函数,在运行时会自动出现在执行环境中。
也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
下面是自定义创建的函数:
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function () {
alert(this.name);
}
}
var person = new Person("Greg",23, "Doctor");
person为 构造函数 Person的实例。
构造函数与其他函数的唯一区别:
在于它们的调用方式不同。构造函数也只是函数,不存在定义构造函数的特殊语法。任何函数只要通过new操作符来调用,它就可以作为构造函数;而任何函数不能通过new操作符来调用,它和普通函数也不会有什么两样。
在上面展示了Person函数作为构造函数,下面的代码展示Person作为普通函数:
Person("Greg",23,"Doctor");
Window.sayName(); //"Greg"
每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,不同实例上的同名函数是不同的,等于每创建一个对象,就会创建类似的方法,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype
)
原型
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的的所有实例共享的属性和方法。即:protortype是通过调用构造函数而创建的那个对象实例的原型对象。
参照例子:prototype是调用 构造函数Person创建的 实例person的原型对象。
这里 原型是 prototype。
例如:
function Person(){}
Person.prototype.name = "Greg";
Person.prototype.age = "23";
Person.prototype.job = "Doctor";
Person.prototype.sayName = function () {
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Greg"
var person2 = new Person();
person2.sayName(); //"Greg"
alert(person1.sayName == person2.sayName); //true
将属性定义在 Person.prototype(Person的原型也称为原型对象)上,可以达到这个对象Person上的所有属性和方法是由所有实例共享的。且方法函数不会多余创建。解决上面的构造函数产生的问题。
接下来解释原型对象。
原型对象
这里四个点看不太清楚的话,直接看代码和例子解析,然后再回头看。
1.无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性始终指向函数的原型对象。
2.在默认情况下,所有原型对象都会获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
3.在调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。标准中叫 [[Prototype]],但是firefox、safari、及chrome在每个对象上支持 __proto__,这个连接存在于实例和构造函数的原型对象之间。
4.实例的构造函数属性(constructor)指向构造函数。
通过例子理解(注意区分大小写):
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function () {
alert(this.name);
}
}
var person = new Person("Greg",23, "Doctor");
var person1 = new Person("Greg1",24, "Doctor1");
console.log(Person === Person.prototype.constructor); //true
console.log(person.__proto__ === Person.prototype); //true
console.log(person.constructor === Person); //true
console.log(person1.constructor === Person); //true
这里的 :
构造函数 是 Person
原型对象 是 Person.prototype
实例 是 person
这里记下上面的第一句话:
打印的结果中:
1.Person.prototype指向(是)原型对象
2. Person === Person.prototype.constructor 为true, 可以对应上面第二句话:原型对象 Person.prototype 会默认有一个 constructor 属性,然后这个属性指向Person。
3. person.__proto__ === Person.prototype 返回true,可以对应上面第三句话:实例(person)的内部将包含一个内部属性 __proto__,person.__proto__ 指向构造函数的原型对象 Person.prototype。
如下图指向:
接上面的点
4. person.constructor === Person 实例person的constructor (构造函数)属性指向 构造函数Person。
发现:
person1.constructor === Person
Person.prototype.constructor === Person
这两个十分接近。
person1 为什么有 constructor 属性?那是因为 person1 是 Person 的实例。
那 Person.prototype 为什么有 constructor 属性??同理, Person.prototype 也是Person 的实例。
结论:原型对象(Person.prototype)是 构造函数(Person)的一个实例。
原型对象的作用:继承
var Person = function(name){
this.name = name; // tip: 当函数执行时这个 this 指的是谁?
};
Person.prototype.getName = function(){
return this.name; // tip: 当函数执行时这个 this 指的是谁?
}
var person1 = new person('Mick');
person1.getName(); //Mick
从这个例子可以看出,通过给 Person.prototype
设置了一个函数对象的属性,那有 Person 的实例(person1)出来的普通对象就继承了这个属性。
var person1 = new person('Mick');
person1.name = 'Mick'; // 此时 person1 已经有 name 这个属性了
person1.getName(); //Mick
故两次 this 在函数执行时都指向 person1。
原型链
这一部分小测试是小编转的:最详尽的JS原型和原型链(二)
小测试来检验一下你理解的怎么样:
person1.__proto__
是什么?Person.__proto__
是什么?Person.prototype.__proto__
是什么?Object.__proto__
是什么?Object.prototype__proto__
是什么?
答案:
第一题:
因为 person1.__proto__ === person1 的构造函数.prototype
因为 person1的构造函数 === Person
所以 person1.__proto__ === Person.prototype
第二题:
因为 Person.__proto__ === Person的构造函数.prototype
因为 Person的构造函数 === Function
所以 Person.__proto__ === Function.prototype
第三题:Person.prototype
是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以 Person.prototype.__proto__ === Object.prototype
第四题,参照第二题,因为 Person 和 Object 一样都是构造函数
第五题:Object.prototype
对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。Object.prototype.__proto__ === null
JavaScript 只有一种结构:对象。
原型链:每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null
。根据定义,null
没有原型,并作为这个原型链中的最后一个环节。
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
简单点来说就是:
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
参考链接:最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)