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

我们都知道,在经典设计模式中我们最常用的就是工厂模式、构造函数模式、原型模式这几种,听起来‘模式’好像很高大上的样子,实际上我们在日常写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. Norm and Sparse Representation

    因为整理的时候用的是word, 所以就直接传pdf了. 1.关于范数和矩阵求导.pdf 参考的主要是网上的几个博文. 2.稀疏表示的简单整理.pdf 参考论文为: A Survey of Sparse ...

  2. 由于OCR文件损坏造成Oracle RAC不能启动的现象和处理方法

    v$cluster_interconnects 集群节点间通信使用的IP地址 错误信息 使用了公网进行连接 SQL> select * from v$cluster_interconnects; ...

  3. 出现( linker command failed with exit code 1)错误总结 (转)

    这种问题,通常出现在添加第三方库文件或者多人开发时. 这种问题一般是找不到文件而导致的链接错误. 我们可以从如下几个方面着手排查. 1.以如下错误为例,如果是多人开发,你同步完成后发现出现如下的错误. ...

  4. NodeOS操作系统

    导读 我想大多数人听说过 Node.js,但是你听说过 NodeOS 吗?一个用 Node.js 写的操作系统,NodeOS 用 Linux 内核来处理各种底层任务,比如硬件通讯什么的,但是除此之外, ...

  5. JAVA,NET RSA密钥格式转换

    JAVA和NET RSA密钥格式相互转换(公钥,私钥) 做了一个小项目遇到java和.net非对称加密问题,java的公钥和私钥就直接是一个字符串的形式展示的,但是.net是以xml简单包裹形式展示的 ...

  6. public/private/protected访问控制权限的区别

    //public/private/protected访问控制权限的区别//时间:2016/8/16 //(一)修饰成员: //public: 在类内.类外都能使用 . //protected: 在类内 ...

  7. 105 董婷婷 第二次Sprint总结

    总结: 第二次冲刺结束了,这次冲刺的主要任务是建立数据库.项目进行到现在也基本定型了,满满的成就感啊.经过一段时间的合作,团队成员间的默契大大提高,还有最后一次冲刺,队友们,加油哦!

  8. HDFS的工作原理(读和写操作)

    工作原理: NameNode和DateNode,NameNode相当于一个管理者,它管理集群内的DataNode,当客户发送请求过来后,NameNode会 根据情况指定存储到哪些DataNode上,而 ...

  9. python整理之(字符串、元组、列表、字典)

    一.关于字符串的整理总结 对于字符串的操作常用的有这些: 字符串的操作通过dir()函数可以查看 我们先整理没有下划线的用法,有下划线的暂时不去考虑. 1.capitalize 功能:使字符串的首字母 ...

  10. Oracle虚拟索引,大表或生产环境下预估索引效果的好东西

    在数据库优化过程中,索引的重要性是不言而喻的,但是在我们进行性能调整过程中, 一个索引是否能够被使用到,在索引创建之前是存在不确定性的. 而创建索引又是一个代价很高的操作,尤其是数据量很大的情况下,在 ...