this

JavaScript的this总是指向一个对象,至于指向哪个对象,是在运行时基于函数的执行环境的动态绑定的,而非函数被声明时的环境。

this的指向

this的指向大致可以分为以下4类:

  • 作为对象的方法调用

  • 作为普通函数调用

  • 构造器调用

  • Function.prototype.callFunction.prototype.apply调用

1.作为对象的方法调用

当函数作为对象的方法被调用时,this指向该对象:

// 声明obj对象
var obj = {a: 'a属性的值',    // a 属性getA: function(){  // getA()方法console.log(this === obj);  // 输出:trueconsole.log(this.a);  // 输出: a属性的值}
};obj.getA();

2.作为普通函数调用

当函数不作为对象的属性被调用时,也就是以普通函数方式,this指向全局对象。在浏览器的JavaScript里,全局对象是window对象。

window.name = 'globalName';  // 声明全局对象的name属性var getName = function(){  // 定义getName()函数return this.name;
};// 调用函数
console.log(getName());  //输出: globalName
window.name = 'globalName';  // 声明全局对象的name属性var myObject = {  // 声明myObject对象name: 'objectName';getName: function(){  // 定义getName()方法return this.name;}
}var getName = myObject.getName;  // 将getName()方法赋给变量getNameconsole.log(getName());  // 输出: globalName

3.构造器调用

JavaScript没有类,但可以从构造器中创建对象,也提供了new运算符用于调用构造器。

大部分JavaScript函数都可以当作构造器使用。构造器的外表跟普通函数一样,他们的区别在于被调用的方式。即,使用new运算符创建对象时,就是将函数当作构造器调用。当用new运算符调用函数时,该函数总会返回一个对象,此时,构造器里的this指向返回的这个对象。

var myClass = function(){this.name = 'className';
};var obj = new myClass();
console.log(obj.name);  // 输出:seven

但,如果构造器显式地返回了一个object类型的对象,那么此函数将返回这个object类型的对象,而不是函数本身所定义的对象,例如:

