this的指向:在函数运行时,当前的运行环境属于谁,this就指向谁
独立使用
在命名完一个函数后直接使用。其实是省略了window。
console.log(this == window) //true
function test(){
console.log(this == window)//函数中指向window
}
test() // true
window.test() // true
函数在命名完成后直接使用,也是独立使用。
(function(){
console.log(this)
})() //window
隐式绑定(谁调用指向谁)
这里有一个非常实用的小技巧,就是看调用的方法前面有没有出现obj.如果有证明这个调用属于某个对象,而没有则属于window。
- 对于第一个this,是在obj调用foo函数时产生的,因此指向obj
- 对于第二个this,虽然也是在obj调用foo函数时产生的,但是执行到test函数的时候,可以理解为是一个独立调用的函数(可能有点不好理解,可以简单地理解说不是obj直接调用地,而是过了一手的感觉。)
//例子1
var obj = {
a : 2,
foo: function(){
console.log(this); //obj
function test(){
console.log(this) // window
}
test()
}
}
obj.foo()
obj.foo()其实返回值就是一个test函数,所以可以obj.foo()()=test(),所以是独立使用
//例子2
var obj = {
a : 2,
foo: function(){
function test(){
console.log(this)
}
return test
}
}
obj.foo()() //window
第一个是对象直接调用函数
第二个过了一手,obj.foo已经直接指向了这个函数,不能算对象的直接调用,因此是window
//例子3
var obj = {
a : 2,
foo: function(){
console.log(this)
}
}
obj.foo() // obj
var test = obj.foo
test() // window
obj.foo就是指向了test这个函数,但是在bar()执行的时候,它依然是属于window的,然后它的参数是来自与对象的一个属性所指向的函数。这是一个干扰项,要把握住执行的那一个时刻this指向谁。
//例子4
function bar(fn){
fn()
}
var obj = {
foo : test,
}
function test(){
console.log(this)
}
bar(obj.foo) //window
显式绑定(call apply blind)
call apply blind的用法:就是为了改变this指向而存在的
new绑定
实例化对象
this就指向这个新的实例化后的对象。
function Person(){
this.name = 'Frank';
this.age = 18;
return 1;
}
var oPerson = new Person()
console.log(oPerson.name+'\'s age is', oPerson.age) // Frank's age is 18
console.log(oPerson) // 是一个对象而不是1
function Person(){
this.name = 'Frank';
this.age = 18;
return {};
}
var oPerson = new Person()
console.log(oPerson) // {} 但是如果返回值是一个引用,this指向就不会指向这个对象.多以如果要实例化对象,一般不写返回值
console.log(oPerson.name) // undefined
四种this的作用方式中,优先级为:
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
//这个例子是关于new绑定和显式绑定的比较
function foo(b){
this.a = b;
}
var obj1 = {}
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a)
/*
这里通过new对构造函数进行实例化的时候:
因为bar是foo通过foo的显式绑定构建的。如果new的优先级比显式绑定低,则此时this还是
指向obj1,则obj1.a = 3。这与输出的结果矛盾,因此new的优先级比显式绑定高。
*/
var baz = new bar(3);
console.log(obj1.a);
console.log(baz.a)
箭头函数中的this
结论:箭头函数中没有this指向,它的this同其父级作用域的this指向。
由于子函数的this可能与父函数的this指向不相同,下面是两个常用的解决办法。
//方法一
var object = {
outer : function(){
var that = this;
function inner(){
console.log(that) //定义了一个that用来记录父函数的this
}
inner()
}
}
object.outer()
//方法二
var object = {
outer : function(){
function inner(){
console.log(this)
}
inner.apply(this) //对inner函数进行显式绑定到夫函数上。
}
}
object.outer()
this指向父作用域
//例子一
var object = {
outer : () => {
console.log(this) //输出结果是window而不是object。在这里outer函数的父级作用域就是window
}
}
object.outer()
//例子二
var object = {
outer : function(){
console.log(this)
var inner = () =>{
console.log(this) //指向object对象。因为inner的父级作用域中的this指向了object.
}
inner()
}
}
object.outer()
这一部分的代码不是很重要,主要是要理解:4中更改this指向的绑定规则,对箭头函数都无效。箭头函数的this指向,只与父级作用域的this指向有关。
function foo(){
console.log(this);
var test = () =>{
console.log(this)
}
return test
}
var obj1 = {
a : 1,
foo : foo,
}
var obj2 = {
a : 2,
foo : foo,
}
//如果test不是箭头函数,那么这个调用应该是独立调用,应该指向window,但是结果是obj1,
//这表示:独立调用对箭头函数无效
obj1.foo()()
//foo中的this指向obj2,但是由于是独立调用,因此指向window
//f00()执行返回了test,相同于test.apple(obj2),如果显式绑定生效,则this指向obj2,但是结果是window,证明显式绑定没用。
foo().apply(obj2)
// 箭头函数是不能当作构造函数使用的,即不能用new来创建对象。语法上是直接报错的。
一个练习小例子
var name = "window";
var obj1 = {
name : '1',
fn1 : function(){
console.log(this.name)
},
fn2 : () => console.log(this.name),
fn3 : function(){
return function(){
console.log(this.name)
}
},
fn4 : function(){
return () => console.log(this.name)
}
}
var obj2 = {
name: '2'
}
obj1.fn1() //1
obj1.fn1.call(obj2)//2
obj1.fn2() // window
obj1.fn2.call(obj2)// window
obj1.fn3()() //window
obj1.fn3().call(obj2) //2
obj1.fn3.call(obj2)()// window
obj1.fn4()() //1
obj1.fn4().call(obj2) // 1
obj1.fn4.call(obj2)() //2