搞懂javascript中的this

181 阅读12分钟

记录下来自己学习过程

this

this的几种规则

显式绑定 > 隐式绑定 > 默认绑定

new绑定 > 隐式绑定 > 默认绑定 为什么显式绑定不和new绑定比较呢?因为不存在这种绑定同时生效的情景,如果同时写这两种代码会直接抛错,可以看下面例子测试

function Fn(){
    this.name = 'ld';
};
let obj = {
    name:'lds'
}
let fn = new Fn().call(obj);//报错 call is not a function

默认绑定

一般都是window全局调用

function fn (){ 
    console.log(this) //this -> window 
} 
fn() // fn()独立调用函数,this指向window

***** 面试题

"use strict";
var a = 10; // var定义的a变量挂载到window对象上
function foo () {
  console.log('this1', this)  // undefined
  console.log(window.a)  // 10
  console.log(this.a)  //  报错,Uncaught TypeError: Cannot read properties of undefined (reading 'a')
}
console.log('this2', this)  // window
foo();
let a = 10
const b = 20
function foo () {
  console.log(this.a)  // undefined
  console.log(this.b)  // undefined
}
foo();
console.log(window.a) // undefined
var a = 1
function foo () {
  var a = 2
  console.log(this)  // window
  console.log(this.a) // 1
}
foo()
var a = 10;
function foo () {
  console.log(this.a)  //10
} 
foo();
tion foo() {
  console.log(this.a)
}
window.foo();
"use strict";
var a = 10;
function foo () {
  console.log('this1', this) //undfined
  console.log(window.a) //10
  console.log(this.a) //报错 Uncaught TypeError: Cannot read property 'a' of undefined
}
console.log(window.foo) //function foo
console.log('this2', this) //window
foo();
let a = 10
const b = 20

function foo () {
  console.log(this.a) //undfined
  console.log(this.b) //undfined
}
foo();
console.log(window.a) //undfined
var a = 1
function foo () {
  var a = 2
  console.log(this) //window
  console.log(this.a) //1
}

foo()
var a = 1
function foo () {
  var a = 2
  function inner () { 
    console.log(this.a) //1
  }
  inner() //注意this确定是前面调用的,所以这里this指的是window
}

foo()

隐式绑定

谁调用this指最后调用的对象,window.obj.fn this=>obj,

如果你把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined

var obj = { 
    test:1, 
    fn:function(){ 
        console.log(this) //this -> obj 
    } 
} 
obj.fn() // obj.fn() //指向 obj

*****面试题

function foo () {
  console.log(this.a)
}
var obj = { a: 1, foo }
var a = 2
obj.foo()

相当于
var obj = {
  a: 1,
  foo: function () {
    console.log(this.a)
  }
}
var a = 2
obj.foo()

function foo () {
  console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;

obj.foo();
foo2(); 

分析过程:
var foo obj a foo2

a=2 obj={
    a:1,
    foo:function(){
       console.log(this.a)
    }
}  
foo2=obj.foo
foo2=function(){
    console.log(this.a)
}
obj.foo() 进入函数:
console.log(this.a) //1 //this=>obj
函数结束
foo2() 进入函数
console.log(this.a) //2 this=>window

function foo () {
  console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a: 3, foo2: obj.foo }

obj.foo();
foo2();
obj2.foo2();

分析过程:
a=undefined
var foo=function(){console.log(this.a)} 
obj={ 
    a: 1, 
    foo:function(){
        console.log(this.a)
    } 
}
a=2
foo2=() {
  console.log(this.a)
}
obj2={
    a: 3, 
    foo2: function(){
       console.log(this.a) 
    }
}


执行过程:
obj.foo()   //1
foo2()      //2
obj2.foo2() //3
function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)

分析过程:
obj={
    a:1,
    foo:function(){
        console.log(this.a)
    }
}
var a=undefined 
foo(){
    console.log(this.a)
}
doFoo(fn){
    console.log(this)
    fn()
}
a=2

foFoo(obj.foo) 进入foFoo函数,把obj.foo当参数传入进去
doFoo(fn){
    console.log(this)//window
    fn() //obj.foo执行进入obj.foo函数
    foo:function(){
        console.log(this.a) //2 这里obj.foo 传进来好像this会改变 
    }
}
function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }

obj2.doFoo(obj.foo)

