原型
说起原型prototype首先要说对象,JS中的对象分为两大类一类是函数对象都是Function的实例(包括Object、Array等),另一类是普通对象都是Object的实例。
- 只有函数对象才有prototype属性,prototype也是一个普通对象(Function.prototype除外)
- 函数对象和普通对象都有_proto_属性
原型链
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("小天");
p.toString();
p.sayName();
首先看一段代码,实例p上并没有定义toString和sayName这两个方法,但是众所周知这两个方法都是存在的,p是如何拥有这这两个方法的呢,下面我们就通过这段代码来了解下原型链。
上面这张图是这段代码的原型链图,从左下角的Person实例p看起 ,p是由new Person()得来的,new操作符在这个过程中做了四件事情:
(1)创建了一个普通对象
(2)将构造函数的this替换为新创建的对象并执行构造函数(这个过程新对象就具有了构造函数的属性)
(3)将新对象的_proto属性指向构造函数的prototype(本例中就是将p._proto指向了Person.prototype,这个过程实际的意义就是使实例拥有了构造函数的方法)
(4)将新对象返回出去
当实例p调用sayName方法时,会首先找到自身的属性方法,当自身没有这个属性方法时会通过自己的_proto属性向上查找,这是就查找了Person.prototype上,因为Person.prototype拥有这个sayName方法,所以p就调用了这个方法,toString方法也是一个道理,本例中Person.prototype上也并不存在toString这个方法,那就通过Person.prototype._proto继续向上查找,由于Person.prototype也是一个普通对象,所以 Person.prototype._proto指向了Object.prototype,Object.prototype上有toString这个方法,所以p就可以调用了这个toString方法。假如这个要调用的这个方法Object.prototype上也没有就会通过Object.prototype._proto向上找就是null了,浏览器就会报错了。
解决了实例p是如何调用sayName和toString方法这两个问题,我们继续将这个原型链闭合完整。 Object、Person这两个都是函数对象,它们都是Funciton的实例,所以他们的_proto属性指向了Function.prototype,而Function比较有意思Function._proto属性指向Funciton.prototype,换句话说就是Function自己创建了自己。大概设计语言的时候就需要这样一个终点吧,要不然这样一直想上找也没有尽头啊,而Funciton.prototype_proto指向了Object.prototype。Function并不是Object的实例反而Object是Function的实例,但Funciton.prototype_proto却指向了Object.prototype。这有点不符合上面的逻辑,但这样完成了原型链的闭合,可能布兰登觉得这样能更好的提现JS“万物皆对象”的逻辑吧。
继承
原型继承方法1
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
Person.prototype.getName = function() {
console.log(this.name);
};
function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}
var F = function() {};
F.prototype = Person.prototype;
Male.prototype = new F();
Male.prototype.getAge = function() {
console.log(this.age);
};
var ruoyu = new Male('小明', '男', 27);
ruoyu.getName();
ruoyu.getAge();
console.log(ruoyu.constructor);
原型继承方法2:Object.create
es5方法Object.create() 方法使用指定的原型对象和其属性创建了一个新的对象。
这里顺便介绍下es6的Object.setPrototypeOf方法做下区分。
Object.setPrototypeOf(obj,prototype) 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null.
- obj
要设置其原型的对象。. - prototype
该对象的新原型(一个对象 或 null).
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
Person.prototype.getName = function() {
console.log(this.name);
};
function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}
Male.prototype = Object.create(Person.prototype);
//不要忘记修改constructor指向
Male.prototype.constructor = Male;
Male.prototype.getAge = function() {
console.log(this.age);
};
var ruoyu = new Male('小明', '男', 27);
ruoyu.getName();
ruoyu.getAge();
console.log(ruoyu.constructor);
原型继承3:ES6
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
console.log(this.name + ':我是社会主义接班人');
}
}
class Coder extends Person {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
printSex() {
console.log(this.sex);
}
}
var ruoyu = new Coder('若愚', '27', '男');
ruoyu.say();
ruoyu.printSex();