10.原型

一些应该掌握的知识点

function Person(name, age, job) {
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');

  上面的例子中person1和person2都是Person的实例。这两个实例都有一个constructor(构造函数)属性,该属性(是一个指针)指向Person。即:

  console.log(person1.constructor == Person); //true
  console.log(person2.constructor == Person); //true

  我们要记住两个概念(构造函数,实例):person1和person2都是构造函数Person的实例。
  一个公式:实例的构造函数属性(constructor)指向构造函数。

  • 对象可通过属性consructor来查看自己的构造函数

  • 原型(prototype)是函数对象(Function)的一个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法(注:需要注意的是,其实原型也是一种对象)。

  • 原型原理案例分析

function carFactory (str) { //设定一个车工厂函数
  this.name = 'BMW';
}
var oCar = new carFactory(); //用构造函数的方法早一辆车
console.log(oCar.color); //试图让控制台显示一个oCar所没有的color属性,最终控制台显示undefined

  如上例所示,由于JavaScript是一种弱类型语言,当你在一个对象下直接使用一个没有定义的属性,该属性值会为undefined,用控制台查看该属性时不会报错。

function carFactory (str) {
  this.name = 'BMW';
}
carFactory.prototype.color = 'black'; /* 用原型给车工厂函数的增加一个color属性,
                                         并给该属性赋予字符串'black' */
carFactory.prototype.door = door || 4;/* 用原型给车工厂函数的增加一个door属性,
                                         并给该属性赋予door,默认赋予数值4 */
var oCar1 = new carFactory(); //用构造函数的方法造车
var oCar2 = new carFactory();
console.log(oCar1.color); //控制台显示black
console.log(oCar2.door); //控制台显示4

  如首例所示,当控制台需要查看oCar的color属性时发现oCar自己并没有这个属性,这时候oCar就回去找原型,即车工厂函数的对象,由于车工厂函数的对象并没有color属性,所以最终控制台显示undefined,但如果像上例所示,利用原型给车工厂函数创建一个color属性并给该属性赋予字符串’black’,最终控制台就显示black,door属性同理。
  从以上案例可以看出,在原型上所添加的任何属性都是给祖先所添加的,当祖先的子类需要使用这些属性的时候,由于这些属性已经通过原型来添加到祖先上了,所以子类都可以使用这些属性。

  • 不能通过修改后代修改祖先的内容
function sample () {
    this.x = 1;
}
var sample1 = new sample();
var sample2 = new sample();
var sample3 = new sample();
sample1.x = 2; //修改sample1中x属性的值
delete sample2.x; //删除sample2中的x属性
console.log(sample1.x); //控制台显示2
console.log(sample2.x); //控制台显示undefined
console.log(sample3.x); //控制台显示1
  • 可以利用原型的概念与特性来提取共有属性
shoeFactory.prototype.size = 40; //在创建鞋工厂函数前先用原型创建一些属性
shoeFactory.prototype.type = 'slipper';
function shoeFactory (color) { //创建一个鞋工厂函数
    this.color = color || 'white'; //创建一个color属性,并给该属性赋予默认值'white'
}
shoe1 = new shoeFactory('red'); //用构造函数造鞋,设定新鞋的color属性的值为'red'
shoe2 = new shoeFactory('yellow'); //设定新鞋的color属性的值为'yellow'
shoe3 = new shoeFactory('blue'); //设定新鞋的color属性的值为'blue'
console.log(shoe1.color); //控制台显示red
console.log(shoe2.size); //控制台显示40
console.log(shoe3.type); //控制台显示slipper

注:通过以上方法,即提取共有特性的方法,可以缩短每次用构造函数创建新对象所调用函数的长度

  • __proto__属性

    • __proto__属性是一种隐式属性,相对的prototype属性则是一种显示属性
    • __proto__属性并不标准,一般不提倡使用
    • 一般的,__proto__属性一般指向创建这个对象的函数的显式原型,而Object.creat()所创建的对象的__proto__属性指向这个对象的函数的显示原型的隐式原型
    • 对象可通过__proto__属性来查看自己的原型
    • 构造函数中的this = { __proto__: 函数名.protoype}
    • 关于prototype、__proto__和 constructor三者关系分析的一篇博客参考:博客园 小火柴的蓝色理想:一张图理解prototype、proto和constructor的三角关系
  • 关于__proto__属性一般情况下的指向的案例

function Sample () {
    this.o = 0;
}
Sample.prototype.x = 1;
var oSample = new Sample();
oSample.__proto__.x = 2;
console.log(oSample.x); //控制台显示2,说明使用__proto__属性也可修改自己的相关属性
  • 对象实例的原型未必非得是自己构造函数的原型(趣解:真正意义上的认贼作父)
function Adidas () {}; //设定一个阿迪达斯函数
Adidas.prototype.name = 'adidas'; //给自己的品牌命名为adidas
function Nike () {}; //设定一个耐克函数
Nike.prototype.name = 'nike'; //给自己的品牌命名为nike
var oNike = new Nike(); //用构造函数造鞋
console.log(oNike.name); //控制台显示nike
oNike.__proto__ = Adidas.prototype; /* 把阿迪达斯的显示原型
                                       赋予nike的隐式原型 */
console.log(oNike.name); //因为原本耐克对象实例oNike的原型
                           已经被替换为阿迪达斯,所以控制台
                           显示adidas */
  • 原型链

    • 因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型的层层连接就是原型链
    • 原型链与原型终端 注:原型终端即原型链中的最顶层,一般是object()
    • 原型链的形成是真正是靠__proto__属性而非prototype属性
    • 一个原型链案例

      Grand.prototype.lastname = 'Lin';
      function Grand () {};
      var grand = new Grand();
      Father.prototype = grand;
      function Father () {
          // this.fortune = {money: 100}; 
          this.fortune = 100;
      };
      var father = new Father();
      Son.prototype = father;
      function Son () {}
      var son = new Son();
      // son.fortune.money += 100;
      son.fortune += 100; /* 对象son下的属性自加100,由于对象son下
                            并没有fortune属性,于是它会沿着原型链
                            网上找,最终它在祖先father对象中找到了
                            fortune属性,于是对象son继承了这个属性,
                            并执行了`son.fortune += 100;`这个语句
      console.log(son.fortune); //控制台显示200
      console.log(father.fortune); /* 由于对象son继承的属性改变并不
                                     影响父类father对象的属性值,控
                                      制台显示100 */
      console,log(son); //控制台显示对象son

          下图为上例代码末行console.log(son);的执行结果,从中可见原型链的端倪



关于原型与原型链的一篇博客参考:简书 Yi罐可乐:最详尽的 JS 原型与原型链终极详解,没有「可能是」

(注,该博文分为三节,本链接为第一节,其他两节在博文内容首行有链接)





一些练习

  • 练习一
Person.prototype.name = 'sunny';
function Person () {}
Person.prototype.name = 'cheery';
var oPerson = new Person();
console.log(oPerson.name); //控制台显示cherry
  • 练习二
Person.prototype.name = 'sunny';
function Person () {}
Person.prototype = {
    name:'cherry'
};
var oPerson = new Person();
console.log(oPerson.name); //控制台显示cherry
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值