4.原型

每个函数都有一个prototype属性,这个属性是一个对象,用途是包含可以由特定类型的所有实例共享的属性和方法。

逻辑上可以这么理解:prototype通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

function Box(){};                  //构造函数体内什么都没有,这里如果有,叫做实例属性、实例方法
Box.prototype.name = 'Lee';        //原型属性
Box.prototype.age = 20;
Box.prototype.run = function(){    //原型方法return "姓名:" + this.name + ",年龄:" + this.age;
}var box1 = new Box();              //实例化
console.log(box1.name);            //Lee
console.log(box1.run());           //姓名:Lee,年龄:20

那么,构造方法和原型方法的区别为共享,具体结束如下:

(1)如果是实例方法不同的实例化,他们的方法地址是不一样的,唯一的,详见JavaScript之面向对象与原型笔记整理--------创建对象(1)。

(2)如果是原型方法,那么他们的地址是共享的,大家都是一样的。

看下面这个例子:

function Box(){};
Box.prototype.name = 'Lee';
Box.prototype.age = 20;
Box.prototype.run = function(){    return "姓名:" + this.name + ",年龄:" + this.age;
}var box1 = new Box();
var box2 = new Box();
console.log(box1.run==box2.run);   //true  如果是构造函数的话则为false,因为引用地址不同

为了更好的理解,接下来给出构造函数的声明方式和原型模式的声明方式的示意图。

构造函数方式                                                                                                                                                                  原型模式声明方式

                     

在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。通过这两个属性,就可以访问到原型里的属性和方法了。

看下例子:

function Box(){};
Box.prototype.name = 'Lee';
Box.prototype.age = 20;
Box.prototype.run = function(){    return "姓名:" + this.name + ",年龄:" + this.age;
}var box1 = new Box();
console.log(box1.prototpye);   //undefined 这个属性是一个对象,访问不到,通过__proto__指针才能访问到prototype原型对象
console.log(box1.__proto__);  //这个属性是一个指针,指向prototype原型对象console.log(Box.prototype);   //使用构造函数名(对象名)访问prototype,和上一行输出结果一样
//constructor构造属性,可以获取构造函数本身,作用是被原型指针定位,然后得到构造函数本身,其实就是对象实例对应原型对象的作用
console.log(box1.constructor);  //function Box(){}

//判断一个对象实例是不是指向了原型对象,基本上,只要实例化了,他自动指向
Box.prototype.isPrototypeOf(box1);   //true var obj = new Object(); //只要实例化就默认指向了
Object.prototype.isPrototypeOf(obj );   //true
Box.prototype.isPrototypeOf(obj);   //false 

通过例子和图示理解了声明方式,看一下原型模式的执行流程:

(1)先查找构造函数实例里的属性或方法,如果有,立刻返回。

(2)如果构造函数实例里没有,则去它的原型对象里找。如果有,就立刻返回。

实例如下:

//情况一
function Box(){};
Box.prototype.name = 'Lee';
Box.prototype.age = 20;
Box.prototype.run = function(){    return "姓名:" + this.name + ",年龄:" + this.age;
}var box1 = new Box();
box1.name = 'Luck';  //实例属性,并没有重写原型属性
console.log(box1.name);  //Luck 就近原则
//多说几句:var box2 = new Box();console.log(box2.name);  //Lee 实例属性不会共享,所以box2访问不到实例属性,就只能访问原型//若想box1访问原型对象,删除实例中的属性delete box1.name;console.log(box1.name);   //Lee//覆盖原型里的属性Box.prototype.name = "Lily";console.log(box1.name);  //Lily//情况二
function Box(){this.name = 'Luck';
};
Box.prototype.name = 'Lee';
Box.prototype.age = 20;
Box.prototype.run = function(){    return "姓名:" + this.name + ",年龄:" + this.age;
}var box1 = new Box();
console.log(box1.name);  //Luck   先访问构造函数实例里的方法

//多说几句://如何判断属性是否在构造函数里?使用hasOwnProperty来判断console.log(box1.hasOwnProperty('name'));  //true  实例里有返回true 没有返回false//不管实例属性或原型属性是否存在,只要有,就返回true,两边都没有返回falseconsole.log('name' in box1);  //true//那么,判断只有原型中有属性:function isProperty(object,property){    return !object.hasOwnProperty(property)&&(property in object)}isProperty(box1,name);

构造函数实例属性和原型属性示意图

上面这个示意图说明了,box1中,name属性冲突时,就近原则,先访问构造函数里的name。box2中,name不冲突,直接访问原型属性name。

原型字面量方法:

为了让属性和方法更好的体现封装的效果,并且减少不必要的输入,原型的创建可以使用字面量的方式,体现更好的封装性。

实例如下:

function Box(){};
Box.prototype={name:'Lee',age:20,run:function(){return "姓名:" + this.name + ",年龄:" + this.age;}
}
var box = new Box();
console.log(box.run());   //姓名:Lee,年龄:20

