当一个函数被调用时,会创建一个活动记录
(有时候也称为执行上下文
)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this
就是记录(上下文)的其中一个属性,会在函数执行的过程中用到。
一、this的指向
this 总是指向执行时
的当前对象
。
JavaScript 的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
也就是说 this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
除了使用 with 和 eval 的情况,常用的可分为以下几种:
- 作为对象的方法调用。
- 作为普通函数调用。
- 构造器调用。
- Function.prototype.call 或 Function.prototype.apply 调用。
1. 作为对象的方法调用
对象的方法被调用时,this 指向该对象。
var obj = {
a: 1,
getA() {
alert ( this === obj ); // 输出:true alert ( this.a ); // 输出: 1
}
};
obj.getA();
2. 作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指向全局对象。
这里注意对象的方法被单独拷贝出来后执行,那么原来的this会丢失
,变成指向全局对象
//在浏览器的JavaScript 里,这个全局对象是window 对象。
window.name = 'globalName';
var getName = function(){
return this.name;
};
console.log( getName() ); // 输出:globalName
//或者:
window.name = 'globalName';
var myObject = {
name: 'sven',
getName: function(){
return this.name;
}
};
var getName = myObject.getName;
console.log( getName() ); // globalName
3. 作为对象的方法调用
通常情况下,构造器里的 this 就指向返回的这个对象;
- 如果构造器不显式地返回任何数据,或者是返回一个非对象类型的数据,this指向返回的这个对象;
- 如果构造器显式地返回了一个对象,则实例化的结果会返回这个对象,而不是this;
//构造器里的this 就指向返回的这个对象,见如下代码:
var MyClass = function(){
this.name = 'sven';
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
var MyClass = function(){
this.name = 'sven';
return { // 显式地返回一个对象
name: 'anne'
}
};
var obj = new MyClass();
alert ( obj.name ); // 输出:anne
var MyClass = function(){
this.name = 'sven'
return 'anne'; // 返回string 类型
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
4. call 或 apply 调用
apply和call可以动态地改变传入函数的 this
var obj1 = {
name: 'sven',
getName: function(){
return this.name;
}
};
var obj2 = {
name: 'anne'
};
console.log( obj1.getName() ); // 输出: sven
console.log( obj1.getName.call( obj2 ) ); // 输出:anne
二、call, apply 和 bind
简介及区别
call 和 apply作用一模一样,区别仅在于传入参数形式的不同。
apply
apply
接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组
。
类数组:
- 对象本身要可以存取属性;
- 对象的 length 属性可读写。
call
call
传入的参数数量不固定,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数
bind
bind
参数类型和 call 相同,不过它不会执行函数,而是修改 this 后返回一个新的函数
var func = function( a, b, c ){
alert ( [ a, b, c ] );
};
func.apply( null, [ 1, 2, 3 ] );
func.call( null, 1, 2, 3 );
call 和 bind 是包装在 apply 上面的语法糖
:
Function.prototype.call = function( context ){
var argus = [];
for (var i = 1; i < arguments.length; i++) {
argus.push(arguments[i]);
}
this.apply(context, argus);
};
Function.prototype.bind = function( context ){
var self = this; // 保存原函数
// 返回一个新的函数
return function(){
// 执行新的函数的时候,会把之前传入的 context // 当作新函数体内的 this
return self.apply( context, arguments );
}
};
用途
- 改变 this 指向
- 借用其他对象的方法: 在
操作类数组
(比如 arguments) 的时候,可以找 Array.prototype 对象借用方法Array.prototype.push.call(a, 'first' )
三、eval 和 with
词法作用域由写代码期间函数所声明的位置来定义,而 eval 和 with 可以在运行时修改词法作用域。
eval(..) 和 with 会在运行时修改或创建新的作用域,以此来欺骗其他在书写时定义的词 法作用域。 看起来它们能实现更复杂的功能,并且代码更具有扩展性。
但这些功能已经过时,性能也较差,使用不当容易导致难以预料的问题,已经不被提倡,严格模式下会被限制和禁止,所以不要轻易使用它们。
eval
eval(..) 函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码并运行,就好像代码是写在那个位置的一样。
function foo(str, a) {
eval( str );
console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3
// 相当于
function foo(str, a) {
var b = 3;
console.log( a, b );
}
字符串的内容可以是一段动态生成的函数代码。
JavaScript中 还有其他一些功能效果和eval(..)很相似。setTimeout
、setInterval
的第一个参数可以是字符串。
with
with 可以改变作用域,通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。
var obj = {
a: 1,
b: 2,
c: 3
};
// 单调乏味的重复 "obj"
obj.a = 2;
obj.b = 3;
obj.c = 4;
// 简单的快捷方式
with (obj) {
a = 3;
b = 4;
c = 5;
}
性能
eval 和 with性能差
JavaScript 引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。
但如果引擎在代码中发现了 eval(..) 或 with,就无法在词法分析阶段明确知道 eval(..) 会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给 with 用来创建新词法作用域的对象的内容到底是什么。
因此如果大量使用会导致性能变差。
参考
- JavaScript设计模式与开发实践
- 你不知道的JavaScript
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!