我写JS代码,可以说一直都是面向过程的写法,除了一些用来封装数据的对象或者jQuery插件,可以说对原生对象了解的是少之又少。所以我拿着《JavaScript高级程序设计 第3版》恶补了一下,这里坐下总结笔记,属于菜鸟级别,大神请直接无视。

1、工厂模式

 /**
* 工厂模式
*/
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
// 等价于 o.sayName = new Function("console.log(this.name);");
return o;
}
var p1 = createPerson('lvyahui1',12,'devloper');
var p2 = createPerson('lvyahui2',23,'desigen');
p1.sayName();
p2.sayName();
console.log(p1.sayName === p2.sayName); // false

这种方法存在很多问题,比如多个对象的方法是独立的,没用共享。不能判断对象的类型

2、构造函数模式

 /**
* 构造函数模式
*/
function Person (name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var pp1 = new Person('lvyahui1',12,'dev');
var pp2 = new Person('lvyahui2',12,'desien');
pp1.sayName();
pp2.sayName();
console.log(pp1.constructor === Person); // true
console.log(pp2.constructor === Person); // true
console.log(pp1 instanceof Object); // true
console.log(pp2 instanceof Person); // true

这中方式解决了对象类型判断的问题,但却没有解决方法共享的问题,方法依然在每个对象里创建了一遍。要解决这样的问题,可以像下面这样

 function Home (name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayAge = sayAge;
}
function sayAge(){
console.log(this.age);
}

但这样有带来了新的问题,在全局作用域定义的function只能在对象环境运行,违背了全局的概念;而且,当要定义的方法非常多的石猴。就要定义n多的全局函数,毫无封装性可言。

另外,需要注意的是,构造函数也是函数,它与普通函数的唯一区别,就在于调用方式的不同。如下面这样,我门可以拿它当普通函数用,这样属性和方法将被绑定到浏览器全局的执行环境window上,或者绑定到别的对象上运行,将属性方法绑定到别的对象

 // 构造函数也是函数
//
var person = new Person('hahha',23,'ddd');
// 2 做普通函数
Person('window',11,'2323dd');
//window.sayName(); // 3 在另一个对象的作用域中使用
var o = new Object();
Person.call(o,'lvyahui',23,'2323'),
o.sayName();

3、原型模式

js中每一个函数都有一个特殊的属性-prototype(原型),这个属性是一个指针,指向一个对象。这个对象包含所有以这个函数为构造函数创建的对象共享的属性和方法,首先看下面的代码

 /**
* 原型模式
*/
function Dog(){}
Dog.prototype.name = 'a mi';
Dog.prototype.age = 1;
Dog.prototype.sayName = function(){
console.log(this.name);
} var d1 = new Dog(),
d2 = new Dog();
d1.sayName();
d2.sayName();
console.log(d1.sayName === d2.sayName); // true
console.log(Dog.prototype.isPrototypeOf(d1)); // true
console.log(Object.getPrototypeOf(d1)); //返回 [[prototype]]的值
console.log(Object.getPrototypeOf(d1) === Dog.prototype);

这里将属性和方法都添加到了函数的原型对象中。在第4行的代码中我定义了Dog构造方法,这让Dog的原型对象的constructor属性指向了Dog。5-7行代码又为Dog的原型对象添加了2个属性和一个方法。

第11行、12行代码创建了两个对象实例,这两个实例中都只包含了一个特殊的属性[[Prototype]],这个属性指向了构造函数的prototype,构造函数的prototype属性指向了原型对象,原型对象的constructor属性有指会了Dog构造方法,这里比较绕,我直接取书上面的图给大家看,稍作了修改,图里面的是Person构造函数,这里实在抱歉,暂时没在ubuntu系统上找到好的作图工具,只能将就了,有知道的可以介绍给我用用。

另外这个图其实还省略了以个继承关系,就是Person对象其实是继承Obejct的,这是默认规则,别的语言也有。这也是js对象一创建就有object的方法和属性的原因。

再一个,上面的代码中的

  • isPrototypeOf方法返回的是参数对象的[[Prototype]]属性是否指向调用isPrototypeOf的对象。
  • getPrototype方法返回[[Prototype]]属性的值。

原型很重要的一个概念是属性搜索(方法我认为也是属性),从实例对象开始一直往原型链上游搜索,如果找到了就停止搜索,所以如过我们在实例对象中添加原型中已有的属性或方法,会屏蔽掉原型链中的属性或方法。看下面的代码

 d1.name = 'sai mao';    // 屏蔽原型中的属性
console.log(d1.name);
// 要恢复原型中的属性,必须显示删除实例中的属性
delete d1.name;
console.log(d1.name);

可以看到,可以使用delete恢复原型中的属性,而下面的方法可以用来检测一个属性是在实例对象中,还是在原型链的原型对象中。

 // 检测一个属性是在实例中,还是在原型中
d1.name = 'hhhh';
console.log(d1.hasOwnProperty('name')); // 只有在给定的属性存在于对象实例中才返回true
delete d1.name;
console.log(d1.hasOwnProperty('name')); //单独使用in操作符,只要能在对象中找到属性则返回true
d1.name = 'dsfdsfsd';
console.log('name' in d1); // true
console.log('name' in d2); // ture //同时使用hasOwnProperty 和 in操作符,就能确定这个属性到底是在原型中还是在实例中
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && name in object;
}
console.log('d1 hasPrototypeProperty :'+hasPrototypeProperty(d1, 'name'));
console.log('d2 hasPrototypeProperty :'+hasPrototypeProperty(d2, 'name'));

