函数的this指向
js中,函数的this指向是在调用函数的时候决定的
函数的调用大致分为四种情况:
1. 作为函数调用
// 例子1
var a = 100;
function foo() {
var a = 10;
console.log(this.a);
}
foo(); // 在非严格模式下输出:100
foo(); // 在严格模式下输出:Uncaught TypeError: Cannot read property 'a' of undefined
- 非严格模式:浏览器环境的全局上下文对象是
window,所以这里的this指向的是window对象,this.a是挂载在window上的a属性 - 严格模式:浏览器环境的全局上下文是
undefined,所以执行foo()会报错
ES6语法中用let声明的全局变量不会挂载到window上
2.作为对象方法调用
// 例子2
var name = 'xxx';
var obj = {
name: 'objName',
sayName: function() {
console.log(this.name);
}
}
obj.sayName(); // 'objName',此时调用sayName的是obj,所以this指向的也是obj
var say = obj.sayName;
say(); // 'xxx',将obj.sayName赋值给say,执行say(),此时调用say的是全局对象window,因此结果为 'xxx'
接着看下面这个例子
// 例子3
var name = 'xxx';
function foo() {
var name = 'foo';
innerFoo();
function innerFoo() {
console.log(this.name);
}
}
foo(); // 'xxx'
上面代码中,window对象调用了foo函数,在foo函数中又执行了innerFoo函数,本质上仍然是在全局上下文中调用了innerFoo函数,所以this指向仍然是window
3.作为构造函数调用
如果当前this对象本身没有对应的属性,则会从它的原型链上查找
function Foo() {
this.sayName = function () {
console.log(this.name);
}
}
Foo.prototype.name = 1000;
var foo = new Foo();
foo.sayName(); // 1000,由于foo的原型链指向Foo的原型,所以打印 1000
使用new操作符的时候,js内部做了以下几件事情:
1.创建一个全新的对象,称为实例
2.将实例的原型链__proto__链接到构造函数的prototype上
3.将实例内部的this指向到实例本身
4.如果没有定义构造函数的返回一个对象,则返回创建好的实例
4.箭头函数调用
// 例子4
var name = 'xxx';
let obj = {
name: 'obj',
sayName: () => {
console.log(this.name);
}
}
obj.sayName(); // 'xxx'
箭头函数与ES5的函数有以下几点区别:
1.箭头函数没有自己的this、super、arguments和new.target绑定
2.不能使用new来生成实例,即不是构造函数没有constructor
3.没有原型对象
4.无法改变内部的this指向
5.行参名称不能重复
6.箭头函数内部的this指向缓存的是上层的普通函数的this指向,如果没有上层函数,则this指向的是window对象
修改函数内部的this指向
可以通过call,apply,bind来指定普通函数内部的this指向
用法
var name = 'xxx';
var obj = {
name: 'obj-name',
sayName: function() {
console.log(this.name);
}
}
var say = obj.sayName;
say(); // 'xxx'
say.call(obj); // 'obj-name'
say.apply(obj); // 'obj-name'
say.bind(obj)(); // 'obj-name'
call, apply, bind的差异
相同点:call,apply,bind的第一个参数都是需要绑定的this指向
不同点:
1.bind改变this指向后不会指向函数,apply,call改变this指向后会立即执行函数
2.apply的其余参数是以数组传入,call,bind的其余参数是一个一个传入的