很快就要从新浪离职了,最近心情比较轻松,抽点空整理一下构造函数和原型的机理。

我们都知道,在经典设计模式中我们最常用的就是工厂模式、构造函数模式、原型模式这几种,听起来‘模式’好像很高大上的样子,实际上我们在日常写js代码中会经常用到。

简单的说,工厂模式就是我们平常写的一个个函数,当要完成某项功能时,就一遍一遍调用这个函数。如:

function add(a,b){
return a+b;
}

这是一个很简单的代码,完成的是一个很简单的两数相加的功能,当我们需要两数相加的计算时,调用这个函数就可以了。

我要在这里申明:工厂模式不一定比构造函数模式低级,因为用的场合不一样。富士康是全球最大的代工工厂,但我们没法说它很low,它的工厂流水模式也是非常先进的。

但是工厂模式无法实现实例识别的问题(太晦涩,不好懂),简单的说就是无法实现java、c#等类的功能。当然js没有类,只是用构造函数模拟类。创建一个类,然后功能相同或相近的都调用这个类,产生不同的实例,这样是不是引用起来就更方便一些呢?

在什么情况下用一般函数就可以,什么时候用类(构造函数)比较好呢?这个我认为没有定论。当实现一个很简单的功能的时候,如上面的这个相加函数,就没有必要变为构造函数模式。但是当我们要封装一个大的模块,比如手机滚动图等比较复杂的功能的时候,用构造函数就相对比较好一些,因为封装成类了,可以保持很好的封装性,更符合面向对象的特性。

这里就不把设计模式往细里谈了,拉回正题。

构造函数我们都知道如何构造,那么当new一个实例的时候,背后发生了什么呢?先看一个构造函数

function Person(name,age,gender){
this.name=name;
this.age = age;
this.gender=gender;
this.work = function(){
    alert(this.name);
}
} var person1 = new Person('zhangsan',24,'male');

那么person1这个实例是个什么?函数?对象?

