- 对象方法
- 类方法
- 原型方法
1.分类定义
代码
function Student(theName){
this.name = theName || 'A student';
// 对象方法
this.reading = function(){
console.log(this.name + ' can read!')
}
}
// 类方法
Student.learning = function(){
console.log('The student can learn!')
}
// 原型方法
Student.prototype.writing = function(){
console.log('The students can write!')
}
测试
var tom = new Student('Tom');
tom.reading();//Tom can read
Student.writing();//The student can write
tom.learning();//The students can learn
这里用三种方法,分别定义了一个学生应有的技能。学生都会学习,而学生Tom可以读书、写字。
2.prototype
javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。
在看prototype之前,先了解几个例子
2.1私有属性、方法
function Student(){
var age = 23;//私有属性
var canGames = function(){
//私有方法
}
}
a.此时外部无法访问
age
和canGames
——所以就不知道该学生的年龄,也不知道他会玩游戏。
b.只能在函数内部访问age
和canGames
——只有学生自己知道自己的年龄,自己会玩游戏
测试
var tom = new Student();
tom.age;//undefined
tom.canGames;//tom.canGames is not a function
2.2静态属性、方法
代码
function Student(){
}
Student.type = 'junior school student';
// 上面说到的类方法
Student.canWalk = function(){
console.log('Student can walk!')
}
测试
Student.type;//junior school student
Student.canWalk();//Student can walk!
var tom = new Student();
tom.type;//undefined;
tom.canWalk();//tom.canWalk is not a function
注意:
这种类属性叫做静态属性,类方法叫做静态方法;
只能通过类属性访问,不能通过实例访问
2.3实例变量、方法
代码
function Student(){
// 上面说的对象方法
this.scores = [];
this.reading = function(){
}
}
测试
var tom = new Student();
tom.scores.push(90);
tom.reading = {};
console.log(tom.scores);//[90]
console.log(typeof tom.reading);//object
var jack = new Student();
console.log(jack.scores);//[]
console.log(typeof jack.reading);//function
可以看出,上面两个实例之间互不影响。因为数组和函数都是引用类型,所以虽然名称相同,但却已经分配了不同地址。也就是说,tom和jack的属性都是对Student的一个复制。
故而:
当一两个实例没有任何影响,当需要构建成千上万个实例的时候,每个实例都会分配新的地址,都会拷贝一份。内存占用可想而知…
这时,就需要 prototype
3.3prototype
function Student(){
}
Student.prototype;
观察
这里并没有报错,并且控制打出一系列信息。
说明:
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个
prototype
属性,默认情况下prototype
属性会默认获得一个constructor
(构造函数)属性,这个属性是一个指向prototype
属性所在函数的指针。
function Student(name){
this.name = name;
}
Student.prototype.introduce = function(){
console.log(this.name);
}
var tom = new Student('Tom');
var jack = new Student('Jack');
可以看出,在新建一个Student的实例时,出现了
__proto__
这个属性,而这个属性正好指向了Student
的prototype
属性
当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(很多浏览器这个指针名字为__proto__
)指向构造函数的prototype
。
那么
prototype
原型方法是否会像函数方法一样,每个实例都单独复制一遍属性呢?
再测试一下,代码
function Student(name){
this.name = name
}
Student.prototype.learned = [];
Student.prototype.readed = function(){
console.log(1);
};
测试
var tom = new Student('Tom');
tom.learned.push('Java');
tom.readed = {};
console.log(tom.learned);//['Java']
console.log(typeof tom.readed);//object
var jack = new Student('Jack');
console.log(jack.learned);//['Java']
console.log(typeof jack.readed);//function
方才说了实例中的__proto__
指向的是Student
中的prototype
(图中红色圈中)。所以在实例jack的时候learned被tom改变了,jack的learned也改变了,因为指向的是同一个prototype的learned属性。
可是为什么,tom也改变了function,jack中的没有变?
因为javascript中,属性的查找:先去查找当前对象属性,是否有相应同名属性或方法;如果没有,再去prototype中查找;如果prototype也没有;继续向prototype的constructor中的prototype递归查找;如果一直递归到Object对象还没有,则停止查找。
上面tom定义了一个同名属性readed(图中黑色圈中),所以直接匹配成功,未去查询prototype中的readed。
总结
- 需要复用的组件,放入原型方法中定义
Class.prototype.XXX
- 需要互不影响、分开工作的组件,放入对象方法中定义
this.XXX
- 禁止实例调用,通过类属性调用,放入类方法中定义
Class.XXX
贴个简单例子:
function Student(name,age){
this.name = name;
this.age = age;
}
Student.prototype.introduce = function(){
console.log('I\'m ' + this.name + ', and I am a junior school student!')
}
Student.makeFriends = function(){
var tom = new Student('Tom',12);
var jack = new Student('Jack',13);
tom.introduce();
jack.introduce();
}
测试:
Student.makeFriends();