好家伙,本篇为《JS高级程序设计》第八章“对象、类与面向对象编程”学习笔记

 

1.工厂模式

工厂模式是另外一种关注对象创建概念的创建模式。

它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器。

取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型。

function createPerson(name,age,job){
let person =new Object();
person.name= name;
person.age =age;
person.job =job;
person.getName = function(){
console.log(this.name);
}
return person;
}
let person_1 = createPerson("panghu","20","student")
person_1.getName();
console.log(person_1);

 

 

 

(看上去没什么问题,但怎么总觉得怪怪的)

let person =new Object();

/...
...
...
../ return person;

 

 

2.构造函数模式

前面几章提到过,ECMAScript中的构造函数是用于创建特定类型对象的。

像Object和Array这样的原生构造函数,运行时可以直接在执行环境中使用。

当然也可以自定义构造函数,以函数的形式为自己的对象类型定义属性和方法。

 

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
console.log(this.name);
};
}
let person_1 = new Person("panghu", "20", "student");
person_1.getName();
console.log(person_1);

 

 

在这个例子中,Person()构造函数代替了 createPerson()工厂函数。

实际上,Person()内部的代码跟createPerson()基本是一样的,只是有如下区别。
□没有显式地创建对象。
□属性和方法直接赋值给了this。
□没有return。
另外,要注意函数名Person的首字母大写了。

按照惯例,构造函数名称的首字母都是要大写的,非构造函数则以小写字母开头。

这是从面向对象编程语言那里借鉴的( 是的,非常好的借鉴 ),有助于在ECMAScript中区分构造函数和普通函数。

毕竟ECMAScript的构造函数就是能创建对象的函数。
要创建Person的实例,应使用new操作符。以这种方式调用构造函数会执行如下操作。

(1)在内存中创建一个新对象。
(2)这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性。

(3)构造函数内部的this被赋值为这个新对象(即this指向新对象)。
(4)执行构造函数内部的代码(给新对象添加属性)。
(5)如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

 

2.1.构造函数也是函数
构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。

并没有把某个函数定义为构造函数的特殊语法。

任何函数只要使用new操作符调用就是构造函数,而不使用new操作符调用的函数就是普通函数。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
console.log(this.name);
};
}
let person_1 = new Person("panghu", "20", "student");
person_1.getName();
//作为函数调用,添加到window对象
Person("xiaofu","20","student")
window.getName();

此处如果将Person当做普通函数来调用,那么this指向的就是全局作用域,数据会被添加到window对象 

2.2. 构造函数的问题

构造函数虽然有用,但也不是没有问题。

构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍。

因此对前面的例子而言,person1和person2都有名为sayName()的方法,但这两个方法不是同一个Function 实例。

我们知道,ECMAScript中的函数是对象,因此每次定义函数时,都会初始化一个对象。

如果把方法分离出来,在全局作用域中进行定义,在当前对象需要多个方法,那么就要在全局作用域中定义多个函数.

这个问题可以通过原型模式解决

3.原型模式

(他来了,被营销号誉为Js三大难点的"原型"他来了)

 理解复杂概念之前我们先从简单的地方入手(比如新华字典)

 

然后我们知道,原型指的是原来的模型

每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。

实际上,这个对象就是通过调用构造函数创建的对象的原型。

使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。

原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型,

3.1.实例共享原型模式的属性和方法

function Person(){};

Person.prototype.name = "panghu";
Person.prototype.age = "20";
Person.prototype.job = "student";
Person.prototype.getName =function(){
console.log(this.name);
} let person_1 = new Person();
person_1.getName();
let person_2 = new Person();
person_2.getName();

 

然后,我们来理解一下这段代码,

首先,我们要把Person的原型当成一个对象来看待,

于是我们现在有了三方势力,Person构造函数,Person原型对象,person_1和person_2两个实例对象

 

看看这幅图,

Person构造函数Person.prototype指向原型对象,而Person原型对象的constructor指向Person构造函数,

两个实例都只有唯一属性[[Prototype]]指向Person.prototype

3.2.原型层级

在通过对象访问属性时,会按照这个属性的名称开始搜索,搜索开始于对象实例本身

如果在对象实例上找到了,则返回对应的值,如果没找到,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值

function Person(){};

Person.prototype.name = "panghu";
Person.prototype.age = "20";
Person.prototype.job = "student";
Person.prototype.getName =function(){
console.log(this.name);
} let person_1 = new Person();
person_1.getName(); person_1.name ="xiaofu";
person_1.getName(); delete person_1.name
person_1.getName();

 

只要给对象实例添加一个属性,这个属性就会遮蔽原型对象上的同名属性,虽然不会修改,但会屏蔽对它的访问

 

3.3.原型的动态性

因为从原型上搜索值的过程是动态的,所以即使实例在修改原型之前已经存在,任何时候对原型对象所做的修改也会在实例上反映出来

function Person(){};

Person.prototype.saysomething =function(){
console.log("yes,we can");
} let person_1 = new Person(); person_1.saysomething();

 

但重写原型又是另一码事了

function Person() {};

let person_1 = new Person();

Person.prototype = {
constructor: Person,
name: "panghu",
age: "20",
job: "student",
saySomething() {
console.log("yes,we can");
}
}
person_1.saySomething();

 

虽然随时能给原型添加属性和方法,并能够立即反映在所有对象实例上、但这跟重写整个原型是两回事。

实例的[[Prototype]]指针是在调用构造函数时自动赋值的,这个指针即使把原型修改为不同的对象也不会变。