var myClass = function(){this.name = 'className';return {  //显式地返回一个对象name: 'anne'}
};var obj = new myClass();
console.log(obj.name);  //  输出:anne

而,如果构造器不显式地返回任何数据,或返回一个非对象类型的数据,就不会造成上述情形。

var myClass = function(){this.name = 'className';return 'anne';  // 返回string类型
};var obj = new myClass();
console.log(obj.name);  //  输出:className

4.Function.prototype.call 或 Function.prototype.apply调用

跟普通函数调用相比,用 Function.prototype.callFunction.prototype.apply 可以动态地改变传入函数的this。

var A = {name: 'ObjectA',getName: function(){return this.name;}
};var B = {name: 'ObjectB'
};console.log(A.getName()); // 作为对象的方法调用,输出:ObjectA
console.log(A.getName.call(B)); // 输出:ObjectB

丢失的this

我们经常会因为this的指向与我们的期待不同,而出现undefined的情况,例如:

var obj = {name: 'objName';getName: function(){return this.name;}
};// 作为对象的方法调用,指向obj对象
console.log(obj.getName());   // 输出:objName// 作为普通函数调用,指向全局对象window,name属性尚未定义
var getName2 = obj.getName;
console.log(getName2());  // 输出:Lundefined

call 和 apply

ECAMScript3给Function的原型定义了两个方法,分别是Function.prototype.call 或 Function.prototype.apply。在一些函数式风格的代码编写中,call和apply方法尤为有用。

call和apply的区别

Function.prototype.call 或 Function.prototype.apply的作用一模一样,区别仅在于传入参数形式的不同。

apply接受两个参数,第一个参数制定了函数体内this对象的指向,第二个函数为一个带下标的集合,这个集合可以是数组,也可以是类数组。apply方法把这个集合中的元素作为参数传递给被调用的函数。

var func = function(a, b, c){console.log([a, b, c]);  // 输出:[1,2,3]
};func.apply(null, [1, 2, 3]);

call传入的参数数量不固定,第一个参数也是代表了函数体内的this指向,从第二个参数开始往后,每个参数依次被传入函数:

var func = function(a, b, c){console.log([a, b, c]);  // 输出:[1,2,3]
};func.call(null, 1, 2, 3);

当调用一个函数时,JavaScript的解释器并不会计较形参和实参在数量、类型、以及顺序上的区别,JavaScript的参数在内部就是用一个数组来表示的。从这个意义上说,apply比call的使用率更高,我们不必关心具体有多少参数被传入函数,只要用apply一股脑地推过去就可以了。

当使用call或apply的时候,如果我们传入的第一个参数为null,函数体内的this会指向默认的宿主对象,在浏览器中则是window:

var func = function(a, b, c){console.log(this);
};func.apply(null, [1, 2, 3]);  //输出:window对象
func.call(null, 1, 2, 3);  //输出:window对象

call和apply的用途

  • 改变this指向

  • Function.prototype.bind

  • 借用其他对象的方法

1.改变this指向

call和apply最常见的用途是改变函数内部的this指向:

var A = {name: 'nameA';
};var B = {name: 'nameB';
};window.name = 'nameWindow';var getName = function(){conlole.log(this.name);
};getName();  // 以普通函数调用,指向了window对象,输出:nameWindow
getName.call(A);  // 改变了this的指向,指向了传入的对象,输出:nameA
getName.call(B);  // 改变了this的指向,指向了传入的对象,输出:nameB

2.Function.prototype.bind

大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this指向。
若没有原生的Function.prototype.bind实现,可以通过模拟一个:

Function.prototype.bind = function(context){var self = this;  // 保存原函数return function(){  // 返回一个新的函数return self.apply(context, arguments);  // 执行新函数的时候,会把之前传入的context当作新函数体内的this}
};var obj = {name: "objName"
};var func = function(){console.log(this.name);  // 输出:objName
}.bind(obj);func();

我们通过Function.prototype.bind来“包装”func函数,并且传入一个对象context当作参数,这个context对象就是我们想要修正的this对象,即让函数内部的this指向这个对象。

3.借用其他对象的方法

我们知道,杜鹃即不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等其他鸟类,让它们代为孵化和养育。在JavaScript中也存在类似的借用现象。

场景一:借用构造函数
通过这种技术,能够实现一些类似继承的效果:

var A = function(name){this.name = name;
};var B = function(){  // 借用A的构造函数A.apply(this, arguments);
};B.prototype.getName = function(){return this.name;
};var b = new B('baby');
console.log(b.getName());  // 输出:baby

场景二:类数组对象的操作
函数的参数列表arguments是一个类数组对象,虽然它也有下标,但它并非真正的数组,所以也不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这时,可以借用Array.prototype对象上的方法。

比如,想往arguments中添加一个新的元素,可以借用Array.prototype.push:

(function(){Array.prototype.push.call(arguments, 3);console.log(arguments); // 输出:[1,2,3]
})(1, 2);

想把arguments转成真正的数组的时候,可以借用Array.prototype.slice方法;想截去arguments列表中的头一个元素时,可以借用Array.prototype.shift方法。

PS:本节内容为《JavaScript设计模式与开发实践》第二章 笔记。

JavaScript设计模式与开发实践 | 02 - this、call和apply相关推荐

  1. 《JavaScript设计模式与开发实践》模式篇(12)—— 装饰者模式

    在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活, 还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变;另一方面,继承这种功能复 ...

  2. JavaScript设计模式与开发实践——JavaScript的多态

    "多态"一词源于希腊文polymorphism,拆开来看是poly(复数)+ morph(形态)+ ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的 ...

  3. 《JavaScript设计模式与开发实践》阅读摘要

    <JavaScript设计模式与开发实践>作者:曾探 系统的介绍了各种模式,以及js中的实现.应用,以及超大量高质量代码,绝对值得一读 面向对象的js 静态类型:编译时便已确定变量的类型 ...

  4. JS代理模式《JavaScript设计模式与开发实践》阅读笔记

    代理模式 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问. 保护代理和虚拟代理 保护代理:当有许多需求要向某对象发出一些请求时,可以设置保护代理,通过一些条件判断对请求进行过滤. 虚拟 ...

  5. 专访《Javascript设计模式与开发实践》作者曾探:爱编程 爱生活

     专访<Javascript设计模式与开发实践>作者曾探:爱编程 爱生活 发表于12小时前| 2742次阅读| 来源CSDN| 8 条评论| 作者夏梦竹 专访曾探图书作者Javascr ...

  6. 《JavaScript设计模式与开发实践》模式篇(5)—— 观察者模式

    发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知.在 JavaScript 开发中,我们一般用事件模型 来替代传统的发布- ...

  7. 《JavaScript设计模式与开发实践》模式篇(3)—— 代理模式

    代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问 故事背景: 假设当 A 在心情好的时候收到花,小明表白成功的几率有 60%,而当 A 在心情差的时候收到花,小明表白的成功率无限趋近于 ...

  8. 《JavaScript设计模式与开发实践》原则篇(3)—— 开放-封闭原则

    在面向对象的程序设计中,开放封闭原则(OCP)是最重要的一条原则.很多时候,一个程序具有良好的设计,往往说明它是符合开放封闭原则的. 当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增 ...

  9. JavaScript设计模式与开发实践系列之单例模式

    本系列为<JavaScript设计模式与开发实践>(作者:曾探)学习总结,如想深入了解,请支持作者原版 单例模式 实现单例模式 单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的 ...

最新文章

  1. 最常见企业管理SAAS软件有哪些?要解决哪些问题?
  2. 全能音乐助手序列号_颜控最爱的全能本 惠普星14全能轻薄本深度解析
  3. hdoj-2039-三角形
  4. Velodyne收购高清地图公司 将研发更安全的ADAS系统
  5. python十分钟教程_简洁的十分钟Python入门教程
  6. Java 全半角转换
  7. Magicodes.IE Csv导入导出
  8. 数据中台应该具备的能力
  9. 使用ASP.Net WebAPI构建REST服务(四)——参数绑定
  10. 使用tomcat7-maven-plugin部署Web项目
  11. 【项目实战】课程管理系统(SpringBoot+mybatis-plus+Vue)
  12. 如何在visio中插入矩阵公式
  13. 蓝海灵豚医疗器械管理软件医用耗材专版
  14. 大学计算机基础试题第一套,大学计算机基础试题及答案(完整版)
  15. 你拍一我拍一上学得学计算机,儿歌你拍一我拍一
  16. 计算机被格式化怎么找回资料,文件被格式化 硬盘格式化删除的文件怎么找回...
  17. python爬网站信息_一个爬取实习僧网站信息的爬虫
  18. c语言中gets与puts,请教puts和gets函数
  19. 几款制作网页线框图的软件介绍
  20. 【Java】JavaMail发送QQ邮件邮件

热门文章

  1. python 正则表达式语法大全_Python 之父撰文回忆:为什么要创造 pgen 解析器?
  2. bootstraptable 分页循环_关于bootstrap-table服务端分页问题
  3. php表单写入数据库,php表单写入数据库
  4. oracle where in优化,【求助】为什么在where过滤条件中使用in这样子查询后速度变的非常慢?!...
  5. adcclk最大_TMS320F28xxADC配置说明中文版
  6. php curl 库参数,PHP 关于curl库参数问题的求助!!!
  7. export default (imported as router) was not found_小学生必须知道的英语语法知识:as…as的七大用法...
  8. 创建java ee_eclipse Java EE安装和web项目的创建
  9. 十分钟入门RocketMQ
  10. Spring整合mybatis 1 查询