例外有一种更简单的原型写法

 // 更简单的原型语法
function Cat(){}
Cat.prototype = {
name : 'mimi',
age : 12,
job : 'doubi',
sayName : function(){
console.log(this.name);
}
}; var cat = new Cat();
console.log(cat instanceof Object);
console.log(cat instanceof Cat);
console.log(cat.constructor === Cat);//false
console.log(cat.constructor === Object);//true

这种方式其实是以字面量的方法重新创建了一个对象,然后赋值给了原型指针。它丢弃了原来的原型对象,所以很显然的原型对象的constructor属性不再指向Cat,而是指向了Obejct,有多种方法可以修复构造函数,比如在定义字面量对象的时候,就显示制定constructor属性为Cat,也可以使用下面的方法。

 // 重设Cat的constructor属性
Cat.prototype.constructor = Cat;
// 但这样constructor变成可枚举的了
var cat_keys = Object.keys(Cat.prototype);
console.log(cat_keys);//[ 'name', 'age', 'job', 'sayName', 'constructor' ]
console.log(Object.keys(cat));//[] // 重设constructor的属性
Object.defineProperty(Cat.prototype,'constructor',{
enumerable:false,
value:Cat
});
cat_keys = Object.keys(Cat.prototype);
console.log(cat_keys);//[ 'name', 'age', 'job', 'sayName' ]

原型模式也不是没有问题,比如不好向构造函数传递参数。它最大的问题是对引用类型的原型属性的共享问题,看下面的代码

 // 原型模式最大的问题在于对引用类型的属性共享问题

 function House(){}

 House.prototype = {
constructor:House,
friends:['lvyahui','d']
}; var h1 = new House(),
h2 = new House();
h1.friends.push('li');
console.log(h1.friends);//[ 'lvyahui', 'd', 'li' ]
console.log(h2.friends);//[ 'lvyahui', 'd', 'li' ]

4、构造函数与原型对象方式组合的模式

组合模式可以说吸取了构造函数模式与原型模式的优点,既保证了每个对象实例都有自己独立的属性和方法,同时有可以实现共享的属性和方法。

 /**
* 常用方式,组合使用构造函数模式和原型模式
*/ function Movie(name,length){
this.name= name;
this.length = length;
this.links = ['h1','h2'];
}
Movie.prototype = {
constructor:Movie,
sayName : function (){
console.log(this.name);
}
};
var m1 = new Movie('diany1',14),
m2 = new Movie('diany2',23);
m1.links.push('h3'); console.log(m1.links);
console.log(m2.links);
console.log(m1.links === m2.links);
console.log(m1.sayName === m2.sayName);

这种方式集各家之长,我想这种方式应该是用的比较多的了吧(本人还未毕业,对企业里实际情况不太了解,有知道的可以悄悄告诉我)

当然,还有一种更好的写法,就是所谓的动态原型模式

5、动态原型模式

 function Miss(name,age){
this.name = name;
this.age = age; if(typeof this.sayName != 'function'){
Miss.prototype.sayName = function(){
console.log(this.name);
}
}
} var miss = new Miss('lvyahui',12);
miss.sayName();

这种方式的在保持了组合模式的优点的前提下,让代码看起了封装性更好,也更安全。

6、寄生构造模式

这中方式与工厂模式,就只有一点区别,通过new 构造函数的形式创建对象,像下面这样,注意它只带创建对象的时候与工厂模式有区别(16行 new)

 /**
* 寄生构造模式
*/ function createPerson2(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
// 等价于 o.sayName = new Function("console.log(this.name);");
return o;
}
var p1 = new createPerson2('lvyahui1',12,'devloper');

7、稳妥构造函数模式

这种模式基于稳妥对象的概念,这种对象是没有公共属性,它的方法中也不使用this的对象。大家都知道js中的this一直都是让人头疼的问题。

