牛客最新前端笔试题解析 this指向题目解析及扩展 #yyds干货盘点#
Posted 战场小包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客最新前端笔试题解析 this指向题目解析及扩展 #yyds干货盘点#相关的知识,希望对你有一定的参考价值。
前言
这篇文章主要来解析牛客笔试题部分的 this
指向题目。首先我们先从宏观上了解一下 javascript
中得 this
指向问题。
JavaScript
中 this
共有四种绑定(包括隐式绑定丢失)加 ES6
新增得箭头函数绑定,如果把这几种绑定全学会了,this
指向完全不成问题。 this
指向更详细的讲解,可以去看前面写过的一篇博文,里面还有 38
道题目,详细完整的讲解了 this
指向问题。
- 默认绑定: 非严格模式下
this
指向全局对象,严格模式下this
会绑定为undefined
- 隐式绑定: 满足
XXX.fn()
格式,fn
的this
指向XXX
。如果存在链式调用,this
永远指向最后调用它的那个对象 - 隐式绑定丢失:起函数别名,通过别名运行;函数作为参数会造成隐式绑定丢失。
- 显式绑定: 通过
call/apply/bind
修改this
指向 - new绑定: 通过
new
来调用构造函数,会生成一个新对象,并且把这个新对象绑定为调用函数的this
。 - 箭头函数绑定: 箭头函数没有
this
,它的this
是通过作用域链查到外层作用域的this
,且指向函数定义时的this
而非执行时
题目一. 隐式绑定与隐式绑定丢失
var x = 1;
var obj =
x: 3,
fun:function ()
var x = 5;
return this.x;
;
var fun = obj.fun;
console.log(obj.fun(), fun());
解析
JavaScript
对于引用类型,其地址指针存放在栈内存中,真正的本体是存放在堆内存中的。fun = obj.fun
相当于将 obj.fun
指向得堆内存指针赋值给了 fun
,此后 fun
执行与 obj
不会有任何关系,发生隐式绑定丢失。
obj.fun()
: 隐式绑定,fun
里面的this
指向obj
,打印3
fun()
: 隐式绑定丢失:fun
默认绑定,非严格模式下,this
指向window
,打印1
答案
3 1
题目二: 隐式绑定丢失
var person =
age: 18,
getAge: function()
return this.age;
;
var getAge = person.getAge
console.log(getAge())
简单的隐式绑定丢失问题
答案
undefined
题目三: 隐式绑定丢失
var obj =
name:"zhangsan",
sayName:function()
console.log(this.name);
var wfunc = obj.sayName;
obj.sayName();
wfunc();
var name = "lisi";
obj.sayName();
wfunc();
简单的隐式绑定问题,不多做赘述了。
答案
zhangsan
undefined
zhangsan
lisi
题目四:new绑定
var a = 5;
function test()
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
new test();
解析
使用new
来构建函数,会执行如下四部操作:
- 创建一个空的简单
JavaScript
对象(即);
- 为步骤1新创建的对象添加属性
__proto__
,将该属性链接至构造函数的原型对象 ; - 将步骤1新创建的对象作为
this
的上下文 ; - 如果该函数没有返回对象,则返回
this
。
通过 new
来调用构造函数,会生成一个新对象,并且把这个新对象绑定为调用函数的 this
。
console.log(a)
: 打印变量a
的值,当前testAO
中存在a
变量,打印0
console.log(this.a)
:new
绑定this
指向新的实例对象,当前题目没有给实例对象添加a
属性,打印undefined
console.log(a)
: 同第一个,打印0
答案
0
undefined
0
题目五:箭头函数与显式绑定
function fun ()
return () =>
return () =>
return () =>
console.log(this.name)
var f = fun.call(name: foo)
var t1 = f.call(name: bar)()()
var t2 = f().call(name: baz)()
var t3 = f()().call(name: qux)
-
箭头函数没有
this
,它的this
是通过作用域链查到外层作用域的this
,且指向函数定义时的this
而非执行时。 -
箭头函数,不能通过
call\\apply\\bind
来修改this
指向,但可以通过修改外层作用域的this
来达成间接修改。 JavaScript
是静态作用域,即函数的作用域在函数定义的时候就决定了,而箭头函数的this
是通过作用域链查到的,因此箭头函数定义后,它的作用域链就定死了。
f = fun.call(name: foo)
: 将fun
函数的this
指向name: foo
,并返回一个箭头函数,因此箭头函数的this
也指向name: foo
t1 = f.call(name: bar)()()
: 对第一层箭头函数执行call
操作,无效,当前this
仍指向name: foo
,第二层、第三层都是箭头函数,第三层的this
也指向name: foo
,打印foo
- 后续
t2 t3
分别对第二层、第三层箭头函数使用call
,无效,最终都打印foo
。
答案
foo
foo
foo
题目六:箭头函数
let obj1 =
a: 1,
foo: () =>
console.log(this.a)
// log1
console.log(obj1.foo())
const obj2 = obj1.foo
// log2
console.log(obj2())
解析
obj1.foo
为箭头函数,obj1
为对象,无法提供外层作用域,因此obj.foo
里面的this
指向window
obj1.foo()
: 箭头函数,this
指向window
,打印undefined
obj2
隐式绑定丢失: 打印undefined
答案
undefined
undefined
题目七: 综合题(推荐看)
var name = global;
var obj =
name: local,
foo: function()
this.name = foo;
console.log(this.name);
.bind(window)
;
var bar = new obj.foo();
setTimeout(function()
console.log(window.name);
, 0);
console.log(bar.name);
var bar3 = bar2 = bar;
bar2.name = foo2;
console.log(bar3.name);
解析
这个题的整体出题质量还是挺高的,首先咱们来把涉及到的知识罗列一下:
bind
是显式绑定,会修改this
指向,但bind()
函数不会立即执行函数,会返回一个新函数setTimeout
是异步任务,同步任务执行完毕后才会执行异步任务- 绑定优先级: new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
解析
obj.foo
将它的this
通过bind
显式的绑定为window
,但bind
不会立即执行var bar = new obj.foo()
:new
绑定优先级大于bind
,因此bind
失效了,此时this
指向new
实例,因此obj.foo
内部的console
打印foo
bar
是new obj.foo()
的实例,console.log(bar.name)
打印foo
setTimeout
异步任务,等到同步执行完毕再来调用它的回调bar3 = bar2 = bar
,将bar2,bar3,bar
的地址都指向bar
所指向的空间。bar2.name = foo2
,修改地址指向堆内存的值console.log(bar3.name)
: 由于三个变量指向同一块地址,bar3
修改了name
,bar3
也随之改变,打印foo2
setTimeout
的回调执行,打印global
答案
foo
foo
foo2
global
题目八: 综合题目七修改
题目六虽然出的很有质量,但是我感觉有几个地方考察的让人不满足,咱们来改一下题目。
改法一: 去除 new 绑定
var name = global;
var obj =
name: local,
foo: function()
this.name = foo;
console.log(this.name);
.bind(window)
;
obj.foo();
setTimeout(function()
console.log(this.name);
, 0);
console.log(name);
obj.foo
没有做修改,它的this
通过bind
显式的绑定为window
,但bind
不会立即执行obj.foo()
执行,显式绑定优先级大于隐式绑定,因此此时foo
函数this -> window
this.name
相当于window.name
,修改了全局的name
变量值,打印foo
setTimeout
回调等同步代码执行完毕- 全局的
name
已经修改为foo
,打印foo
- 执行
setTimeout
的回调,默认绑定,打印foo
答案
foo
foo
foo
改法一: 修改 bind 为 call
var name = global;
var obj =
name: local,
foo: function()
this.name = foo;
console.log(this.name);
;
obj.foo.call(window);
var bar = new obj.foo();
setTimeout(function()
console.log(window.name);
, 0);
console.log(bar.name);
var bar3 = bar2 = bar;
bar2.name = foo2;
console.log(bar3.name);
call
与 bind
的区别就在于 call
函数会立即执行
因此 obj.foo.call(window)
会立即执行函数,同时也修改了全局的 name
值。其他部分与上面所讲类似,不多做赘述了。
答案
foo
foo
foo
foo2
foo
以上是关于牛客最新前端笔试题解析 this指向题目解析及扩展 #yyds干货盘点#的主要内容,如果未能解决你的问题,请参考以下文章