原型与原型链

原型

原型是什么

每个函数都有一个原型对象(prototype),它是一个属性也是一个指针,指向一个对象。
当所有对象有一共有的属性时,就可以用prototype添加,不用一个一个的重复添加。

Person.prototype.name = "Nicholas";
Person.prototype.age = 23;
function Person(){
}
var person1 = new Person();

原型的属性

  • constructor
    创建了构造函数后,系统默认其对象有constructor属性。这个属性指向原函数。
Person.prototype.constructor == Person;

相应的,在每一个使用构造函数新建的实例中,也存在一个指针,指向构造函数的原型对象。这个指针可以为[[prototype]]或__proto__。

  • isPrototypeOf
    确定传入的对象的原型是否是当前的对象。
var result = Person.prototype.isprototypeOf(person1);//true
  • getPrototypeOf
    ES5新添加的属性,可以返回实例对象的原型。
var result = Object.getPrototypeOf(person1);//Person.prototype

原型的增删查改

虽然我们能用对象实例去访问对象原型中的值,但是不能使用实例去改变原型中已有的属性和值。

Person.prototype.name = "Nicholas";
        Person.prototype.age = 23;
        function Person(){ 
        }
        var person1 = new Person();
        var person2 = new Person();

        person1.name = 'Greg';
        console.log(person1.name);

当像这样使用实例强制改变原型中的值时,原型中的值会被覆盖,
这种情况被称为"属性遮蔽 (property shadowing)。
此时再访问 person1 . name,返回值为‘Greg’;
原有的原型值没有并没有改变。
当delete person1.name时,再次访问person1 . name,返回为‘Nicholas’;

对象字面量创建原型

为了减少代码量,有一种更简便的方法来写对象的原型。

function Person(){ 
        }
        Person.prototype = {
            name : 'Nicholas',
            age : 23,
            job : 'teacher'
        }
        var person1 = new Person();
        var person2 = new Person();

但是这种写法也有缺点:此时原型的constructor属性不再指向Person
这时constructor指向Object。
如果constructor不能改变,可以通过以下方法特意设置其值。

function Person(){ 
        }
        Person.prototype = {
            constructor : Person,
            name : 'Nicholas',
            age : 23,
            job : 'teacher'
        }

原型的重写

关于创建原型实例和修改原型的问题。

  1. 当先创建实例,后添加属性值时:
function Person(){ 
        }
var person1 = new Person();
    Person.prototype.name = 'Nicholas';
    console.log(person1.name);//'Nicholas'

尽管原型赋值在后,但是由于实例与原型松散的连接,所以在寻找person1.name中后从实例找到原型中。所以可以随时给原型添加属性和方法。
2. 当改变重写原型对象时。

function Person(){ 
        }
var person1 = new Person();
Person.prototype = {
            name : 'Nicholas',          //上面讲到当使用这种方法创建原型时,
            age : 23,                   //会改变原型constructor的指向
            job : 'teacher'
}
console.log(person1.name);           //因此现在找不到person1.name,返回undefined

原型链

二话不说,先看代码

Grand.prototype.lastName = 'Nicholas';
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
    this.name = 'xiaoming';
} 
var father = new Father();
son.prototype = father;
function Son(){
    this.hobbit = 'havana';
}
var son = new Son();

由上可以看出原型链的实现方法。
此时原型对象包含一个指向另一个原型的指针,则另一个原型中也包含着一个指向另一个构造函数的指针。当另一个原型又是另一个原型的实例,如此层层递进,就构成原型与实例的链条。
而原型链的终端则是Object.prototype。
简而言之,上面的代码中:Son继承了Father,Father继承了Grand,Grand继承了Object。

虽然绝大多数对象都继承自Object.prototype,但是还是例外。
我们可以构建出不继承自Object的对象
使用Object.create(null)即可

谨慎定义

在原型链中定义方法时,不能使用对象字面量创建原型方法。

function Father(){
    this.name = 'xiaoming';
} 
var father = new Father();
function Son(){
    this.hobbit = 'havana';
}
son.prototype = father;
//这时添加方法,会切断son和father之间的连接,原型链被切断了
son.prototype = {
    action : function(){
        return 'walk';
    }
}
var son = new Son();

原型链的问题

含有引用值的原型属性会被所有实例共享,这就造成了原型链的缺陷。
让我们来看一个栗子。

function Father(){
    this.TshirtColors = ['red','blue','yellow'];
} 
var father = new Father();
function Son(){
    this.hobbit = 'havana';
}
son.prototype = father;
var son1 = new Son();
var son2 = new Son();
son1.TshirtColors.push('black');
alert(son1.TshirtColors);   //'red','blue','yelllow','black'
alert(son2.TshirtColors);   //'red','blue','yelllow','black'

关于解决这个问题的方法,将在下次博客介绍。

原型实战

//    1
Person.prototype.name = 'sunny';
function Person(){
}
var person = new Person();

person.peototype = {
 name : 'cherry'
}

//      2
Person.prototype.name = 'sunny';
function Person(){
}

person.peototype = {
 name : 'cherry'
}

var person = new Person();

请比较这两块代码的输出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值