一、为什么要用this
能够隐式的传递对象引用,更加简洁。
二、this指向规则和词法作用域
词法作用域:作用域由函数书写代码时决定(定义时上下文)
this指向规则:不指向函数词法作用域,不指向函数本身,由函数调用时的位置决定(运行时上下文,类似于动态作用域)
三、this指向规则
this的指向规则如下四条,优先级为:new +构造函数 > 显式绑定 > 隐式绑定 > 默认
1.默认(独立调用)
独立函数调用情况下,this指向全局变量或undefined。
(1)函数在非严格模式下,this指向全局变量
(2)函数在严格模式下(use strict),this 指向undefined
2.隐式绑定
(1)调用位置有上下文对象的,this绑定在上下文对象上
(2)调用位置有多个链式上下文对象的,this绑定在就近的对象上
注意:隐式绑定可能会出现this的隐式丢失,原因如下:
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
(上例和图片来自http://www.ruanyifeng.com/blog/2018/06/javascript-this.html)
由以上例子能够看出,当定义一个对象,并在对象中定义函数的过程是先开辟一个内存存储函数,然后将地址传递给对象的foo。当调用foo()时,是直接调用全局中的foo函数;当调用obj.foo()时是访问obj.foo存储的地址,此时this指向obj。
若再次定义var a = obj.foo;即将地址传给了全局的a,此时调用a()相当于直接调用全局中的函数,此时this指向全局变量(非严格模式下)造成this的隐式丢失。
在回调函数和参数传递(隐式赋值)时,容易发生this的隐式丢失。
隐式丢失的解决办法:
(1)var self = this
(2)硬绑定(bind)
(3)箭头函数(ES6)
3.显式绑定
显式的绑定this指向可以采用call()、apply()和硬绑定bind()
call()和apply()的作用完全一样,只是参数传递方式不一样
(1)函数.call ( this新指向,数据1,... ,数据n)
将函数中的this指向新指向,并向该函数传递数据1-n
(2)函数.apply(this新指向,数组或类数组) 其中数组或类数组为:[数据1,... ,数据n]
将函数中的this指向新指向,并向该函数传递数组 [数据1,...,数据n]
call()和apply() 的用法:
- 劫持别人的方法
var foo = {
name:"mingming",
logName:function(){
console.log(this.name);
}
}
var bar={
name:"xiaowang"
};
foo.logName.call(bar);//xiaowang
- 实现继承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat("Black Cat");
cat.showName(); //Black Cat
- 强制改变this指向
window.id="window";
document.querySelector('#test').onclick = function(){
console.log(this.id);//test
var fun = function(){
console.log(this.id);
}
fun();//window
fun.call(this);//test
}
(3)硬绑定bind(未完)
bind()是ES5新增(IE6,7,8不支持)
var 新函数 = 旧函数.bind(this新指向,数据1,... ,数据n)
bind() 作用和前两个相同,即改变this指向
call()和apply()在改变指向以后立即执行改变后的函数,而bind()却返回一个改变后的新函数。
注意:call(),apply()和bind() 在非严格模式下,第一个参数为null或者undefined时this会自动替换为指向全局对象
4.new+构造函数
在传统面向对象语言中,构造函数是类的特殊函数
在JavaScript中,构造函数只是能够使用new操作符调用的普通函数,他不属于某个类,也不能实例一个类
当使用new 来调用构造函数时,自动执行一下操作:
(1)创建一个新的对象
(2)对新对象执行 [[原型]] 连接
(3)把新对象绑定到函数的this
(4)若函数没有返回其他对象,则函数自动返回这个新对象
四、ES6改进(箭头函数)
箭头函数的this不按照以上四条规则,而是按照词法作用域由外部作用域决定this指向
箭头函数能够通过call、apply、bind 改变this指向,但只能改变一次
参考资料:
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind