1.对象创建的3中方法

1.1.对象字面量

 var obj = {
name: "mingzi",
work: function () { console.log("working...") },
_age: 18, //下划线开头表示该属性不建议在外部被直接访问,但是任然可以被访问,除非使用类或函数定义
//age: 18,使用下划线定义属性后,对象上会默认生成这样一个没有下划线的属性引用,指向同一个值,用来被外部访问
get age(){
console.log("get方法被调用");
return this._age; //这里不能是this.age,否则会陷入死循环
},
set age(val){
console.log("set方法被调用");
if (val > 100 || val < 0) {
throw new Error("invalid value");
} else {
this._age = val;//这里也一样,不能是this.age
}
},
address: {
home: "dingxi",
office: "xian"
}
} console.log(obj.age); //这里如果写成obj._age,则属性访问不经过get方法
obj.age = 20;//同样,这里写obj._age也不经过set方法,但能设置成功
console.log(obj.age);

使用get/set的好处就是可以对值进行逻辑判断,这种方法定义的对象并不能实现Java中的private私有化效果。

在对多层嵌套对象的属性级联访问时,要对中间属性判空,除了多个if 判断之外,还可以这么写:

 var result = obj && obj.address && obj.address.home; //这种方式,只要中途有任何一个地方为null或undefined,则result被赋值为空,否侧赋值为最后一个。

注意:没有这个属性的时候会返回undefined,有属性没值的时候才会返回null。

1.2. var obj = new Object()

与第一种创建的对象是一样的,因为其构造器一样。

构造器结构图:

1.3.Object.defineProperties方式

可以定义属性配置项,不常用

2.对象工厂

加入我们需要创建两个具有相同结构的对象,你可能会这么做:

 var a = {
name: "zhangsan",
age: 12
}
var b = a;
console.log(a === b);//true
b.age = 13;
console.log(a);//{ name: 'zhangsan', age: 13 }

但是,显然这么做并不能创建一个具有相同结构的对象b,因为只是复制了一个栈中的引用,堆中的对象是一样的,修改了b.age也就修改了a.age,并不能达到复制对象的效果。

除非复制代码,但这不是程序员应该做的事,所以我们就可以使用对象工厂:

 function PersonFactory(name, age) {

     return {
name: name,
age: age
}
} var p1 = PersonFactory("zz",13);
var p2 = PersonFactory("zz",13);
console.log(p1 === p2); //false

通过一个工厂方法,就可以创建具有相同结构的不同对象了,工厂方法每次被调用,就相当于“复制”了一份代码,达到了复制对象的效果。

工厂方法的缺点是:

生产出来的对象虽然结构一样,但他们之间是完全独立的个体,并不是像Java一样的父对象与子对象之间的继承关系,每个对象占用一份独立的内存,无法共用。

3.构造函数

虽然JS是一门面向对象语言,但是并没有像Java一样的类(class)的概念

因为面向对象的三大特征是继承、封装、多态,并没有类、接口等

对象是一种运行时的构造,类是一种开发时的构造、类和接口只是一种实现面向对象的手段。

所以不同语言在面向对象的本质上是一样的,但实现方式可能有所不同,为了满足需求,JS也通过构造函数的形式实现了类:

 //构造函数名默认大写,以作区分,可以看作是一个类
function Person(name, age) {
//私有的不同属性直接挂载在this上,指向创建的对象
this.name = name;
this.age = age;
}
//共有的属性挂载在对象原型上,就像父对象
Person.prototype.address = "xian"; var p1 = new Person("xx",13);
var p2 = new Person("yy",14);
console.log(p1);//Person { name: 'xx', age: 13 }
console.log(p1.address); //xian
console.log(p2.address); //xian

构造方法和工厂方法除了可以设置对象原型的属性外效果是一样的,但后者显然更高端一点。

4.原型模式

4.1.原型的定义

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor

(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。它们与构造函数没有直接的关系。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,
则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。也就是说,在我们调用 person1.sayName() 的时候,会先后执行两次搜
索。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。

当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为 null ,也
只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。

4.2更简单的原型语法

 function Person(){

 }
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};

在上面的代码中,我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外: constructor 属性不再指向 Person 了,而是指向Object,不过可以再设置回来。

4.3原型的动态性

可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时会为实例添加一个指向最初原型[[Prototype]] 指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。

4.4原型对象的问题

 function Person(){

 }
