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 ); // ?
本文参考链接:https://blog.csdn.net/neweastsun/article/details/70197768