Javascript浅谈之this


Posted in Javascript onDecember 17, 2013

介绍
this在各种对面对象编程中起着非常重要的作用,主要用于指向调用的对象。不过在JavaScript中,this的表现存在很大差异,特别是不同执行上下文。

由前文我们知道this也是属于执行上下文中的一个属性,所有它命中注定和执行上下文脱不了干系。

activeExecutionContext = {
VO: {...},
this: thisValue};

在Javascript中,this的取值取决于调用的模式。调用模式一共有四种:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。

调用模式
方法调用模式
当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象,即方法调用模式中的this指向调用对象。这个理解起来非常容易,你是我的一个方法,你属于我,你的this当然指向我啦。

var myObject = {
        value : 0,
        increment : function(inc) {
             this.value += typeof inc === "number" ? inc : 1;
       }    
}    
myObject.increment();
console.log(myObject.value);  //输出:1
myObject.increment(3);
console.log(myObject.value);   //输出:4

因为可以通过this访问到自己所属的对象,所有可以通过它调用和修改对象中属性或者方法。由前文可知,this作为执行上下文中属性的一员,必然是在上下文创建时才创建,所有this到对象的绑定发生在调用的时候,这属于“延迟绑定”。通过延迟绑定可以实现对this的高度复用。

function showValue(){
        console.log(this.value);  
}
var a = { value : "a"};
var b = { value : "b"};
a.showValue = showValue;
b.showValue = showValue;
a.showValue();  //输出“a”
b.showValue();  //输出“b”

上例中函数showValue就属于延迟绑定。

函数调用模式
当一个函数并非作为一个对象的方法来调用,这时就是函数调用。函数调用模式中,this被绑定到全局对象。(这是语言设计上的一个错误)

myObject.double = function(){
    var that = this;   //解决方法
    var helper = function(){
        console.log(that, ": ", that.value); //输出 Object {value: 4, increment: function, double: function} ": " 4
        console.log(this, ": ", this.value); //输出  Window {top: Window, window: Window…} ": " undefined
    }    helper(); //以函数形式调用
}

按照正常思路,应该如第四行所输出那样,this指向函数所属对象,可是由于语言设计上面的问题导致this指向的却是全局对象。这个更是让this变得神秘,令人捉摸不透。但是作为开发者,这种情况肯定是我们所不愿意见到的,不按常理出牌这是,还好补救措施也很简单,就是上例中用that指代this。这样,在helper方法中调用that就可以当this使用,简单方便。至于函数调用模式为什么this会这样,后面在分析引用类型时会详加说明。

构造器调用模式
由于javascript是基于原型继承,但是它的设计者又想要它能像传统的面向对象语言那样能通过new和构造函数创建对象,现实面向对象编程。这个貌似不是什么好的构想,有点画虎不成反类犬的尴尬。一是学不来,而是没必要学。javascript的原型继承机制已经非常强大,足以满足面向对象所需的继承多态。

闲话少叙,还行言归正传说说构造器调用模式。构造器调用模式这个非常简单,它就是就一个函数当做构造器,然后将你打算公用的属性和方法用this引进声明。如下

function Person(name, age){
        this.name = name;
        this.age = age;
        this.say = function(){
            console.log("name : %s, age : %n", this.name, this.age);
       }
}
var p1 = new Person("jink", 24);
p1.say(); //输出  name : jink, age : 24
var p2 = new Person("张三", 33);
p2.say();//输出  name : 张三, age : 33

上面例子我们可以清楚看出,this是指向通过new和构造函数创建的对象。为什么会这样?这是因为在javascript中通过new调用构造函数时,new运算符调用“Person”函数的内部的[[Construct]] 方法,接着,在对象创建后,调用内部的[[Call]] 方法。 所有相同的函数“Person”都将this的值设置为新创建的对象。

apply调用模式
javascript中所有函数创建之后,都会自带两个方法:apply和call。这两个方法的的具体使用,我在此就不想详细说明,不知道的同学可以百度一下,挺简单的。通过两个方法,我们可以手动设置this。虽然this在创建时候是不允许修改的,但是,我们在创建之前,手动设置过,那就是另外一回事了。这一设置,可不得了,你就可以让你的对象调用任意方法,就好像你可以让汽车大海中航行,非洲象如美洲豹一样飞驰,程序员像钢琴师一样弹奏。哈哈想象总是美好的,调用归调用,但是调用了能不能实现功能就另说了。

var programmer = { 
    name : "程序员",
    hand : "灵活的双手", 
    programme : function(){ 
        console.log(this.name+"用"+this.hand+"编写代码。");
    }
}
var pianist = { 
    name : "钢琴家",
    hand : "灵活的双手", 
    play : function(){ 
        console.log(this.name+"用"+this.hand+"弹奏动听的乐曲。");
    }
}
var player = { 
    name : "运动员",
    foot : "矫健的双腿", 
    run : function(){ 
        console.log(this.name+"用"+this.foot+"在赛场奔驰。");
    }
}
//循规蹈矩
programmer.programme(); //程序员用灵活的双手编写代码。
pianist.play(); //钢琴家用灵活的双手弹奏动听的乐曲。
player.run(); //运动员用矫健的双腿在赛场奔驰。
//异想天开
pianist.play.apply(programmer); //程序员用灵活的双手弹奏动听的乐曲。
player.run.apply(programmer); //程序员用undefined在赛场奔驰。   由于自身运动缺少,没有矫健的双腿