Person.prototype = {
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
friends : ["Shelby", "Court"],
sayName : function () {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true

即引用类型的原型属性一般来说是私有的,与原型的共享性冲突。

4.5组合使用构造函数模式和原型模式

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。下面的代码重写了前面的例子。

 function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
} var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

这种构造函数与原型混成的模式,是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式,这便是JS的伪类模式。

关于原型对象一节,详见《JavaScript高级程序设计(第三版)》148页,写的巨好。

JS构造函数、对象工厂、原型模式的更多相关文章

  1. js原生设计模式——7原型模式之真正的原型模式——对象复制封装

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  2. js 构造函数(construction)与原型(prototype)

    1.面向对象:js原型 java有class和instance,js仅仅有构造函数(function Cat(name,age){this.name=name;this.age=age}),为了实现数 ...

  3. JS构造函数原理与原型

    1.创建对象有以下几种方式: ①.var obj = {}; ②.var obj = new Object(); ③.自定义构造函数,然后使用构造函数创建对象 [构造函数和普通函数的区别:函数名遵循大 ...

  4. JS创建对象之动态原型模式

    动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了 同时使用构造函数和原型的优点:换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初 ...

  5. js原生设计模式——7原型模式之new+call(this)组合应用再探讨实例

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  6. 面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统

    面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统 原型模式和基于原型继承的JavaScript对象系统 在 Brendan Eich 为 JavaScrip ...

  7. 设计模式系列之原型模式(Prototype Pattern)——对象的克隆

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  8. Objective-C设计模式——工厂方法模式virtual constructor(对象创建)

    工厂方法模式 工厂方法模式可以控制对象的创建过程,屏蔽对象创建的细节,可以直接创建出我们所需要的已经配置好的对象. 工厂方法模式定义了创建方法的接口,让子类决定实例化哪一个类,工厂方法模式使得一个类的 ...

  9. JavaScript--面向对象与原型(15)

    // ECMAScript有两种开发模式:1.函数式(过程化);2.面向对象(OOP); 一 创建对象 1.普通的创建对象 1 // 创建一个对象,然后给这个对象新的属性和方法; 2 var box ...

  10. js中对象和对象创建方法

    这一次我们来说一说在JavaScript中经常会用到的一个复杂基本类型,对象,先从对象的属性讲起,再讲对象的创建方法,基本涵盖了创建对象的各种方法,大家一起学习呀~ 一.对象 要掌握对象的使用及继承, ...

随机推荐

  1. ios开发经常使用到的第三方库

    由于iOS SDK相对照较底层,所以开发人员就得受累多做一些体力活.只是幸运的是,有非常多第三方的类库能够用来简化非常多不必要的工作.经过作者团队的谨慎讨论.他们 评选出了10款可以极大提高iOS开发 ...

  2. JavaScript检查手机格式是否错误

    编写自己定义的JavaScript函数checkPhone(),在函数中应用正則表達式推断手机号码的格式是否正确,不对的给出提示 <script type="text/javascri ...

  3. mysql-联结

    一.联结 联结是利用SQL的select能执行的最重要的操作. 1.关系表:假如有一个包含产品目录的数据库表,其中每个类别的物品占一行.对于每种物品要求存储的信息包括产品描述和价格,以及生产该产品的供 ...

  4. iOS自动布局高级用法 && 纯代码约束写法

    本文主要介绍几个我遇到的总结的高级用法(当然我相信肯定有不少比这还高级的). 简单的storyboard中上下左右约束,固定宽高啥的用法在这里就不做赘述了. autolayout自动布局是iOS6以后 ...

  5. Laravel-错误调试与记录日志

    Laravel-错误调试与记录日志 标签(空格分隔): php 错误调试 配置 修改/config/app.php 'debug' => env('APP_DEBUG', true), 开启de ...

  6. Spring MVC模式示例(未采用解耦控制器)

    Product package com.mstf.bean; import java.io.Serializable; /** * Product类,封装了一些信息,包含三个属性 * @author ...

  7. PostgreSQL Replication之第七章 理解Linux高可用(3)

    7.3 高可用软件的历史 有大量的专有的和开源的高可用性软件.专有的例子有:Solaris Cluster (有时称为Sun 集群 or SunCluster), SteelEye LifeKeepe ...

  8. [Bug]Python3.x SyntaxError: 'ascii' codec can't decode byte 0xe4 in position

    安装arch后就没再用python了 昨天管服务器的大佬在跑贝叶斯分类器的时候发现正确率有问题 我赶紧去做优化,然后就有这样的报错 Python 3.6.4 (default, Jan 5 2018, ...

  9. eBay起诉指控亚马逊利用非法手段挖走其卖家

    [摘要]eBay在诉状中称,亚马逊的代表滥用eBay的内部电子邮件系统联系卖家,这违反了市场政策. 腾讯科技讯 10月18日消息,据外媒报道,拍卖网站eBay对亚马逊提起诉讼,指控这家美国零售巨头利用 ...

  10. Could not create connection to database server. Attempted reconnect 3 times. Giving up.错误

    项目是基于springboot框架,昨天从git上pull代码之后也没有具体看更改的地方,结果运行的时候就报错了. java.sql.SQLNonTransientConnectionExceptio ...