·原型模式
我们创建的每一个函数都由一个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面向对象的程序设计 --- 中篇(创建对象) 之 原型模式的更多相关文章

  1. js面向对象的程序设计 --- 中篇(创建对象) 之 工厂模式和 构造函数模式

    创建对象 虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量重复代码. ·工厂模式 工厂模式是一种广为人知的设计模式,这种模式 ...

  2. 重学js之JavaScript 面向对象的程序设计(创建对象)

    注意: 本文章为 <重学js之JavaScript高级程序设计>系列第五章[JavaScript引用类型]. 关于<重学js之JavaScript高级程序设计>是重新回顾js基 ...

  3. JS面向对象的程序设计

    面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义J ...

  4. Js 面向对象之封装,继承,原型,原型链

    封装 ,继承 ,原型, 原型链 封装 ? 面向对象有三大特性,封装.继承和多态.对于ES5来说,没有class(类)的概念,并且由于JS的函数级作用域(函数内部的变量在函数外访问不到),所以我们就可以 ...

  5. js面向对象的程序设计 --- 下篇 继承启蒙

    继承是oo语言中一个最为人津津乐道的概念.ECMAScript支持实现继承,而且实现继承只要是靠原型链来实现的 ·原型链 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾一 ...

  6. javascript创建对象之原型模式(三)

    少废话,先上代码: function Human() { } Human.prototype.name = "成吉思汗"; Human.prototype.sex = " ...

  7. .NET应用架构设计—面向对象分析与设计四色原型模式(彩色建模、领域无关模型)(概念版)

    阅读目录: 1.背景介绍 2.问自己,UML对你来说有意义吗?它帮助过你对系统进行分析.建模吗? 3.一直以来其实我们被一个缝隙隔开了,使我们对OOAD遥不可及 4.四色原型模式填补这个历史缝隙,让我 ...

  8. OC编程之道-创建对象之原型模式

    一 什么是原型模式?(what) 有些对象的创建代价过大或过于复杂,要是可以重建相同的对象并作轻微的改动,事情会容易的多(效率变高).典型的例子是复制组合结构(eg树形结构),从零开始构建一个树型组合 ...

  9. JS面向对象组件(一) ---包装对象与原型链

    首先我们可以看看平时我们常用的 var str = 'hello'; alert(typeof str); //string var str = new String("hello" ...

随机推荐

  1. 怎样在GitHub上新建一个文件夹

    GitHub如何创建文件夹 创建新文件的时候名字后面加个斜杠(/)就可以了 点击新建文件,输入文件名的时候后面加上斜杠/就是创建了一个文件夹,没有斜杠就是创建了一个文 创建好后点提交 Commit n ...

  2. Java 日期格式化,Java 日期工具类,Java Date工具类

    ================================ ©Copyright 蕃薯耀 2020-01-19 https://www.cnblogs.com/fanshuyao/ import ...

  3. pandas模块的数据操作

    数据操作 数据操作最重要的一步也是第一步就是收集数据,而收集数据的方式有很多种,第一种就是我们已经将数据下载到了本地,在本地通过文件进行访问,第二种就是需要到网站的API处获取数据或者网页上爬取数据, ...

  4. 再访JavaScript对象(原型链和闭包)

    一:原型链简介 JavaScript通常被描述为基于原型的语言 (从继承机制的角度)- 为了提供继承,对象(注意:区别于实例)可以拥有一个原型对象,它充当一个模板对象,它继承了方法和属性.对象的原型对 ...

  5. cat基础用法

    Linux中的cat命令连接文件并打印到标准输出设备上(通常是shell).cat的最常见用法之一是显示文件,还可以即时创建文件,并可以直接在终端上进行基本编辑. 创建文件 使用cat命令创建文件,请 ...

  6. 递归查询 start with connect by prior

    1.语法:start with 子节点ID='...' connect by prior 子节点ID = 父节点ID 含义:查询结果我所有的后代节点(包括我) 例子: select id,parent ...

  7. xctf-i-got-id-200(perl网页文件+ARGV上传造成任意文件读取)

    打开url发现有三个链接,点进去都是.pl文件,且只有files可以上传文件. .pl文件都是用perl编写的网页文件 这里上传了又将文件的内容全部打印出来,那么猜想后台应该用了param()函数. ...

  8. LeetCode 572. 另一个树的子树

    题目链接:https://leetcode-cn.com/problems/subtree-of-another-tree/ 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和 ...

  9. 中软国际首届嘉年华晚会 创新网络年会PK“春晚”

    随着新年脚步的来临,各大公司的年会陆续出炉,但是中软国际的首届嘉年华晚会,以创新网络年会和全国八地同步进行的模式,演绎不一样的互联网年会,简直可以PK“春晚”.IT届中最漂亮的美女热舞,程序员中最会唱 ...

  10. Tomcat报错Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

    问题描述:后台报错 Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.java ...