理解原型和实例的创建
来源:互联网 发布:金属探测器软件 编辑:程序博客网 时间:2024/06/06 18:21
原型 , 构造模式 , 原型模式 ,组合模式
- 理解原型和实例的创建
- 理解原型
- 与原型相关的属性和方法
- constructor 属性
- prototype属性
- hasOwnProperty 方法
- in操作符
- 两个模型构造函数模型 和 原型模型
- 1构造函数模型
- 11构造过程
- 12构造函数的优势
- 13构造函数的缺陷
- 2原型模型
- 21原型模式的优势
- 14 原型模式的缺陷
- 4 总结
- 1构造函数模型
- 组合使用两种模型
- 1 组合模式
- 2 动态原型模式
- 3 优化
理解原型和实例的创建
1. 理解原型
在JS中,只要声明一个函数A , 就会对应的产生一个对象 , 这个对象就叫原型对象
试试看
function A() { } console.log(A.prototype); console.log(A.prototype.constructor);
返回值如下:
定义了一个函数 A , 没有添加任何的属性和方法 , A 的两未定义个属性 却可以返回两个值?
prototype
属性返回了一个对象 , constructor
属性返回了函数A
prototype
是 A 里面的属性 , 指向一个对象,这个对象就叫原型对象 ,
而这个原型对象里 默认情况 储存着 constructor
属性和从Object继承而来,这里暂且不表;
constructor
是一个属性 , 存在原型对象里 , 指向了这个函数
比如俩人吵架 , 都用手指头指着对方骂;
而这两个属性就是手指头,构造函数和原型对象就是吵架的人;
手指头是人的一部分,就像这两个属性存在对象的内部是一样的
2.与原型相关的属性和方法
constructor
属性
存在于原型对象中,指向构造函数;
如果重写原型对象则会导致constructor
属性不再指向构造函数;
如果需要constructor
依旧指向构造函数,可以在重写的时候加上以下代码
Person.prototype = { constructor : Person //让constructor重新指向Person函数}
[[prototype]]
属性
用构造函数创建一个实例对象后,这个对象中会有一个不可访问的属性[[prototype]]
,这个属性就指向了构造函数的原型对象
Chrome浏览器和火狐浏览器提供这个对这个属性的访问
使用__proto__
方法可以访问到原型对象(左右各两个下划线),这个属性存在于被构造函数创建的实例对象中;一般不建议使用
hasOwnProperty()
方法
判断一个属性是否来自对象自身 ;
返回 true
表示为对象自身属性;返回false
可以判断出存在原型中或属性不存在,所以要如何确定一个属性存在原型里呢?
in
操作符
in
操作符会从对象的本身开始 , 查找是否有对应的属性 , 在对象自身中没有找到 , 就会沿着原型链开始查找 , 直到找到返回true
,反之,则返回false
3. 两个模型–构造函数模型 和 原型模型
3.1构造函数模型
理论上任何函数都可以作为构造函数,但是一般约定,构造函数的函数名以大写字母开头
比如:
构造函数 :
function Person(){ }
当把一个函数作为构造函数,并利用new创建的一个新的实例对象
利用构造函数创建实例 : var p1 = new Person( );
3.1.1构造过程
相关:
实例与构造函数的关系 :
实例对象p1被构造函数Person创建后,P1和Person是没有任何的关系了;
实例与原型 :
- 实例对象中有一个[[prototype]]属性指向原型对象;
- 使用new Person()创建多个对象,则多个对象都会同时指向原型对象。
可以手动给这个原型对象添加属性和方法,那么p1,p2,p3…都会共享这些属性和方法
属性和方法的查找会从实例开始 , 沿着原型链查找 , 直到找到属性或方法;
- 所以给实例对象添加和原型中同名的属性 , 会优先访问实例中的属性
- 通过实例只能访问到原型的属性或方法,不能修改;
//这段代码将会抛出错误function Person(name,age) { } Person.prototype.sex = "男" let p1 = new Person(); p1.prototype.sex = "女"; console.log(p1.sex);
3.1.2构造函数的优势
可以传入参数,适用于 每个实例都有的同名但是值不相同的属性
function Person(name,age) { this.name = name; this.age = age; this.speakName = function (){ console.log(this.name) } } let p1 = new Person("shark",3); let p2 = new Person("dd",4) console.log(p1.name,p1); console.log(p1.age) //输出结果 shark 3 dd 4
内存模型:
目前来说,每个实例都有了自己的name和age , 但是新的问题也随之而来了
3.1.3构造函数的缺陷
观察内存模型 , 就会发现每个实例中都有一个相同的方法 , 浪费了内存
这不是很OK , 不是我们想要的结果,所以一种新的模型站了出来 - -原型模型
3.2原型模型
3.2.1原型模式的优势
针对以上的问题 , 对代码做出了以下修改
function Person(name,age) { this.name = name; this.age = age; } Person.prototype.speakName = function (){ console.log(this.name) } let p1 = new Person("shark",3); let p2 = new Person("dd",4) console.log(p1.name,p1); console.log(p1.age)
利用原型模式的优势 —共享所有的方法
可以让我们共享speakName
这个方法;
这正好解决了构造函数的缺陷 , 每个实例都有自己的名字 , 方法在不浪费内存和性能的情况下共用;
3.14 原型模式的缺陷
那假如使用原型模式添加属性呢?
function Person() { } Person.prototype.country = "China"; Person.prototype.name = "小李"; var p1 = new Person(); var p2 = new Person();
场景:
现在有假如两个中国人p1,p2 ,他们的国籍一样,使用原型模式,使得两个人的国籍一致
结合上一个例子说明了:
原型中适合存储大家共有的属性和方法;
不过原型也存在缺陷 :
名字没有办法做到每个人都独一无二 , 不过在之前提到的构造函数模型中 , 正好解决了给不同的实例的相同name
赋不同值的情况;
3.4 总结
原型模式适合封装方法和共享的属性,构造方法模式适合封装值不同的属性
如果把这两个模式结合起来,就有了组合模式;
4.组合使用两种模型
组合构造是基于两种模式互补的一种新的构造方法;
总结一下两种模式的优缺点:
不难看出,两者的正好是互补的,所以组合起来使用是最佳的方法;
4.1 组合模式
//在构造方法内部封装属性 function Person(name, age) { this.name = name; this.age = age; } //在原型对象内封装方法 Person.prototype.eat = function (food) { alert(this.name + "爱吃" + food); } Person.prototype.play = function (playName) { alert(this.name + "爱玩" + playName); } var p1 = new Person("李四", 20); var p2 = new Person("张三", 30); p1.eat("苹果"); p2.eat("香蕉"); p1.play("志玲"); p2.play("凤姐");
虽然完美解决了种模式的缺陷,但是还不够完美
因为 , 代码还不够优雅
4.2 动态原型模式
动态原型模式把所有的属性和方法都封装在构造方法中,而仅仅在需要的时候才去在构造方法中初始化原型,又保持了同时使用构造函数和原型的优点。
<script type="text/javascript"> //构造方法内部封装属性 function Person(name, age) { //每个对象都添加自己的属性 this.name = name; this.age = age; /* 判断this.eat这个属性是不是function,如果不是function则证明是第一次创建对象, 则把这个funcion添加到原型中。 如果是function,则代表原型中已经有了这个方法,则不需要再添加。 perfect!完美解决了性能和代码的封装问题。 */ if(typeof this.eat !== "function"){ Person.prototype.eat = function () { alert(this.name + " 在吃"); } } } var p1 = new Person("志玲", 40); p1.eat(); </script>
看起来优美多了..但是还是差一丝优美…
4.3 优化
function Person(opt) { this._init(opt) } //方法和属性都被封装到了一起,但是属性实际上还是属于构造模式创建的 //体现了封装性,但是重写Prototype带来了一个小问题 Person.prototype = { //新的原型对象不存在constructor属性,故补齐 constructor:Person //初始化属性 _init: function (opt) { this.name = opt.name; this.age = opt.age; }, eat: function () { return "名字:" + this.name }, howOld: function () { return "年龄:" + this.age }, }; var p1 = new Person({ name: "李四", age: 99 });
前端新人一枚 , 欢迎批评指正~
- 理解原型和实例的创建
- js 原型,实例的理解
- 深入理解JavaScript的创建对象(构造函数、原型对象、实例)
- 理解原型和原型链
- 简单的原型,原型对象和实例对象先后顺序
- js关于原型构造函数和原型链的理解
- 个人对"原型"和"原型链"的理解
- 深入理解JavaScript的原型和原型链
- 深入理解JavaScript系列--------强大的原型和原型链
- js的原型链和对象理解
- JavaScript中的类和原型的理解
- JavaScript原型链的理解和分析
- Javascript对象和原型继承的理解
- 对js对象和原型的理解
- 一句话彻底理解JavaScript的静态、实例、原型!
- 基于原型的对象创建和继承
- 关于js中,原型对象,原型链,构造函数,实例之间关系的理解与区别
- 对于js原型和原型链继承的简单理解(第一种,原型链继承)
- boost中circular_buffer存储字符串的使用示例
- html的表格
- Spring
- 【POJ】 1017
- 前端优化网站方法整理
- 理解原型和实例的创建
- html之表单
- css的属性
- HTTP报文讲解和tcp三次握手和四次挥手
- Java面试个人整理(手打勿喷,易于个人学习)3
- vue2.0之axios使用详解
- TypeError: Value passed to parameter 'targets' has DataType float32 not in list of allowed values: i
- css的边界和补白
- Android_非UI:SpannableString