重写整个原型会切断最初原型与构造函数的联系,但实例引用的仍然是最初的原型。记住,实例只有指向原型的指针,没有指向构造函数的指针。

Person的新实例是在重写原型对象之前创建的。在调用person_1.saySomething()的时候,会导致错误。

这是因为person_1指向的原型还是最初的原型,而这个原型上并没有saySomething属性。

 

That's all

第一百一十七篇: JavaScript 工厂模式和原型模式的更多相关文章

  1. javascript 面向对象编程(工厂模式、构造函数模式、原型模式)

      javascript 面向对象编程(工厂模式.构造函数模式.原型模式) CreateTime--2018年3月29日17:09:38 Author:Marydon 一.工厂模式 /** * 工厂模 ...

  2. 初涉JavaScript模式 (5) : 原型模式 【一】

    什么是原型模式? 原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象.--引自JavaScript设计模式 我们创建的每一个函数都有一个prototype ...

  3. 初涉JavaScript模式 (7) : 原型模式 【三】

    组合使用构造函数模式和原型模式 上篇,我们提到了原型模式的缺点,就是每个实例不能拥有自己的属性,因为纯原型模式所有的属性都是公开给每个实例的,故我们可以组合使用构造函数模式和原型模式.构造函数用来定义 ...

  4. Java进阶篇设计模式之三 ----- 建造者模式和原型模式

    前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...

  5. 【js基础】创建对象的几种常见模式(工厂模式,构造函数模式,原型模式,构造原型组合模式)

    一.工厂模式 缺点:没有解决对象识别的问题 优点:解决了创建多个相似对象的问题 function createPerson(name,age,job){ var o = new Object(); o ...

  6. js设计模式:工厂模式、构造函数模式、原型模式、混合模式

    一.js面向对象程序 var o1 = new Object();     o1.name = "宾宾";     o1.sex = "男";     o1.a ...

  7. JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)

    什么是面向对象?面向对象是一种思想. 面向对象可以把程序中的关键模块都视为对象, 而模块拥有属性及方法. 这样如果我们把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.   工厂 ...

  8. javascript创建对象的方法--原型模式

    javascript创建对象的方法--原型模式 一.总结 1.原型模式解决内存浪费的方法(继承):通过继承,对象继承原型模式下的所有属性,对象不同于其它对象的的属性自己创建或者修改 2.原型的使用(p ...

  9. JavaScript之面向对象学习六原型模式创建对象的问题,组合使用构造函数模式和原型模式创建对象

    一.仔细分析前面的原型模式创建对象的方法,发现原型模式创建对象,也存在一些问题,如下: 1.它省略了为构造函数传递初始化参数这个环节,结果所有实例在默认的情况下都将取得相同的属性值,这还不是最大的问题 ...

  10. javascript创建对象之函数构造模式和原型模式结合使用(四)

    创建自定义类型的常见方式就是组合使用构造函数模式与原型模式一起使用. 构造函数模式用于定义实例对象的特有的部分(属性和方法),原型模式用于定义共享的部分. 这样最大限度的节省了内存的开销. funct ...

随机推荐

  1. [题解] Atcoder ARC 142 D Deterministic Placing 结论,DP

    题目 (可能有点长,但是请耐心看完,个人认为比官方题解好懂:P) 首先需要注意,对于任意节点i上的一个棋子,如果在一种走法中它走到了节点j,另一种走法中它走到了节点k,那么这两种走法进行完后,棋子占据 ...

  2. SpringBoot入门项目CRM学习过程中的报错记录(更新ing)

    在用mybatis自动生成实体类和mapper时报错..... is unrecognized or represents more than one time zone. You must conf ...

  3. Mysql编程中遇到的小错误

    我在mysql中创建的数据库表语句为如下 create table grade (id int not null, name varchar(255), desc varchar(255), prim ...

  4. 简析 Linux 的 CPU 时间

    从 CPU 时间说起... 下面这个是 top 命令的界面,相信大家应该都不陌生. top - 19:01:38 up 91 days, 23:06, 1 user, load average: 0. ...

  5. CentOS 7.9 安装 Containerd-1.6.5

    一.CentOS 7.9 安装 Containerd-1.6.5 地址 https://containerd.io/downloads 在安装containerd前,我们需要优先升级libseccom ...

  6. java中的栈(利用数组实现栈)

    java中的栈(利用数组实现栈) 常见的数据结构:https://blog.csdn.net/weixin_43304253/article/details/119764275 栈的介绍 1.栈的英文 ...

  7. AI带你省钱旅游!精准预测民宿房源价格!

    作者:韩信子@ShowMeAI 数据分析实战系列:https://www.showmeai.tech/tutorials/40 机器学习实战系列:https://www.showmeai.tech/t ...

  8. SQL--Case When.. Then.. end的使用

    Case  When.. Then.. end的使用场景 当字段有不同的值,根据不同的值表示不同的内容 use [数据库名] go if exists( select * from sys.views ...

  9. miniconda使用

    基本指令 conda create -n xxx python=3.7 // 创建Python3.7的名为xxx虚拟环境 conda env list // 显示所有的虚拟环境 conda activ ...

  10. AR Engine光照估计能力,让虚拟物体在现实世界更具真实感

    AR是一项现实增强技术,即在视觉层面上实现虚拟物体和现实世界的深度融合,打造沉浸式AR交互体验.而想要增强虚拟物体与现实世界的融合效果,光照估计则是关键能力之一. 人们所看到的世界外观,都是由光和物质 ...