原型
prototype是function对象的一个属性,构造函数实例化以后出现的结果,定义构造函数构造出的每个实例对象的公共祖先。一般配置项写在构造函数里,不变的属性和方法写在原型里,并且无法通过实例化对象影响原型上的属性和方法。
constructor:原型上的构造器,指向构造函数。可以通过这个属性更改指向的构造函数,必须要经过实例化才会生效。要注意Test.prototype.method = function(){}和Test.prototype = {}的区别就是,后者会改变constructor,前者还是原来的constructor。
proto:属于实例化对象,必须构造函数实例化之后,才会出现这个属性,指向原型。可以通过这个属性更改指向的原型。
function Car(){}
Car.prototype.name = 'Mazda';
var car = new Car();
Car.prototype.name = 'Benz';
console.log(car.name); //'Benz'
console.log(Car.prototype);
这么做就是对属性的重新赋值(修改的同一个实例对象的prototype),放new前面和后面都没有关系
function Car(){}
Car.prototype.name = 'Mazda';
var car = new Car();
Car.prototype = {
name: 'Benz' //修改的是constructor里面的值,而constructor要实例化之后才会生效
}
console.log(car.name); //'Mazda'
这么做就不一样了,Car.prototype = {}这种形式的原型编写是把整个原型都重写了。并且本例中的原型修改是在实例化之后修改的(constructor不一样),也就是说两个prototype不是同一个实例化对象的原型
时间线梳理:
- 没有实例化之前:Car.prototype.constructor -> Car() -> Prototype -> name: Mazda
- 实例化之后:constructor生效
this{
__proto__: Car.prototype {
name: 'Mazda'
}
}
- 重写原型(constructor改变):Car.prototype.constructor -> Car() -> Prototype -> name: Banz
- 未实例化,所以打印的结果是第一次实例化的结果
再次实例化可以看到两个结果
function Car(){}
Car.prototype.name = 'Mazda';
var car1 = new Car();
Car.prototype = {
name: 'Banz'
}
var car2 = new Car();
console.log(car1, car2);
原型链
原型链:沿着__proto__找原型,形成链条式的继承关系
原型链的顶端:Object.prototype( 不是Object!!!)
Object.prototype里面有toString()方法
子级可以更改父级的引用值属性,原始值不行(复制处理)
//教授
Professor.prototype.tSkill = 'JAVA';
function Professor(){}
var professor = new Professor();
//老师
Teacher.prototype = professor;
function Teacher(){
this.mSkill = 'JS/JQ';
this.students = 200;
this.data = {
name: 'jackson'
}
}
var teacher = new Teacher();
//学生
Student.prototype = teacher;
function Student(){
this.pSkill = 'HTML/CSS'
}
var student = new Student();
//student没有这个属性,但是会复制并在处理之后进行添加,teacher原始值不变
student.students++; //201 200
student.data.name = 'eden';// eden eden 引用值都发生了改变
console.log(student, teacher);
就相当于:
var obj = {
name: 'win'
}
obj.age = 5;
console.log(obj);
这样就会有age属性了
this谁调用指向谁
function Car(){
this.brand = 'benz'
}
Car.prototype = {
brand:'Mazda',
intro: function(){
console.log(this.brand);
}
}
var car = new Car();
car.intro();//'benz'
car.prototype.intro();//'mazda'
function Person(){
//{
// weight: 129 //从原型中复制,经过方法计算后,添加进this
//}
this.smoke = function(){
this.weight--;
}
}
Person.prototype = {
weight: 130 //不变
}
var Person = new Person();
打印person -> 129
打印Person.prototype -> 130
Object.create(对象,null) 创建对象
可以自定义原型,构建对象
function Obj(){}
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype); //以Obj.prototype为原型创建实例对象
var obj2 = new Obj();
console.log(obj1, obj2);
做出来的两个obj是一模一样的
new方法:
- 实例化obj2
- 调用构造函数Obj的初始化属性和方法
- 指定实例对象的原型Obj.prototype
Object.create():
var obj1 = Object.create(null); //创建obj1空对象
console.log(obj1); //真·什么都没有·对象
obj1.num = 1;
var obj2 = Object.create(obj1); //原型是obj1,自定义原型
console.log(obj2);
console.log(obj2.num); //1
可以把其他对象作为原型继承
Object.create(null)的原型
并不是所有对象都继承于Obj.prototype,Object.create(null)就不是
自己指定的__proto__是没用的,必须要用系统的
可以更改,但不能自造
var obj1 = Object.create(null);
obj1.num = 1;
var obj1 = {
count: 2
}
obj.__proto__ = obj1;
console.log(obj.count); //undefined
继承方法:
1. 原型链继承
Professor.prototype.tSkill = 'JAVA';
function Professor(){}
var professor = new Professor();
Teacher.prototype = professor;
function Teacher(){}
var teacher = new Teacher();
console.log(teacher);
让构造函数作为原型,但是没有必要把Professor和Professor.prototype的东西全部继承
2. call/apply
其实这种方法不是继承,而是借用,不会继承Teacher的原型
Teacher.prototype.wife = 'Ms.Liu';
function Teacher(name, mSkill){
this.name = name;
this.mSkill = mSkill;
}
function Student(name, mSkill, age){
Teacher.apply(this, [name, mSkill]);
this.age = age;
}
var student = new Student(
'Mr.Zhang', 'JS', 20
);
console.log(student)
3. 公共原型
原型相等Student.prototype = Teacher.prototype,改一个,另外一个跟着改
function Teacher(){
this.name = 'Ms.Liu';
this.mSkill = 'JS';
}
Teacher.prototype = {
pSkill: 'JAVA'
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'eden';
}
Student.prototype = Teacher.prototype;
Student.prototype.age = 18;
var s = new Student();
console.log(s);
4. 添加缓冲函数(圣杯模式)
function Teacher(){
this.name = 'Jackson';
this.tSkill = 'JAVA';
}
Teacher.prototype = {
pSkill: 'JS'
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'eden';
}
//建立一个缓冲函数,将student和teacher隔离开来
function Buffer(){}
//Buffer什么都没有,但是原型继承自Teacher.prototype
//只要Buffer.prototype不变,Teacher.prototype也不会变
Buffer.prototype = Teacher.prototype;
var buffer = new Buffer();
Student.prototype = buffer;
var s = new Student();
console.log(s)
s -> Student -> Student.prototype = buffer -> Buffer.prototype = Teacher.prototype
这样做既不会继承Teacher构造函数上的属性,也不会让student和teacher之间相互影响。
封装
function inherit(Target, Origin){
function Buffer(){}
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
//构造器指向纠正
Target.prototype.constructor = Target;
// 继承源(超类
Target.prototype.super_class = Origin;
}
改良封装的函数:立即执行函数会有自己的作用域,自己的命名空间
var inherit = (function(){
var Buffer = function(){}; //私有变量
return function (Target, Origin){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
Target.prototype.constructor = Target;
Target.prototype.super_class = Origin;
}
})();