使用构造函数创建原型对象和使用字面量创建原型对象在使用上基本相同,但是还有一些区别,字面量创建的方式使用constructor属性不会指向实例,而会指向Object,构造函数创建的方式则相反。

请看如下代码:

//为了省事将两种情况写在了一起,测试时请分开测试
function Box(){};
Box.prototype.name = 'Lee';
Box.prototype.age = 20;
Box.prototype.run = function(){return "姓名:" + this.name + ",年龄:" + this.age;
}
var box = new Box();
console.log(box.constructor);   //function Box(){}function Box(){};
//使用字面的方式创建原型对象,这里{}就是对象,是Object。
//new Object()就相当于{}
Box.prototype={name:'Lee',age:20,run:function(){return "姓名:" + this.name + ",年龄:" + this.age;}
}
var box = new Box();
console.log(box.constructor);   //function Object(){}

//字面量方式创建原型对象验证方法:console.log(box.constructor == Box);    //falseconsole.log(box.constructor == Object); //true//构造函数方法创建原型对象验证方法:console.log(box.constructor == Box);    //trueconsole.log(box.constructor == Object); //false

字面量方式为什么constructor会指向Object?

因为Box.prototype={};这种写法其实就是创建了一个新对象,而每创建一个对象,就会同时创建它的prototype,这个对象也会自动获取constructor属性。所以,新对象的constructor重写了Box原来的constructor,因此会指向新对象,那个新对象没有指向构造函数,那么就默认为Object。

如果想让字面量方式的constructor指向实例对象,可以强制指向:

Box.prototype = {

constructor:Box;  // 直接强制指向即可,添加此句

}

接下来看另一个问题:

原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型。实例如下:

Box.prototype={    constructor:Box,         //强制指向Boxname:'Lee',age:20,run:function(){return "姓名:" + this.name + ",年龄:" + this.age;}
}
//下面重写了原型对象
Box.prototype={age:18,                   //这里不会保留之前原型的任何信息,吧原来的原型对象和构造函数对象实例之前的关系切断了
}
var box = new Box();console.log(box.age);         //18
console.log(box.name);        //undefined
console.log(box.run());       //run() is not a function 报错

原型对象不仅仅可以在自定义对象的情况下使用,而ECMAScript内置的引用类型都可以使用这种方法,并且内置的引用类型本身也使用了原型。

//数组排序
var box = [5,1,6,9,3,5];
console.log(box.sort());    //1,3,5,5,6,9//查看sort是否是Array原型对象里的方法
console.log(Array.prototype.sort);  //function sort(){}  证明是
//类似的还有
console.log(String.prototype.substring); //function substring(){}  证明是//内置引用类型的功能扩展
//现在我们想添加一个原型方法,先判断这个方法是否存在。
console.log(String.prototype.addstring);     //undefined  不存在,可以添加
String.prototype.addstring = function(){return this + "被添加了";
}
var box = 'Lee';
console.log(box.addstring());   //Lee被添加了

尽管给原生的内置引用类型添加方法使用起来特别方便,但我们不推荐使用这种方法,因为它可能会导致命名冲突,不利于代码维护。

原型模式创建对象也有自己的缺点,它省略了构造函数传初始化这一过程,带来的缺点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,共享。

原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以,但如果属性包含引用类型,就存在一定的问题:

原型的缺点:

function Box(){}
//实例化的时候没有办法通过传参改变它们的值
Box.prototype = {constructor:Box,name:'Lee',age:20,family:['哥哥','姐姐'],run:function(){return this.name + this.age + this.family;}
}
var box1 = new Box();
console.log(box1.family);          //'哥哥','姐姐'
box1.family.push('弟弟');           //在第一个实例修改后引用类型,保持了共享
console.log(box1.family.push('弟弟'));      //哥哥,姐姐,弟弟var box2 = new Box();
console.log(box2.family);                  //哥哥,姐姐,弟弟   共享了box1添加后的引用类型的原型

数据共享的缘故,导致很多开发者放弃使用原型,因为每次实例化的数据需要保持自己的特性,而不能共享。

为了解决构造传参和共享问题,使用组合构造函数+原型模式。

5.组合构造函数+原型模式

function Box(name,age){ //保持独立的用构造函数this.name = name;this.age = age;this.family = ['哥哥','姐姐'];
}
Box.prototype = {  //保持共享的用原型
      constructor:Box,run:function(){return this.name + this.age;}
}var box1 = new Box('Luck',18);
console.log(box1.run());    //Luck18
box1.family.push('弟弟');
console.log(box1.family);  //哥哥,姐姐,弟弟var box2 = new Box('Lee',20);
console.log(box2.run());   //Lee20
console.log(box2.family);  //哥哥,姐姐       引用类型没有使用原型,所以没有共享

