浅谈Javascript中的函数、this以及原型


Posted in Javascript onOctober 09, 2016

关于函数

在Javascript中函数实际上就是一个对象,具有引用类型的特征,所以你可以将函数直接传递给变量,这个变量将表示指向函数“对象"的指针,例如:

function test(message){
     alert(message);
   }
   var f = test;
   f('hello world');

你也可以直接将函数申明赋值给变量:

var f = function(message){
     alert(message);   
   };
f('hello world');

在这种情况下,函数申明中可以省略函数名称,因为此时名称已经没有任何意义,我们可直接通过变量f来调用函数。

通过Function类型,我们可以更好地理解函数即对象:

var f = new Function("message","alert(message);");
f('hello world');

关于this

this可以看成调用函数的实际作用域上下文。比较以下函数的执行结果:

function test(){
     this.property = 'hello world';

   }
   test();
   alert(window.property);  //由于在全局范围内调用,test函数中的this实际指向全局对象(window)

   var obj = {};
   test.call(obj);   //通过call第一个参数指定执行上下文范围,所以test函数中this指向obj实例。
   alert(obj.property);   

   var obj2 = {};
   obj2.test2 = test;   //将obj2实例方法test指向 全局test方法
   obj2.test2();      //由于是在obj2上调用test方法,所以test函数中的this也指向了obj2实例
   alert(obj2.property);

定义类型

在Javascript中可以定义构造函数,构造函数与一般函数没有任何区别,在创建实例时,如果我们使用了new关键字,那么这个函数就具有构造函数的特性,否则就是一般函数,如下所示,我们定义了一个Person类型:

function Person(){
   this.name = 'xfrog';
   this.Say = function(){
     alert(this.name);
   };
}

当使用new关键字时,可以创建一个新的Person对象实例:

var p1 = new Person();
p1.Say();

如果不使用new关键字,将直接执行Person函数,由于执行上下文为全局范围,故name属性和Say方法将被添加到window对象:

Person();
Say();
window.Say();

原型

注意上述Person的定义方式,当使用new来创建Person实例时,将会执行Person构造函数,也就是会声明name属性和Say方法,这样可能产生效率问题,注意以下代码:

var p1 = new Person();
var p2 = new Person();
var test = p1.Say == p2.Say;

比较p1和p2两个Say函数指针,返回false,表示每个Person实例中的Say方法都是独立的,而事实上Say函数的功能是完全一样的,我们完全没有必要为每个对象重新分配Say函数”对象“,如果Person实例很多,将会造成大量的内存耗用。

如果将Say函数提取出来放入全局执行范围,似乎可解决次问题:

function Person(){
     this.name = 'xfrog';
     this.Say = say;   
   }

   function say(){
     alert(this.name);   
   }

   var p1 = new Person();
   var p2 = new Person();
   alert(p1.Say == p2.Say);
   p1.name = 'wang';
   p1.Say();

由于this始终和执行上下文相关,p1和p2实例中的Say方法中会正确地返回对应实例的name属性。但是,使用此方式有违面向对象的思想,也失去了类型密封的原则。还会造成大量的全局函数。

为了解决这些缺点,Javascript引出了原型的概念,简单理解,原型可以看成是类型的共享区,原型本身是一个对象,而对象中的属性对于类型来说是共享的。Javascript中每个类型通过prototype属性来表示原型,通过这个属性可指定共享方法:

function Person(){

   }
   Person.prototype.name = 'xfrog';
   Person.prototype.Say = function(){
     alert(this.name);
   };

   var p1 = new Person();
   var p2 = new Person();
   alert(p1.Say == p2.Say);   //返回true

为什么这里可以通过p1.Say来访问Say方法呢?这是因为ECMAScript标准规定了类型属性的查找顺序:先在类型的实例上查找,如果没有则继续在类型原型上查找,这一查找路径采用短路算法,即找到首个后即返回,考虑如下代码:

function Person(){
     this.name = 'wang';
   }

   Person.prototype.name = 'xfrog';
   Person.prototype.Say = function(){
     alert(this.name);
   }

   var p1 = new Person();
   p1.Say();   //将返回wang

上面提到prototype实际上是一个对象,那么我们是否可以直接访问呢? 在一些浏览器实现(如Chrome、Fixfox等)的确可通过实例的__proto__属性来访问内部的prototype对象,这种特征表明Javascript引擎在每个对象的内部都是通过一个变量来保存对prototype的引用,这保证了prototype对应整个类型的实例来说是共享的,例如,你可在Chrome浏览器内使用如下方式来访问Say方法:

p1.__proto__["Say"]();

由于原型是一个对象,我们可以直接将一个对象赋值给prototype:

function Person(){

   }

   Person.prototype = {name:'xfrog', Say:function(){
     alert(this.name);
   }};

注意这个方式下,实际上是完全替换了Person的prototype,这与上面Person.prototype.name方式还是有细微差异的,这是因为任何类型,Javascript引擎都会添加默认的prototype,在这个prototype中包含一个对构造函数的引用,即原型对象属性constructor,所以通常使用替代prototype方式时,我们需要手动加上constructor属性:

Person.prototype = { 
     constructor: Person,
     name :'xfrog',
     Say:function(){
        alert(this.name);
     }
   }