分析过程:
var a=undefined
obj={
    a:1,
    foo:function(){
        console.log(this.a)
    }
}
obj2 = { 
    a: 3, 
    doFoo: function (fn) {
      console.log(this)  //obj2
      fn() //obj.foo this.a是window.a //2
    } 
}

foo(){
  console.log(this.a)
    
}
doFoo (fn) {
  console.log(this)
  fn()
}
a=2

执行过程:
obj2.doFoo(obj.foo)

或一样的代码严格模式
"use strict"
function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }

obj2.doFoo(obj.foo) 
{ a:3, doFoo: f } 
Uncaught TypeError: Cannot read property 'a' of undefined


**所以说,如果你把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined。**

显示绑定

通过call()、apply()或者bind()方法直接指定this的绑定对象, 如foo.call(obj)。 apply(),call(),bind() apply,call,bind当传入null,undefind时,this指向window

这里有几个知识点需要注意:

  • 使用.call()或者.apply()的函数是会直接执行的
  • bind()是创建一个新的函数,需要手动调用才会执行
  • .call().apply()用法基本类似,不过call接收若干个参数,而apply接收的是一个数组

*****面试题

function foo () {
  console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo.apply(obj)
foo.bind(obj)

分析过程:
var a=undefined 
obj={a:1}
a=2
foo(){
   console.log(this.a) 
}

执行过程:
foo() //2
foo.call(obj) //this=>obj 1
foo.apply(obj) //1
foo.bind(obj) //不执行,仅仅是使用`bind`创建了一个新的函数,且这个新函数也没用别的变量接收并调用,因此并不会执行。

let obj1 = {
    name: '我'
};
let obj2 = {
    name: '你'
};
let obj3 = {
    name: '123'
}
var name = '他';
​
function fn() {
    console.log(this.name);
};
fn(); //
fn.call(obj1); //
fn.apply(obj2); //
fn.bind(obj3)(); //echo

分析过程:
var name=undefined

执行赋值过程:
fn() //他
fn.call(obj1)// 我
obj1{
    name:'我',
    fn:function(){
       console.log(this.name); 
    }
}
fn.apply(obj2) //你

fn.bind(obj3)() //123
var obj1 = {
  a: 1
}
var obj2 = {
  a: 2,
  foo1: function () {
    console.log(this.a)
  },
  foo2: function () {
    setTimeout(function () {
      console.log(this)
      console.log(this.a)
    }, 0)
  }
}
var a = 3

obj2.foo1()
obj2.foo2()

分析过程:
var a=undefined
var obj1 obj2

执行赋值过程:
a=3
obj2.foo1() //this => obj2
console.log(this.a) //2
obj2.foo2() //本来是this=>obj2,但是注意setTimeout函数会丢失this这里this真正指向window在非严格模式下 
console.log(this)//window
console.log(this.a)//3
var obj1 = {
  a: 1
}
var obj2 = {
  a: 2,
  foo1: function () {
    console.log(this.a)
  },
  foo2: function () {
    setTimeout(function () {
      console.log(this)
      console.log(this.a)
    }.call(obj1), 0)
  }
}
var a = 3
obj2.foo1()
obj2.foo2()

分析过程:
var a=undefined obj1 obj2

执行赋值:
obj1={
    a:1
}
obj2={
   a: 2,
  foo1: function () {
    console.log(this.a)
  },
  foo2: function () {
    setTimeout(function () {
      console.log(this)
      console.log(this.a)
    }.call(obj1), 0)
  } 
}
a=3
obj2.foo1() //this=>obj2 2
obj2.foo2() //setTimeout里面把this指向了obj1,
{
    a:1
},1

注意 obj2.foo2.call(obj1) 如果是这种写法的话,我改变的就是`foo2`函数内的`this`的指向了,但是我们知道,`foo2`函数内`this`的指向和`setTimeout`里函数的`this`是没有关系的,因为调用定时器的始终是`window`。

并且这里使用`.bind()`也是可以的,因为定时器里的函数在时间到了之后本就是会自动执行的。

var obj1 = {
  a: 1
}
var obj2 = {
  a: 2,
  foo1: function () {
    console.log(this.a) //2
  },
  foo2: function () {
    function inner () {
      console.log(this) //window
      console.log(this.a) //3
    }
    inner()
  }
}
var a = 3
obj2.foo1()
obj2.foo2()
function foo () {
  console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
  • foo()会正常打印出window下的a,也就是2
  • foo.call(obj)由于显式绑定了this,所以会打印出obj下的a,也就是1
  • foo().call(obj)开始会执行foo()函数,打印出2,但是会对foo()函数的返回值执行.call(obj)操作,可是我们可以看到foo()函数的返回值是undefined,因此就会报错了。

所以我们可以看到foo.call()foo().call()的区别了,一个是针对于函数,一个是针对于函数的返回值。

function foo () {
  console.log(this.a) 
  return function () {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
结果:2 1 2 1

分析:
foo()执行this就是指向window 全局a 2
foo.call(obj) 这个this直接指向obj对象了,里面a是1
foo().call(obj) 这个是重点先执行一次foo()里面打印2,注意哈有个返回函数接着把函数this指向obj也就是1

function foo () {
  console.log(this.a)
  return function () {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo()
foo.bind(obj)
foo().bind(obj)

结果:2  2
分析:
foo()会执行没错,打印出了2。
foo.bind(obj)却不会执行,它返回的是一个新函数。
foo().bind(obj)只会执行前面的foo()函数,打印出2
.bind(obj)只是将foo()返回的匿名函数显式绑定this而已,并没有调用

function foo () {
  console.log(this.a)
  return function () {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo.call(obj)()
结果:1 2
foo.call(obj) //this是obj
function(){}() //前面foo.call(obj)返回的是一个函数this指向window
var obj = {
  a: 'obj',
  foo: function () {
    console.log('foo:', this.a)
    return function () {
      console.log('inner:', this.a)
    }
  }
}
var a = 'window'
var obj2 = { a: 'obj2' }

obj.foo()()
obj.foo.call(obj2)()
obj.foo().call(obj2)
结果:
foo: obj
VM1307:6 inner: window
VM1307:4 foo: obj2
VM1307:6 inner: window
VM1307:4 foo: obj
VM1307:6 inner: obj2

分析:
obj.foo()() 执行obj.foo() this是obj 返回函数再次执行this就变成了window
obj.foo.call(obj2)() 
obj.foo 等于function(){}.call this是foo.call(obj2) this指向obj2 返回函数执行this=>window

obj.foo().call(obj2)
obj.foo() =>this是obj 返回函数.call(obj2) this指向obj2

加入一些参数来看看吧

var obj = {
  a: 1,
  foo: function (b) {
    b = b || this.a 
    return function (c) {
      console.log(this.a + b + c)
    }
  }
}
var a = 2
var obj2 = { a: 3 }

obj.foo(a).call(obj2, 1)
obj.foo.call(obj2)(1)
结果:6 6
分析:
开始调用`obj.foo(a)``2`传入`foo`函数并赋值给型参`b`,
并且由于闭包的原因,使得匿名函数内能访问到`b`,
之后调用匿名函数的时候,用`call()`改变了`this`的指向,
使得匿名函数内`this.a``3`,并传入最后一个参数`1`,
所以第一行输出的应该是`3 + 2 + 1`,也就是`6`。

而第二行,`obj.foo.call(obj2)`这里是将`foo`函数内的`this`指向了`obj2`,
同时并没有传递任何参数,所以`b`开始是`undefined`的,
但是又因为有一句`b = b || this.a`,使得`b`变为了`3`;
同时最后一段代码`(1)`,是在调用匿名函数,且和这个匿名函数内的`this`
应该是指向`window`的,因此输出也为`3+2+1`,为`6`

function foo1 () {
  console.log(this.a)
}
var a = 1
var obj = {
  a: 2
}

var foo2 = function () {
  foo1.call(obj) //重点是这里固定了this的指向了
}

foo2()
foo2.call(window)

结果:2 2

function foo1 (b) {
  console.log(`${this.a} + ${b}`)
  return this.a + b
}
var a = 1
var obj = {
  a: 2
}

var foo2 = function () {
  return foo1.call(obj, ...arguments)
}

var num = foo2(3)
console.log(num)
结果: ‘2’+‘35
分析:
var num=foo2(3)直接转成 var num=foo1.call(obj,3) this指向obj
foo1 (b) { //b=>3
  console.log(`${this.a} + ${b}`) this=>obj
  return this.a + b
}
  • forEach、map、filter函数的第二个参数也是能显式绑定this
function foo (item) {
  console.log(item, this.a)
}
var obj = {
  a: 'obj'
}
var a = 'window'
var arr = [1, 2, 3]

// arr.forEach(foo, obj)
// arr.map(foo, obj)
arr.filter(function (i) {
  console.log(i, this.a)
  return i > 2
}, obj)
结果:
1 'obj'
2 'obj'
3 'obj'

new()绑定

function Person(){
    console.log(this) //代表的就是new出来的实例
}
let person=new Person
function Person (name) {
  this.name = name
}
var name = 'window'
var person1 = new Person('ld')
console.log(person1.name) //ld
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  }
  this.foo2 = function () {
    return function () {
      console.log(this.name)
    }
  }
}
var name='ld'
var person1 = new Person('person1')
person1.foo1()
person1.foo2()()

结果:person1   ld
var name = 'window'
function Person (name) {
  this.name = name
  this.foo = function () {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var person2 = {
  name: 'person2',
  foo: function() {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
  
var person1 = new Person('person1')
person1.foo()()
person2.foo()()


VM1915:5 person1
VM1915:7 window
VM1915:14 person2
VM1915:16 window
var name = 'window'
function Person (name) {
  this.name = name
  this.foo = function () {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo.call(person2)()
person1.foo().call(person2)

VM2231:5 person2
VM2231:7 window
VM2231:5 person1
VM2231:7 person2

箭头函数

箭头函数的this是由外层作用域来决定的,且指向函数定义时的this而非执行时** 。

var obj = {
 name: 'obj',
 foo1: () => {
   console.log(this.name) // window
 },
 foo2: function () {
   console.log(this.name) // obj
   return () => {
     console.log(this.name) // obj
   }
 }
}
var name = 'window'
obj.foo1()
obj.foo2()()
var obj = {
  name: 'obj',
  foo1: () => { //注意这个是箭头函数作用域名由外层决定就是obj的作用域名是window
    console.log(this.name)
  },
  foo2: function () {
    console.log(this.name) //这里的不是箭头函数this就是obj
    return () => { 
      console.log(this.name) //这里的就是箭头函数作用域由外层foo2来决定,foo作用域this是obj
    }
  }
}
var name = 'window'
obj.foo1()
obj.foo2()()


VM2314:4 window
VM2314:7 obj
VM2314:9 obj
var name = 'window'
var obj1 = {
	name: 'obj1',
	foo: function () {
		console.log(this.name)
	}
}

var obj2 = {
	name: 'obj2',
	foo: () => {
		console.log(this.name)
	}
}

obj1.foo()
obj2.foo()


VM2391:5 obj1
VM2391:12 window
var name = 'window'
var obj1 = {
  name: 'obj1',
  foo: function () {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: 'obj2',
  foo: function () {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  }
}
var obj3 = {
  name: 'obj3',
  foo: () => {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var obj4 = {
  name: 'obj4',
  foo: () => {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  }
}

obj1.foo()()
obj2.foo()()
obj3.foo()()
obj4.foo()()

VM2754:5 obj1
VM2754:7 window
VM2754:14 obj2
VM2754:16 obj2
VM2754:23 window
VM2754:25 window
VM2754:32 window
VM2754:34 window
var name = 'window'
var obj1 = {
  name: 'obj1',
  foo1: function () {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  },
  foo2: () => {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: 'obj2'
}
obj1.foo1.call(obj2)()
obj1.foo1().call(obj2)
obj1.foo2.call(obj2)()
obj1.foo2().call(obj2)

结果:

obj2
obj2 //这里做的时候错了,因为大意了,foo1返回的函数this指向的是obj2
obj1
obj1
window
window
window
obj2
//注意箭头函数在定义的时候就固定了this指向上一级的

箭头函数需要注意:

  • 它里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时
  • 字面量创建的对象,作用域是window,如果里面有箭头函数属性的话,this指向的是window
  • 构造函数创建的对象,作用域是可以理解为是这个构造函数,且这个构造函数的this是指向新建的对象的,因此this指向这个对象。
  • 箭头函数的this是无法通过bind、call、apply直接修改,但是可以通过改变作用域中this的指向来间接修改。

应该避免在以下四种场景中使用箭头函数

  1. 使用箭头函数定义对象的方法
let obj = {
    value: 'ld',
    getValue: () => console.log(this.value)
}
obj.getValue() // undefined
复制代码
  1. 定义原型方法
function Foo (value) {
    this.value = value
}
Foo.prototype.getValue = () => console.log(this.value)

const foo1 = new Foo(1)
foo1.getValue() // undefined
复制代码
  1. 构造函数使用箭头函数
const Foo = (value) => {
    this.value = value;
}
const foo1 = new Foo(1)
// 事实上直接就报错了 Uncaught TypeError: Foo is not a constructor
console.log(foo1);
复制代码
  1. 作为事件的回调函数
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
    console.log(this === window); // => true
    this.innerHTML = 'Clicked button';
});

特殊this

对于setTimeout中的函数,这里存在隐式绑定的this丢失,也就是当我们将函数作为参数传递时,会被隐式赋值,回调函数丢失this绑定,因此这时候setTimeout中函数内的this是指向window

var obj2 = {
    a: 2,
    foo1: function () {
      console.log(this.a) // 2
    },
    foo2: function () {
      setTimeout(function () {
        console.log(this) // window
        console.log(this.a) // 3
      }, 0)
    }
  }
  var a = 3
  
  obj2.foo1()
  obj2.foo2()

this的综合题

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

结果:
person1
person2

window
window

window
window
person2

person1
person2
person1

分析:

  • person1.foo1() this指向的是person1
  • person1.foo1.call(person2) foo1里的this指向person2
  • person1.foo2() 箭头函数this指向上一层person1的作用域window
  • person1.foo2.call(person2)箭头函数this指向上一层person1的作用域window
  • person1.foo3()()等同于function () { console.log(this.name) }()this指向window
  • person1.foo3.call(person2)() 等同于

function () { return function () { console.log(this.name) } }.call(person2)() =>

function () { console.log(this.name) }()

  • person1.foo3().call(person2) 执行过程 person1.foo3()=> function () { console.log(this.name) }.call(person2)

  • person1.foo4()() 箭头函数定义时this就确定了person1

  • person1.foo4.call(person2)() 执行过程 person1.foo4=》function () { return () => { console.log(this.name) } }.call(person2)() //把foo4整个函数的this指向person2

  • person1.foo4().call(person2) person1.foo4()=> () => { console.log(this.name) }.call(person2) 箭头函数在定义时this已经确定上一层的

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
      console.log(this,'=====')
  },
  this.foo2 = () => function(){
      console.log(this.name)
  },
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1() // 'person1'
person1.foo1.call(person2) // 'person2'

person1.foo2() // 'person1'
person1.foo2.call(person2) // 'person1'

person1.foo3()() // 'window'
person1.foo3.call(person2)() // 'window'
person1.foo3().call(person2) // 'person2'

person1.foo4()() // 'person1'
person1.foo4.call(person2)() // 'person2'
person1.foo4().call(person2) // 'person1'
var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

结果:
/*
person1.obj.foo1()() // window
person1.obj.foo1.call(person2)()  //window
person1.obj.foo1().call(person2)  //person2

person1.obj.foo2()() //obj
person1.obj.foo2.call(person2)()  //person2
person1.obj.foo2().call(person2) //obj
*/
分析:
-1`person1.obj.foo1()()` 
=》 person1.obj.foo1 =>function () { //this指向obj
      return function () {
        console.log(this.name)
      }
    }()() 执行=> function () {
        console.log(this.name)
      }() =>`this指向window`

-2`person1.obj.foo1.call(person2)()` 
=> function () {
      return function () {
        console.log(this.name)
      }
    }.call(person2) =>foo1 函数里this指向person2
=》function () {
        console.log(this.name)
      }()=>thiswindow
      
-3,`person1.obj.foo1().call(person2)` 
=> person1.obj.foo1()执行返回结果function () {
        console.log(this.name)
      }.call(person2) =>this指向person2
      
-4,`person1.obj.foo2()()` =>箭头函数已经确定this是obj
-5, `person1.obj.foo2.call(person2)()` =>foo2函数
-6`person1.obj.foo2().call(person2)`中使用`.call(person2)`想要改变内层箭头函数的`this`指向,但是失败了,所以还是为外层作用域里的`this`,打印出`obj`
function foo() {
  console.log( this.a ); //2
}
var a = 2;
(function(){
  "use strict";
  foo(); //foo的this就是window,这里和严格模式没有影响,如果是this.foo()那就会报错了,在这里面的this是undfined
})();
var name = 222;
var a = {
  name: 111,
  say: function () {
    console.log(this.name);
  },
};
var fun = a.say;
fun();    //fun.call(window)  //222
a.say();  //a.call(a)  //111
var b = {
  name: 333,
  say: function (fun) {
    fun();  //fun.call(window) //222
  },
};
b.say(a.say);
b.say = a.say;
b.say(); //b.say.call(b)  //333