原型、原型链、call/apply、继承模式

193 阅读3分钟

原型

定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

//Person.prototype 原型
Person.prototype = {
    lastName : 'lee',
    name : 'hi'
}
function Person(){
    this.name = 'hay',
    this.sex = 'female'
}
var person1 = new Person()
person.lastName //lee 去原型中找属性
person.name //hay 自己有的属性不会去原型找

原型的使用场景:当需要一些公有的属性或方法时,可以写在prototype中。一些个性化的属性或方法,则写在this对象中。

constructor

是prototype对象(原型)自带的属性。用来查看对象的构造器。

function Car(){}

function Person(){
    this.name = 'hay',
    this.sex = 'female'
}
var person1 = new Person()
person1.constructor // ƒ Person() {...}

Person.prototype.constructor = Car //更改自带的constructor
person1.constructor // ƒ Car() {...}

像上面这样,constructor是可以被手动更改的。

proto (前后两个下划线没有展示出来)

是对象自带的属性,__proto__里装的就是原型。

Person.prototype = { name:'hhh' }
function Person(){
    //隐式创建的this并不为空对象,里面会自带__proto__属性,指向原型
    //var this = { __proto__ = Person.prototype } 
}
var person1 = new Person()

当访问一个对象的属性,如果在对象中没有找到对应属性,会通过__proto__的指向找到原型,再看原型中有没有对应属性。 __proto__的指向也可以手动更改。

原型链

function Grand(){
    this.lastName = 'lee'
}
var grand = new Grand()

Father.prototype = grand //让Father的原型指向grand对象
function Father(){
    this.hobby = 'learning'
}
var father = new Father() 

Son.prototype = father //让Son的原型指向father对象
function Son(){}
var son = new Son()

son.hobby //learning
son.lastName //lee
son.toString() //这个toString方法是Object.prototype中的

访问对象的属性或方法时,会通过原型链,一层一层往上找。原型链的顶端是Object.prototype,绝大多数对象都会继承这个原型顶端。然而,var obj = Object.create(null) 通过这种方式创建出来的对象,括号里面没有指定原型,那么obj就没有原型链了。

var num = 123
num.toString() 
// new Number(num).toString() --> '123'
//用的是Number.prototype中的toString方法
//当然Object.prototype中也有toString方法,但是Number**重写**了这个方法,所以会调用Number的

call/apply

作用:改变this指向

function Father(lastName){
    this.lastName = lastName
}
function Person( lastName, name ){
    Father.call(this,lastName) //让Father中的this指向Person
    this.name = name
}
var person = new Person('lee','hhh')
person // {lastName:'lee', name:'hhh'}

call需要一一传实参,apply则需要以数组形式传参,也就是直接传一个arguments。

继承模式

原型链:会继承很多自己不需要的属性

公有原型:两个构造函数共同使用一个prototype对象,它们构造的所有对象的原型都是同一个。

Father.prototype.name = 'aa'
function Father(){}
Son.prototype = Father.prototype //共同使用同一个原型
function Son(){}
Son.prototype.age = 6 //会给Father的原型也增加同样属性
var father = new Father()
father.age //6

如果Son想要给原型增加自己的属性,也会改变Father的原型,因为引用的是同一个原型。

圣杯模式:可以给自己的原型增加属性,但不会影响继承的原型。

function inherit(Target,Origin){
    function F(){}
    F.prototype = Origin.prototype //充当一个中间层
    Target.prototype = new F() //prototype指向的是新的对象,但又继承了Origin的原型
    Target.prototype.constructor = Target //归位构造器,否则构造出的对象的constructor会是Origin,而不是Target
    Target.prototype.uber = Origin.prototype //超级父级,查看真正继承自谁
}
Father.prototype.lastName = 'lee'
function Father(){}
function Son(){}

inherit(Son,Father)
Son.prototype.name = 'hi' //Son给自己的原型增加属性
var son = new Son()
var father = new Father()

son.constructor //Son()
son.lastName //lee 
father.name //undefined