稳妥模式与寄生模式类似,区别在于

  • 不通过new操作符调用构造函数
  • 不在行创建对象的实例方法中使用this
 /**
* 稳妥构造模式
*/
function Girl(name,age){
var o = new Object();
o.sayName = function(){
console.log(name);
};
o.sayAge = function(){
console.log(age);
};
return o;
}
var gril = Girl('d',21);
console.log(gril.sayName());
console.log(gril.sayAge());
// 输出
// d
// undefined
//
// undefined
// 为什么呢?

大家知道这个输出为什么是这么吗?知道的留言告诉我吧。

好了,就写这么多了,总结一下,原生的创建js对象的最普适的方法应该是组合模式了吧。

JavaScript OOP 创建对象的7种方式的更多相关文章

  1. OOP 创建对象的7种方式

    JavaScript OOP 创建对象的7种方式   我写JS代码,可以说一直都是面向过程的写法,除了一些用来封装数据的对象或者jQuery插件,可以说对原生对象了解的是少之又少.所以我拿着<J ...

  2. JavaScript中创建对象的三种方式!

    JavaScript中创建对象的三种方式! 第一种 利用对象字面量! // 创建对象的三种方式! // 1 对象字面量. var obj = { // 对象的属性和方法! name: 'lvhang' ...

  3. javascript中创建对象的几种方式

    1. 使用Object构造函数来创建一个对象,下面代码创建了一个person对象,并用两种方式打印出了Name的值. var person = new Object(); person.name=&q ...

  4. JavaScript 创建对象的七种方式

    转自:xxxgitone.github.io/2017/06/10/JavaScript创建对象的七种方式/ JavaScript创建对象的方式有很多,通过Object构造函数或对象字面量的方式也可以 ...

  5. JavaScript创建对象的几种 方式

    //JavaScript创建对象的七种方式 //https://xxxgitone.github.io/2017/06/10/JavaScript%E5%88%9B%E5%BB%BA%E5%AF%B9 ...

  6. 第184天:js创建对象的几种方式总结

    面向对象编程(OOP)的特点: 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有的对象下继承出新的对象 多态:多对象的不同形态 一.创建对象的几种方式 javascript 创建对象简单 ...

  7. js中面向对象(创建对象的几种方式)

    1.面向对象编程(OOP)的特点: 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有的对象下继承出新的对象 多态:多对象的不同形态 一.创建对象的几种方式 javascript 创建对象 ...

  8. js中面向对象(创建对象的几种方式)

    1.面向对象编程(OOP)的特点: 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有的对象下继承出新的对象 多态:多对象的不同形态 注:本文引用于 http://www.cnblogs. ...

  9. javascript创建类的6种方式

    javascript创建类的7种方式 一 使用字面量创建 1.1 示例 var obj={}; 1.2 使用场景 比较适用于临时构建一个对象,且不关注该对象的类型,只用于临时封装一次数据,且不适合代码 ...

随机推荐

  1. final、抽象类、接口、多态、

       final———最终.作为一个修饰符 可以修饰类. 函数. 变量: 被final修饰的类不可以被继承: 被final修饰的方法不可以被重写: 被final修饰的变量只能够被赋值一次,既可以修饰成 ...

  2. Gprinter Android SDK V2.1 使用说明

    下载:http://download.csdn.net/download/abc564643122/8872249

  3. HDU 4389——X mod f(x)(数位DP)

    X mod f(x) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Probl ...

  4. [Node.js] Web Scraping with Pagination and Advanced Selectors

    When web scraping, you'll often want to get more than just one page of data. Xray supports paginatio ...

  5. MFC 一个类訪问还有一个类成员对象的成员变量值

    作者:卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/35263857 MFC中一个类要訪问另外一个类的的对象的成员变量值,这就须要获得 ...

  6. CentOS 更新yum源

    公司买了一台刀片机服务器,安装的系统版本太低,导致yum源不合适,安装就会报错. 在网上找了好长时间,才发现是yum源的问题.   转载原文: 突然想起试试 Docker,在一台计算机上安装了 Cen ...

  7. oracle用户管理实例

    oracle中的用户角色分为预定义角色和自定义角色. 角色是把常用的权限集中起来形成角色. 授权/分配角色命令 grant 权限/角色 to 用户 收回权限命令: revoke 综合案例: 创建一个用 ...

  8. mac 神奇时光机

    http://bbs.zol.com.cn/nbbbs/d544_8216.html

  9. 数据库的CRUD操作

    一:数据库的CRUD操作,C是指create新增,R是指retrieve检索,U是指update更改,D是指delete删除 SQL语句分为3类: 1.DDL指数据定义语言如:create,drop, ...

  10. C#基础入门--关于C#背景介绍以及变量相关

    在正式探索C#的奥秘之前,我们先谈一谈关于学习方法的问题吧.你会不会有这样的感悟,自己努力奋斗得到的东西倍加珍惜,飘到眼前的,却不屑一顾.我认为,学习的整个历程亦是如此.在学习过程中,只有我们遇到了问 ...