这种混合模式很好的解决了传参和引用共享的大难题,是创建对象比较好的方法。

原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型部分让人感觉有很怪异,最好就是把构造函数和原型分装到一起,为了解决这个问题,可以使用动态原型模式。

转载于:https://www.cnblogs.com/manru75/p/9478237.html

JavaScript之面向对象与原型笔记整理--------创建对象之原型(2)相关推荐

  1. javaScript数组操作--有道笔记整理

    javascript之数组操作 1.数组的创建 var arrayObj = new Array(); //创建一个数组 var arrayObj = new Array([size]); //创建一 ...

  2. javascript学习笔记整理

    javascript从零到精通笔记整理 js写在哪 - css写在哪- 内联(行内):属性形式:style="样式属性:样式属性值"- 内部:style双标签,包裹css样式- 外 ...

  3. JavaScript高级第02天笔记

    本资源由 itjc8.com 收集 JavaScript高级第02天笔记 1.构造函数和原型 1.1对象的三种创建方式–复习 字面量方式 var obj = {}; new关键字 var obj = ...

  4. 尚硅谷 JavaScript笔记 整理

    整理自https://github.com/codeOflI/codeOflI.github.io/blob/dev/source/_posts/js-note/javaScript/javaScri ...

  5. JavaScript高级笔记_002_构造函数和原型

    JavaScript高级笔记_002_构造函数和原型 构造函数和原型 构造函数和原型 概述 构造函数 构造函数的问题 构造函数原型`prototype` 对象原型`__proto__` (四个下划线) ...

  6. JavaScript面向对象和ES6笔记

    JavaScript面向对象 1.面向对象编程介绍 1.1 两大编程思想 面向过程 面向对象 1.2 面向过程编程POP(Process-oriented programming) **面向过程**就 ...

  7. JavaScript的面向对象--原型

    在以类为中心的面向对象编程语言中,类和对象的关系可以想象成铸模和铸件的关系,对象总是从类中创建而来.而在原型编程的思想中,类并不是必需的,对象未必需要从类中创建而来,一个对象是通过克隆另外一个对象所得 ...

  8. JavaScript笔记整理

    JavaScript笔记整理 这段时间没有之前花在学习上的时间多了 目前进度还停留在JavaScript和D3.js(这个目前只找到了教程) 主要原因是因为决定自己找单位实习,到了今天26号才有了眉目 ...

  9. JavaScript的面向对象原理之原型链

    二.JavaScript的对象 为了能够清楚的解释这一切,我先从对象讲起.从其他面向对象语言(如Java)而来的人可能认为在JS里的对象也是由类来实例化出来的,并且是由属性和方法组成的. 实际上在JS ...

最新文章

  1. 三、垃圾收集之判断对象是否存活
  2. python快速编程入门课后题答案-《Python编程:从入门到实践》第五章 if语句 习题答案...
  3. Linq专题之提高编码效率—— 第一篇 Aggregate方法
  4. JavaScript事件与jQuery方法
  5. C# 获取文件MD5值的方法
  6. linux手动安装mysql8.16,MySQL8.0.16-linux-x64安装介绍(binary package)
  7. 为什么复制粘贴格式总是出错_想把图片转换成pdf格式怎么做?你找对方法了吗...
  8. linux系统电脑白屏,在Deepin Linux 15.7系统中换桌面后关机界面白屏的解决
  9. 牛客竞赛,GDDU第十届文远知行杯新生程序设计竞赛,摸鱼记(BDEIKL题解,补G,ACFHJ)
  10. JSP中乱码问题,你真的理解了么?
  11. SpringMVC中的父子容器关系
  12. lisp 获取横断面数据_CAD中高程点提取横断面数据的方法
  13. bccomp php扩展,PHP 中文工具包 ChineseUtil v2.0 发布,引入 FFI 提升性能节省内存
  14. kettle 提交数据量_kettle大数据量读写mysql性能优化
  15. AI语音红外遥控配网教程
  16. 请根据以下需求使用决策表设计测试用例
  17. 小米联系人删除怎么恢复
  18. 在公交车上想出的一个java算法
  19. 模拟前端ADC芯片LH001-91,用于开发心电、脑电医疗设备
  20. win7原版安装版系统

热门文章

  1. Hive partition prune Failed
  2. Dijkstra算法图文详解和C++代码
  3. Yearn V2 Vaults Swap发布,目前未经审核
  4. TokenInsight:BTC新增流量稳定,但泡沫指数已超17年峰值
  5. 以太坊2.0抵押地址新增13.47万ETH
  6. SAP License:ERP失败案例集
  7. 《如何搭建小微企业风控模型》第十二节 模型检验 节选
  8. spring boot test [ 2.0.6.RELEASE version ]
  9. 20155307 《Java程序设计》课堂实践项目数据库
  10. Graham Scan凸包算法