this是指向当前的函数吗? 不是
- demo
我们试图在函数foo中调用自己count变量,但是事实上,this.count并不是foo的count,执行foo函数时,this的指向是window,所以this.count会在全局中查找,但是没有找到,会在全局变量中创建一个count变量,count为undefined,当对count变量进行++时出错,全局count的值为NaN
function foo(num) {
this.count++; // 调用时候的this 指向windows 创建全局变量 count
}
foo.count = 0;
foo()
console.log(foo.count)
this是什么
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有关系,只取决于函数的调用方式。
找到函数的调用位置
浏览器的Call Stack的第二个元素,就是函数的调用位置。
如何找到this
默认绑定
- demo
foo是直接调用的,this指向window,foo运行在严格模式下,this为undefined。严格模式下调用foo,不影响默认绑定。
function foo() {
// 'use strict'; // 严格模式下this为undefinedUncaught TypeError: Cannot read property 'a' of undefined
console.log('默认绑定', this.a);
}
var a = 2;
foo(); // 2
(() => {
'use strict'; // 在严格模式下调用foo ,this还是window
foo(); // 2
})();
隐式绑定
当函数引用有上下文对象时,this绑定到这个上下文对象
- demo1
调用foo时,this被绑定到obj,所以this.a相当于obj.a
function foo() {
console.log('隐式绑定', this.a);
}
var obj = {
a: 2,
foo
};
obj.foo(); // 隐式绑定 2
- demo2
对象的属性调用中,只有最后一层影响this
隐式丢失,bar是obj1.obj2.foo的一个引用,bar()是直接调用,默认绑定
function foo() {
console.log('隐式绑定', this.a);
}
var obj2 = {
a: 3,
foo
};
var obj1 = {
a: 2,
obj2
};
var a = 'global'
obj1.obj2.foo(); // 隐式绑定 3
var bar = obj1.obj2.foo;
bar(); // 隐式丢失 global
显式绑定
应用call(obj,arg),apply(obj,arguments),bind(obj)来指定this绑定。
- demo1
通过call,apply,bind指定this绑定
硬绑定,函数bar和baz每次执行的时候都会绑定obj,所以叫硬绑定,硬绑定的this指向不可以修改
例外,当null/undefined 作为this绑定对象传入call,apply,bind时,应用默认绑定规则,指向window
function foo(arg) {
console.log(arg, this.a);
}
var obj = { a: 2 };
var a = 3
foo.call(obj, 'call'); // call 2
foo.apply(obj, ['apply']); // apply 2
foo.bind(obj)('bind'); // bind 2
var bar = foo.bind(obj);
bar('bind'); // bind 2
bar.call(window, 'bind window'); // bind window 2 bind绑定后,不可以在修改它的this 硬绑定
// this 绑定为null 或 undefined时,指向window
foo.call(null,'window'); // window 3
foo.call(undefined,'window'); // window 3
// 可以模拟bind
function baz() {
foo.call(obj, '硬绑定');
}
baz(); // 硬绑定 2
baz.call(window); // 硬绑定 2 硬绑定 不可以修改this的指向
new绑定
JavaScript 的构造函数只是使用new操作符调用的函数,不属于某个类,也不会实例化一个类。任何函数都可以用new来调用,这种函数调用被称为构造函数调用。
new 调用函数过程
- 创建(或者说构造)一个全新的对象
- 这个新对象会被执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
- demo
new 调用foo时,我们会构造一个新对象并把它绑定到foo调用中this上。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log('bar.a', bar.a); // bar.a 2
this绑定优先级
new>显式绑定(call,apply,bind)>隐式绑定>默认绑定
- demo1 new绑定>隐式绑定
function foo(a) {
this.a = a;
}
var obj1 = { foo };
var obj2 = {};
obj1.foo(2); // 隐式绑定
console.log('隐式绑定', obj1.a); // 隐式绑定 2
obj1.foo.call(obj2, 3); // 显式绑定
console.log('显式绑定', obj2.a); // 显式绑定 3
var bar = new obj1.foo(4); // new 绑定
console.log(obj1.a); // 2
console.log('new绑定', bar.a); // new 绑定4
- demo2 new绑定>显式绑定
function foo(a) {
this.a = a;
}
var obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log('显式绑定', obj1.a); // 显式绑定 2
var baz = new bar(3);
console.log(obj1.a);
console.log('new 绑定', baz.a); // new 绑定 3
- demo3 bind可以将除了第一参数外的其他参数都传给下层函数(“部分应用”)
function foo(p1, p2) {
this.val = p1 + p2;
}
var bar = foo.bind(null, 'p1');
var baz = new bar('p2');
console.log(baz.val); // p1p2
软绑定
默认绑定一个全局对象,实现硬绑定的效果,同时保留隐式绑定或者显式绑定来修改this
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this;
curried = [].slice.call(arguments, 1);
var bound = function() {
return fn.apply(!this || this === (window || global) ? obj : this, [
...curried,
...arguments
]);
};
bound.prototype = Object.create(fn.prototype);
return bound;
};
}
function foo() {
console.log(`name:${this.name}`);
}
var obj = { name: 'obj' },
obj2 = { name: 'obj2' },
obj3 = { name: 'obj3' },
fooOBJ = foo.softBind(obj);
fooOBJ(); // name:obj
obj2.foo = foo.softBind(obj);
obj2.foo(); // name obj2
fooOBJ.call(obj3); // name obj3
箭头函数
箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
箭头函数的绑定无法被修改
- demo1
foo()调用时this被绑定到obj1,不会再被修改,所以输出2。
function foo() {
return a => {
console.log('箭头函数', this.a);
};
}
var obj1 = { a: 2 },
obj2 = { a: 3 },
bar = foo.call(obj1);
bar.call(obj2); // 箭头函数 2
- demo2
上面的箭头函数类似于我们经常写的这种方式
function foo() {
var self = this;
return function(a) {
console.log('模拟箭头函数', self.a);
};
}
var obj1 = { a: 2 },
obj2 = { a: 3 },
bar = foo.call(obj1);
bar.call(obj2); // 模拟箭头函数 2