0.前言
最近在网上学习原型这一块,看了很多博客,都不尽人意,因此笔者自己写一篇文章,尽量让大家明白这是怎么一回事。上一节我们只是了解一下js中的常用方法,同时也了解到prototype和constructor这两个属性,就是为了这一节做铺垫。
1.原型属性(prototype属性)
原型属性也叫prototype属性,每一个函数都有prototype属性,初始指向一个空对象(也叫原型对象)。我们可以给prototype进行修改,让它引用一个非空对象,只有在该函数是构造函数时才有实际意义。对于非构造函数,不会对函数的运行造成影响。当函数为构造函数时,我们可以通过原型属性修改原型对象,达到给用构造函数创建的对象增加属性和行为的功能。
2.利用原型添加方法与属性并修正constructor属性
首先定义一个构造函数
function Person(name, age){
this.name = name;
this.age = age;
this.say = function(){
console.log("My name is " + this.name);
};
}
(1)利用原型添加方法和属性
Person.prototype.height = 175;
Person.prototype.weight = 65;
Person.prototype.run = function(){
console.log("run");
};
(2)用已存在的对象去给prototype赋值
Person.prototype = {
height: 175,
weight:65,
run: function(){
console.log("run");
}
};
定义上面两个方法时,我们发先一个问题,因为在上一节中我们了解到constructor是指向创建对象的构造函数,但是在这里由构造函数的prototype指向的原型对象变成了现有的对象,不是构造函数本省的原型对象,因此让它的constructor重新只回来,指向构造函数,这也是原型链的原理来张图片:
所以要添加如下的方法:
//解决:重新赋值成对应的构造函数
Person.prototype.constructor = Person;
var per = new Person("sunck", 18);
console.log(per.constructor);
console.log(Person.prototype.constructor);
结果:
实例化对象
var per1 = new Person("sunck", 18);
console.log(per1.name);
console.log(per1.age);
per1.say();
console.log(per1.height);
console.log(per1.weight);
per1.run();
结果:
发现实例化的对象可以访问原型对象中的属性和方法,可以发现原型对象可以为对象添加属性和方法。
3.使用原型的方法与属性
上一个方法主要讲了用原型设置属性和方法,既然能设置,那么肯定能访问,我们来看一下。
还是上面的代码:
//访问自身属性与方法
console.log(per.name);
console.log(per.age);
per.say();
结果:
当前对象访问原型对象中的属性(访问原型属性):
访问原型属性的方式与访问自身属性的方式是一样的。
对象访问属性的原理:当我们访问name属性时,首先在自身属性中查找,找到了立即返回当前属性的值。当我们访问height属性时,也会先在自身属性中查找,找不到的话去它的原型对象中查找,找到就返回数值。如果找不到还会去上一级原型对象中查找,最终找不到反返回undefined。
console.log(per.height);
console.log(per.constructor.prototype.height);
per.run();
结果:
我们来总结一下:
原型对象的精髓:实时性;在JS中,对象的传递都是引用传递,对于同一个构造函数创建的对象来说,仅仅是拥有一个原型对象的引用,不会拥有原型对象的副本。如:
Person.prototype.eat = function() {
console.log("eat");
};
per.eat();
结果:
在上面的代码,你发现了什么?在构造函数中没有eat()这个函数,于是在原型对象上我们添加了一个eat()方法;per对象是在添加eat()方法之前就被创建了,但是它还是能访问后来创建的eat()函数,这就是原型对象的精髓——实时性。
4.利用自身属性重写原型属性——hasOwnProperty()与isPrototypeOf()方法
这个知识点主要是介绍hasOwnProperty()与isPrototypeOf()这两个方法。可能上代码更能直观点:
var base = {
height: 180,
weight:70,
};
function Person(name, age){
this.name = name;
this.age = age;
this.height = 175;
this.say = function(){
console.log("My name is " + this.name);
};
}
//
Person.prototype = base;
Person.prototype.constructor = Person;
console.log(base.constructor);
var per = new Person("sunck", 18);
console.log(per.constructor);
console.log(per.height);
在这扯个题外话:访问height属性,是自身属性还是原型属性呢?再上一个知识点说到,现在滋生属性上找,没有找到的话就到原型对象上找。所以这是——自身属性;
结果:
看见 console.log(per.height);打印出来的是自身属性。
hasOwnProperty():判断属性是否是自身属性;自身属性返回true,原型属性返回false
console.log(per.hasOwnProperty("weight"));
结果:
删除与原型属性同名的height属性,那么原型属性暴露出来
delete per.height;
console.log(per.height);
isPrototypeOf():判断一个对象是否是另一个对象的原型对象
console.log(base.isPrototypeOf(per));
console.log(new Array(5).isPrototypeOf(per));
结果:
5.for-in枚举属性(propertyIsEnumerable()方法)
var base = {
height: 180,
weight:70,
};
function Person(name, age){
this.name = name;
this.age = age;
this.say = function(){
console.log("My name is " + this.name);
};
}
Person.prototype = base;
// Person.prototype.constructor = Person;
var per = new Person();
for(var i in per){
console.log(i);
}
结果:
总结一下:
1.不是所有的属性都能遍历出来,比方说数组中的length和constructor。自身属性与原型属性(原型链上的属性)都可以遍历。
2.內建属性,对象自身自带的属性。內建属性大多数都是不可以枚举的。
3.propertyIsEnumerable判断属性是不是可枚举的,原型属性都会返回false。
console.log(per.propertyIsEnumerable("name"));
console.log(per.propertyIsEnumerable("height"));
console.log(per.propertyIsEnumerable("constructor"));
console.log(new Array(5).propertyIsEnumerable("length"));
结果:
6.总结
这一节的原型对象我自认为说的还算通畅吧,如果又不懂得地方欢迎来信哦!!!