先介绍目前在ECMAScript中使用最广泛,认同度最高的默认模式。

1.组合使用构造函数及原型

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('Nocholas',29,'Software Engineer');
alert(person1.friends);  //"Shelby,Count,Van"
person1.sayName();  //"Nocholas"

其中实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName() 则是在原型中定义的。

constructor属性始终指向创建当前对象的构造函数,不用刻意牢记。constructor属性

1.在构造函数中用this.创建属性 而不是在原型上。

这么做的本质是因为:对于一个属性而言,在类的对象中的值要求是独立的,对象独自保存自己属性的值,修改的同时不能影响到其他实例化对象。在构造函数中使用this创建的属性,在每个实例上重新创建一遍,保证了此特性。

原型创建的内容是这个类所有对象所共享的,如果使用原型对象来创建对象属性,那么任何一个类的对象修改了自己的某个属性(即原型属性),其他对象的相同属性也会被修改。

所以一般情况我们在函数内部创建属性。

2.用原型来保存方法 而不在构造函数中(之后我们会讲到什么情况下我们才需要在构造函数中定义方法)

方法对所有类对象来说都应该是一样的,没有必要每个对象都保存一个方法,只要由类的原型保存一份,每个对象需要使用方法的时候就调用原型对象中保存的方法。节省了资源。

传统的方式在构造函数定义方法,每个类的被实例化的时候,都会重复创建这个方法,这样会耗 
费很多资源。

下面我们来系统地认识构造函数模式和原型模式

2.构造函数模式

function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.sayName : function(){alert(this.name);}
}var person1 = new Person('Nocholas',29,'Software Engineer');
var person2 = new Person('Greg',27,'Doctor');person1.sayName();  //"Nocholas"
person1.sayName();  //"Greg"

按照惯例构造函数始终都应该以一个大写字母开头

特点:这种方法没有显式地创建对象;直接将属性和方法赋给了 this 对象;没有返回值。

要创建Person的新实例时,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下4个步骤:

  (1) 创建一个新对象

  (2) 将构造函数的作用域赋给对象(因此 this 就指向了这个新对象)

  (3) 执行构造函数中的代码(为这个新对象添加属性)

  (4) 返回新对象

我们来检测一下对象类型

alert( person1 instanceof Object); //true
alert( person1 instanceof Person);  //true
alert( person2 instanceof Object);  //true
alert( person2 instanceof Person);  //true

这个例子中,person1 和 person2 都是Person的实例,同时所有对象均继承自Object。

  构造函数模式的缺点:

  使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。在前面的例子中,person1 和 person2 都有一个名为 sayName 的方法,但那两个方法不是同一个 Function 的实例。 ECMAScript中的函数是对象,因此每定义一个函数就是实例化了一个对象,从逻辑角度讲,此时的构造函数也可以这样定义:

function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.sayName  = new  Function(){alert(this.name);}
}

  从这个角度看构造函数更容易明白每个Person 实例都包含一个不同的 Function 实例的本质,如前所述这两个函数是不相等的,

alert(person1.sayName() == person2.sayName()) //false

  我们没有理由对实现同一功能的方法多次创建,特别是在方法数量较多的情况,即便是可以通过下面的方法来避免多次创建:

function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.sayName  = sayName;
}function sayName(){alert(this.name);
}

  我们创建全局函数 sayName,将构造函数内部的属性设置成等于全局的 sayName 函数;由于sayName 包含的是一个指向函数的指针,person1 和 person2 对象就共享了全局作用域中的函数,这样确实解决了两个函数共做一件事的问题,可是这样在全局作用域中的函数 sayName 只是为了Person 实例化的对象调用,让全局作用域有点名不副实。

  而更让人无法接受的是要定义很多方法的时候,就要定义很多函数,于是我们自定义的类就变得丝毫没有封装性可言了。还好这些问题可以通过原型模式来解决。

3.原型模式

  简单理解:我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途就是可以让所有实例共享它包含的的属性和方法。 换句话说,不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中,如下所示:

function Person(){}Person.prototype.name = "Nicholas";
proson.prototype.age = 29;
Person.prototype.sayName = function(){alert(this.name);
}var person1 = new Person();
person1.sayName();                  //"Nicholas"var person2 = new Person();
person2.sayName();                  //"Nicholas"

