深入研究js构造函数和原型
很快就要从新浪离职了,最近心情比较轻松,抽点空整理一下构造函数和原型的机理。
我们都知道,在经典设计模式中我们最常用的就是工厂模式、构造函数模式、原型模式这几种,听起来‘模式’好像很高大上的样子,实际上我们在日常写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构造函数和原型的更多相关文章
- js 构造函数 & 静态方法 & 原型 & 实例方法
js 构造函数 & 静态方法 & 原型 & 实例方法 ES5 "use strict"; /** * * @author xgqfrms * @licens ...
- 完整原型链详细图解之JS构造函数、原型 原型链、实例化对象
一.首先说一下什么是构造函数: 构造函数:用来在创建对象时初始化对象.特点:构造函数名一般为大写字母开头:与new运算符一起使用来实例化对象. 举例: function Person(){} //Pe ...
- JS构造函数、原型对象、隐含参数this
This 解析器再调用函数每次都会向函数内部传递一个隐含的参数this,this指向的是一个对象(函数执行的上下文对象) 1.以函数形式调用时,this永远是window. 2.以方法形式调用时,th ...
- 笔记: js构造函数与原型
目录 构造函数与原型介绍 涉及三种引用的操作 有关原型及原型链的一些相关方法总结 @ 构造函数与原型介绍 1.函数与函数的原型对象(prototype object): 在JavaScript中,创建 ...
- 一句话总结JS构造函数、原型和实例的关系
"每个构造函数都有一个原型对象, 原型对象都包含一个指向构造函数的指针, 实例都包含一个指向原型对象的内部指针." --此段话摘自<JavaScript高级程序设计>. ...
- JS中构造函数与原型对象的同名属性,实例会取哪一个
构造函数与原型对象的同名属性,实例会取哪一个? 看了下面的过程,再回忆JS高程3里关于这部分的示意图.实例my在new的时候,本身就获得了a属性,所以my.a是1,倘若在new的时候如果没有赋予a属性 ...
- JS构造函数的用法和JS原型
$(function(){ var rec = new Rectangle(5, 10); //alert(rec.width + "*" + rec.height + " ...
- Js笔记(对象,构造函数,原型,原型链,继承)及一些不熟悉的语法
对象的特性: 1.唯一标识性,即使完全不一样的对象,内存地址也不同,所以他们不相等 2.对象具有状态,同一个对象可能处在不同状态下 3.对象具有行为,即对象的状态可能因为他的行为产生变迁 Js直到es ...
- JS高级. 02 面向对象、创建对象、构造函数、自定义构造函数、原型
面向对象的三大特性: 封装 a) 把一些属性和方法装到一个对象里 2. 继承 a) js中的继承是指: 一个对象没有一些方法和属性,而另一个对象有 把另一个个对象的属性和方法,拿过来自己用, ...
随机推荐
- python 后台爆破工具
sys:使用sys模块获得脚本的参数 queue模块,创建一个“队列”对象 time 模块 Python time time() 返回当前时间的时间戳(1970纪元后经过的浮点秒数). fin ...
- iOS 系统架构
https://developer.apple.com/library/ios/documentation/Miscellaneous/Conceptual/iPhoneOSTechOverview/ ...
- 模仿console自写函数打印js的对象
本以为写个递归函数就可以将js的对象打印出来. 当然第一个想到的估计是JSON.stringify() 这个函数.但这个函数打印到浏览器 显示效果不友好.最友好的显示肯定是 控制台打印咯. 结果尝试打 ...
- A log about Reading the memroy of Other Process in C++/WIN API--ReadProcessMemory()
Memory, is a complex module in Programing, especially on Windows. This time, I use cpp with win wind ...
- id生成策略 id工具类
import java.util.Random; /** * 各种id生成策略 * <p>Title: IDUtils</p> * <p>Description: ...
- aop配置
前置通知 后置通知 环绕通知 异常通知 定义切面 aop-config 切入点 切面 通知 spring jdbc
- MATLAB 绘图时的相关心得
matlab中如何调整legend的位置? legend('sinx',-1); %----位于图形框外面-----------------------legend('sinx',0);------- ...
- 深入浅出SQL Server中的死锁
简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...
- GDI+ 操作TIFF ccitt t.6 压缩
Bitmap Bi=new Bitmap("C:\img.tif"); SaveFileDialog sfdlg = new SaveFileDialog(); sfdlg.Fil ...
- lumen 构建api(dingo api)
什么是 API API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力, ...