this
this 指向的是一个对象,只用当函数执行时才能确定this指向的对象,与函数声明的环境无关。
- this 指向
有4中情况:对象的方法调用、普通函数调用、构造函数实例化、call和apply
1.对象的方法调用
//指向对象本身
var obj = {
name: 'bby',
getName: function(){
return this.name;
}
};
obj.getName() // bby
- 普通函数调用
// 一般指向全局对象,window
window.name = 'globalName';
var getName = function(){
return this.name;
};
console.log( getName() ); // globalName
当调用一个对象的方法时,this本来是指向该对象,怎样修改可以指向 window?
window.name = 'globalName';
var myObject = {
name: 'sven',
getName: function(){
return this.name;
}
};
var getName = myObject.getName;
console.log( getName() ); // globalName
在全局域中定义个变量,指向对象的方法引用,最后在执行个变量,此时this指向window。
思考:怎么修改上面的代码,this还是指向myObject ?这个其实要求访问私有变量,可以使用闭包,返回一个函数:
window.name = 'globalName';
var myObject = {
name: 'bby',
getName: function(){
var _this = this;
return function(){
console.log(_this.name)
}
}
};
var getName = myObject.getName();
console.log( getName() ); // bby
接下来还有一个例子,不是很好理解,直接看代码:
<html>
<body>
<div id="div1">我是一个div</div>
</body>
<script>
window.id = 'window';
document.getElementById( 'div1' ).onclick = function(){
alert ( this.id ); // 输出:'div1'
var callback = function(){
alert ( this.id ); // 输出:'window'
}
callback();
};
</script>
</html>
点击事件的this指向的是点击的对象,既然 callback 是在绑定的函数中定义的,那this应该也是指向#div1
对象?
这样理解是错的,因为this的指向与函数声明环境无关,只有函数执行环境有关,问题又来了,callback是在绑定函数中执行的,按理说 此时的 this 应该指向 #div1
对象?但实际上指向的是window,为什么?
好吧,我暂时也没有想清楚,不过怎么修改,到时会的,用变量把this保存下来。
<html>
<body>
<div id="div1">我是一个div</div>
</body>
<script>
window.id = 'window';
document.getElementById( 'div1' ).onclick = function(){
var that = this; // (1) 保存div 的引用
var callback = function(){
alert ( that.id ); // (2) 输出:'div1'
}
callback();// (3)
};
</script>
</html>
改变(1)(2)(3) 处代码的顺序,可以得到不同的结果,这个与预解析有关。
- 构造函数的调用
this 指向的就是实例对象,但是需要理解new操作的实际过程,简单理解分为以下4步:
1.创建一个对象obj ; 2. obj.proto 指向函数的原型对象;3. 指向构造函数,并将this指向obj ; 4.判读执行返回的结果,如果是个对象,就返回这个对象,不是的话,返回obj对象
看下面这个例子,显示地返回对象:
var MyClass = function(){
this.name = 'bby';
return { // 显式地返回一个对象
name: '365'
}
};
var obj = new MyClass();
alert ( obj.name ); // 输出:365
- Function.prototype.call 或Function.prototype.apply 调用
可以动态改变传入函数的this
- 丢失 this
写代码过程中,经常遇到this指向不明确的,原因是一开始我们就忽略了this的指向。
比如:封装document.getElementById()
,一般都会习惯这样写:
var getId = function( id ){
return document.getElementById( id );
};
getId( 'div1' );
有没有想过,下面这样的写法:
var getId = document.getElementById;
getId( 'div1' );
这种写法会报错,因为getId 和document.getElementById 指向同一个引用,document.getElementById方法内部现实时需要用到this,且this要指向document。但是直接用上面的方法,执行getId() , this 指向的是window 。
所以可以这样修改:
var getId = document.getElementById;
getId.apply(document, ['div1'] );
call 和 apply
call 和 apply 比较常用,基本区别就是传参的形式不同。
- call 和 apply 的用途
有三种情况:一是改变this指向;二是封装Function.prototype.bind();三是借用其他对象的方法。
- 改变 this 指向
场景一:div 上绑定onclick 事件,this 指向的是div。如果onclick中定义了函数func,func调用时,func 内部 this指向window;这时可以修改this指向。
document.getElementById('div1').onclick = function(){
console.log(this.id); // div1
var func = function(){
console.log(this.id) // undefined
}
func()
}
// 改变 this 指向
document.getElementById('div1').onclick = function(){
console.log(this.id); // div1
var func = function(){
console.log(this.id) // undefined
}
func.call(this)
}
2.Function.prototype.bind()
首先将 this 保存,返回一个新函数fn ,fn执行时,实际上执行的是原函数。
Function.prototype.bind = function(){
var self = this, // 保存原函数
context = [].shift.call( arguments ), // 需要绑定的this 上下文
args = [].slice.call( arguments ); // 剩余的参数转成数组
return function(){ // 返回一个新的函数
return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) );
// 执行新的函数的时候,会把之前传入的context 当作新函数体内的this
// 并且组合两次分别传入的参数,作为新函数的参数
}
};
var obj = {
name: 'sven'
};
var func = function( a, b, c, d ){
alert ( this.name ); // 输出:sven
alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 );
func( 3, 4 );
- 借用其他对象的方法
场景一:借用构造函数,用在构造函数继承
var A = function( name ){
this.name = name;
};
var B = function(){
A.apply( this, arguments );
};
B.prototype.getName = function(){
return this.name;
};
var b = new B( 'sven' );
console.log( b.getName() ); // 输出: 'sven'
场景二:arguments 类数组 使用数组的方法
[].shift.call(arguments);
[].push.call(arguments,1);
Array.prototype.slice(arguments)