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
上,所以此时a
是window
下的属性。而函数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
都使用了call
或apply
来改变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
分析:
第一个数字2
是foo()
输出的,虽然foo()
函数也返回了一个匿名函数,但是并没有调用。
第二个数字1
是foo.call(obj)
输出的,由于.call()
是紧跟着foo
的,所以改变的是foo()
内this
的指向,并且.call()
是会使函数立即执行的,因此打印出1
,同理,它也没有调用返回的函数。
第三个数字2
是foo().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: obj
和inner: window
。
obj.foo.(obj2)()
打印出foo: obj2
和inner: window
。
obj.foo().call(obj2)
打印出foo: obj
和inner: 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)
}
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.name
为obj1
。 - 使用箭头函数的
obj2.foo()
的外层作用域是window
,所以this.name
为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()()
//obj1
//window
//obj2
//obj2
//window
//window
//window
//window
分析:
obj1.foo()()
两层都是普通函数,类似于题目4.6
,分别打印出obj1
和window
。
obj2.foo()()
外层为普通函数,内层为箭头,类似于题目7.1
,都是打印出obj2
。
obj3.foo()()
外层为箭头函数,内层为普通函数,箭头函数的this
由外层作用域决定,因此为window
,内层普通函数由调用者决定,调用它的是window
,因此也为window
。
obj4.foo()()
两层都是箭头函数,第一个箭头函数的this
由外层作用域决定,因此为window
,第二个箭头函数的this
也由外层作用域决定,它的外层作用域是第一个箭头函数,而第一个箭头函数的this
是window
,因此内层的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()()
两层都是普通函数,这个不再重复说了,打印出person1
和window
。(类似题目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"
以下代码的this
为undefined
,也就是这里的立即执行函数中的this
是undefined
。
但是调用foo()
函数的依然是window
,所以foo()
中的this
依旧是window
,所以会打印出2
。
如果使用this.foo()
调用的话,就会报错了,因为现在立即执行函数中的this
是undefined
。
或者将"use strict"
放到foo()
函数里面,也会报错。
手写实现:
(几道手写题,暂时没自己想,参考代码建议直接去看呆呆大佬原文获取,此处等自己写出来了再补上)
38.new
39.call
40.apply
41.bind
参考文章:
《木易杨前端进阶-深度解析bind原理、使用场景及模拟实现》
《sunshine小小倩-this、apply、call、bind》