this详解,2024前端不死我不倒

本文详细解释了JavaScript中构造函数的返回值如何影响this指向,介绍了闭包的概念以及如何在对象方法和原型方法中正确处理this。此外,还对比了bind,call和apply方法在改变函数this值的作用。
摘要由CSDN通过智能技术生成

return {

name: “Lucy”,

};

}

var p1 = new Person(“Bob”, 10);

console.log(p1.name); //“Lucy”

console.log(p1.age); //undefined

很明显,这是对象p1中的this指向返回值对象

当然,构造函数还可以返回函数:

function Fun(x){

console.log(this);

return function(){

this.x = x;

this.get = function(){

alert(this.x);

}

}

}

var o1 = new Fun(2); //Fun {}

var o2 = Fun(2); //window

console.log(o1 == o2); //false, 这里的o1,o2形式是一样的,由于构成闭包结构,所以应用不同

但如果构造函数返回了一个基本类型:

function Fun(n){

this.name = n;

return 2;

}

var o;

console.log(o = new Fun(“Bob”)); // {name: “Bob”}

此时得到的对象和返回值无关。

到此我们就明白了,构造函数的返回值如果是基本数据类型,那返回值和得到的对象无关;否则,得到的对象就是返回值的引用并构成闭包。

区分一下面这个具体问题:

Click Here

Click Here

第一个按钮得到Window,而第二个得到input元素!为什么!

再想想,click函数定义在全局,不在对象上。而btn.onclick = function(){}中的函数明显是在btn对象上定义的。

对象方法中的闭包

说闭包前先理解一个简单的:

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

function fun(){

console.log(this);

}

fun();

}

};

o.speak(); //Window

什么,这里是Window了?对!我们仔细想想,这个fun函数是对象的方法吗?显然不是,它是个一般函数。它仅仅是在另一个函数中的一个函数,显然符合上文描述的:“凡是没有定义在对象、构造函数或prototype中的函数,其中的this都是Window”

如果想在内部函数访问这个对象,也很好解决:

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

var _this = this; //首选_this,有的资料上会用self。

function fun(_this){

console.log(_this);

}

fun();

}

};

o.speak(); //Object {name: “Lily”, age: 18, gender: “F”}

下面做个闭包,为了说明this的值,这里不定义太多变量,如果对闭包和作用域有疑惑可以参看博主的另一篇文章:作用域链与闭包

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

return function(){

console.log(this);

}

}

};

o.speak()(); //Window

这个难理解吗?返回的函数依然是个定义在别的函数里面的一般函数。如果想让返回的函数可以继续访问该对象,依然使用上面的 varthis=this v a r t h i s = t h i s var _this = this解决。不过这里引出了一个新的问题:

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

console.log(this);

}

};

var fun = o.speak;

fun(); //Window

什么?这里还是Window!o.speak明显是一个对象方法啊!那么问题来了?第10行调用的是谁?是fun函数。那么fun函数怎么定义的?对,fun的定义决定它是一个一般函数。那怎么解决?这个不用解决,没人会试图在对象外获取对象方法,即便是有需要也应该获取对象方法内的闭包。当然,如果你要强行解决它,那就用bind方法吧。

原型中的this

什么?原型方法中的this? 看看下面代码就明白了,这个理解起来不会很难

function F(){

return F.prototype.init();

}

F.prototype = {

init: function(){

return this;

},

test: “test”

}

var f = F();

console.log(f); //F{test:test}

可见,原型中方法里的this.就是一个该构造函数的实例化对象。jQuery中使用的就是这个构造方法。

bind call和apply方法

这3个方法用来改变调用函数内的this值

bind方法

将对象绑定到函数,返回内部this值为绑定对象的函数。

如果我们不能修改库中对象的方法,我们就不能用 var_this=this; v a r _ t h i s = t h i s ; var \_this = this;的方法改变this值,那么我们换个角度考虑上面的问题:

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

return function(){

console.log(this);

}

}

};

o.speak()(); //Window

最后一行中,o.speak()执行完后得到一个函数,这是个临时函数,定义在全局作用域,如果我们把这个临时函数绑定到o对象上,再继续调用这个函数不就可以了么:

var o = {

name: “Lily”,

age: 18,

gender: “F”,

speak: function (){

return function(){

console.log(this);

}

}

};

o.speak().bind(o)(); //Object {name: “Lily”, age: 18, gender: “F”}

bind不只可以传入一个参数,后面的多个参数可以作为返回函数的绑定参数,如下:

function add(a, b){

console.log(a+b);

return a+b;

}

var add2 = add.bind(null, 2); //参数顺序保持一致,第一参为null,不改变this值(但这里会改变,因为add2在全局中定义)

add2(4); //6

可如果是构造函数呢?记住一点,函数作为构造函数调用时,bind的第一参数无效,注意,仅仅是第一参数无效。

function Person(pname, page){

this.name = pname;

this.age = page;

}

var Person2 = Person.bind({name:“hello”,city:“Beijing”}, “world”);

var p = new Person2(12);

console.log§;//Person{name:“world”, age:12}

call方法 和 apply方法

这里举几个和上文不一样的例子

function Animal(){

this.name = “Animal”;

}

Animal.prototype.showName = function(){

alert(this.name);

};

function Cat(){

this.name = “cat”;

}

var cat = new Cat();

这里Cat没有showName方法,怎么实现输出名字呢?

有c++和java经验的人会认为猫属于动物,所以Cat应该继承Animal,所以我们可以这样修改:

function Animal(){

this.name = “Animal”;

}

Animal.prototype.showName = function(){

alert(this.name);

};

function Cat(){

this.name = “cat”;

}

Cat.prototype = Animal.prototype;

var cat = new Cat();

cat.showName(); //Cat

或者:

function Animal(){

this.name = “Animal”;

}

Animal.prototype.showName = function(){

alert(this.name);

};

function Cat(){

Animal.call(this, “cat”); //继承

}

var cat = new Cat();

cat.showName(); //Cat

有c++和java经验就会知道,在做一个大型项目之前都是要做UML设计的,用例图、活动图、类图、状态图等等十几种图,对于没有一定经验的开发者做这个简直就是噩梦,而js把各种类或模块独立出来,需要的时候用call、bind、apply把多个类联系起来,这样的做法即简化了设计,又简化了维护。

所以js里面很少有上面的写法,怎么写看下面:

function Animal(){

this.name = “Animal”;

}

Animal.prototype.showName = function(){

alert(this.name);

}

function Cat(){

this.name = “Cat”;

}

var cat = new Cat();

Animal.prototype.showName.call(cat); //cat

Animal.prototype.showName.apply(cat); //cat

对,不过感觉那里怪怪的,call和apply一样?他们功能上一样,只是接受的参数不同,简单写就是下面这样:

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-yZ84AtjN-1711008439995)]
[外链图片转存中…(img-XNdx9QtC-1711008439996)]
[外链图片转存中…(img-t8PYDIKY-1711008439996)]
[外链图片转存中…(img-4oi9DwFf-1711008439997)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-tALH0SCk-1711008439997)]

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值