注意,由于prototype对于整个类型是共享的,那么在prototype中的引用类型可能会存在问题,前面的Say函数作为一个对象,也是引用类型,所以每个实例中的Say都指向原型对象中的同一个函数,这本身没有问题,也是我们使用原型的初衷,但对于其他引用对象,可能结果并不是我们想要的:

function Person(){
   }

   Person.prototype = {
     name: 'xfrog',
     obj : { age: 18 },
     Say : function(){
        alert(this.obj.age);
     }
   };

   var p1 = new Person();
   var p2 = new Person();
   p1.obj.age = 20;
   p1.Say();
   p2.Say();

p2.Say返回的是20,这是因为obj属性作为原型属性是共享的,在内存中只存在一个实例,所以通过p1修改后,p2只能得到修改后的状态。如果要避免此情况,可将obj属性放到实例中:

function Person(){
     this.obj = { age: 18 };
   }

以上就是小编为大家带来的浅谈Javascript中的函数、this以及原型全部内容了,希望大家多多支持三水点靠木~

Javascript 相关文章推荐
jQuery解决iframe高度自适应代码
Dec 20 Javascript
取得窗口大小 兼容所有浏览器的js代码
Aug 09 Javascript
jquery无法设置checkbox选中即没有变成选中状态
Mar 27 Javascript
JS简单操作select和dropdownlist实例
Nov 26 Javascript
innerHTML在IE中报错解决方案
Dec 15 Javascript
JS+CSS实现淡入式焦点图片幻灯切换效果的方法
Feb 26 Javascript
9个让JavaScript调试更简单的Console命令
Nov 14 Javascript
Vue的MVVM实现方法
Aug 16 Javascript
微信小程序实现跑马灯效果
Oct 21 Javascript
js操作table中tr的顺序实现上移下移一行的效果
Nov 22 Javascript
浅谈Vue.js中如何实现自定义下拉菜单指令
Jan 06 Javascript
微信小程序MUI侧滑导航菜单示例(Popup弹出式,左侧不动,右侧滑动)
Jan 23 Javascript
常用Javascript函数与原型功能收藏(必看篇)
Oct 09 #Javascript
javascript汉字拼音互转的简单实例
Oct 09 #Javascript
Javascript农历与公历相互转换的简单实例
Oct 09 #Javascript
Angularjs 创建可复用组件实例代码
Oct 09 #Javascript
Boostrap实现的登录界面实例代码
Oct 09 #Javascript
深入理解bootstrap框架之第二章整体架构
Oct 09 #Javascript
javascript 判断是否是微信浏览器的方法
Oct 09 #Javascript
You might like
php urlencode()与urldecode()函数字符编码原理详解
2011/12/06 PHP
使用PHP实现蜘蛛访问日志统计
2013/07/05 PHP
PHP利用MySQL保存session的实现思路及示例代码
2014/09/09 PHP
ThinkPHP独立分组使用的注意事项
2014/11/25 PHP
微信公众平台消息接口校验与消息接口响应实例
2014/12/23 PHP
php简单日历函数
2015/10/28 PHP
Yii2 assets清除缓存的方法
2016/05/16 PHP
PHP实现的大文件切割与合并功能示例
2018/04/10 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
表单填写时用回车代替TAB的实现方法
2007/10/09 Javascript
封装的原生javascript弹出层代码
2010/09/24 Javascript
轻量级的原生js日历插件calendar.js使用指南
2015/04/28 Javascript
实例代码详解javascript实现窗口抖动及qq窗口抖动
2016/01/04 Javascript
vue中的计算属性实例详解
2018/09/19 Javascript
利用Vue实现简易播放器的完整代码
2020/12/30 Vue.js
vue3.0 项目搭建和使用流程
2021/03/04 Vue.js
[40:06]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第一场
2018/04/04 DOTA
Python中如何优雅的合并两个字典(dict)方法示例
2017/08/09 Python
python多线程调用exit无法退出的解决方法
2019/02/18 Python
Django异步任务线程池实现原理
2019/12/17 Python
python使用协程实现并发操作的方法详解
2019/12/27 Python
Python 统计位数为偶数的数字代码详解
2020/03/15 Python
Django通过json格式收集主机信息
2020/05/29 Python
CSS3 @keyframes简单动画实现
2018/02/24 HTML / CSS
德国、奥地利和瑞士最大的旅行和度假门户网站:HolidayCheck
2019/11/14 全球购物
德国二手设计师时装和复古时装跳蚤市场:Mädchenflohmarkt
2020/11/09 全球购物
什么是WEB控件?使用WEB控件有哪些优势?
2012/01/21 面试题
SOA的常见陷阱或者误解是什么
2014/10/05 面试题
高中生学习生活的自我评价
2013/11/27 职场文书
竞选班干部演讲稿
2014/04/24 职场文书
借名购房协议书范本
2014/10/06 职场文书
股东协议书范本2016
2016/03/21 职场文书
apache基于端口创建虚拟主机的示例
2021/04/22 Servers
教你使用VS Code的MySQL扩展管理数据库的方法
2022/01/22 MySQL
redis调用二维码时的不断刷新排查分析
2022/04/01 Redis
SQL Server中使用表变量和临时表
2022/05/20 SQL Server