我写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. Android中实现控件圆角边框

    首先,在drawable文件夹下新建一个xml文件: <?xml version="1.0" encoding="utf-8"?> <shap ...

  2. Android 开发60条技术经验总结(转)

    Android 开发60条技术经验总结: 1. 全部Activity可继承自BaseActivity,便于统一风格与处理公共事件,构建对话框统一构建器的建立,万一需要整体变动,一处修改到处有效. 2. ...

  3. SQL 中having 和where的区别分析

    在select语句中可以使用groupby子句将行划分成较小的组,然后,使用聚组函数返回每一个组的汇总信息,另外,可以使用having子句限制返回的结果集 在select语句中可以使用groupby子 ...

  4. VIM中的正则表达式及替换命令

    VIM中的正则表达式及替换命令 一.使用正则表达式的命令 使用正则表达式的命令最常见的就是 / (搜索)命令.其格式如下: /正则表达式 另一个很有用的命令就是 :s(替换)命令,将第一个//之间的正 ...

  5. Jsp(3):内置对象和四种域对象的理解

    由来:在jsp开发中,会频繁使用到一些对象 .例如HttpSession,ServletContext,ServletContext,HttpServletRequet.所以Sun公司设计Jsp时,在 ...

  6. How to easily concatenate text based on criteria in Excel? 如何将Excel中的文本按条件合并

    To combine text with the unique ID numbers, you can extract the unique values first and then create ...

  7. poj 3294 Life Forms

    后缀数组的题目,把后缀连接起来,这个还是先二分答案,然后选取一段连续的height值,判断这些height代表的后缀有没有覆盖一半以上的字符串. 得出答案的长度之后还要在枚举连续的heigh,判断有没 ...

  8. 【Oracle】OCR的备份和恢复之导出导入

    使用导出导入进行OCR的备份和恢复: 在对集群做调整前.如:增删节点等操作前,应该对OCR进行一次备份.能够使用export备份到指定文件. 实验环境: OS:OEL5.6 RAC:10.2.0.1. ...

  9. Android开发:如何安全的中止一个自定义线程Thread

    http://blog.csdn.net/yanzi1225627/article/details/8582078 经研究,我推荐这种写法: /*自定义线程*/ class MyThread impl ...

  10. Coordinate System

    Coordinate System Introduction of Different Coordinate Systems Cartesian Coordinate System UI Coordi ...