alert(person1.sayName == person2.sayName);   //true

  我们将sayName() 方法和所有属性直接添加到了Person 的 prototype 属性中,构造函数变成了空函数,即便如此也仍可以通过调用构造函数来创建一个新对象,而且新对象还会具有相同的实行和方法,新对象中的属性和方法是由所有实例共享的。

 下面我们来理解原型模式的工作原理,有点抽象,不过却是js面向对象编程的最核心部分,理解他很重要,多看几遍就是:

 理解原型(prototype)

   每一个JavaScript对象(null除外)都和另一个对象相关联,“另一个”对象就是我们熟知的原型,每个对象都从原型继承属性。

  无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,我们可以为 prototype 添加属性和方法。默认情况下,prototype 属性都会自动获得一个 constructor 属性,constructor属性始终指向创建当前对象的构造函数,默认情况下指向函数自己,我们不用深究constructor。

function Person(name,age,job){this.name = name;this.age = age;this.job = job;this.friends = ["Shelby","Court"];
}
Person.prototype.sayName =function(){alert(this.name);
}
console.log(Person.prototype.constructor === Person); //true

  另外每个对象都会在其内部初始化一个属性,就是__proto__,__proto__指向当前对象父对象的pertotype 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。

function Person( name, age, job ){
    this.namee = name;
    this.age = age;
    this.job = job;
}
Person.prototype.sayName = function(){alert(this.name);
}
console.log(Person.__proto__ === Function.prototype); //true from Function;
console.log("******************");
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null); //true

  原型链的起点是Object.prototype Object.prototype中包含着toString()、valueOf() 等内置方法,这也是各种数据类型的同名方法,其实都是继承于此。

  看下面,注意区分 prototype 和 __proto__ ,通俗的来理解:

  一个普通的函数 function Person(){} 同时拥有 prototype 和 __proto__。 Person.prototype 包含着Person拥有的一切以后要传给儿子的属性和方法 ,一开始只包含一个constructor属性 可以自由增加 Person.prototype.familyName = "陈";Person.prototype.skill = “泡妞”;

  Person.__proto__ 则指向 Person的老爸的原型 Function.prototype,显然默认也只包含一个constructor属性 如果曾经发生过 Function.prototype.car = "劳斯莱斯",老爸有辆劳斯莱斯的车,那么  console.log(Person.car)  //劳斯莱斯Person也继承了。

  实例化的对象 person1 = new Person(); 是没有prototype的  console.log(person.prototype); //undefined。其他的对象类型也一样。

var arr = new Array();
var fun = new Function();
var obj= new Object();console.log(arr.prototype) //undefined
console.log(fun .prototype) //undefined
console.log(obj.prototype) //undefined 

更简单的原型语法

 前面的例子中没添加一个属性和方法就要敲一遍 Person.prototype。为减少不需要的输入,也从视觉上更好地封装原型的功能,常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下

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

原型对象的问题:

  首先,它省略了为构造函数传递初始化参数这一环节,结果所有实力在默认情况下都取得相同的属性值,这会在某种程度上带来一些不便,但这还不是原型的最大问题,原型的最大问题是由其共享的本性所导致的。

  原型中的所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去(通过在实例上添加一个同名属性,可以隐藏原型中的对应属性),然而对于包含引用类型值得属性来说,问题就比较突出了。

  如果对于值类型,引用类型不太清楚的同学,请参阅 Javascript传值方式

function Person(){}
Psrson.prototype = {constructor : Person,name : "Nicholas",age : "29",friends : ["Shelby","Court"],sayName : function(){alert(this.name);}
}var person1 = new Person();
var Person2 = new persin();person1.friends.push("Van"); // 向friends属性添加一个元素

alert(person1.friends);  //  ["Shelby","Court","Van"]
alert(person2.friends);  //  ["Shelby","Court","Van"]
alert(person1.friends === person2.friends);  //  true 

  由于Person的friends属性是一个数组,是引用类型(对象),我们修改了person1.friends 引用的数组,向数组中添加了一个字符串。由于friends数组存在于Person.prototype 而非 person1 中,所以我们的修改会影响到到所有的实例,假如我们的初衷就是这样在所有实例中共享一个数组,那么这个结果倒也可以接受,可是实例一般都是要有属于自己的全部属性的,而这个问题正是我们很少看到有人单独使用原型模式的原因所在。

  我们最常见的方式,就是在开篇中介绍的组合使用构造函数模式与原型模式,构造函数用于定义实例属性,原型模式用于定义方法和共享属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外这种混成模式还支持向构造函数传递参数,可谓是集两种模式之长。

  下面贴出Javascript中最常用的继承模式:

  

