this面试题

this面试题

最近想刷点this的面试题,这不,呆呆大佬的文章让我激动不已,对症下药,获益良多,此处做个记录。
知识参考:《你不知道的javascript上卷》
原文链接:呆呆大佬的酸爽this面试题
知识点预备:

this指向

this总是指向调用的对象,就是说this指向谁与函数声明的位置没有关系,只与调用的位置有关。this的指向大概分为如下四种:

1.new绑定

new方式是优先级最高的一种调用方式,只要是使用new方式来调用一个构造函数,this一定会指向new调用函数新创建的对象:

2.显式绑定

显示绑定指的是通过call()和apply()方法,强制指定某些对象对函数进行调用,this则强制指向调用函数的对象

3.隐式绑定

隐式绑定是指通过为对象添加属性,该属性的值即为要调用的函数,进而使用该对象调用函数

4.默认绑定

默认绑定是指当上面这三条绑定规则都不符合时,默认绑定会把this指向全局对象window

隐式丢失

当进行隐式绑定时,如果进行一次引用赋值或者传参操作,会造成this的丢失,使this绑定到全局对象中去。

引用赋值丢失

原理:因为newData实际上引用的是foo函数本身,这就相当于:var newData = thisTo;data对象只是一个中间桥梁,data.foo只起到传递函数的作用,所以newData跟data对象没有任何关系。而newData本身又不带a属性,最后a只能指向window。

传参丢失

所谓传参丢失,就是在将包含this的函数作为参数在函数中传递时,this指向改变。setTimeout函数的本来写法应该是setTimeout(function(){…},100);100ms后执行的函数都在“…”中,可以将要执行函数定义成var fun = function(){…},即:setTimeout(fun,100),100ms后就有:fun();所以此时此刻是data.foo作为一个参数,是这样的:setTimeout(thisTo,100);100ms过后执行thisTo(),没有调用thisTo的对象,this只能指向window。

隐式丢失解决方法

为了解决**隐式丢失(隐式丢失专用)**的问题,ES5专门提供了bind方法,bind()会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。(这个bind可跟$(selector).bind(‘click’,function(){…})的用法不同)

间接引用

间接引用是指一个定义对象的方法引用另一个对象存在的方法,这种情况下会使得this指向window

箭头函数中this

ES6的箭头函数在this这块是一个特殊的改进,箭头函数使用了词法作用域取代了传统的this机制,所以箭头函数无法使用上面所说的这些this优先级的原则,注意的是在箭头函数中,是根据外层父亲作用域来决定this的指向问题。

判断this:

函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。

var bar = new foo()

函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。 var bar = foo.call(obj2)

函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,th-is 绑定的是那个上下文对象。

var bar = obj1.foo()

如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。

ok,话不多说,题来!

var a = 10;
function foo () {
  console.log(this.a)
}
foo();

//10

分析:

​ 使用var创建变量的时候(不在函数里),会把创建的变量绑定到window上,所以此时awindow下的属性。而函数foo也是window下的属性。调用foo()函数的是window对象,且又是在非严格模式下,所以foo()this的指向是window对象,因此this.a会输出10

"use strict";
var a = 10;
function foo () {
  console.log('this1', this)
  console.log(window.a)
  console.log(this.a)
}
console.log(window.foo)
console.log('this2', this)
foo();

//foo(){...}
//this2 window{...}
//this1 undefined
//10
//Uncaught TypeError: Cannot read property 'a' of undefined

分析:

​ 开启了严格模式,只是说使得函数内的this指向undefined,它并不会改变全局中this的指向。因此this1中打印的是undefined,而this2还是window对象。

let a = 10
const b = 20

function foo () {
  console.log(this.a)
  console.log(this.b)
}
foo();
console.log(window.a)

//undefined
//undeifned
//undefined

分析:

​ let 或者 const声明的变量是不会被绑定到window上的,所以此时会打印出三个undefined

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