上面看着是不是挺有意思的,apply的第一个参数就是执行方法中的this指向。这样我们就可以借用别人的方法自己私下偷偷的用,可谓方便至极。在一些框架中经常用的此类技巧。

总结
关于this就说这么多,相信大家看过之后,对在不同情境中this的判定都有些了解了,本来打算讨论接下来讨论引用对象的,阐述一下方法调用模式和函数调用模式中this取值的原理,但害怕篇幅过长,所以决定用单独一章向大家分析一下引用对象这个概念。

Javascript 相关文章推荐
用jquery模仿的a的title属性(兼容ie6/7)
Jan 21 Javascript
jQuery怎么解析Json字符串(Json格式/Json对象)
Aug 09 Javascript
推荐JavaScript实现继承的最佳方式
Nov 11 Javascript
分享10个优化代码的CSS和JavaScript工具
May 11 Javascript
BootStrap 图标icon符号图标glyphicons不正常显示的快速解决办法
Dec 08 Javascript
基于 Vue 的树形选择组件的示例代码
Aug 18 Javascript
VueJS组件之间通过props交互及验证的方式
Sep 04 Javascript
如何以Angular的姿势打开Font-Awesome详解
Apr 22 Javascript
jQuery实现模拟搜索引擎的智能提示功能简单示例
Jan 27 jQuery
Vue动态修改网页标题的方法及遇到问题
Jun 09 Javascript
JS异步宏队列微队列原理详解
Sep 09 Javascript
vue 数据双向绑定的实现方法
Mar 04 Vue.js
将list转换为json失败的原因
Dec 17 #Javascript
js实现可拖动DIV的方法
Dec 17 #Javascript
js复制网页内容并兼容各主流浏览器的代码
Dec 17 #Javascript
鼠标移入移出事件改变图片的分辨率的两种方法
Dec 17 #Javascript
jquery简单实现鼠标经过导航条改变背景图
Dec 17 #Javascript
javascript确认框的三种使用方法
Dec 17 #Javascript
js 剪切板应用clipboardData详细解析
Dec 17 #Javascript
You might like
BBS(php & mysql)完整版(三)
2006/10/09 PHP
PHP 组件化编程技巧
2009/06/06 PHP
php header Content-Type类型小结
2011/07/03 PHP
php socket客户端及服务器端应用实例
2014/07/04 PHP
11个PHPer必须要了解的编程规范
2014/09/22 PHP
jquery 操作DOM案例代码分享
2012/04/05 Javascript
有关于JS构造函数的重载和工厂方法
2013/04/07 Javascript
基于Unit PNG Fix.js有时候在ie6下不正常的解决办法
2013/06/26 Javascript
JavaScript中instanceof运算符的用法总结
2013/11/19 Javascript
详解javascript函数的参数
2015/11/10 Javascript
javascript移动开发中touch触摸事件详解
2016/03/18 Javascript
JS原型链 详解及示例代码
2016/09/06 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
基于LayUI实现前端分页功能的方法
2017/07/22 Javascript
微信小程序tabBar底部导航中文注解api详解
2017/08/16 Javascript
BootStrap 标题设置跨行无效的解决方法
2017/10/25 Javascript
详解vue-cli+es6引入es5写的js(两种方法)
2019/04/19 Javascript
基于Layui自定义模块的使用方法详解
2019/09/14 Javascript
vue实现点击出现操作弹出框的示例
2020/11/05 Javascript
vue 基于abstract 路由模式 实现页面内嵌的示例代码
2020/12/14 Vue.js
跟老齐学Python之关于类的初步认识
2014/10/11 Python
Python的面向对象思想分析
2015/01/14 Python
Python文件和目录操作详解
2015/02/08 Python
Python实现购物车功能的方法分析
2017/11/10 Python
Python基于列表list实现的CRUD操作功能示例
2018/01/05 Python
Django框架之登录后自定义跳转页面的实现方法
2019/07/18 Python
Python爬虫:Request Payload和Form Data的简单区别说明
2020/04/30 Python
检测浏览器是否支持html5视频的代码
2013/03/28 HTML / CSS
城野医生官方海外旗舰店:风靡亚洲毛孔收敛水
2018/04/26 全球购物
莫斯科隐形眼镜网上商店:Linzi
2019/07/22 全球购物
银行行长竞聘演讲稿
2014/04/23 职场文书
2014年干部作风建设总结
2014/10/23 职场文书
2015重阳节敬老活动总结
2015/07/29 职场文书
导游词之绍兴柯岩古镇
2020/01/09 职场文书
浅谈为什么我的 z-index 又不生效了
2022/07/15 HTML / CSS
CSS 实现磨砂玻璃(毛玻璃)效果样式
2023/05/21 HTML / CSS