js面向对象的程序设计 --- 中篇(创建对象) 之 原型模式
·原型模式
我们创建的每一个函数都由一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有
实例共享的属性和方法。
如果按照字面意思来理解,那么prototype就是通过构造函数创建的那个对象实例的原型对象。使用原型对象的好处就是可以让所有对象实例共享它
所包含的属性和方法 // code示例
function Person(){}
Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person();
person1.sayName(); //Nicholas var person2 = new Person();
person2.sayName(); //Nicholas
console.log(person1.sayName === person2.sayName); // true
理解原型对象:
1:无论什么时候,只要创建了一个新函数们就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
2:在默认情况下,所有原型对象都会自动获得一个constructor属性,至于其它方法,则都是从Object继承来的。
3:当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMAScript中管这个
指针叫[[Prototype]]
4: 要明确 [[Prototype]] 这个连接存在于实例与原型对象之间,而不是存在于实例与构造函数之间 示意图: // 图片
对示意图的理解:
1:Person.prototype指向了原型对象。而Person.prototype.constructor又指回了Person
2:Person的每一个实例,都包含一个内部属性,该属性仅仅指向了Person.prototype
3:原型最初只包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问
4:当代码读取某个对象的属性时,都会执行一次搜索,如果在实例中找到,则返回该属性。没有则在原型对象中查找
5:当为对象实例添加一个属性时,这个属性会屏蔽原型对象的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改
那个属性。即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。
注意:所有实现都无法访问到[[Prototype]],那我们怎么得到一个对象的原型对象呢?
// 返回[[Prototype]]的值
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
使用Object.getPrototypeOf()返回的对象其实就是这个对象的原型,而这在利用原型实现继承的情况下是非常重要的
注意:我们如何得出一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从Object继承来的)只在给定属性存在于对象实例中时,才会返回true
function Dog(){ }
Dog.prototype.name = "tom";
var dog_a = new Dog();
console.log(dog_a.hasOwnProperty("name")); // false
dog_a.name = "jack"; // 该实例重写了name属性
console.log(dog_a.hasOwnProperty("name")); // true
原型与in操作符
有两种情况使用in操作符:单独使用和在for-in循环使用
·单独使用:
in操作符通常会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
于是我们可以结合对象的hasOwnProperty()方法设计一个公共函数,来确定是否只存在原型中
// code..
// 如果属性只存在原型中则返回true
function test(obj,val){
return (!obj.hasOwnProperty(val)) && val in obj;
}
·for-in循环:
使用for-in循环,返回的时所有能够通过对象访问,可枚举(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性 更简单的原型语法
为了减少不必要的输入,也为了从视觉上更好的封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象
//code..
function Person(age){
this.age = age;
}
Person.prototype = {
area : "CHINA",
say : function () {
console.log('hello');
}
} var p1 = new Person(11);
console.log(p1.constructor); //ƒ Object() { [native code] }
// 这里出了一个意外,constructor属性不再指向Person了。我们刚才的语法,实际上是重写了默认的prototype对象,因此constructor属性
//变成了新对象的constructor属性(指向Object)构造函数,尽管instanceof操作符还能返回正确结果,但通过constructor已无法确定对象类型
console.log(p1 instanceof Person);
console.log(p1.constructor == Person);
console.log(p1.constructor == Object); // 解决方式初级版
Person.prototype = {
constructor : Person,
area : "CHINA",
say : function () {
console.log('hello');
}
}
//弊端:这种方式重设constructor属性会导致它的[[Enumerable]]特性为true。默认情况下,原生的constructor属性是不可枚举的。
//最终解决方法
Object.defineProperty(Person.prototype,"constructor",{
enumerable : false,
value : Person
})
原型的动态性
由于在原型中查找值的过程中是一次搜索,因此我们对原型对象所作的任何修改都能够立即从实例上反应出来---即使是先创建了实例后修改原型也照样如此。
function Person (name) {
this.name = name;
} Person.prototype.area = 'china'; var friend = new Person('cl');
console.log(friend.area); // china
Person.prototype.area = 'japan';
console.log(friend.area); // japan
// 实例与原型对象之间的连接只不过是一个指针,而非一个副本
Person.prototype = {
area : 'test',
}
console.log(friend.area); // japan
// 重写原型对象切断了现有原型与之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。
原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有远程的引用类型,都是采用这种模式创建的。
所有原生引用类型(Object,Array,String等等),都在其构造函数的原型上定义了方法。
console.log(typeof Array.prototype.sort); // function
console.log(typeof String.prototype.substring); // function
// 通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型
// 因此可以随时添加方法
String.prototype.startsWith = function () {
return this.indexOf(text) == 0;
}
// 尽管可以这样做,但是不推荐。因为这样会导致命名冲突或者意外的重写原生方法
组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数与原型模式。构造函数模式用来定义实例属性,而原型属性用与定义方法和共享的属性。
结果,每个实例都会有自己的一份实例属性的副本,但又共享着对方法的引用,最大限度地节省了内存
这种构造函数与原型混成的模式,是目前在ECMAScript中使用最广泛,认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认方式 动态原型模式
有其他oo语言经验的开发人员看到独立的函数和原型时,很可能会感到困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在
构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持同时使用构造函数和原型的优点。
function Person(name ,age, job){
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName != "function"){
// 这段代码只有在初次调用构造函数才会执行
Person.prototype.sayName = function () {
console.log(this.name);
}
}
}
// 使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系
寄生构造函数模式
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
console.log(this.name);
}
return o;
} var friend = new Person('Ni',29,"eng");
friend.sayName(); // Ni
//在这个例子中,Person函数创建了一个对象,并以相应的属性和方法初始化该对象,然后又返回了这个对象。除了使用new操作符并且把使用的包装函数
// 叫做构造函数之外,这个模式跟工厂模式其实是一模一样的
稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也没不引用this对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),
或者在方式数据被其他应用程序改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的常例方法不引用this;
二是不使用new操作符调用构造函数。按照稳妥构造函数的要求,可以将前面的Person构造函数重写如下:
function Person(name){
var o = new Object();
o.sayName = function () {
console.log(name);
}
return o;
} var friend = Person('Ni');
friend.sayName(); // Ni
//注意 : 在以这种模式创建的对象中,除了使用sayName()方法之外,没有其他方法访问name的值
// 即使有其他代码会给这个对象添加方法和数据成员,但也不可能有别的方法传入构造函数中的初始数据。
// 构造函数模式提供的这种安全性,使得它非常适合在某些安全执行环境。
js面向对象的程序设计 --- 中篇(创建对象) 之 原型模式的更多相关文章
- js面向对象的程序设计 --- 中篇(创建对象) 之 工厂模式和 构造函数模式
创建对象 虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量重复代码. ·工厂模式 工厂模式是一种广为人知的设计模式,这种模式 ...
- 重学js之JavaScript 面向对象的程序设计(创建对象)
注意: 本文章为 <重学js之JavaScript高级程序设计>系列第五章[JavaScript引用类型]. 关于<重学js之JavaScript高级程序设计>是重新回顾js基 ...
- JS面向对象的程序设计
面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义J ...
- Js 面向对象之封装,继承,原型,原型链
封装 ,继承 ,原型, 原型链 封装 ? 面向对象有三大特性,封装.继承和多态.对于ES5来说,没有class(类)的概念,并且由于JS的函数级作用域(函数内部的变量在函数外访问不到),所以我们就可以 ...
- js面向对象的程序设计 --- 下篇 继承启蒙
继承是oo语言中一个最为人津津乐道的概念.ECMAScript支持实现继承,而且实现继承只要是靠原型链来实现的 ·原型链 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾一 ...
- javascript创建对象之原型模式(三)
少废话,先上代码: function Human() { } Human.prototype.name = "成吉思汗"; Human.prototype.sex = " ...
- .NET应用架构设计—面向对象分析与设计四色原型模式(彩色建模、领域无关模型)(概念版)
阅读目录: 1.背景介绍 2.问自己,UML对你来说有意义吗?它帮助过你对系统进行分析.建模吗? 3.一直以来其实我们被一个缝隙隔开了,使我们对OOAD遥不可及 4.四色原型模式填补这个历史缝隙,让我 ...
- OC编程之道-创建对象之原型模式
一 什么是原型模式?(what) 有些对象的创建代价过大或过于复杂,要是可以重建相同的对象并作轻微的改动,事情会容易的多(效率变高).典型的例子是复制组合结构(eg树形结构),从零开始构建一个树型组合 ...
- JS面向对象组件(一) ---包装对象与原型链
首先我们可以看看平时我们常用的 var str = 'hello'; alert(typeof str); //string var str = new String("hello" ...
随机推荐
- Java自学-Lambda 方法引用
Lambda 方法引用 步骤 1 : 引用静态方法 首先为TestLambda添加一个静态方法: public static boolean testHero(Hero h) { return h.h ...
- Python语法速查: 14. 测试与调优
返回目录 本篇索引 (1)测试的基本概念 (2)doctest模块 (3)unittest模块 (4)调试器和pdb模块 (5)程序探查 (6)调优与优化 (1)测试的基本概念 对程序的各个部分建立测 ...
- Docke-ce 安装
Docker-ce 的安装 安装系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 添加docker镜像源 yum-con ...
- TamperMonkey 使用指南以及脚本推荐
写在前面 Chrome浏览器是最适合开发者使用的浏览器,不仅仅是因为Chrome对于Js的友好支持,更是由于Chrome支持丰富且功能强大的插件,扩展了浏览器的功能和使用体验. 在这些插件里面,相信你 ...
- 对于tensorflow中的gradient_override_map函数的理解
# #############添加############## def binarize(self, x): """ Clip and binarize tensor u ...
- ip连接mysql时报不能连接
问题:springboot项目在用localhost连接mysql时没问题,但当localhost换成ip时出现 该问题:message from server: "Host 'DESKTO ...
- 【Linux远程连接工具】Xshell、Xftp家庭/学生版(免费使用)
注:Xshell.Xftp家庭/学生版无需激活,可以免费使用! 1.步骤一: 官网下载地址:https://www.netsarang.com/zh/ 选择[所有下载]->[家庭/学校免费] ...
- MAT(memory anlayzer tool)使用方法
Analyzing and understanding the memory use of an application is challenging. A subtle logic error ca ...
- NetCore使用使用Scaffold-DbContext命令生成数据库表实体类
一.为了模拟项目,本处创建了一个NetCore的Web项目.打算在Models文件夹下生成数据库表的实体类. 二.在程序包管理控制台,输入“Scaffold-DbContext "Serve ...
- webpack 代理问题
devServer host: '0.0.0.0' 或者是ip 形式的 ,proxy 中 的 target,host 需要为ip形式的地址, host: 'aa.a.com' 为字符形式的 ,prox ...