如果console.log(person1) 就可以很清晰的看到,是一个对象{‘name':'zhangsan',age:24,gender:'male',work:function(){alert(this.name)}};

如果再创建一个person2,那么person2里的属性值和person1里的属性值是完全独立的,这样是不是我们就可以通过实例对象进行进一步的操作了呢?

但是这样定义还是有待商榷的,因为每次创建一个实例,都会让构造函数执行一次,所以每个实例里都有相同的work函数定义。这样既耗内存又没有任何必要。所以需要把这个构造函数改造如下:

function Person(name,age,gender){
this.name=name;
this.age = age;
this.gender=gender;
this.work =work;
}
function work(){
alert(this.name);
}
var person1 = new Person('zhangsan',24,'male');

大家看这样是不是就好很多了呢?

但是,这样也不是最优的,因为这个work函数在全局中,但是它的用途只是给Person这个构造函数用的,所以缺点就是1、暴露在全局中 2、没有封装

于是可以利用函数的原型。注意两个概念:函数天生有prototype属性,但是普通对象并没有。普通对象有的是_ _ proto_ _属性,所以实例对象通过__proto__属性可以访问到函数的prototype属性。这也就是原型链的一环。原型链后面再说。

运用prototype可以很好的解决构造函数的上述两个缺点。如下

function Person(name,age,gender){
this.name=name;
this.age = age;
this.gender=gender; }
Person.prototype={
work:function(){
alert(this.name);
}
}
var person1 = new Person('zhangsan',24,'male');

大家看,这样是不是更加优化,更加有面向对象的思想了呢?

原型的由来和好处说完了,再说说上面提到的原型链。

实例person1的__prototype__属性可以访问Person的prototype属性,所以可以正确的解析work函数。这就是原型链。具体来说就是:

person1要寻找属性时,首先看自己有没有这个属性。(不要忘了person1就是一个对象),如果没有,好,那就往Person的protype上找。找到了,然后执行就可以了。

这里注意寻找的顺序: 自身----》构造函数的Prototype---》Object的prototype,如果再没有,就报错了。

那么我们如果要判断一个实例的某个属性是类上的(构造函数)还是类的原型上的呢?这里有in操作符合hasOwnProperty方法来定位。

为什么要判断呢?因为有的需求可能只要实例所在的类上的属性,不要原型上的属性。那么这时这两个操作符/方法就派上大用场了

function Person(name,age,gender){
this.name=name;
this.age = age;
this.gender=gender; }
Person.prototype={
work:function(){
alert(this.name);
}
} var person1 = new Person('zhangsan',24,'male');
console.log(person1.hasOwnProperty('name')) ; //true
console.log('name' in person1); //true

如果hasOwnProperty返回的是true,那么说明name这时属性是类上的。in操作符返回的是true,说明name这个属性肯定是类上的或者类的原型上的。

通过hasOwnProperty和in就可以定位确定啦。

我遇到过一个面试题,是考察构造函数和原型的,大概是这样:

function Person(name,age,gender){
this.name=name;
this.age = age;
this.gender=gender; }
Person.prototype={
work:function(){
alert(this.name);
}
} var person1 = new Person('lisi',34,'male');
Person.study= 'English';
Person.prototype.course='Math'; console.log(person1.study);
console.log(person1.course);

ok,看看console.log(person1.study);返回的是什么呢?再看看console.log(person1.course);返回的是什么呢?

呵~呵~

答案是 undefined 和Math

这又是为什么呢?这就涉及到原型的动态性。什么意思?也就是说原型上的属性是动态性的,虽然创建实例在前面,但是当在下面再创建prototype上的属性的时候,prototype会自动更新。

但是构造函数没有动态性,定义了Person类以后再给Person这个类添加属性或方法是不会添加成功的。

要深究起来,构造函数和原型这块确实是水很深,坑不少,需要我们认真研究,不拘泥于效果出来就万事大吉。

先写到这里啦!

深入研究js构造函数和原型的更多相关文章

  1. js 构造函数 & 静态方法 & 原型 & 实例方法

    js 构造函数 & 静态方法 & 原型 & 实例方法 ES5 "use strict"; /** * * @author xgqfrms * @licens ...

  2. 完整原型链详细图解之JS构造函数、原型 原型链、实例化对象

    一.首先说一下什么是构造函数: 构造函数:用来在创建对象时初始化对象.特点:构造函数名一般为大写字母开头:与new运算符一起使用来实例化对象. 举例: function Person(){} //Pe ...

  3. JS构造函数、原型对象、隐含参数this

    This 解析器再调用函数每次都会向函数内部传递一个隐含的参数this,this指向的是一个对象(函数执行的上下文对象) 1.以函数形式调用时,this永远是window. 2.以方法形式调用时,th ...

  4. 笔记: js构造函数与原型

    目录 构造函数与原型介绍 涉及三种引用的操作 有关原型及原型链的一些相关方法总结 @ 构造函数与原型介绍 1.函数与函数的原型对象(prototype object): 在JavaScript中,创建 ...

  5. 一句话总结JS构造函数、原型和实例的关系

    "每个构造函数都有一个原型对象, 原型对象都包含一个指向构造函数的指针, 实例都包含一个指向原型对象的内部指针." --此段话摘自<JavaScript高级程序设计>. ...

  6. JS中构造函数与原型对象的同名属性,实例会取哪一个

    构造函数与原型对象的同名属性,实例会取哪一个? 看了下面的过程,再回忆JS高程3里关于这部分的示意图.实例my在new的时候,本身就获得了a属性,所以my.a是1,倘若在new的时候如果没有赋予a属性 ...

  7. JS构造函数的用法和JS原型

    $(function(){ var rec = new Rectangle(5, 10); //alert(rec.width + "*" + rec.height + " ...

  8. Js笔记(对象,构造函数,原型,原型链,继承)及一些不熟悉的语法

    对象的特性: 1.唯一标识性,即使完全不一样的对象,内存地址也不同,所以他们不相等 2.对象具有状态,同一个对象可能处在不同状态下 3.对象具有行为,即对象的状态可能因为他的行为产生变迁 Js直到es ...

  9. JS高级. 02 面向对象、创建对象、构造函数、自定义构造函数、原型

    面向对象的三大特性: 封装 a)  把一些属性和方法装到一个对象里 2.  继承 a)  js中的继承是指:   一个对象没有一些方法和属性,而另一个对象有 把另一个个对象的属性和方法,拿过来自己用, ...

随机推荐

  1. 提升Nginx+PHP-FPM性能技巧

    /etc/php-fpm.d 2.1进程数   php-fpm初始/空闲/最大worker进程数      pm.max_children = 300      pm.start_servers = ...

  2. 支持向量机 (SVM)分类器原理分析与基本应用

    前言 支持向量机,也即SVM,号称分类算法,甚至机器学习界老大哥.其理论优美,发展相对完善,是非常受到推崇的算法. 本文将讲解的SVM基于一种最流行的实现 - 序列最小优化,也即SMO. 另外还将讲解 ...

  3. 遍历 Input检测是否有重复的值

    在项目中需要遍历某个Table中的Input输入是否有重复的值,为此基于Jquery写了两种实现方式(关键在于取值方式): 方法1: function CheckGoodsNo() { var ish ...

  4. IOS照片颠倒分析及移动/页面端的处理策略和思路

    前言: 前几天, 写了一篇关于IOS手机上传照片颠倒的技术分析文章: IOS照片颠倒分析及PHP服务端的处理. 不过其思路是从服务器来进行处理的, 这种做法相当普遍. 今天来讲述下, 如何从移动端/页 ...

  5. Cantor的数表

    题目描述 如下数列,前5项分别是1/1,1/2,2/1,3/1,2/2…….输入n,输出第n项. 1/1   1/2   1/3   1/4   1/5 2/1   2/2   2/3   2/4 3 ...

  6. xmind的第十二天笔记

  7. javascript学习笔记全记录

          js的初步了解     1.就是用来修改样式的,修改的是行内样式.任何样式都能够修改.     2.css里面怎么写js就怎么写.     3.任何元素都能加事件:事件都要小写 js的三大 ...

  8. web安全之sql注入报错型注入

    前提: echo mysql_error(),输出错误信息. 熟悉的函数: floor()向下取整 concat()返回的字符串参数连接的结果 count()函数返回匹配指定条件的行数 rand()函 ...

  9. websphere性能调优之dump命令

    websphere性能调优之dump命令 基于WebSphere 构建的企业应用,时常会出现性能问题,在严重的情况下还会提示出内存溢出,这是一件很让人恼怒的事情.在WebSphere Applicat ...

  10. Objective-C关键字和概念

    Objective-C关键字和概念 @ 看到这个关键字,我们就应该想到,这是Object-C对C语言的扩展,例如@interface XXX. @interface 声明类 @implementati ...