/*=========== 父类 ============*/
function SuperType(name){this.name = name;this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){alert(this.name);
}/*=========== 子类 ============*/
function SubType(name, age){this.age = age;    SuperType.call(this,name);
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){alert(this.age);
}

  注:本文知识点源自《javascript高级程序设计》,想要对javascript面向对象了解更多的园友,可以自行查阅。

  如果感觉本文对您有所助益,劳驾您推荐下,在此谢过。

转载于:https://www.cnblogs.com/v10258/archive/2013/05/20/3065247.html

Javascript面向对象全面剖析 —创建对象相关推荐

  1. Javascript 面向对象全新理练之数据的封装

    JavaScript 是一种非常灵活的面向对象程序设计语言,它与传统的强类型的面向对象程序设计语言(如 C++,Java,C# 等)有很大不同,所以要实现如 C++.java.C# 当中的一些特性就需 ...

  2. 《JavaScript面向对象精要》读书笔记

    JavaScript(ES5)的面向对象精要 标签: JavaScript 面向对象 读书笔记 2016年1月16日-17日两天看完了<JavaScript面向对象精要>(参加异步社区的活 ...

  3. javascript面向对象系列第一篇——构造函数和原型对象

    前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如何用构造函数和原型对象来创建对象 构造函数 构造函数是 ...

  4. JavaScript面向对象及原型 及setTimeout

    JavaScript面向对象 最笨的写法: function Foo(n) {     this.name = n;     this.sayName = function() {         c ...

  5. 《JavaScript面向对象精要》——第1章 原始类型和引用类型1.1 什么是类型

    本节书摘来自异步社区<JavaScript面向对象精要>一书中的第1章,第1.1节,作者:[美]Nicholas C. Zakas 译者: 胡世杰 更多章节内容可以访问云栖社区" ...

  6. javaScript面向对象表示

    这里主要简单写一下javaScript面向对象的不断发展的一些写法和优缺点.直接开门见山. 1.最简单的方式 创建Object的实例,为它添加属性和方法 var car = new Object(); ...

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

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

  8. 《javascript面向对象编程指南》读书笔记

    <javascript面向对象编程指南>读书笔记 <javascript面向对象编程指南>读书笔记 第一章 面向对象的JavaScript 第二章 基本数据类型与流程控制 变量 ...

  9. JavaScript面向对象——深入理解寄生组合继承

    JavaScript面向对象--深入理解寄生组合继承 之前谈到过组合继承,会有初始化两次实例方法/属性的缺点,接下来我们谈谈为了避免这种缺点的寄生组合继承 寄生组合继承: 思路:组合继承中,构造函数继 ...

最新文章

  1. java实验2词法分析程序设计
  2. 需要排序的最短子数组的长度——是一个排序好的数组,中间某一部分被打乱了,让你找出打乱的那个子数组...
  3. 【AI-1000问】人脸的4个方向,你还分的清楚吗?
  4. 谷歌吃苹果:新系统让Macbook秒变Chromebook
  5. C语言代码注释 - C语言零基础入门教程
  6. dnf服务器延迟怎么看,dnf如何判断自己网络还是服务器出问题_dnf判断自己网络还是服务器出问题详细介绍_游戏堡...
  7. 史上最详细的hadoop安装教程
  8. html网上购物系统界面,网上购物系统界面设计要点有哪些?设计思路是什么?...
  9. 编译原理:上下文无关文法
  10. Synchronized和Reentrantlock的区别
  11. pbe近似_量子化学中的主要近似.doc
  12. 用 GreaseMonkey (油猴)解码 TinyURL
  13. 系统集成项目管理工程师有什么用?你真的了解吗
  14. 人力资源管理的现状及发展趋势
  15. 物联网 | HASS+MQTT+树莓派室内监测小型物联网系统
  16. java 图片合成_java 将两张相片合成一张,开发实用类
  17. Java学习基础语法
  18. linux环境下编译部署php生产环境
  19. 基于PHP+MySQL的旅游景点网站的设计与开发
  20. 电子设备雷击/浪涌测试

热门文章

  1. c+mysql主从切换_mysql主从配置
  2. Spark集群资源如何分配
  3. [tensorflow]win 环境 安装anacoda 4.8.2 和tensorflow 2.1.0
  4. Redisson实现分布式锁
  5. 驴妈妈、途牛们该如何收割亲子游市场的红利?
  6. CSS 自定义属性 -- 使用 JS 和不使用 JS
  7. 怎么使用Diff和Meld工具发现两个目录间的不同之处
  8. windows 7 与linux 双系统 安装
  9. 《R in Action》读书笔记(3) 数据变换
  10. Attribute 和 Parameter 的区别