foo()

//window{...}
//1

分析:

foo()函数内的this指向的是window,因为是window调用的foo。但是打印出的this.a呢?注意,是this.a,不是a,因此是window下的a。并且由于函数作用域的原因我们知道window下的a还是1

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

foo()

//1

分析:
inner中,this指向的还是window。因此答案是1

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

//1

分析:

​ 函数foo()虽然是定义在window下,但是我在obj对象中引用了它,并将它重新赋值到obj.foo上。且调用它的是obj对象,因此打印出来的this.a应该是obj中的a

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

obj.foo();
foo2();

//1
//2

分析:

​ obj中foo,满足隐式绑定,foo2隐式丢失,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();

//1
//2
//3

分析:

  • obj.foo()中的this指向调用者obj
  • foo2()发生了隐式丢失,调用者是window,使得foo()中的this指向window
  • foo3()发生了隐式丢失,调用者是obj2,使得foo()中的this指向obj2
function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)

//window{...}
//2

分析:

​ 将obj.foo当成参数传递到doFoo函数中,在传递的过程中,obj.foo()函数内的this发生了改变,指向了window

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 }
//2
"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。

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

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

//2
//1
//1
function foo () {
  console.log(this.a)
}
var a = 2
foo.call()
foo.call(null)
foo.call(undefined)

//2
//2
//2

分析:

​ 第一个foo() 就是默认绑定,而第二个和第三个foo都使用了callapply来改变this的指向,并且是立即执行的。第四个foo,仅仅是使用bind创建了一个新的函数,且这个新函数也没用别的变量接收并调用,因此并不会执行。

​ 如果call、apply、bind接收到的第一个参数是空或者null、undefined的话,则会忽略这个参数,指向window。

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()

//2
//window{...}
//3

分析:

​ 对于setTimeout中的函数,这里存在隐式绑定的隐式丢失,因此这时候setTimeout中的函数内的this是指向window的。

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)
  },
    foo3: function () {
    setTimeout(function () {
      console.log(this)
      console.log(this.a)
    }, 0)
  }
}
var a = 3;
obj2.foo1();
obj2.foo2();
obj2.foo3.call(obj1);

//2
//{a:1}
//1
//window{...}
//3

分析:

​ 使用call、apply 或者bind来改变函数中this的指向。

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

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

foo()
foo.call(obj)
foo().call(obj)

//2
//1
//2
//Uncaught TypeError: Cannot read property 'call' of undefined

分析:

​ foo()会正常打印出window下的a,也就是2

​ foo.call(obj)由于显式绑定了this,所以会打印出obj下的a,也就是1

foo().call(obj)开始会执行foo()函数,打印出2,但是会对foo()函数的返回值执行.call(obj)操作,foo()函数的返回值是undefined,因此就会报错了

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

分析:

​ 第一个数字2foo()输出的,虽然foo()函数也返回了一个匿名函数,但是并没有调用。

​ 第二个数字1foo.call(obj)输出的,由于.call()是紧跟着foo的,所以改变的是foo()this的指向,并且.call()是会使函数立即执行的,因此打印出1,同理,它也没有调用返回的函数。

​ 第三个数字2foo().call(obj)先执行foo()时打印出来的,此时foo()this还是指向window

​ 在执行完foo()之后,会返回一个匿名函数,并且后面使用了.call(obj)来改变这个匿名函数的this指向并调用了它,所以输出了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()函数内的this虽然指定了是为obj,但是调用最后调用匿名函数的却是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
//inner:window
//foo:obj2
//inner:window
//foo:obj
//inner:obj2

分析:

obj.foo()自然是打印出foo: objinner: window

obj.foo.(obj2)()打印出foo: obj2inner: window

obj.foo().call(obj2)打印出foo: objinner: 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.a3,并传入最后一个参数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)
}

foo2()
foo2.call(window)

//2
//2

分析:

​ 这里foo2函数内部的函数foo1我们使用call来显式绑定obj,就算后面再用call来绑定window也没有用了。

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 + 3
//5
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"

分析:

forEach、map、filter函数的第二个参数也是能显式绑定this

function Person (name) {
  this.name = name
}
var name = 'window'
var person1 = new Person('LinDaiDai')
console.log(person1.name)

//'LinDaiDai'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  }
  this.foo2 = function () {
    return function () {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
person1.foo1()
person1.foo2()()

//'person1'
//''

分析:

​ 第一个this.name打印的肯定是person1对象中的name,也就是构造person1对象时传递进去的person1字符串。

​ 第二个this.name打印的应该就是window下的name了,但是这里window对象中并不存在name属性,所以打印出的是空。

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()()

//person1
//window
//person2
//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)

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

//window
//obj
//obj

分析:

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。

​ 对于obj.foo1()函数的调用,它的外层作用域是window,对象obj当然不属于作用域了(我们知道作用域只有全局作用域window和局部作用域函数)。所以会打印出window

obj.foo2()(),首先会执行obj.foo2(),这不是个箭头函数,所以它里面的this是调用它的obj对象,因此打印出obj,而返回的匿名函数是一个箭头函数,它的this由外层作用域决定,那也就是函数foo2咯,那也就是它的this会和foo2函数里的this一样,就也打印出了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()

//obj1
//window

分析:

  • 不使用箭头函数的obj1.foo()是由obj1调用的,所以this.nameobj1
  • 使用箭头函数的obj2.foo()的外层作用域是window,所以this.namewindow
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()()

//obj1
//window
//obj2
//obj2
//window
//window
//window
//window

分析:

obj1.foo()()两层都是普通函数,类似于题目4.6,分别打印出obj1window

obj2.foo()()外层为普通函数,内层为箭头,类似于题目7.1,都是打印出obj2

obj3.foo()()外层为箭头函数,内层为普通函数,箭头函数的this由外层作用域决定,因此为window,内层普通函数由调用者决定,调用它的是window,因此也为window

obj4.foo()()两层都是箭头函数,第一个箭头函数的this由外层作用域决定,因此为window,第二个箭头函数的this也由外层作用域决定,它的外层作用域是第一个箭头函数,而第一个箭头函数的thiswindow,因此内层的this也是window

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

//person1
//person1
//window

分析:

person1.foo1()是个普通函数,this由最后调用它的对象决定,即person1

person1.foo2()为箭头函数,this由外层作用域决定,且指向函数定义时的this而非执行时,在这里它的外层作用域是函数Person,且这个是构造函数,并且使用了new来生成了对象person1,所以此时this的指向是为person1

person2.foo2()字面量创建的的对象person2中的foo2是个箭头函数,由于person2是直接在window下创建的,你可以理解为它所在的作用域就是在window下,因此person2.foo2()内的this应该是window

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

//person1
//window
//person1
//person1
//person1
//window
//person1
//person1

分析:

person1.foo1()()两层都是普通函数,这个不再重复说了,打印出person1window。(类似题目6.2)

person1.foo2()()第一层普通函数,它的this是由最后调用它的对象决定也就是person1,第二层为箭头函数,它的this由外层作用域决定,也就是foo2这个函数,因此也为person1

person1.foo3()()第一层为箭头函数,this由外层作用域决定,因此为person1,第二层为普通函数,由最后调用者决定,因此为window

person1.foo4()()两层都是箭头函数,this由外层作用域决定,所以都是person1

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
//obj1
//obj1
//window
//window
//window
//obj2

分析:

箭头函数的this无法通过bind、call、apply来直接修改,但是可以通过改变作用域中this的指向来间接修改。

obj1.foo1.call(obj2)()第一层为普通函数,并且通过.call改变了this指向为obj2,所以会打印出obj2,第二层为箭头函数,它的this和外层作用域中的this相同,因此也是obj2

obj1.foo().call(obj2)第一层打印出obj1,第二层为箭头函数,使用了.call想要修改this的指向,但是并不能成功,因此.call(obj2)对箭头函数无效,还是打印出obj1

obj1.foo2.call(obj2)()第一层为箭头函数,并且想要通过.call(obj2)改变this指向,但是无效,且它的外层作用域是window,所以会打印出window,第二层为普通函数,this是最后调用者window,所以也会打印出window

obj1.foo2().call(obj2)第一层为箭头函数,外层作用域是window,打印出window,第二层为普通函数,且使用了.call(obj2)来改变this指向,所以打印出了obj2

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

优点

  • 箭头函数写代码拥有更加简洁的语法(当然也有人认为这是缺点)
  • this由外层作用域决定,所以在某些场合我们不需要写类似const that = this这样的代码

避免使用的场景

根据箭头函数的特性,所以我们应该避免在以下四种场景中使用它:

使用箭头函数定义对象的方法

let obj = {
    value: 'LinDaiDai',
    getValue: () => console.log(this.value)
}
obj.getValue() // undefined

定义原型方法

function Foo (value) {
    this.value = value
}
Foo.prototype.getValue = () => console.log(this.value)

const foo1 = new Foo(1)
foo1.getValue() // undefined

构造函数使用箭头函数

const Foo = (value) => {
    this.value = value;
}
const foo1 = new Foo(1)
// 事实上直接就报错了 Uncaught TypeError: Foo is not a constructor
console.log(foo1);

作为事件的回调函数

const button = document.getElementById('myButton');
button.addEventListener('click', () => {
    console.log(this === window); // => true
    this.innerHTML = 'Clicked button';
});
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
var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => 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.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

//person1
//person1

//window
//window
//person2

//person1
//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)

//window
//window
//person2

//obj
//person2
//obj

分析:

person1.obj.foo1()()返回的是一个普通的匿名函数,调用它的是window,所以打印出window

person1.obj.foo1.call(person2)()中是使用.call(person2)改变第一层函数中的this,匿名函数和它没关系,依旧是window调用的,所以打印出window

person1.obj.foo1().call(person2)是通过.call(person2)改变匿名函数内的this,所以绑定有效,因此打印出person2

person1.obj.foo2()()第一层为普通函数,第二层为匿名箭头函数。首先让我们明确匿名箭头函数内的this是由第一层普通函数决定的,所以我们只要知道第一层函数内的this是谁就可以了。而这里,第一层函数是由obj这个对象调用的,所以打印出obj

person1.obj.foo2.call(person2)()中使用.call(person2)改变了第一层函数中的this指向,所以第二层的箭头函数会打印出person2

person1.obj.foo2().call(person2)中使用.call(person2)想要改变内层箭头函数的this指向,但是失败了,所以还是为外层作用域里的this,打印出obj

function foo() {
  console.log( this.a );
}
var a = 2;
(function(){
  "use strict";
  foo();
})();

//2

分析:

​ 使用了"use strict"开启严格模式会使得"use strict"以下代码的thisundefined,也就是这里的立即执行函数中的thisundefined

​ 但是调用foo()函数的依然是window,所以foo()中的this依旧是window,所以会打印出2

​ 如果使用this.foo()调用的话,就会报错了,因为现在立即执行函数中的thisundefined

​ 或者将"use strict"放到foo()函数里面,也会报错。

手写实现:
(几道手写题,暂时没自己想,参考代码建议直接去看呆呆大佬原文获取,此处等自己写出来了再补上)
38.new

39.call

40.apply

41.bind

参考文章:

《木易杨前端进阶-深度解析bind原理、使用场景及模拟实现》

《sunshine小小倩-this、apply、call、bind》

《木易杨前端进阶-深度解析bind原理、使用场景及模拟实现》

《js中的new做了什么?》

《JavaScript进阶-执行上下文(理解执行上下文一篇就够了)》

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值