Javascript面向对象(四)——函数原型

Javascript面向对象(四)——函数原型

现代javascript中,我们可以通过使用__proto__设置原型,但这不总是这样。javascript从一开始就有原型继承,它是javascript语言的核心特性。在过去,有其他方式设置原型:设置构造函数的”prototype”属性,现在仍然有很多脚本使用。

“prototype”属性

我们已经知道,new F()创建一个对象,但我们没有使用F.prototype属性,javascript自身使用该属性给新对象设置[[Prototype]]
当使用new F()创建新对象时,通过F.prototype设置[[Prototype]]属性。
注意这里的F.prototype属性意味著F的正常属性,名称为prototype。听起来和“原型”术语类似,但这里我们说的是带这个名称的正常属性名称。
示例如下:

let animal = {
  eats: true
};

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = animal;

let rabbit = new Rabbit("White Rabbit"); //  rabbit.__proto__ == animal

alert( rabbit.eats ); // true

设置Rabbit.prototype = animal字面上表达如下:当新创建一个对象时(new Rabbit),赋值[[Prototype]]animal
图示如下:

在上图中,"prototype"是横向箭头,作为正常属性,[[Prototype]]是垂直的,意味着rabbit从animal继承。

缺省 F.prototype, 构造函数属性

每个函数都有prototype属性,即使我们没有提供。缺省prototype属性是一个对象,其仅有的constructor属性,值指向函数自身。
代码展示如下:

function Rabbit() {}

/* default prototype
Rabbit.prototype = { constructor: Rabbit };
*/

我们可以测试:
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }

alert( Rabbit.prototype.constructor == Rabbit ); // true

当然,我们什么也不用做,construction属性多所有rabbits都有效。
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }

let rabbit = new Rabbit(); // inherits from {constructor: Rabbit}

alert(rabbit.constructor == Rabbit); // true (from prototype)


我们能使用constructor属性去创建一个新对象,实际使用相同的构造器。

function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("White Rabbit");

let rabbit2 = new rabbit.constructor("Black Rabbit");

这很便捷,当我们有一个对象,不知道是使用那个构造函数创建的(来自第三方库),我们需要创建另一个相同的对象。
但可能更重要关于“constructor”的事情是…
Javascript本身不能保证constructor的值是正确的
是的,存在缺省的函数原型,但不总是,后面会怎样,取决我们自己。
特别,如果整体覆盖了缺省原型,那么没有constructor在其中。
举例:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false

所以,为了保证constructor正确,不要完整覆盖,而是在缺省原型的基础上增加/删除属性。

function Rabbit() {}

// Not overwrite Rabbit.prototype totally
// just add to it
Rabbit.prototype.jumps = true
// the default Rabbit.prototype.constructor is preserved

或者,重新手工创建constructor属性。

Rabbit.prototype = {
  jumps: true,
  constructor: Rabbit
};

// now constructor is also correct, because we added it

总结

本文简要描绘通过构造函数方式设置对象[[Prototype]]。后面我们将看见更高级编程模型代替。
整体很简单,有些注意点让事情更清晰:
- F.prototype 属性与[[Prototype]]不同。当通过new F()创建对象时,新对象的[[Prototype]]被设置。
- F.prototype的值可以是对象或null,其他值不起作用。
- “prototype”属性仅有特别影响是当执行new时,在构造函数中被设置。

普通对象的prototype无特别意义:
let user = {
name: “John”,
prototype: “Bla-bla” // no magic at all
};

缺省所有函数有F.prototype = { constructor: F },所以我们能获得对象构造器,通过访问“constructor”属性。

 练习

下面代码我们创建对象通过new Rabbit,然后修改其原型,检验下自己,看看下面结果是什么?
开始我们的代码如下:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

alert( rabbit.eats ); // true

1、我们增加更多内容, alert返回什么内容?

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

Rabbit.prototype = {};

alert( rabbit.eats ); // ?

2、如果代码象这样,结果呢?

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

Rabbit.prototype.eats = false;

alert( rabbit.eats ); // ?

3、下面呢?

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

delete rabbit.eats;

alert( rabbit.eats ); // ?

4、最后一个,结果是啥?

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

delete Rabbit.prototype.eats;

alert( rabbit.eats ); // ?
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值