

1、prototype原型对象
js规定,每个构造函数都有一个prototype,其实prototype就是一个对象,这个对象的属性和方法都会被构造函数所拥有。
我们可以把那些不变的方法定义在prototype对象上,其实所有对象的实例就可以共享这些方法。
话不多说,上代码。
<script>
function Person(name,age){
this.name=name
this.age=age
this.hobby=function(){
console.log("唱歌")
}
}
var xm=new Person('小明','10')
var xh=new Person('小红','12')
console.log(xm.hobby===xh.hobby) //输出false
xm.hobby()//唱歌
xh.hobby()//唱歌
</script>
es6之前是把方法写在构造函数里面,每个对象都会去开辟一个函数的内存空间,所以xm.hobby===xh.hobby比较会返回false,如果有多个对象,这样做会存在浪费内存空间,下面建议用prototype对象
<script>
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.hobby=function(){
console.log("唱歌")
}
var xm=new Person('小明','10')
var xh=new Person('小红','12')
console.log(xm.hobby===xh.hobby) //输出true
xm.hobby()//唱歌
xh.hobby()//唱歌
</script>
此时hobby为同一个方法,所以xm.hobby===xh.hobby返回true
一般来说,公共属性定义在构造函数里面,公共方法定义在原型对象身上
2、_proto_对象原型
其实在每个对象中会有一个_proto_属性指向我们的构造函数的原型对象prototype
<script>
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.hobby=function(){
console.log("唱歌")
}
var xm=new Person('小明','10')
var xh=new Person('小红','12')
console.log(xm._proto_===Person.prototype) //输出true
</script>
hobby()方法的查找规则:首先看看xm对象身上是否有hobby方法,如果有就执行它,如果没有,因为有_proto_的存在,就会去构造函数原型对象prototype身上去查找。
在JavaScript的原型链体系中,最重要的莫过于 __proto__ 属性,只有通过它才能将原型链串联起来。 我们先实例化一个字符串,然后在输出字符串的值,具体代码如下:
Function.prototype.a='a';
Object.prototype.b='b';
function Person(){}
var p = new Person;
console.log('p.a',p.a);
console.log('p.b',p.b);
上面的代码需要输出实例p的a属性和b属性的值,所以我们需要先了解实例p的属性查找过程,属性的查找是根据 __proto__ 属性沿着原型链来完成的,因此我们需要先梳理出实例p的原型链。
//实例p的原型链
P.__proto__ = Person.prototype;
//Person原型对象的原型
Person.prototype.__proto__ = Object.prototype;
因此实例输出p的属性时,最终会找到Object.prototype中去,根据一开始定义的值可以得到以下的结果:
p.a undefined
p.b b
3、原型constructor构造函数
<script>
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype={//如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数。
constructor:Star,
hobby1:function(){
console.log("唱歌")
}
hobby2:function(){
console.log("跳舞")
}
}
var xm=new Person('小明','10')
var xh=new Person('小红','12')
console.log(xm.prototype.constructor)
console.log(Star.prototype.constructor)
</script>
4、原型链
对象的每个实例都具有一个 __proto__ 属性,指向的是构造函数的原型对象,而原型对象同样存在一个 __proto__ 属性指向上一级构造函数的原型对象,就这样层层往上,直到最上层某个原型对象为 null。
在JavaScript中几乎所有的对象都具有 __proto__ 属性,由 __proto__ 属性链接而成的链路构成了 JavaScript的原型链,原型链的顶端Object.prototype,他的 __proto__ 属性为null。
我们通过一个实例来看看一个简单的原型链过程,首先定义一个构造函数,并生成一个实例。
function Person(){}
var person = new Person();
然后person实例沿着原型链第一次追溯, __proto__ 属性指向Person()构造函数的原型对象
console.log(person.__proto__ === Person.prototype);
person实例沿着原型链第二次追溯,Person原型对象的 __proto__ 属性指向Object类型的原型对象。
console.log(person.__proto__.__proto__ === Person.prototype.__proto__);
person实例沿着原型链第三次追溯,Object类型的原型对象的 __proto__ 属性为null
console.log(person.__proto__.__proto__.__proto__ === Object.prototype.__proto__=== null);
5、原型链的特点
原型链的特点主要有以下两个:
1. 由于原型链的存在,属性查找的过程不在是只查找自身的原型对象,而是会沿着整个原型链一直向上,知道追溯到Object.prototype,如果Object.prototype上也找不到该属性,则返 回’undefined’,如果期间在实例本身或者某个原型对象上找到了该属性,则会直接返回结果,因此 会存在属性覆盖的问题,
由于特点1的存在,我们在生成在定义对象的实例时,也可以调用到某些未在在定义构造函数上的函数,例如toString()函数。
function Person(){}
var p = new Person();
p.toString(); //Object Object 实际调用的是Object.prototype.toString()函数
2. 由于属性查找会经历整个原型链,因此查找的链路越长,对性能的影响越大。
6、属性区分
对象属性的寻找往往会涉及这个原型链,那么该怎么区分属性是实例自身还是从原型链中继承的呢? Object()构造函数的原型对象中提供了一个hasOwnProperty()函数,用于判断属性是否为自身拥有的。
function Person(name){
//实例属性name
this.name = name;
}
//原型对象上的属性age
Person.prototype.age = 12;
var person = new Person('cao teacher');
console.log(person.hasOwnProperty('name')); console.log(person.hasOwnProperty('age'));
7、内置构造函数
JavaScript中有一些特定的内置构造函数,如String()函数,Number()构造函数,Array()构造函数, Object()构造函数等。 它们本身的 __proto__ 属性都统一指向Function.prototype
console.log(String.__proto__ === Function.prototype);
console.log(Number.__proto__ === Function.prototype);
console.log(Array.__proto__ === Function.prototype);
console.log(Object.__proto__ === Function.prototype);
console.log(Date.__proto__ === Function.prototype);
console.log(Function.__proto__ === Function.prototype);