Skip to main content
 首页 » 编程设计

Javascript面向对象之函数原型

2022年07月19日129jackei

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